aboutsummaryrefslogtreecommitdiff
path: root/worker
diff options
context:
space:
mode:
Diffstat (limited to 'worker')
-rw-r--r--worker/Dockerfile14
-rw-r--r--worker/package.json3
-rwxr-xr-xworker/scripts/ansible_playbook.ts8
-rwxr-xr-xworker/scripts/build_docker_image.ts4
-rwxr-xr-xworker/scripts/checkout_ci.ts2
-rw-r--r--worker/scripts/npm_publish.ts8
-rw-r--r--worker/secret.ts146
7 files changed, 161 insertions, 24 deletions
diff --git a/worker/Dockerfile b/worker/Dockerfile
index 0dfd816..c93fa2c 100644
--- a/worker/Dockerfile
+++ b/worker/Dockerfile
@@ -1,18 +1,9 @@
# -- <worker_dependencies> --
-FROM debian:stable-slim AS worker_dependencies
-
-RUN apt-get update && apt-get install -yqq unzip curl
-
-ARG BITWARDEN_VERSION=2025.4.0
-RUN curl -L -o /bw-linux.zip "https://github.com/bitwarden/clients/releases/download/cli-v${BITWARDEN_VERSION}/bw-linux-${BITWARDEN_VERSION}.zip"
-RUN unzip /bw-linux.zip -d / \
- && chmod +x /bw
-
-COPY --from=docker:dind /usr/local/bin/docker /usr/local/bin/
+FROM docker:dind AS worker_dependencies
# -- </worker_dependencies> --
# -- <ci_worker> --
-FROM oci.liz.coffee/emprespresso/ci_base:release AS worker
+FROM img.liz.coffee/emprespresso/ci_base:release AS worker
RUN apt-get update && apt-get install -yqq git jq
@@ -33,7 +24,6 @@ ARG DOCKER_GID="995" # but it may be overridden via this `DOCKER_GID` build arg.
RUN groupadd -g ${DOCKER_GID} docker
RUN usermod -a -d /var/lib/laminar -G docker node
-COPY --from=worker_dependencies /bw /usr/local/bin/
COPY --from=worker_dependencies /usr/local/bin/docker /usr/local/bin/
USER node
diff --git a/worker/package.json b/worker/package.json
index b912750..f8eae65 100644
--- a/worker/package.json
+++ b/worker/package.json
@@ -18,7 +18,8 @@
},
"dependencies": {
"@emprespresso/ci_model": "*",
- "@emprespresso/pengueno": "^0.0.13"
+ "@emprespresso/pengueno": "^0.0.13",
+ "@infisical/sdk": "^4.0.6"
},
"devDependencies": {
"copyfiles": "2.4.1"
diff --git a/worker/scripts/ansible_playbook.ts b/worker/scripts/ansible_playbook.ts
index f7315ab..5c101ba 100755
--- a/worker/scripts/ansible_playbook.ts
+++ b/worker/scripts/ansible_playbook.ts
@@ -12,7 +12,7 @@ import {
TraceUtil,
} from '@emprespresso/pengueno';
import type { AnsiblePlaybookJob } from '@emprespresso/ci_model';
-import { Bitwarden, BitwardenKey, getPathOnHost, type SecureNote } from '@emprespresso/ci_worker';
+import { Infisical, InfisicalKey, getPathOnHost, type SecureNote } from '@emprespresso/ci_worker';
import { writeFile, mkdir } from 'fs/promises';
import { join } from 'path';
import { rmSync } from 'fs';
@@ -25,7 +25,7 @@ const eitherJob = getRequiredEnvVars(['path', 'playbooks']).mapRight(
},
);
-const eitherVault = Bitwarden.getConfigFromEnvironment().mapRight((config) => new Bitwarden(config));
+const eitherVault = Infisical.getConfigFromEnvironment().mapRight((config) => new Infisical(config));
const playbookMetric = Metric.fromName('ansiblePlaybook.playbook');
const _logJob = LogTraceable.of(eitherJob).flatMap(TraceUtil.withTrace('ansible_playbook'));
@@ -49,8 +49,8 @@ await LogMetricTraceable.ofLogTraceable(_logJob)
Error,
{
secretFiles: { ssh_key: string; ansible_secrets: string };
- key: BitwardenKey;
- vault: Bitwarden;
+ key: InfisicalKey;
+ vault: Infisical;
job: AnsiblePlaybookJob;
}
>
diff --git a/worker/scripts/build_docker_image.ts b/worker/scripts/build_docker_image.ts
index 759dfc1..527120a 100755
--- a/worker/scripts/build_docker_image.ts
+++ b/worker/scripts/build_docker_image.ts
@@ -10,7 +10,7 @@ import {
Command,
} from '@emprespresso/pengueno';
import type { BuildDockerImageJob, BuildDockerImageJobProps } from '@emprespresso/ci_model';
-import { Bitwarden, type LoginItem } from '@emprespresso/ci_worker';
+import { Infisical, type LoginItem } from '@emprespresso/ci_worker';
import path from 'path';
const job = getRequiredEnvVars([
@@ -36,7 +36,7 @@ const job = getRequiredEnvVars([
(x) => x,
);
-const eitherVault = Bitwarden.getConfigFromEnvironment().mapRight((config) => new Bitwarden(config));
+const eitherVault = Infisical.getConfigFromEnvironment().mapRight((config) => new Infisical(config));
const buildImageMetric = Metric.fromName('dockerImage.build').asResult();
const loginMetric = Metric.fromName('dockerRegistry.login').asResult();
diff --git a/worker/scripts/checkout_ci.ts b/worker/scripts/checkout_ci.ts
index c4006e6..009d3fa 100755
--- a/worker/scripts/checkout_ci.ts
+++ b/worker/scripts/checkout_ci.ts
@@ -26,7 +26,7 @@ function isCiWorkflow(t: unknown): t is CiWorkflow {
}
const CI_WORKFLOW_FILE = '.ci/ci.json';
-const OCI_REGISTRY = 'oci.liz.coffee';
+const OCI_REGISTRY = 'img.liz.coffee';
const PIPELINE_IMAGE = OCI_REGISTRY + '/emprespresso/ci_worker:release';
const READONLY_CREDENTIALS = { username: 'readonly', password: 'readonly' };
diff --git a/worker/scripts/npm_publish.ts b/worker/scripts/npm_publish.ts
index 9324856..bcb2796 100644
--- a/worker/scripts/npm_publish.ts
+++ b/worker/scripts/npm_publish.ts
@@ -11,7 +11,7 @@ import {
TraceUtil,
getStdoutMany,
} from '@emprespresso/pengueno';
-import { Bitwarden, getPathOnHost, type SecureNote } from '@emprespresso/ci_worker';
+import { Infisical, getPathOnHost, type SecureNote } from '@emprespresso/ci_worker';
import { writeFile, mkdir } from 'fs/promises';
import { join } from 'path';
import { rmSync } from 'fs';
@@ -25,11 +25,11 @@ const eitherJob = getRequiredEnvVars(['source', 'registry']).mapRight(
},
);
-const eitherVault = Bitwarden.getConfigFromEnvironment().mapRight((config) => new Bitwarden(config));
+const eitherVault = Infisical.getConfigFromEnvironment().mapRight((config) => new Infisical(config));
const READONLY_CREDENTIALS = { username: 'readonly', password: 'readonly' };
-const REGISTRY = 'oci.liz.coffee';
-const CI_PACKPUB_IMG = 'oci.liz.coffee/emprespresso/ci_packpub_npm:release';
+const REGISTRY = 'img.liz.coffee';
+const CI_PACKPUB_IMG = 'img.liz.coffee/emprespresso/ci_packpub_npm:release';
const packPubMetric = Metric.fromName('npm_publish.packpub');
const _logJob = LogTraceable.of(eitherJob).flatMap(TraceUtil.withTrace('npm_publish'));
diff --git a/worker/secret.ts b/worker/secret.ts
index 34056c2..c375693 100644
--- a/worker/secret.ts
+++ b/worker/secret.ts
@@ -12,6 +12,7 @@ import {
import { randomUUID } from 'node:crypto';
import { mkdirSync } from 'node:fs';
import path from 'node:path';
+import { InfisicalSDK } from '@infisical/sdk';
// -- <ISecret> --
export interface SecretItem {
@@ -155,3 +156,148 @@ export interface BitwardenConfig {
clientId: string;
}
// -- </Vault> --
+
+// -- <Infisical> --
+export type InfisicalKey = {
+ client: InfisicalSDK;
+};
+
+export class Infisical implements IVault<TClient, InfisicalKey, TItemId> {
+ constructor(private readonly config: InfisicalConfig) {}
+
+ public async unlock(client: TClient): Promise<IEither<Error, InfisicalKey>> {
+ return client
+ .move(this.config)
+ .flatMap(TraceUtil.withMetricTrace(Infisical.loginMetric))
+ .peek((tConfig) => tConfig.trace.trace('initializing infisical client (⑅˘꒳˘)'))
+ .map(async (tConfig) => {
+ try {
+ const sdk = new InfisicalSDK({
+ siteUrl: tConfig.get().siteUrl,
+ });
+
+ const authenticatedClient = await sdk.auth().universalAuth.login({
+ clientId: tConfig.get().clientId,
+ clientSecret: tConfig.get().clientSecret,
+ });
+
+ return Either.right<Error, InfisicalKey>({ client: authenticatedClient });
+ } catch (error) {
+ return Either.left<Error, InfisicalKey>(
+ error instanceof Error ? error : new Error(`failed to authenticate: ${error}`),
+ );
+ }
+ })
+ .flatMapAsync(TraceUtil.promiseify(TraceUtil.traceResultingEither(Infisical.loginMetric)))
+ .get();
+ }
+
+ public async fetchSecret<T extends SecretItem>(
+ client: TClient,
+ key: InfisicalKey,
+ item: string,
+ ): Promise<IEither<Error, T>> {
+ return client
+ .move(key)
+ .flatMap(TraceUtil.withMetricTrace(Infisical.fetchSecretMetric))
+ .peek((tSession) => tSession.trace.trace(`looking for your secret ${item} (⑅˘꒳˘)`))
+ .map(async (tSession) => {
+ const eitherSecret = await Either.fromFailableAsync<Error, any>(() =>
+ tSession.get().client.secrets().getSecret({
+ secretName: item,
+ projectId: this.config.projectId,
+ environment: this.config.environment,
+ }),
+ );
+
+ return eitherSecret.flatMapAsync<T>(async (secret) => {
+ if (!secret || !secret.secretValue) {
+ return Either.left<Error, T>(new Error(`couldn't find the secret ${item} (。•́︿•̀。)`));
+ }
+
+ const parsedSecret = await Either.fromFailableAsync<Error, any>(() =>
+ Promise.resolve().then(() => JSON.parse(secret.secretValue)),
+ );
+
+ const secretItem = parsedSecret.fold(
+ () => ({
+ name: secret.secretKey,
+ notes: secret.secretValue,
+ }),
+ (parsed: any) => ({
+ name: secret.secretKey,
+ ...parsed,
+ }),
+ );
+
+ return Either.right<Error, T>(secretItem as unknown as T);
+ });
+ })
+ .flatMapAsync(TraceUtil.promiseify(TraceUtil.traceResultingEither(Infisical.fetchSecretMetric)))
+ .get();
+ }
+
+ public async lock(client: TClient, key: InfisicalKey): Promise<IEither<Error, InfisicalKey>> {
+ return client
+ .move(key)
+ .flatMap(TraceUtil.withMetricTrace(Infisical.lockVaultMetric))
+ .peek((tSession) => tSession.trace.trace('closing infisical client :3'))
+ .map(async (tSession) => {
+ try {
+ // Infisical SDK doesn't require explicit logout, but we can cleanup here if needed
+ return Either.right<Error, InfisicalKey>(tSession.get());
+ } catch (error) {
+ return Either.left<Error, InfisicalKey>(
+ error instanceof Error ? error : new Error(`failed to close client: ${error}`),
+ );
+ }
+ })
+ .peek(TraceUtil.promiseify(TraceUtil.traceResultingEither(Infisical.lockVaultMetric)))
+ .peek(
+ TraceUtil.promiseify((tEitherWithLocked) =>
+ tEitherWithLocked
+ .get()
+ .mapRight(() => tEitherWithLocked.trace.trace('all cleaned up now~ (。•̀ᴗ-)✧')),
+ ),
+ )
+ .map(TraceUtil.promiseify((e) => e.get().mapRight(() => key)))
+ .get();
+ }
+
+ public static getConfigFromEnvironment(): IEither<Error, InfisicalConfig> {
+ return getRequiredEnvVars([
+ 'INFISICAL_CLIENT_ID',
+ 'INFISICAL_CLIENT_SECRET',
+ 'INFISICAL_PROJECT_ID',
+ 'INFISICAL_ENVIRONMENT',
+ 'INFISICAL_SITE_URL',
+ ]).mapRight(
+ ({
+ INFISICAL_CLIENT_ID,
+ INFISICAL_CLIENT_SECRET,
+ INFISICAL_PROJECT_ID,
+ INFISICAL_ENVIRONMENT,
+ INFISICAL_SITE_URL,
+ }) => ({
+ clientId: INFISICAL_CLIENT_ID,
+ clientSecret: INFISICAL_CLIENT_SECRET,
+ projectId: INFISICAL_PROJECT_ID,
+ environment: INFISICAL_ENVIRONMENT,
+ siteUrl: INFISICAL_SITE_URL,
+ }),
+ );
+ }
+
+ private static loginMetric = Metric.fromName('Infisical.login').asResult();
+ private static fetchSecretMetric = Metric.fromName('Infisical.fetchSecret').asResult();
+ private static lockVaultMetric = Metric.fromName('Infisical.lock').asResult();
+}
+
+export interface InfisicalConfig {
+ clientId: string;
+ clientSecret: string;
+ projectId: string;
+ environment: string;
+ siteUrl: string;
+}
+// -- </Infisical> --