summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorElizabeth <me@liz.coffee>2025-06-02 16:52:52 -0700
committerElizabeth <me@liz.coffee>2025-06-02 16:52:52 -0700
commit98f5c21aa65bbbca01a186a754249335b4afef57 (patch)
tree0fc8e01a73f0a3be4534c11724ad2ff634b4fd2f
parent373d9ec700c0097a22cf665a8e33cf48998d1dc2 (diff)
downloadci-98f5c21aa65bbbca01a186a754249335b4afef57.tar.gz
ci-98f5c21aa65bbbca01a186a754249335b4afef57.zip
fixup the Either monad a bit for type safetyp
-rw-r--r--deno.json8
-rw-r--r--server/ci.ts4
-rw-r--r--server/job.ts325
-rw-r--r--u/fn/either.ts183
-rw-r--r--u/process/argv.ts13
-rw-r--r--u/server/activity/health.ts12
-rw-r--r--u/server/response.ts4
-rw-r--r--worker/executor.ts6
-rw-r--r--worker/jobs/ci_pipeline.run6
-rwxr-xr-xworker/scripts/build_docker_image16
-rw-r--r--worker/secret.ts343
11 files changed, 491 insertions, 429 deletions
diff --git a/deno.json b/deno.json
index adeae7b..4a34a73 100644
--- a/deno.json
+++ b/deno.json
@@ -1,4 +1,10 @@
{
"package": "@emprespresso/ci",
- "workspace": ["./*"]
+ "workspace": ["./*"],
+ "tasks": {
+ "test": "cd u/tests && deno test --allow-env --allow-read --allow-write",
+ "test:trace": "cd u/tests && deno test --allow-env --allow-read --allow-write trace_test.ts",
+ "test:either": "cd u/tests && deno test --allow-env --allow-read --allow-write either_test.ts",
+ "test:coverage": "cd u/tests && deno test --coverage=coverage --allow-env --allow-read --allow-write"
+ }
}
diff --git a/server/ci.ts b/server/ci.ts
index e1a9ca7..f8d4a17 100644
--- a/server/ci.ts
+++ b/server/ci.ts
@@ -23,8 +23,8 @@ export class CiHookServer {
constructor(
healthCheck: HealthChecker = _healthCheck,
jobQueuer: IJobQueuer<ITraceable<Job, ServerTrace>> = new LaminarJobQueuer(
- getRequiredEnv("LAMINAR_URL").fold((err, val) =>
- err ? "https://ci.liz.coffee" : val!,
+ getRequiredEnv("LAMINAR_URL").fold(({ isLeft, value }) =>
+ isLeft ? "https://ci.liz.coffee" : value,
),
),
private readonly healthCheckActivity: IHealthCheckActivity = new HealthCheckActivityImpl(
diff --git a/server/job.ts b/server/job.ts
index 62582d6..4e12b45 100644
--- a/server/job.ts
+++ b/server/job.ts
@@ -1,21 +1,21 @@
import {
- getStdout,
- type Mapper,
- memoize,
- Either,
- ErrorSource,
- type IActivity,
- type IEither,
- type ITraceable,
- jsonModel,
- JsonResponse,
- LogLevel,
- Metric,
- PenguenoError,
- type PenguenoRequest,
- type ServerTrace,
- TraceUtil,
- validateExecutionEntries,
+ getStdout,
+ type Mapper,
+ memoize,
+ Either,
+ ErrorSource,
+ type IActivity,
+ type IEither,
+ type ITraceable,
+ jsonModel,
+ JsonResponse,
+ LogLevel,
+ Metric,
+ PenguenoError,
+ type PenguenoRequest,
+ type ServerTrace,
+ TraceUtil,
+ validateExecutionEntries,
} from "@emprespresso/pengueno";
import { isJob, type Job } from "@emprespresso/ci_model";
@@ -23,93 +23,98 @@ import { isJob, type Job } from "@emprespresso/ci_model";
const wellFormedJobMetric = Metric.fromName("Job.WellFormed");
const jobJsonTransformer = (
- j: ITraceable<unknown, ServerTrace>,
+ j: ITraceable<unknown, ServerTrace>,
): IEither<PenguenoError, Job> =>
- j
- .bimap(TraceUtil.withMetricTrace(wellFormedJobMetric))
- .map((tJson) => {
- if (!isJob(tJson) || !validateExecutionEntries(tJson)) {
- const err = "seems like a pwetty mawfomed job \\(-.-)/";
- tJson.trace.addTrace(LogLevel.WARN).trace(err);
- return Either.left<PenguenoError, Job>(new PenguenoError(err, 400));
- }
- return Either.right<PenguenoError, Job>(tJson);
- })
- .peek((tJob) =>
- tJob.trace.trace(
- tJob
- .get()
- .fold((err) =>
- err ? wellFormedJobMetric.failure : wellFormedJobMetric.success,
- ),
- ),
- )
- .get();
+ j
+ .bimap(TraceUtil.withMetricTrace(wellFormedJobMetric))
+ .map((tJson) => {
+ if (!isJob(tJson) || !validateExecutionEntries(tJson)) {
+ const err = "seems like a pwetty mawfomed job \\(-.-)/";
+ tJson.trace.addTrace(LogLevel.WARN).trace(err);
+ return Either.left<PenguenoError, Job>(
+ new PenguenoError(err, 400),
+ );
+ }
+ return Either.right<PenguenoError, Job>(tJson);
+ })
+ .peek((tJob) =>
+ tJob.trace.trace(
+ tJob
+ .get()
+ .fold(({ isLeft }) =>
+ isLeft
+ ? wellFormedJobMetric.failure
+ : wellFormedJobMetric.success,
+ ),
+ ),
+ )
+ .get();
export interface IJobHookActivity {
- processHook: IActivity;
+ processHook: IActivity;
}
const jobHookRequestMetric = Metric.fromName("JobHook.process");
export class JobHookActivityImpl implements IJobHookActivity {
- constructor(
- private readonly queuer: IJobQueuer<ITraceable<Job, ServerTrace>>,
- ) {}
+ constructor(
+ private readonly queuer: IJobQueuer<ITraceable<Job, ServerTrace>>,
+ ) {}
- private trace(r: ITraceable<PenguenoRequest, ServerTrace>) {
- return r
- .bimap(TraceUtil.withClassTrace(this))
- .bimap(TraceUtil.withMetricTrace(jobHookRequestMetric));
- }
+ private trace(r: ITraceable<PenguenoRequest, ServerTrace>) {
+ return r
+ .bimap(TraceUtil.withClassTrace(this))
+ .bimap(TraceUtil.withMetricTrace(jobHookRequestMetric));
+ }
- public processHook(r: ITraceable<PenguenoRequest, ServerTrace>) {
- return this.trace(r)
- .map(jsonModel(jobJsonTransformer))
- .map(async (tEitherJobJson) => {
- const eitherJob = await tEitherJobJson.get();
- return eitherJob.flatMapAsync(async (job) => {
- const eitherQueued = await tEitherJobJson
- .move(job)
- .map(this.queuer.queue)
+ public processHook(r: ITraceable<PenguenoRequest, ServerTrace>) {
+ return this.trace(r)
+ .map(jsonModel(jobJsonTransformer))
+ .map(async (tEitherJobJson) => {
+ const eitherJob = await tEitherJobJson.get();
+ return eitherJob.flatMapAsync(async (job) => {
+ const eitherQueued = await tEitherJobJson
+ .move(job)
+ .map(this.queuer.queue)
+ .get();
+ return eitherQueued.mapLeft(
+ (e) => new PenguenoError(e.message, 500),
+ );
+ });
+ })
+ .peek(
+ TraceUtil.promiseify((tJob) =>
+ tJob.get().fold(({ isRight, value }) => {
+ if (isRight) {
+ tJob.trace.trace(jobHookRequestMetric.success);
+ tJob.trace.trace(
+ `all queued up and weady to go :D !! ${value}`,
+ );
+ return;
+ }
+
+ tJob.trace.trace(
+ value.source === ErrorSource.SYSTEM
+ ? jobHookRequestMetric.failure
+ : jobHookRequestMetric.warn,
+ );
+ tJob.trace.addTrace(value.source).trace(`${value}`);
+ }),
+ ),
+ )
+ .map(
+ TraceUtil.promiseify(
+ (tEitherQueuedJob) =>
+ new JsonResponse(r, tEitherQueuedJob.get(), {
+ status: tEitherQueuedJob
+ .get()
+ .fold(({ isRight, value }) =>
+ isRight ? 200 : value.status,
+ ),
+ }),
+ ),
+ )
.get();
- return eitherQueued.mapLeft((e) => new PenguenoError(e.message, 500));
- });
- })
- .peek(
- TraceUtil.promiseify((tJob) =>
- tJob
- .get()
- .fold(
- (err: PenguenoError | undefined, _val: string | undefined) => {
- if (!err) {
- tJob.trace.trace(jobHookRequestMetric.success);
- tJob.trace.trace(
- `all queued up and weady to go :D !! ${_val}`,
- );
- return;
- }
- tJob.trace.trace(
- err.source === ErrorSource.SYSTEM
- ? jobHookRequestMetric.failure
- : jobHookRequestMetric.warn,
- );
- tJob.trace.addTrace(err.source).trace(`${err}`);
- },
- ),
- ),
- )
- .map(
- TraceUtil.promiseify(
- (tEitherQueuedJob) =>
- new JsonResponse(r, tEitherQueuedJob.get(), {
- status: tEitherQueuedJob
- .get()
- .fold((err, _val) => (_val ? 200 : err?.status ?? 500)),
- }),
- ),
- )
- .get();
- }
+ }
}
// -- </job.hook> --
@@ -118,76 +123,82 @@ export class JobHookActivityImpl implements IJobHookActivity {
type QueuePosition = string;
export class QueueError extends Error {}
export interface IJobQueuer<TJob> {
- queue: Mapper<TJob, Promise<IEither<QueueError, QueuePosition>>>;
+ queue: Mapper<TJob, Promise<IEither<QueueError, QueuePosition>>>;
}
export class LaminarJobQueuer
- implements IJobQueuer<ITraceable<Job, ServerTrace>>
+ implements IJobQueuer<ITraceable<Job, ServerTrace>>
{
- constructor(private readonly queuePositionPrefix: string) {}
-
- private static GetJobTypeTrace = (jobType: string) =>
- `LaminarJobQueue.Queue.${jobType}`;
- private static JobTypeMetrics = memoize((jobType: string) =>
- Metric.fromName(LaminarJobQueuer.GetJobTypeTrace(jobType)),
- );
+ constructor(private readonly queuePositionPrefix: string) {}
- public queue(j: ITraceable<Job, ServerTrace>) {
- const { type: jobType } = j.get();
- const trace = LaminarJobQueuer.GetJobTypeTrace(jobType);
- const metric = LaminarJobQueuer.JobTypeMetrics(trace);
+ private static GetJobTypeTrace = (jobType: string) =>
+ `LaminarJobQueue.Queue.${jobType}`;
+ private static JobTypeMetrics = memoize((jobType: string) =>
+ Metric.fromName(LaminarJobQueuer.GetJobTypeTrace(jobType)),
+ );
- return j
- .bimap(TraceUtil.withTrace(trace))
- .bimap(TraceUtil.withMetricTrace(metric))
- .map((j) => {
- const { type: jobType, arguments: args } = j.get();
- const laminarCommand = [
- "laminarc",
- "queue",
- jobType,
- ...Object.entries(args).map(([key, val]) => `"${key}"="${val}"`),
- ];
- return laminarCommand;
- })
- .peek((c) =>
- c.trace.trace(
- `im so excited to see how this queue job will end!! (>ᴗ<): ${c
- .get()
- .toString()}`,
- ),
- )
- .map(getStdout)
- .peek(
- TraceUtil.promiseify((q) =>
- q.trace.trace(
- q
- .get()
- .fold((err, _val) => (err ? metric.failure : metric.success)),
- ),
- ),
- )
- .map(
- TraceUtil.promiseify((q) =>
- q
- .get()
- .mapRight((stdout) => {
- q.trace.addTrace(LogLevel.DEBUG).trace(`stdout ${stdout}`);
- const [jobName, jobId] = stdout.split(":");
- const jobUrl = `${this.queuePositionPrefix}/jobs/${jobName}/${jobId}`;
+ public queue(j: ITraceable<Job, ServerTrace>) {
+ const { type: jobType } = j.get();
+ const trace = LaminarJobQueuer.GetJobTypeTrace(jobType);
+ const metric = LaminarJobQueuer.JobTypeMetrics(trace);
- q.trace.trace(
- `all queued up and weady to go~ (˘ω˘) => ${jobUrl}`,
- );
- return jobUrl;
+ return j
+ .bimap(TraceUtil.withTrace(trace))
+ .bimap(TraceUtil.withMetricTrace(metric))
+ .map((j) => {
+ const { type: jobType, arguments: args } = j.get();
+ const laminarCommand = [
+ "laminarc",
+ "queue",
+ jobType,
+ ...Object.entries(args).map(
+ ([key, val]) => `"${key}"="${val}"`,
+ ),
+ ];
+ return laminarCommand;
})
- .mapLeft((err) => {
- q.trace.addTrace(LogLevel.ERROR).trace(err.toString());
- return err;
- }),
- ),
- )
- .get();
- }
+ .peek((c) =>
+ c.trace.trace(
+ `im so excited to see how this queue job will end!! (>ᴗ<): ${c
+ .get()
+ .toString()}`,
+ ),
+ )
+ .map(getStdout)
+ .peek(
+ TraceUtil.promiseify((q) =>
+ q.trace.trace(
+ q
+ .get()
+ .fold(({ isLeft }) =>
+ isLeft ? metric.failure : metric.success,
+ ),
+ ),
+ ),
+ )
+ .map(
+ TraceUtil.promiseify((q) =>
+ q.get().fold(({ isLeft, value }) => {
+ if (isLeft) {
+ q.trace
+ .addTrace(LogLevel.ERROR)
+ .trace(value.toString());
+ return Either.left<Error, string>(value);
+ }
+ q.trace
+ .addTrace(LogLevel.DEBUG)
+ .trace(`stdout ${value}`);
+ const [jobName, jobId] = value.split(":");
+ const jobUrl = `${this.queuePositionPrefix}/jobs/${jobName}/${jobId}`;
+
+ q.trace.trace(
+ `all queued up and weady to go~ (˘ω˘) => ${jobUrl}`,
+ );
+ return Either.right<Error, string>(jobUrl);
+ }),
+ ),
+ )
+ .get();
+ }
}
// -- </job.queuer> --
diff --git a/u/fn/either.ts b/u/fn/either.ts
index b228af2..124557c 100644
--- a/u/fn/either.ts
+++ b/u/fn/either.ts
@@ -1,97 +1,118 @@
-import type { BiMapper, Mapper, Supplier } from "@emprespresso/pengueno";
-import { isObject } from "../leftpadesque/mod.ts";
+import { type Mapper, type Supplier, isObject } from "@emprespresso/pengueno";
type IEitherTag = "IEither";
const iEitherTag: IEitherTag = "IEither";
+export interface _Either<LeftT, RightT, T> {
+ readonly isLeft: LeftT;
+ readonly isRight: RightT;
+ readonly value: T;
+}
+export type Left<E> = _Either<true, false, E>;
+export type Right<T> = _Either<false, true, T>;
+
export interface IEither<E, T> {
- readonly _tag: IEitherTag;
- mapBoth: <Ee, Tt>(
- errBranch: Mapper<E, Ee>,
- okBranch: Mapper<T, Tt>,
- ) => IEither<Ee, Tt>;
- fold: <Tt>(folder: (err: E | undefined, val: T | undefined) => Tt) => Tt; //BiMapper<E | undefined, T | undefined, Tt>) => Tt;;
- moveRight: <Tt>(t: Tt) => IEither<E, Tt>;
- mapRight: <Tt>(mapper: Mapper<T, Tt>) => IEither<E, Tt>;
- mapLeft: <Ee>(mapper: Mapper<E, Ee>) => IEither<Ee, T>;
- flatMap: <Tt>(mapper: Mapper<T, IEither<E, Tt>>) => IEither<E, Tt>;
- flatMapAsync: <Tt>(
- mapper: Mapper<T, Promise<IEither<E, Tt>>>,
- ) => Promise<IEither<E, Tt>>;
+ readonly _tag: IEitherTag;
+
+ mapBoth: <_E, _T>(
+ errBranch: Mapper<E, _E>,
+ okBranch: Mapper<T, _T>,
+ ) => IEither<_E, _T>;
+ fold: <_T>(folder: Mapper<Left<E> | Right<T>, _T>) => _T;
+ moveRight: <_T>(t: _T) => IEither<E, _T>;
+ mapRight: <_T>(mapper: Mapper<T, _T>) => IEither<E, _T>;
+ mapLeft: <_E>(mapper: Mapper<E, _E>) => IEither<_E, T>;
+ flatMap: <_T>(mapper: Mapper<T, IEither<E, _T>>) => IEither<E, _T>;
+ flatMapAsync: <_T>(
+ mapper: Mapper<T, Promise<IEither<E, _T>>>,
+ ) => Promise<IEither<E, _T>>;
}
export class Either<E, T> implements IEither<E, T> {
- private constructor(
- private readonly err?: E,
- private readonly ok?: T,
- public readonly _tag: IEitherTag = iEitherTag,
- ) {}
-
- public moveRight<Tt>(t: Tt) {
- return this.mapRight(() => t);
- }
-
- public fold<R>(folder: BiMapper<E | undefined, T | undefined, R>): R {
- return folder(this.err ?? undefined, this.ok ?? undefined);
- }
-
- public mapBoth<Ee, Tt>(
- errBranch: Mapper<E, Ee>,
- okBranch: Mapper<T, Tt>,
- ): Either<Ee, Tt> {
- if (this.err !== undefined) return Either.left(errBranch(this.err));
- return Either.right(okBranch(this.ok!));
- }
-
- public flatMap<Tt>(mapper: Mapper<T, Either<E, Tt>>): Either<E, Tt> {
- if (this.ok !== undefined) return mapper(this.ok);
- return Either.left<E, Tt>(this.err!);
- }
-
- public mapRight<Tt>(mapper: Mapper<T, Tt>): IEither<E, Tt> {
- if (this.ok !== undefined) return Either.right<E, Tt>(mapper(this.ok));
- return Either.left<E, Tt>(this.err!);
- }
-
- public mapLeft<Ee>(mapper: Mapper<E, Ee>): IEither<Ee, T> {
- if (this.err !== undefined) return Either.left<Ee, T>(mapper(this.err));
- return Either.right<Ee, T>(this.ok!);
- }
-
- public async flatMapAsync<Tt>(
- mapper: Mapper<T, Promise<IEither<E, Tt>>>,
- ): Promise<IEither<E, Tt>> {
- if (this.err !== undefined) {
- return Promise.resolve(Either.left<E, Tt>(this.err));
+ private readonly self: Left<E> | Right<T>;
+
+ private constructor(
+ err?: E,
+ ok?: T,
+ public readonly _tag: IEitherTag = iEitherTag,
+ ) {
+ this.self = <Left<E> | Right<T>>{
+ isLeft: typeof err !== "undefined",
+ isRight: typeof ok !== "undefined",
+ value: typeof err !== "undefined" ? err : ok!,
+ };
+ }
+
+ public moveRight<_T>(t: _T) {
+ return this.mapRight(() => t);
+ }
+
+ public fold<_T>(folder: Mapper<Left<E> | Right<T>, _T>): _T {
+ return folder(this.self);
+ }
+
+ public mapBoth<_E, _T>(
+ errBranch: Mapper<E, _E>,
+ okBranch: Mapper<T, _T>,
+ ): IEither<_E, _T> {
+ if (this.self.isLeft) return Either.left(errBranch(this.self.value));
+ return Either.right(okBranch(this.self.value));
+ }
+
+ public flatMap<_T>(mapper: Mapper<T, IEither<E, _T>>): IEither<E, _T> {
+ if (this.self.isRight) return mapper(this.self.value);
+ return Either.left<E, _T>(this.self.value);
+ }
+
+ public mapRight<_T>(mapper: Mapper<T, _T>): IEither<E, _T> {
+ if (this.self.isRight)
+ return Either.right<E, _T>(mapper(this.self.value));
+ return Either.left<E, _T>(this.self.value);
}
- return await mapper(this.ok!).catch((err) => Either.left<E, Tt>(err as E));
- }
-
- static left<E, T>(e: E) {
- return new Either<E, T>(e);
- }
-
- static right<E, T>(t: T) {
- return new Either<E, T>(undefined, t);
- }
-
- static fromFailable<E, T>(s: Supplier<T>) {
- try {
- return Either.right<E, T>(s());
- } catch (e) {
- return Either.left<E, T>(e as E);
+
+ public mapLeft<_E>(mapper: Mapper<E, _E>): IEither<_E, T> {
+ if (this.self.isLeft)
+ return Either.left<_E, T>(mapper(this.self.value));
+ return Either.right<_E, T>(this.self.value);
+ }
+
+ public async flatMapAsync<_T>(
+ mapper: Mapper<T, Promise<IEither<E, _T>>>,
+ ): Promise<IEither<E, _T>> {
+ if (this.self.isLeft) {
+ return Promise.resolve(Either.left<E, _T>(this.self.value));
+ }
+ return await mapper(this.self.value).catch((err) =>
+ Either.left<E, _T>(err),
+ );
+ }
+
+ static left<E, T>(e: E): IEither<E, T> {
+ return new Either<E, T>(e, undefined);
+ }
+ static right<E, T>(t: T): IEither<E, T> {
+ return new Either<E, T>(undefined, t);
+ }
+
+ static fromFailable<E, T>(s: Supplier<T>): IEither<E, T> {
+ try {
+ return Either.right<E, T>(s());
+ } catch (e) {
+ return Either.left<E, T>(e as E);
+ }
}
- }
- static async fromFailableAsync<E, T>(s: Promise<T>) {
- try {
- return Either.right<E, T>(await s);
- } catch (e) {
- return Either.left<E, T>(e as E);
+ static async fromFailableAsync<E, T>(
+ s: Promise<T>,
+ ): Promise<IEither<E, T>> {
+ try {
+ return Either.right<E, T>(await s);
+ } catch (e) {
+ return Either.left<E, T>(e as E);
+ }
}
- }
}
export const isEither = <E, T>(o: unknown): o is IEither<E, T> => {
- return isObject(o) && "_tag" in o && o._tag === "IEither";
+ return isObject(o) && "_tag" in o && o._tag === "IEither";
};
diff --git a/u/process/argv.ts b/u/process/argv.ts
index 657c9a7..7190531 100644
--- a/u/process/argv.ts
+++ b/u/process/argv.ts
@@ -37,14 +37,15 @@ export const argv = <K extends string, V extends string>(
.map((arg) => [arg, getArg(arg, argv)] as [K, IEither<Error, V>])
.map(([arg, specified]): [K, IEither<Error, V>] => [
arg,
- specified.fold((e, val) => {
- const hasDefaultVal = e && defaultArgs && arg in defaultArgs;
+ specified.fold(({ isLeft, isRight, value}): IEither<Error, V> => {
+ if (isRight) {
+ return Either.right(value);
+ }
+ const hasDefaultVal = isLeft && defaultArgs && arg in defaultArgs;
if (hasDefaultVal) {
- return Either.right<Error, V>(defaultArgs[arg]!);
- } else if (!val || e) {
- return Either.left<Error, V>(e ?? new Error("unknown"));
+ return Either.right(defaultArgs[arg]!);
}
- return Either.right<Error, V>(val);
+ return Either.left(value);
}),
])
.reduce(
diff --git a/u/server/activity/health.ts b/u/server/activity/health.ts
index 83be399..b9dedf9 100644
--- a/u/server/activity/health.ts
+++ b/u/server/activity/health.ts
@@ -38,10 +38,10 @@ export class HealthCheckActivityImpl implements IHealthCheckActivity {
.flatMap((r) => r.move(HealthCheckInput.CHECK).map(this.check))
.peek(
TraceUtil.promiseify((h) =>
- h.get().fold((err) => {
- if (err) {
+ h.get().fold(({ isLeft, value }) => {
+ if (isLeft) {
h.trace.trace(healthCheckMetric.failure);
- h.trace.addTrace(LogLevel.ERROR).trace(`${err}`);
+ h.trace.addTrace(LogLevel.ERROR).trace(`${value}`);
return;
}
h.trace.trace(healthCheckMetric.success);
@@ -57,9 +57,9 @@ export class HealthCheckActivityImpl implements IHealthCheckActivity {
() => "think im healthy!! (✿˘◡˘) ready to do work~",
)
.fold(
- (errMsg, okMsg) =>
- new JsonResponse(req, errMsg ?? okMsg, {
- status: errMsg ? 500 : 200,
+ ({ isLeft, value: message }) =>
+ new JsonResponse(req, message, {
+ status: isLeft ? 500 : 200,
}),
),
),
diff --git a/u/server/response.ts b/u/server/response.ts
index 9022fed..4531157 100644
--- a/u/server/response.ts
+++ b/u/server/response.ts
@@ -67,7 +67,7 @@ export class JsonResponse extends PenguenoResponse {
super(
req,
JSON.stringify(
- e.fold((err, ok) => (err ? { error: err! } : { ok: ok! })),
+ e.fold(({ isLeft, value }) => (isLeft ? { error: value } : { ok: value })),
),
optsWithJsonContentType,
);
@@ -76,7 +76,7 @@ export class JsonResponse extends PenguenoResponse {
super(
req,
JSON.stringify(
- Math.floor(opts.status / 100) < 4 ? { ok: e } : { error: e },
+ Math.floor(opts.status / 100) > 4 ? { error: e } : { ok: e },
),
optsWithJsonContentType,
);
diff --git a/worker/executor.ts b/worker/executor.ts
index faa40a6..ea580eb 100644
--- a/worker/executor.ts
+++ b/worker/executor.ts
@@ -36,8 +36,8 @@ export const executeJob = (tJob: ITraceable<Job, LogMetricTraceSupplier>) =>
q
.get()
.fold(
- (err, _val) =>
- jobTypeMetric(tJob.get().type)[err ? "failure" : "success"],
+ ({ isLeft }) =>
+ jobTypeMetric(tJob.get().type)[isLeft ? "failure" : "success"],
),
),
),
@@ -86,7 +86,7 @@ export const executePipeline = (
.get(),
),
);
- const failures = jobResults.filter((e) => e.fold((err) => !!err));
+ const failures = jobResults.filter((e) => e.fold(( { isLeft }) => isLeft));
if (failures.length > 0) {
tJobs.trace.trace(pipelinesMetric.failure);
return Either.left(new Error(failures.toString()));
diff --git a/worker/jobs/ci_pipeline.run b/worker/jobs/ci_pipeline.run
index 434850c..03d9d6d 100644
--- a/worker/jobs/ci_pipeline.run
+++ b/worker/jobs/ci_pipeline.run
@@ -123,9 +123,9 @@ await LogMetricTraceable.from(eitherJob).bimap(TraceUtil.withTrace(trace))
)
.get()
.then((e) =>
- e.flatMap(() => eitherJob).fold((err, val) => {
- if (!val || err) throw err;
- return Deno.remove(getWorkingDirectoryForCiJob(val), { recursive: true });
+ e.flatMap(() => eitherJob).fold(({isLeft, isRight, value}) => {
+ if (isLeft || !isRight) throw value;
+ return Deno.remove(getWorkingDirectoryForCiJob(value), { recursive: true });
})
);
diff --git a/worker/scripts/build_docker_image b/worker/scripts/build_docker_image
index dc0e961..2e19111 100755
--- a/worker/scripts/build_docker_image
+++ b/worker/scripts/build_docker_image
@@ -38,7 +38,7 @@ await LogMetricTraceable.from(eitherJob)
.bimap(
(tEitherJob) => {
const trace = "build_docker_image." +
- tEitherJob.get().fold((_, v) => v?.arguments.buildTarget ?? "");
+ tEitherJob.get().fold(({ isRight, value }) => isRight ? value.arguments.buildTarget : "");
return [tEitherJob.get(), trace];
},
)
@@ -83,8 +83,8 @@ await LogMetricTraceable.from(eitherJob)
.peek(async (tEitherWithAuthdRegistry) => {
const eitherWithAuthdRegistry = await tEitherWithAuthdRegistry.get();
return tEitherWithAuthdRegistry.trace.trace(
- eitherWithAuthdRegistry.fold((err, _val) =>
- loginMetric[err ? "failure" : "success"]
+ eitherWithAuthdRegistry.fold(({ isLeft}) =>
+ loginMetric[isLeft ? "failure" : "success"]
),
);
})
@@ -112,17 +112,17 @@ await LogMetricTraceable.from(eitherJob)
})
.peek(async (tEitherWithBuiltImage) => {
const eitherWithBuiltImage = await tEitherWithBuiltImage.get();
- eitherWithBuiltImage.fold((err, val) => {
+ eitherWithBuiltImage.fold(({ isLeft, value}) => {
tEitherWithBuiltImage.trace.trace(
- buildImageMetric[err ? "failure" : "success"],
+ buildImageMetric[isLeft ? "failure" : "success"],
);
- if (!val || err) {
+ if (isLeft) {
tEitherWithBuiltImage.trace.addTrace(LogLevel.ERROR).trace(
- `oh nyoo we couldn't buiwd the img :(( ${err}`,
+ `oh nyoo we couldn't buiwd the img :(( ${value}`,
);
return;
}
- tEitherWithBuiltImage.trace.addTrace("buildOutput").trace(val.buildOutput);
+ tEitherWithBuiltImage.trace.addTrace("buildOutput").trace(value.buildOutput);
});
})
.map(async (tEitherWithBuiltImage) => {
diff --git a/worker/secret.ts b/worker/secret.ts
index e0a4c5d..f5ae93f 100644
--- a/worker/secret.ts
+++ b/worker/secret.ts
@@ -1,36 +1,36 @@
import {
- Either,
- getRequiredEnvVars,
- getStdout,
- type IEither,
- type ITraceable,
- type LogMetricTraceSupplier,
- Metric,
- TraceUtil,
+ Either,
+ getRequiredEnvVars,
+ getStdout,
+ type IEither,
+ type ITraceable,
+ type LogMetricTraceSupplier,
+ Metric,
+ TraceUtil,
} from "@emprespresso/pengueno";
// -- <ISecret> --
export interface LoginItem {
- login: {
- username: string;
- password: string;
- };
+ login: {
+ username: string;
+ password: string;
+ };
}
export interface SecureNote {
- notes: string;
+ notes: string;
}
export type SecretItem = LoginItem | SecureNote;
export interface IVault<TClient, TKey, TItemId> {
- unlock: (client: TClient) => Promise<IEither<Error, TKey>>;
- lock: (client: TClient, key: TKey) => Promise<IEither<Error, TKey>>;
+ unlock: (client: TClient) => Promise<IEither<Error, TKey>>;
+ lock: (client: TClient, key: TKey) => Promise<IEither<Error, TKey>>;
- fetchSecret: <T extends SecretItem>(
- client: TClient,
- key: TKey,
- item: TItemId,
- ) => Promise<IEither<Error, T>>;
+ fetchSecret: <T extends SecretItem>(
+ client: TClient,
+ key: TKey,
+ item: TItemId,
+ ) => Promise<IEither<Error, T>>;
}
// -- </ISecret> --
@@ -39,155 +39,178 @@ type TClient = ITraceable<unknown, LogMetricTraceSupplier>;
type TKey = string;
type TItemId = string;
export class Bitwarden implements IVault<TClient, TKey, TItemId> {
- constructor(private readonly config: BitwardenConfig) {}
+ 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 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(
+ ({ isLeft }) =>
+ Bitwarden.loginMetric[
+ isLeft ? "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(
+ ({ isLeft }) =>
+ Bitwarden.unlockVaultMetric[
+ isLeft ? "failure" : "success"
+ ],
+ ),
+ );
+ })
+ .get();
+ }
- public fetchSecret<T extends SecretItem>(
- client: TClient,
- key: string,
- item: string,
- ): Promise<IEither<Error, T>> {
- 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<Error, Array<T & { name: string }>> =>
- Either.fromFailable(() => JSON.parse(itemsJson)),
+ public fetchSecret<T extends SecretItem>(
+ client: TClient,
+ key: string,
+ item: string,
+ ): Promise<IEither<Error, T>> {
+ 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<Error, Array<T & { name: string }>> =>
+ Either.fromFailable(() =>
+ JSON.parse(itemsJson),
+ ),
+ )
+ .flatMap((itemsList): IEither<Error, T> => {
+ const secret = itemsList.find(
+ ({ name }) => name === item,
+ );
+ if (!secret) {
+ return Either.left(
+ new Error(
+ `couldn't find the item ${item} (。•́︿•̀。)`,
+ ),
+ );
+ }
+ return Either.right(secret);
+ }),
+ ),
)
- .flatMap((itemsList): IEither<Error, T> => {
- const secret = itemsList.find(({ name }) => name === item);
- if (!secret) {
- return Either.left(
- new Error(`couldn't find the item ${item} (。•́︿•̀。)`),
+ .peek(async (tEitherWithSecret) => {
+ const eitherWithSecret = await tEitherWithSecret.get();
+ return tEitherWithSecret.trace.trace(
+ eitherWithSecret.fold(
+ ({ isLeft }) =>
+ Bitwarden.fetchSecretMetric[
+ isLeft ? "failure" : "success"
+ ],
+ ),
);
- }
- 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();
- }
+ })
+ .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 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(({ isLeft }) => {
+ tEitherWithLocked.trace.trace(
+ Bitwarden.lockVaultMetric[isLeft ? "failure" : "success"],
+ );
+ if (isLeft) return;
+ tEitherWithLocked.trace.trace(
+ "all locked up and secure now~ (。•̀ᴗ-)✧",
+ );
+ });
+ })
+ .get();
+ }
- public static getConfigFromEnvironment(): IEither<Error, BitwardenConfig> {
- 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,
- }));
- }
+ public static getConfigFromEnvironment(): IEither<Error, BitwardenConfig> {
+ 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");
+ 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;
+ server: string;
+ secret: string;
+ clientId: string;
}
// -- </IVault> --