From 373d9ec700c0097a22cf665a8e33cf48998d1dc2 Mon Sep 17 00:00:00 2001 From: Elizabeth Date: Mon, 2 Jun 2025 11:14:52 -0700 Subject: Minor things --- worker/Dockerfile | 10 +- worker/deno.json | 2 +- worker/executor.ts | 99 +++++++++++++++++++ worker/executor/job.ts | 42 --------- worker/executor/mod.ts | 2 - worker/executor/pipeline.ts | 58 ------------ worker/jobs/ci_pipeline.run | 8 +- worker/mod.ts | 4 +- worker/scripts/ansible_playbook | 4 +- worker/scripts/build_docker_image | 10 +- worker/secret.ts | 193 ++++++++++++++++++++++++++++++++++++++ worker/secret/bitwarden.ts | 167 --------------------------------- worker/secret/ivault.ts | 24 ----- worker/secret/mod.ts | 2 - 14 files changed, 312 insertions(+), 313 deletions(-) create mode 100644 worker/executor.ts delete mode 100644 worker/executor/job.ts delete mode 100644 worker/executor/mod.ts delete mode 100644 worker/executor/pipeline.ts create mode 100644 worker/secret.ts delete mode 100644 worker/secret/bitwarden.ts delete mode 100644 worker/secret/ivault.ts delete mode 100644 worker/secret/mod.ts (limited to 'worker') diff --git a/worker/Dockerfile b/worker/Dockerfile index d87d67f..24193be 100644 --- a/worker/Dockerfile +++ b/worker/Dockerfile @@ -1,4 +1,4 @@ -# -- -- +# -- -- FROM debian:stable-slim AS worker-dependencies # Define versions as build arguments to improve caching @@ -11,10 +11,10 @@ RUN unzip /bw-linux.zip -d / \ RUN curl -L "https://get.docker.com/builds/$(uname -s)/$(uname -m)/docker-latest.tgz" > /docker.tgz RUN tar -xvzf /docker.tgz -# -- -- +# -- -- -# -- -- -FROM oci.liz.coffee/emprespresso/ci-base:release AS worker +# -- -- +FROM oci.liz.coffee/emprespresso/ci.base:release AS worker RUN apt-get update && apt-get install -yqq git jq RUN groupadd docker @@ -36,4 +36,4 @@ HEALTHCHECK --interval=10s --retries=3 --start-period=3s \ CMD [ "/usr/bin/laminarc show-jobs" ] CMD [ "/usr/sbin/laminard" ] -# -- -- +# -- -- diff --git a/worker/deno.json b/worker/deno.json index 90f50c9..c908330 100644 --- a/worker/deno.json +++ b/worker/deno.json @@ -1,4 +1,4 @@ { - "name": "@emprespresso/ci-worker", + "name": "@emprespresso/ci_worker", "exports": "./mod.ts" } diff --git a/worker/executor.ts b/worker/executor.ts new file mode 100644 index 0000000..faa40a6 --- /dev/null +++ b/worker/executor.ts @@ -0,0 +1,99 @@ +import { + getStdout, + type ITraceable, + LogLevel, + type LogMetricTraceSupplier, + memoize, + Metric, + TraceUtil, + validateExecutionEntries, + Either, + type IEither, +} from "@emprespresso/pengueno"; +import type { Job, JobArgT, Pipeline } from "@emprespresso/ci_model"; + +// -- -- +const jobTypeMetric = memoize((type: string) => Metric.fromName(`run.${type}`)); +export const executeJob = (tJob: ITraceable) => + tJob + .bimap(TraceUtil.withMetricTrace(jobTypeMetric(tJob.get().type))) + .peek((tJob) => + tJob.trace.trace(`let's do this little job ok!! ${tJob.get()}`), + ) + .map((tJob) => + validateExecutionEntries(tJob.get().arguments) + .mapLeft((badEntries) => { + tJob.trace.addTrace(LogLevel.ERROR).trace(badEntries.toString()); + return new Error("invalid job arguments"); + }) + .flatMapAsync((args) => + getStdout(tJob.move(tJob.get().type), { env: args }), + ), + ) + .peek( + TraceUtil.promiseify((q) => + q.trace.trace( + q + .get() + .fold( + (err, _val) => + jobTypeMetric(tJob.get().type)[err ? "failure" : "success"], + ), + ), + ), + ) + .get(); +// -- -- + +// -- -- +const pipelinesMetric = Metric.fromName("pipelines"); +export const executePipeline = ( + tPipeline: ITraceable, + baseEnv?: JobArgT, +): Promise> => + tPipeline + .bimap(TraceUtil.withFunctionTrace(executePipeline)) + .bimap(TraceUtil.withMetricTrace(pipelinesMetric)) + .map(async (tJobs): Promise> => { + for (const [i, serialStage] of tJobs.get().serialJobs.entries()) { + tJobs.trace.trace( + `executing stage ${i}. do your best little stage :>\n${serialStage}`, + ); + const jobResults = await Promise.all( + serialStage.parallelJobs.map((job) => + tJobs + .bimap((_) => [job, `stage ${i}`]) + .map( + (tJob) => + { + ...tJob.get(), + arguments: { + ...baseEnv, + ...tJob.get().arguments, + }, + }, + ) + .map(executeJob) + .peek( + TraceUtil.promiseify((tEitherJobOutput) => + tEitherJobOutput + .get() + .mapRight((stdout) => + tEitherJobOutput.trace.addTrace("STDOUT").trace(stdout), + ), + ), + ) + .get(), + ), + ); + const failures = jobResults.filter((e) => e.fold((err) => !!err)); + if (failures.length > 0) { + tJobs.trace.trace(pipelinesMetric.failure); + return Either.left(new Error(failures.toString())); + } + } + tJobs.trace.trace(pipelinesMetric.success); + return Either.right(undefined); + }) + .get(); +// -- -- diff --git a/worker/executor/job.ts b/worker/executor/job.ts deleted file mode 100644 index ca7feed..0000000 --- a/worker/executor/job.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { - getStdout, - type ITraceable, - LogLevel, - type LogMetricTraceSupplier, - memoize, - Metric, - TraceUtil, - validateExecutionEntries, -} from "@emprespresso/pengueno"; -import type { Job } from "@emprespresso/ci-model"; - -const jobTypeMetric = memoize((type: string) => Metric.fromName(`run.${type}`)); -export const executeJob = (tJob: ITraceable) => - tJob - .bimap(TraceUtil.withMetricTrace(jobTypeMetric(tJob.get().type))) - .peek((tJob) => - tJob.trace.trace(`let's do this little job ok!! ${tJob.get()}`), - ) - .map((tJob) => - validateExecutionEntries(tJob.get().arguments) - .mapLeft((badEntries) => { - tJob.trace.addTrace(LogLevel.ERROR).trace(badEntries.toString()); - return new Error("invalid job arguments"); - }) - .flatMapAsync((args) => - getStdout(tJob.move(tJob.get().type), { env: args }), - ), - ) - .peek( - TraceUtil.promiseify((q) => - q.trace.trace( - q - .get() - .fold( - (err, _val) => - jobTypeMetric(tJob.get().type)[err ? "failure" : "success"], - ), - ), - ), - ) - .get(); diff --git a/worker/executor/mod.ts b/worker/executor/mod.ts deleted file mode 100644 index 944ab7d..0000000 --- a/worker/executor/mod.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from "./job.ts"; -export * from "./pipeline.ts"; diff --git a/worker/executor/pipeline.ts b/worker/executor/pipeline.ts deleted file mode 100644 index c8423b1..0000000 --- a/worker/executor/pipeline.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { - Either, - type IEither, - type ITraceable, - type LogMetricTraceSupplier, - Metric, - TraceUtil, -} from "@emprespresso/pengueno"; -import type { Job, JobArgT, Pipeline } from "@emprespresso/ci-model"; -import { executeJob } from "./mod.ts"; - -const pipelinesMetric = Metric.fromName("pipelines"); -export const executePipeline = ( - tPipeline: ITraceable, - baseEnv?: JobArgT, -): Promise> => - tPipeline - .bimap(TraceUtil.withFunctionTrace(executePipeline)) - .bimap(TraceUtil.withMetricTrace(pipelinesMetric)) - .map(async (tJobs): Promise> => { - for (const [i, serialStage] of tJobs.get().serialJobs.entries()) { - tJobs.trace.trace( - `executing stage ${i}. do your best little stage :>\n${serialStage}`, - ); - const jobResults = await Promise.all( - serialStage.parallelJobs.map((job) => - tJobs - .bimap((_) => [job, `stage ${i}`]) - .map( - (tJob) => - { - ...tJob.get(), - arguments: { ...baseEnv, ...tJob.get().arguments }, - }, - ) - .map(executeJob) - .peek( - TraceUtil.promiseify((tEitherJobOutput) => - tEitherJobOutput - .get() - .mapRight((stdout) => - tEitherJobOutput.trace.addTrace("STDOUT").trace(stdout), - ), - ), - ) - .get(), - ), - ); - const failures = jobResults.filter((e) => e.fold((err) => !!err)); - if (failures.length > 0) { - tJobs.trace.trace(pipelinesMetric.failure); - return Either.left(new Error(failures.toString())); - } - } - tJobs.trace.trace(pipelinesMetric.success); - return Either.right(undefined); - }) - .get(); diff --git a/worker/jobs/ci_pipeline.run b/worker/jobs/ci_pipeline.run index 337bd53..434850c 100644 --- a/worker/jobs/ci_pipeline.run +++ b/worker/jobs/ci_pipeline.run @@ -1,3 +1,5 @@ +#!/usr/bin/env -S deno run --allow-all + import { type Command, Either, @@ -13,8 +15,8 @@ import { type CheckoutCiJob, type FetchCodeJob, PipelineImpl, -} from "@emprespresso/ci-model"; -import { executeJob, executePipeline } from "@emprespresso/ci-worker"; +} from "@emprespresso/ci_model"; +import { executeJob, executePipeline } from "@emprespresso/ci_worker"; const run = Date.now().toString(); const eitherJob = getRequiredEnvVars(["remote", "refname", "rev"]) @@ -122,7 +124,7 @@ await LogMetricTraceable.from(eitherJob).bimap(TraceUtil.withTrace(trace)) .get() .then((e) => e.flatMap(() => eitherJob).fold((err, val) => { - if (err) throw err; + if (!val || err) throw err; return Deno.remove(getWorkingDirectoryForCiJob(val), { recursive: true }); }) ); diff --git a/worker/mod.ts b/worker/mod.ts index affcb2c..97980a8 100644 --- a/worker/mod.ts +++ b/worker/mod.ts @@ -1,2 +1,2 @@ -export * from "./secret/mod.ts"; -export * from "./executor/mod.ts"; +export * from "./secret.ts"; +export * from "./executor.ts"; diff --git a/worker/scripts/ansible_playbook b/worker/scripts/ansible_playbook index e9e967c..f2cd4b9 100755 --- a/worker/scripts/ansible_playbook +++ b/worker/scripts/ansible_playbook @@ -10,8 +10,8 @@ import { prependWith, TraceUtil, } from "@emprespresso/pengueno"; -import type { AnsiblePlaybookJob } from "@emprespresso/ci-model"; -import { Bitwarden, type SecureNote } from "@emprespresso/ci-worker"; +import type { AnsiblePlaybookJob } from "@emprespresso/ci_model"; +import { Bitwarden, type SecureNote } from "@emprespresso/ci_worker"; const eitherJob = getRequiredEnvVars([ "path", diff --git a/worker/scripts/build_docker_image b/worker/scripts/build_docker_image index 1dd0c3d..dc0e961 100755 --- a/worker/scripts/build_docker_image +++ b/worker/scripts/build_docker_image @@ -11,8 +11,8 @@ import { import type { BuildDockerImageJob, BuildDockerImageJobProps, -} from "@emprespresso/ci-model"; -import { Bitwarden, type LoginItem } from "@emprespresso/ci-worker"; +} from "@emprespresso/ci_model"; +import { Bitwarden, type LoginItem } from "@emprespresso/ci_worker"; const eitherJob = getRequiredEnvVars([ "registry", @@ -38,7 +38,7 @@ await LogMetricTraceable.from(eitherJob) .bimap( (tEitherJob) => { const trace = "build_docker_image." + - tEitherJob.get().fold((_, v) => v?.buildTarget ?? ""); + tEitherJob.get().fold((_, v) => v?.arguments.buildTarget ?? ""); return [tEitherJob.get(), trace]; }, ) @@ -116,13 +116,13 @@ await LogMetricTraceable.from(eitherJob) tEitherWithBuiltImage.trace.trace( buildImageMetric[err ? "failure" : "success"], ); - if (err) { + if (!val || err) { tEitherWithBuiltImage.trace.addTrace(LogLevel.ERROR).trace( `oh nyoo we couldn't buiwd the img :(( ${err}`, ); return; } - tEitherWithBuiltImage.trace.addTrace("buildOutput").trace(val); + tEitherWithBuiltImage.trace.addTrace("buildOutput").trace(val.buildOutput); }); }) .map(async (tEitherWithBuiltImage) => { diff --git a/worker/secret.ts b/worker/secret.ts new file mode 100644 index 0000000..e0a4c5d --- /dev/null +++ b/worker/secret.ts @@ -0,0 +1,193 @@ +import { + Either, + getRequiredEnvVars, + getStdout, + type IEither, + type ITraceable, + type LogMetricTraceSupplier, + Metric, + TraceUtil, +} from "@emprespresso/pengueno"; + +// -- -- +export interface LoginItem { + login: { + username: string; + password: string; + }; +} + +export interface SecureNote { + notes: string; +} + +export type SecretItem = LoginItem | SecureNote; +export interface IVault { + unlock: (client: TClient) => Promise>; + lock: (client: TClient, key: TKey) => Promise>; + + fetchSecret: ( + client: TClient, + key: TKey, + item: TItemId, + ) => Promise>; +} +// -- -- + +// -- -- +type TClient = ITraceable; +type TKey = string; +type TItemId = string; +export class Bitwarden implements IVault { + constructor(private readonly config: BitwardenConfig) {} + + public unlock(client: TClient) { + return client + .move(this.config) + .bimap(TraceUtil.withMetricTrace(Bitwarden.loginMetric)) + .flatMap((tConfig) => + tConfig.move(`bw config server ${tConfig.get().server}`).map(getStdout), + ) + .map(async (tEitherWithConfig) => { + const eitherWithConfig = await tEitherWithConfig.get(); + tEitherWithConfig.trace.trace("logging in~ ^.^"); + return eitherWithConfig.flatMapAsync((_) => + tEitherWithConfig + .move("bw login --apikey --quiet") + .map(getStdout) + .get(), + ); + }) + .peek(async (tEitherWithAuthd) => { + const eitherWithAuthd = await tEitherWithAuthd.get(); + return tEitherWithAuthd.trace.trace( + eitherWithAuthd.fold( + (err, _val) => Bitwarden.loginMetric[err ? "failure" : "success"], + ), + ); + }) + .map(async (tEitherWithAuthd) => { + const eitherWithAuthd = await tEitherWithAuthd.get(); + tEitherWithAuthd.trace.trace("unlocking the secret vault~ (◕ᴗ◕✿)"); + return eitherWithAuthd.flatMapAsync((_) => + tEitherWithAuthd + .move("bw unlock --passwordenv BW_PASSWORD --raw") + .map(getStdout) + .get(), + ); + }) + .peek(async (tEitherWithSession) => { + const eitherWithAuthd = await tEitherWithSession.get(); + return tEitherWithSession.trace.trace( + eitherWithAuthd.fold( + (err, _val) => + Bitwarden.unlockVaultMetric[err ? "failure" : "success"], + ), + ); + }) + .get(); + } + + public fetchSecret( + client: TClient, + key: string, + item: string, + ): Promise> { + return client + .move(key) + .bimap(TraceUtil.withMetricTrace(Bitwarden.fetchSecretMetric)) + .peek((tSession) => + tSession.trace.trace(`looking for your secret ${item} (⑅˘꒳˘)`), + ) + .flatMap((tSession) => + tSession + .move("bw list items") + .map((listCmd) => + getStdout(listCmd, { env: { BW_SESSION: tSession.get() } }), + ), + ) + .map( + TraceUtil.promiseify((tEitherItemsJson) => + tEitherItemsJson + .get() + .flatMap( + (itemsJson): IEither> => + Either.fromFailable(() => JSON.parse(itemsJson)), + ) + .flatMap((itemsList): IEither => { + const secret = itemsList.find(({ name }) => name === item); + if (!secret) { + return Either.left( + new Error(`couldn't find the item ${item} (。•́︿•̀。)`), + ); + } + return Either.right(secret); + }), + ), + ) + .peek(async (tEitherWithSecret) => { + const eitherWithSecret = await tEitherWithSecret.get(); + return tEitherWithSecret.trace.trace( + eitherWithSecret.fold( + (err, _val) => + Bitwarden.fetchSecretMetric[err ? "failure" : "success"], + ), + ); + }) + .get(); + } + + public lock(client: TClient, key: TKey) { + return client + .move(key) + .bimap(TraceUtil.withMetricTrace(Bitwarden.lockVaultMetric)) + .peek((tSession) => + tSession.trace.trace(`taking care of locking the vault :3`), + ) + .flatMap((tSession) => + tSession + .move("bw lock") + .map((lockCmd) => + getStdout(lockCmd, { env: { BW_SESSION: tSession.get() } }), + ), + ) + .peek(async (tEitherWithLocked) => { + const eitherWithLocked = await tEitherWithLocked.get(); + return eitherWithLocked.fold((err, _val) => { + tEitherWithLocked.trace.trace( + Bitwarden.lockVaultMetric[err ? "failure" : "success"], + ); + if (err) return; + tEitherWithLocked.trace.trace( + "all locked up and secure now~ (。•̀ᴗ-)✧", + ); + }); + }) + .get(); + } + + public static getConfigFromEnvironment(): IEither { + return getRequiredEnvVars([ + "BW_SERVER", + "BW_CLIENTSECRET", + "BW_CLIENTID", + "BW_PASSWORD", + ]).mapRight(({ BW_SERVER, BW_CLIENTSECRET, BW_CLIENTID }) => ({ + clientId: BW_CLIENTID, + secret: BW_CLIENTSECRET, + server: BW_SERVER, + })); + } + + private static loginMetric = Metric.fromName("Bitwarden.login"); + private static unlockVaultMetric = Metric.fromName("Bitwarden.unlockVault"); + private static fetchSecretMetric = Metric.fromName("Bitwarden.fetchSecret"); + private static lockVaultMetric = Metric.fromName("Bitwarden.lock"); +} + +export interface BitwardenConfig { + server: string; + secret: string; + clientId: string; +} +// -- -- diff --git a/worker/secret/bitwarden.ts b/worker/secret/bitwarden.ts deleted file mode 100644 index 7145f49..0000000 --- a/worker/secret/bitwarden.ts +++ /dev/null @@ -1,167 +0,0 @@ -import { - Either, - getRequiredEnvVars, - getStdout, - type IEither, - type ITraceable, - type LogMetricTraceSupplier, - Metric, - TraceUtil, -} from "@emprespresso/pengueno"; -import type { IVault, SecretItem } from "./mod.ts"; - -type TClient = ITraceable; -type TKey = string; -type TItemId = string; -export class Bitwarden implements IVault { - constructor(private readonly config: BitwardenConfig) {} - - public unlock(client: TClient) { - return client - .move(this.config) - .bimap(TraceUtil.withMetricTrace(Bitwarden.loginMetric)) - .flatMap((tConfig) => - tConfig.move(`bw config server ${tConfig.get().server}`).map(getStdout), - ) - .map(async (tEitherWithConfig) => { - const eitherWithConfig = await tEitherWithConfig.get(); - tEitherWithConfig.trace.trace("logging in~ ^.^"); - return eitherWithConfig.flatMapAsync((_) => - tEitherWithConfig - .move("bw login --apikey --quiet") - .map(getStdout) - .get(), - ); - }) - .peek(async (tEitherWithAuthd) => { - const eitherWithAuthd = await tEitherWithAuthd.get(); - return tEitherWithAuthd.trace.trace( - eitherWithAuthd.fold( - (err, _val) => Bitwarden.loginMetric[err ? "failure" : "success"], - ), - ); - }) - .map(async (tEitherWithAuthd) => { - const eitherWithAuthd = await tEitherWithAuthd.get(); - tEitherWithAuthd.trace.trace("unlocking the secret vault~ (◕ᴗ◕✿)"); - return eitherWithAuthd.flatMapAsync((_) => - tEitherWithAuthd - .move("bw unlock --passwordenv BW_PASSWORD --raw") - .map(getStdout) - .get(), - ); - }) - .peek(async (tEitherWithSession) => { - const eitherWithAuthd = await tEitherWithSession.get(); - return tEitherWithSession.trace.trace( - eitherWithAuthd.fold( - (err, _val) => - Bitwarden.unlockVaultMetric[err ? "failure" : "success"], - ), - ); - }) - .get(); - } - - public fetchSecret( - client: TClient, - key: string, - item: string, - ): Promise> { - return client - .move(key) - .bimap(TraceUtil.withMetricTrace(Bitwarden.fetchSecretMetric)) - .peek((tSession) => - tSession.trace.trace(`looking for your secret ${item} (⑅˘꒳˘)`), - ) - .flatMap((tSession) => - tSession - .move("bw list items") - .map((listCmd) => - getStdout(listCmd, { env: { BW_SESSION: tSession.get() } }), - ), - ) - .map( - TraceUtil.promiseify((tEitherItemsJson) => - tEitherItemsJson - .get() - .flatMap( - (itemsJson): IEither> => - Either.fromFailable(() => JSON.parse(itemsJson)), - ) - .flatMap((itemsList): IEither => { - const secret = itemsList.find(({ name }) => name === item); - if (!secret) { - return Either.left( - new Error(`couldn't find the item ${item} (。•́︿•̀。)`), - ); - } - return Either.right(secret); - }), - ), - ) - .peek(async (tEitherWithSecret) => { - const eitherWithSecret = await tEitherWithSecret.get(); - return tEitherWithSecret.trace.trace( - eitherWithSecret.fold( - (err, _val) => - Bitwarden.fetchSecretMetric[err ? "failure" : "success"], - ), - ); - }) - .get(); - } - - public lock(client: TClient, key: TKey) { - return client - .move(key) - .bimap(TraceUtil.withMetricTrace(Bitwarden.lockVaultMetric)) - .peek((tSession) => - tSession.trace.trace(`taking care of locking the vault :3`), - ) - .flatMap((tSession) => - tSession - .move("bw lock") - .map((lockCmd) => - getStdout(lockCmd, { env: { BW_SESSION: tSession.get() } }), - ), - ) - .peek(async (tEitherWithLocked) => { - const eitherWithLocked = await tEitherWithLocked.get(); - return eitherWithLocked.fold((err, _val) => { - tEitherWithLocked.trace.trace( - Bitwarden.lockVaultMetric[err ? "failure" : "success"], - ); - if (err) return; - tEitherWithLocked.trace.trace( - "all locked up and secure now~ (。•̀ᴗ-)✧", - ); - }); - }) - .get(); - } - - public static getConfigFromEnvironment(): IEither { - return getRequiredEnvVars([ - "BW_SERVER", - "BW_CLIENTSECRET", - "BW_CLIENTID", - "BW_PASSWORD", - ]).mapRight(({ BW_SERVER, BW_CLIENTSECRET, BW_CLIENTID }) => ({ - clientId: BW_CLIENTID, - secret: BW_CLIENTSECRET, - server: BW_SERVER, - })); - } - - private static loginMetric = Metric.fromName("Bitwarden.login"); - private static unlockVaultMetric = Metric.fromName("Bitwarden.unlockVault"); - private static fetchSecretMetric = Metric.fromName("Bitwarden.fetchSecret"); - private static lockVaultMetric = Metric.fromName("Bitwarden.lock"); -} - -export interface BitwardenConfig { - server: string; - secret: string; - clientId: string; -} diff --git a/worker/secret/ivault.ts b/worker/secret/ivault.ts deleted file mode 100644 index e101e56..0000000 --- a/worker/secret/ivault.ts +++ /dev/null @@ -1,24 +0,0 @@ -import type { IEither } from "@emprespresso/pengueno"; - -export interface LoginItem { - login: { - username: string; - password: string; - }; -} - -export interface SecureNote { - notes: string; -} - -export type SecretItem = LoginItem | SecureNote; -export interface IVault { - unlock: (client: TClient) => Promise>; - lock: (client: TClient, key: TKey) => Promise>; - - fetchSecret: ( - client: TClient, - key: TKey, - item: TItemId, - ) => Promise>; -} diff --git a/worker/secret/mod.ts b/worker/secret/mod.ts deleted file mode 100644 index 70a1ea9..0000000 --- a/worker/secret/mod.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from "./ivault.ts"; -export * from "./bitwarden.ts"; -- cgit v1.2.3-70-g09d2