From dc4ac7742690f8f2bd759d57108ac4455e717fe9 Mon Sep 17 00:00:00 2001 From: Elizabeth Hunt Date: Sun, 20 Jul 2025 13:03:39 -0700 Subject: Mount src directory from path on host running worker container --- .ci/ci.cjs | 23 +- .ci/package-lock.json | 492 ++++++++++++++++++++++++++++++++++++++++++ .ci/package.json | 2 +- model/job/jobs.ts | 1 + model/pipeline/builder.ts | 4 +- package-lock.json | 475 +--------------------------------------- u/process/env.ts | 24 +-- u/process/exec.ts | 59 +++++ u/process/index.ts | 2 +- u/process/run.ts | 40 ---- u/trace/itrace.ts | 6 +- u/types/fn/callable.ts | 2 + u/types/fn/either.ts | 56 +++-- u/types/fn/optional.ts | 8 +- u/types/index.ts | 2 + u/types/misc.ts | 3 + worker/executor.ts | 2 +- worker/jobs/ci_pipeline.run | 11 + worker/scripts/checkout_ci.ts | 80 ++++--- 19 files changed, 697 insertions(+), 595 deletions(-) create mode 100644 .ci/package-lock.json create mode 100644 u/process/exec.ts delete mode 100644 u/process/run.ts create mode 100644 u/types/misc.ts diff --git a/.ci/ci.cjs b/.ci/ci.cjs index 0e7ef6d..6560028 100755 --- a/.ci/ci.cjs +++ b/.ci/ci.cjs @@ -302,7 +302,7 @@ var EmbeddedMetricsTraceable = class _EmbeddedMetricsTraceable extends Traceable } }; -// ../u/process/run.ts +// ../u/process/exec.ts var import_node_util = require("node:util"); var import_node_child_process = require("node:child_process"); var exec = (0, import_node_util.promisify)(import_node_child_process.exec); @@ -369,6 +369,10 @@ var Either = class _Either extends _Tagged2 { if (isRight(this.self)) return mapper(this.self.ok); return _Either.left(this.self.err); } + filter(mapper) { + if (isLeft(this.self)) return _Either.left(this.self.err); + return _Either.fromFailable(() => this.right().filter(mapper).get()); + } async flatMapAsync(mapper) { if (isLeft(this.self)) return Promise.resolve(_Either.left(this.self.err)); return await mapper(this.self.ok).catch((err) => _Either.left(err)); @@ -385,6 +389,15 @@ var Either = class _Either extends _Tagged2 { if (isRight(this.self)) return Optional.from(this.self.ok); return Optional.none(); } + joinRight(other, mapper) { + return this.flatMap((t) => other.mapRight((o) => mapper(o, t))); + } + joinRightAsync(other, mapper) { + return this.flatMapAsync(async (t) => { + const o = typeof other === "function" ? other() : other; + return o.then((other2) => other2.mapRight((o2) => mapper(o2, t))); + }); + } static left(e) { return new _Either({ err: e, _tag: ELeftTag }); } @@ -429,7 +442,7 @@ var Optional = class _Optional extends _Tagged3 { return this; } get() { - if (isNone(this.self)) throw new IOptionalEmptyError("empty value"); + if (isNone(this.self)) throw new IOptionalEmptyError("called get() on None optional"); return this.self.value; } filter(mapper) { @@ -478,10 +491,10 @@ var BasePipelineBuilder = class { } }; var DefaultGitHookPipelineBuilder = class extends BasePipelineBuilder { - constructor(remoteUrl = process.env.remote, rev = process.env.rev, ref = process.env.ref) { + constructor(remoteUrl = process.env.remote, rev = process.env.rev, refname = process.env.refname) { super(); this.remoteUrl = remoteUrl; - this.ref = ref; + this.refname = refname; this.addStage({ parallelJobs: [ { @@ -500,7 +513,7 @@ var DefaultGitHookPipelineBuilder = class extends BasePipelineBuilder { } getBranch() { const branchRefPrefix = "refs/heads/"; - return this.ref.split(branchRefPrefix).at(1); + return this.refname.split(branchRefPrefix).at(1); } }; diff --git a/.ci/package-lock.json b/.ci/package-lock.json new file mode 100644 index 0000000..b45979e --- /dev/null +++ b/.ci/package-lock.json @@ -0,0 +1,492 @@ +{ + "name": ".ci", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "dependencies": { + "@emprespresso/ci_model": "file:../model" + }, + "devDependencies": { + "esbuild": "0.25.5" + } + }, + "../model": { + "name": "@emprespresso/ci_model", + "version": "0.1.0", + "dependencies": { + "@emprespresso/pengueno": "*" + } + }, + "node_modules/@emprespresso/ci_model": { + "resolved": "../model", + "link": true + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.5.tgz", + "integrity": "sha512-9o3TMmpmftaCMepOdA5k/yDw8SfInyzWWTjYTFCX3kPSDJMROQTb8jg+h9Cnwnmm1vOzvxN7gIfB5V2ewpjtGA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.5.tgz", + "integrity": "sha512-AdJKSPeEHgi7/ZhuIPtcQKr5RQdo6OO2IL87JkianiMYMPbCtot9fxPbrMiBADOWWm3T2si9stAiVsGbTQFkbA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.5.tgz", + "integrity": "sha512-VGzGhj4lJO+TVGV1v8ntCZWJktV7SGCs3Pn1GRWI1SBFtRALoomm8k5E9Pmwg3HOAal2VDc2F9+PM/rEY6oIDg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.5.tgz", + "integrity": "sha512-D2GyJT1kjvO//drbRT3Hib9XPwQeWd9vZoBJn+bu/lVsOZ13cqNdDeqIF/xQ5/VmWvMduP6AmXvylO/PIc2isw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.5.tgz", + "integrity": "sha512-GtaBgammVvdF7aPIgH2jxMDdivezgFu6iKpmT+48+F8Hhg5J/sfnDieg0aeG/jfSvkYQU2/pceFPDKlqZzwnfQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.5.tgz", + "integrity": "sha512-1iT4FVL0dJ76/q1wd7XDsXrSW+oLoquptvh4CLR4kITDtqi2e/xwXwdCVH8hVHU43wgJdsq7Gxuzcs6Iq/7bxQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.5.tgz", + "integrity": "sha512-nk4tGP3JThz4La38Uy/gzyXtpkPW8zSAmoUhK9xKKXdBCzKODMc2adkB2+8om9BDYugz+uGV7sLmpTYzvmz6Sw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.5.tgz", + "integrity": "sha512-PrikaNjiXdR2laW6OIjlbeuCPrPaAl0IwPIaRv+SMV8CiM8i2LqVUHFC1+8eORgWyY7yhQY+2U2fA55mBzReaw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.5.tgz", + "integrity": "sha512-cPzojwW2okgh7ZlRpcBEtsX7WBuqbLrNXqLU89GxWbNt6uIg78ET82qifUy3W6OVww6ZWobWub5oqZOVtwolfw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.5.tgz", + "integrity": "sha512-Z9kfb1v6ZlGbWj8EJk9T6czVEjjq2ntSYLY2cw6pAZl4oKtfgQuS4HOq41M/BcoLPzrUbNd+R4BXFyH//nHxVg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.5.tgz", + "integrity": "sha512-sQ7l00M8bSv36GLV95BVAdhJ2QsIbCuCjh/uYrWiMQSUuV+LpXwIqhgJDcvMTj+VsQmqAHL2yYaasENvJ7CDKA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.5.tgz", + "integrity": "sha512-0ur7ae16hDUC4OL5iEnDb0tZHDxYmuQyhKhsPBV8f99f6Z9KQM02g33f93rNH5A30agMS46u2HP6qTdEt6Q1kg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.5.tgz", + "integrity": "sha512-kB/66P1OsHO5zLz0i6X0RxlQ+3cu0mkxS3TKFvkb5lin6uwZ/ttOkP3Z8lfR9mJOBk14ZwZ9182SIIWFGNmqmg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.5.tgz", + "integrity": "sha512-UZCmJ7r9X2fe2D6jBmkLBMQetXPXIsZjQJCjgwpVDz+YMcS6oFR27alkgGv3Oqkv07bxdvw7fyB71/olceJhkQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.5.tgz", + "integrity": "sha512-kTxwu4mLyeOlsVIFPfQo+fQJAV9mh24xL+y+Bm6ej067sYANjyEw1dNHmvoqxJUCMnkBdKpvOn0Ahql6+4VyeA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.5.tgz", + "integrity": "sha512-K2dSKTKfmdh78uJ3NcWFiqyRrimfdinS5ErLSn3vluHNeHVnBAFWC8a4X5N+7FgVE1EjXS1QDZbpqZBjfrqMTQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.5.tgz", + "integrity": "sha512-uhj8N2obKTE6pSZ+aMUbqq+1nXxNjZIIjCjGLfsWvVpy7gKCOL6rsY1MhRh9zLtUtAI7vpgLMK6DxjO8Qm9lJw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.5.tgz", + "integrity": "sha512-pwHtMP9viAy1oHPvgxtOv+OkduK5ugofNTVDilIzBLpoWAM16r7b/mxBvfpuQDpRQFMfuVr5aLcn4yveGvBZvw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.5.tgz", + "integrity": "sha512-WOb5fKrvVTRMfWFNCroYWWklbnXH0Q5rZppjq0vQIdlsQKuw6mdSihwSo4RV/YdQ5UCKKvBy7/0ZZYLBZKIbwQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.5.tgz", + "integrity": "sha512-7A208+uQKgTxHd0G0uqZO8UjK2R0DDb4fDmERtARjSHWxqMTye4Erz4zZafx7Di9Cv+lNHYuncAkiGFySoD+Mw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.5.tgz", + "integrity": "sha512-G4hE405ErTWraiZ8UiSoesH8DaCsMm0Cay4fsFWOOUcz8b8rC6uCvnagr+gnioEjWn0wC+o1/TAHt+It+MpIMg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.5.tgz", + "integrity": "sha512-l+azKShMy7FxzY0Rj4RCt5VD/q8mG/e+mDivgspo+yL8zW7qEwctQ6YqKX34DTEleFAvCIUviCFX1SDZRSyMQA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.5.tgz", + "integrity": "sha512-O2S7SNZzdcFG7eFKgvwUEZ2VG9D/sn/eIiz8XRZ1Q/DO5a3s76Xv0mdBzVM5j5R639lXQmPmSo0iRpHqUUrsxw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.5.tgz", + "integrity": "sha512-onOJ02pqs9h1iMJ1PQphR+VZv8qBMQ77Klcsqv9CNW2w6yLqoURLcgERAIurY6QE63bbLuqgP9ATqajFLK5AMQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.5.tgz", + "integrity": "sha512-TXv6YnJ8ZMVdX+SXWVBo/0p8LTcrUYngpWjvm91TMjjBQii7Oz11Lw5lbDV5Y0TzuhSJHwiH4hEtC1I42mMS0g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.5.tgz", + "integrity": "sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.5", + "@esbuild/android-arm": "0.25.5", + "@esbuild/android-arm64": "0.25.5", + "@esbuild/android-x64": "0.25.5", + "@esbuild/darwin-arm64": "0.25.5", + "@esbuild/darwin-x64": "0.25.5", + "@esbuild/freebsd-arm64": "0.25.5", + "@esbuild/freebsd-x64": "0.25.5", + "@esbuild/linux-arm": "0.25.5", + "@esbuild/linux-arm64": "0.25.5", + "@esbuild/linux-ia32": "0.25.5", + "@esbuild/linux-loong64": "0.25.5", + "@esbuild/linux-mips64el": "0.25.5", + "@esbuild/linux-ppc64": "0.25.5", + "@esbuild/linux-riscv64": "0.25.5", + "@esbuild/linux-s390x": "0.25.5", + "@esbuild/linux-x64": "0.25.5", + "@esbuild/netbsd-arm64": "0.25.5", + "@esbuild/netbsd-x64": "0.25.5", + "@esbuild/openbsd-arm64": "0.25.5", + "@esbuild/openbsd-x64": "0.25.5", + "@esbuild/sunos-x64": "0.25.5", + "@esbuild/win32-arm64": "0.25.5", + "@esbuild/win32-ia32": "0.25.5", + "@esbuild/win32-x64": "0.25.5" + } + } + } +} diff --git a/.ci/package.json b/.ci/package.json index 9d8bfa7..dc42e26 100644 --- a/.ci/package.json +++ b/.ci/package.json @@ -4,7 +4,7 @@ "clean": "rm -rf dist ci.cjs" }, "dependencies": { - "@emprespresso/ci_model": "*" + "@emprespresso/ci_model": "file:../model" }, "devDependencies": { "esbuild": "0.25.5" diff --git a/model/job/jobs.ts b/model/job/jobs.ts index dc23070..eb22afd 100644 --- a/model/job/jobs.ts +++ b/model/job/jobs.ts @@ -43,6 +43,7 @@ export interface CheckoutCiJobProps extends JobArgT { readonly rev: string; readonly run: string; + readonly executorLaminarPath: string; readonly returnPath: string; } diff --git a/model/pipeline/builder.ts b/model/pipeline/builder.ts index 926a97f..b15a044 100644 --- a/model/pipeline/builder.ts +++ b/model/pipeline/builder.ts @@ -23,7 +23,7 @@ export class DefaultGitHookPipelineBuilder extends BasePipelineBuilder { constructor( private readonly remoteUrl = process.env.remote!, rev = process.env.rev!, - private readonly ref = process.env.ref!, + private readonly refname = process.env.refname!, ) { super(); @@ -47,6 +47,6 @@ export class DefaultGitHookPipelineBuilder extends BasePipelineBuilder { public getBranch(): string | undefined { const branchRefPrefix = 'refs/heads/'; - return this.ref.split(branchRefPrefix).at(1); + return this.refname.split(branchRefPrefix).at(1); } } diff --git a/package-lock.json b/package-lock.json index 5f6f6ae..a04d81d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,8 +11,7 @@ "u", "model", "server", - "worker", - ".ci" + "worker" ], "devDependencies": { "@types/node": "^24.0.3", @@ -30,7 +29,7 @@ } }, ".ci": { - "name": "ci.ci", + "extraneous": true, "dependencies": { "@emprespresso/ci_model": "*" }, @@ -61,431 +60,6 @@ "resolved": "u", "link": true }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.5.tgz", - "integrity": "sha512-9o3TMmpmftaCMepOdA5k/yDw8SfInyzWWTjYTFCX3kPSDJMROQTb8jg+h9Cnwnmm1vOzvxN7gIfB5V2ewpjtGA==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.5.tgz", - "integrity": "sha512-AdJKSPeEHgi7/ZhuIPtcQKr5RQdo6OO2IL87JkianiMYMPbCtot9fxPbrMiBADOWWm3T2si9stAiVsGbTQFkbA==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.5.tgz", - "integrity": "sha512-VGzGhj4lJO+TVGV1v8ntCZWJktV7SGCs3Pn1GRWI1SBFtRALoomm8k5E9Pmwg3HOAal2VDc2F9+PM/rEY6oIDg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.5.tgz", - "integrity": "sha512-D2GyJT1kjvO//drbRT3Hib9XPwQeWd9vZoBJn+bu/lVsOZ13cqNdDeqIF/xQ5/VmWvMduP6AmXvylO/PIc2isw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.5.tgz", - "integrity": "sha512-GtaBgammVvdF7aPIgH2jxMDdivezgFu6iKpmT+48+F8Hhg5J/sfnDieg0aeG/jfSvkYQU2/pceFPDKlqZzwnfQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.5.tgz", - "integrity": "sha512-1iT4FVL0dJ76/q1wd7XDsXrSW+oLoquptvh4CLR4kITDtqi2e/xwXwdCVH8hVHU43wgJdsq7Gxuzcs6Iq/7bxQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.5.tgz", - "integrity": "sha512-nk4tGP3JThz4La38Uy/gzyXtpkPW8zSAmoUhK9xKKXdBCzKODMc2adkB2+8om9BDYugz+uGV7sLmpTYzvmz6Sw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.5.tgz", - "integrity": "sha512-PrikaNjiXdR2laW6OIjlbeuCPrPaAl0IwPIaRv+SMV8CiM8i2LqVUHFC1+8eORgWyY7yhQY+2U2fA55mBzReaw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.5.tgz", - "integrity": "sha512-cPzojwW2okgh7ZlRpcBEtsX7WBuqbLrNXqLU89GxWbNt6uIg78ET82qifUy3W6OVww6ZWobWub5oqZOVtwolfw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.5.tgz", - "integrity": "sha512-Z9kfb1v6ZlGbWj8EJk9T6czVEjjq2ntSYLY2cw6pAZl4oKtfgQuS4HOq41M/BcoLPzrUbNd+R4BXFyH//nHxVg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.5.tgz", - "integrity": "sha512-sQ7l00M8bSv36GLV95BVAdhJ2QsIbCuCjh/uYrWiMQSUuV+LpXwIqhgJDcvMTj+VsQmqAHL2yYaasENvJ7CDKA==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.5.tgz", - "integrity": "sha512-0ur7ae16hDUC4OL5iEnDb0tZHDxYmuQyhKhsPBV8f99f6Z9KQM02g33f93rNH5A30agMS46u2HP6qTdEt6Q1kg==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.5.tgz", - "integrity": "sha512-kB/66P1OsHO5zLz0i6X0RxlQ+3cu0mkxS3TKFvkb5lin6uwZ/ttOkP3Z8lfR9mJOBk14ZwZ9182SIIWFGNmqmg==", - "cpu": [ - "mips64el" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.5.tgz", - "integrity": "sha512-UZCmJ7r9X2fe2D6jBmkLBMQetXPXIsZjQJCjgwpVDz+YMcS6oFR27alkgGv3Oqkv07bxdvw7fyB71/olceJhkQ==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.5.tgz", - "integrity": "sha512-kTxwu4mLyeOlsVIFPfQo+fQJAV9mh24xL+y+Bm6ej067sYANjyEw1dNHmvoqxJUCMnkBdKpvOn0Ahql6+4VyeA==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.5.tgz", - "integrity": "sha512-K2dSKTKfmdh78uJ3NcWFiqyRrimfdinS5ErLSn3vluHNeHVnBAFWC8a4X5N+7FgVE1EjXS1QDZbpqZBjfrqMTQ==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.5.tgz", - "integrity": "sha512-uhj8N2obKTE6pSZ+aMUbqq+1nXxNjZIIjCjGLfsWvVpy7gKCOL6rsY1MhRh9zLtUtAI7vpgLMK6DxjO8Qm9lJw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.5.tgz", - "integrity": "sha512-pwHtMP9viAy1oHPvgxtOv+OkduK5ugofNTVDilIzBLpoWAM16r7b/mxBvfpuQDpRQFMfuVr5aLcn4yveGvBZvw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.5.tgz", - "integrity": "sha512-WOb5fKrvVTRMfWFNCroYWWklbnXH0Q5rZppjq0vQIdlsQKuw6mdSihwSo4RV/YdQ5UCKKvBy7/0ZZYLBZKIbwQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.5.tgz", - "integrity": "sha512-7A208+uQKgTxHd0G0uqZO8UjK2R0DDb4fDmERtARjSHWxqMTye4Erz4zZafx7Di9Cv+lNHYuncAkiGFySoD+Mw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.5.tgz", - "integrity": "sha512-G4hE405ErTWraiZ8UiSoesH8DaCsMm0Cay4fsFWOOUcz8b8rC6uCvnagr+gnioEjWn0wC+o1/TAHt+It+MpIMg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.5.tgz", - "integrity": "sha512-l+azKShMy7FxzY0Rj4RCt5VD/q8mG/e+mDivgspo+yL8zW7qEwctQ6YqKX34DTEleFAvCIUviCFX1SDZRSyMQA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.5.tgz", - "integrity": "sha512-O2S7SNZzdcFG7eFKgvwUEZ2VG9D/sn/eIiz8XRZ1Q/DO5a3s76Xv0mdBzVM5j5R639lXQmPmSo0iRpHqUUrsxw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.5.tgz", - "integrity": "sha512-onOJ02pqs9h1iMJ1PQphR+VZv8qBMQ77Klcsqv9CNW2w6yLqoURLcgERAIurY6QE63bbLuqgP9ATqajFLK5AMQ==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.5.tgz", - "integrity": "sha512-TXv6YnJ8ZMVdX+SXWVBo/0p8LTcrUYngpWjvm91TMjjBQii7Oz11Lw5lbDV5Y0TzuhSJHwiH4hEtC1I42mMS0g==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, "node_modules/@eslint-community/eslint-utils": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz", @@ -1089,10 +663,6 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/ci.ci": { - "resolved": ".ci", - "link": true - }, "node_modules/cliui": { "version": "7.0.4", "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", @@ -1243,47 +813,6 @@ "dev": true, "license": "MIT" }, - "node_modules/esbuild": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.5.tgz", - "integrity": "sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.5", - "@esbuild/android-arm": "0.25.5", - "@esbuild/android-arm64": "0.25.5", - "@esbuild/android-x64": "0.25.5", - "@esbuild/darwin-arm64": "0.25.5", - "@esbuild/darwin-x64": "0.25.5", - "@esbuild/freebsd-arm64": "0.25.5", - "@esbuild/freebsd-x64": "0.25.5", - "@esbuild/linux-arm": "0.25.5", - "@esbuild/linux-arm64": "0.25.5", - "@esbuild/linux-ia32": "0.25.5", - "@esbuild/linux-loong64": "0.25.5", - "@esbuild/linux-mips64el": "0.25.5", - "@esbuild/linux-ppc64": "0.25.5", - "@esbuild/linux-riscv64": "0.25.5", - "@esbuild/linux-s390x": "0.25.5", - "@esbuild/linux-x64": "0.25.5", - "@esbuild/netbsd-arm64": "0.25.5", - "@esbuild/netbsd-x64": "0.25.5", - "@esbuild/openbsd-arm64": "0.25.5", - "@esbuild/openbsd-x64": "0.25.5", - "@esbuild/sunos-x64": "0.25.5", - "@esbuild/win32-arm64": "0.25.5", - "@esbuild/win32-ia32": "0.25.5", - "@esbuild/win32-x64": "0.25.5" - } - }, "node_modules/escalade": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", diff --git a/u/process/env.ts b/u/process/env.ts index 88fb490..f59fadf 100644 --- a/u/process/env.ts +++ b/u/process/env.ts @@ -1,27 +1,25 @@ -import { IOptional, Either, Optional, type IEither } from '@emprespresso/pengueno'; +import { IOptional, Either, Optional, type IEither, type ObjectFromList } from '@emprespresso/pengueno'; -export const getEnv = (name: string): IOptional => Optional.from(process.env[name]); +// type safe environment variables -export const getRequiredEnv = (name: string): IEither => - Either.fromFailable(() => getEnv(name).get()).mapLeft( +export const getEnv = (name: string): IOptional => Optional.from(process.env[name]); + +export const getRequiredEnv = (name: V): IEither => + Either.fromFailable(() => getEnv(name).get()).mapLeft( () => new Error(`environment variable "${name}" is required D:`), ); -type ObjectFromList, V = string> = { - [K in T extends ReadonlyArray ? U : never]: V; -}; - -export const getRequiredEnvVars = (vars: Array) => { +export const getRequiredEnvVars = (vars: Array): IEither> => { type Environment = ObjectFromList; const emptyEnvironment = Either.right({}); - const addTo = (env: Environment, key: V) => (val: string) => + const addTo = (env: Environment, key: V, val: string) => { ...env, [key]: val, }; - return Either.joinRight( - vars, - (envVar: V, environment: Environment) => getRequiredEnv(envVar).mapRight(addTo(environment, envVar)), + return vars.reduce( + (environment, key) => + environment.joinRight(getRequiredEnv(key), (value, environment) => addTo(environment, key, value)), emptyEnvironment, ); }; diff --git a/u/process/exec.ts b/u/process/exec.ts new file mode 100644 index 0000000..a2cdbca --- /dev/null +++ b/u/process/exec.ts @@ -0,0 +1,59 @@ +import { + Either, + IEither, + type ITraceable, + LogLevel, + LogMetricTraceSupplier, + Metric, + TraceUtil, +} from '@emprespresso/pengueno'; +import { promisify } from 'node:util'; +import { exec as execCallback } from 'node:child_process'; +const exec = promisify(execCallback); + +export type Command = string[] | string; +export type StdStreams = { stdout: string; stderr: string }; + +export const CmdMetric = Metric.fromName('Exec').asResult(); +type Environment = Record; +type Options = { env?: Environment; clearEnv?: boolean }; +export const getStdout = ( + cmd: ITraceable, + options: Options = {}, +): Promise> => + cmd + .flatMap(TraceUtil.withFunctionTrace(getStdout)) + .flatMap((tCmd) => tCmd.traceScope(() => `Command = ${tCmd.get()}`)) + .map((tCmd) => { + const cmd = tCmd.get(); + const _exec = typeof cmd === 'string' ? cmd : cmd.join(' '); + const env = options.clearEnv ? options.env : { ...process.env, ...options.env }; + return Either.fromFailableAsync(exec(_exec, { env })); + }) + .map( + TraceUtil.promiseify((tEitherStdStreams) => + tEitherStdStreams.get().mapRight(({ stderr, stdout }) => { + if (stderr) tEitherStdStreams.trace.traceScope(LogLevel.DEBUG).trace(`StdErr = ${stderr}`); + return stdout; + }), + ), + ) + .peek(TraceUtil.promiseify(TraceUtil.traceResultingEither(CmdMetric))) + .get(); + +export const getStdoutMany = ( + cmds: ITraceable, LogMetricTraceSupplier>, + options: Options = {}, +): Promise>> => + cmds + .coExtend((t) => t.get()) + .reduce( + async (_result, tCmd) => { + const result = await _result; + return result.joinRightAsync( + () => tCmd.map((cmd) => getStdout(cmd, options)).get(), + (stdout, pre) => pre.concat(stdout), + ); + }, + Promise.resolve(Either.right>([])), + ); diff --git a/u/process/index.ts b/u/process/index.ts index 6945a0f..2d74a5f 100644 --- a/u/process/index.ts +++ b/u/process/index.ts @@ -1,5 +1,5 @@ +export * from './exec.js'; export * from './env.js'; -export * from './run.js'; export * from './validate_identifier.js'; export * from './argv.js'; export * from './signals.js'; diff --git a/u/process/run.ts b/u/process/run.ts deleted file mode 100644 index 1d19129..0000000 --- a/u/process/run.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { - Either, - IEither, - type ITraceable, - LogLevel, - LogMetricTraceSupplier, - Metric, - TraceUtil, -} from '@emprespresso/pengueno'; -import { promisify } from 'node:util'; -import { exec as execCallback } from 'node:child_process'; -const exec = promisify(execCallback); - -export type Command = string[] | string; -export type StdStreams = { stdout: string; stderr: string }; - -export const CmdMetric = Metric.fromName('Exec').asResult(); -export const getStdout = ( - c: ITraceable, - options: { env?: Record; clearEnv?: boolean } = {}, -): Promise> => - c - .flatMap(TraceUtil.withFunctionTrace(getStdout)) - .flatMap((tCmd) => tCmd.traceScope(() => `Command = ${tCmd.get()}`)) - .map((tCmd) => { - const cmd = tCmd.get(); - const _exec = typeof cmd === 'string' ? cmd : cmd.join(' '); - const env = options.clearEnv ? options.env : { ...process.env, ...options.env }; - return Either.fromFailableAsync(exec(_exec, { env })); - }) - .map( - TraceUtil.promiseify((tEitherStdStreams) => - tEitherStdStreams.get().mapRight(({ stderr, stdout }) => { - if (stderr) tEitherStdStreams.trace.traceScope(LogLevel.DEBUG).trace(`StdErr = ${stderr}`); - return stdout; - }), - ), - ) - .peek(TraceUtil.promiseify(TraceUtil.traceResultingEither(CmdMetric))) - .get(); diff --git a/u/trace/itrace.ts b/u/trace/itrace.ts index 9c33ad2..57c4419 100644 --- a/u/trace/itrace.ts +++ b/u/trace/itrace.ts @@ -28,8 +28,8 @@ export interface ITraceable { readonly map: <_T>(mapper: ITraceableMapper) => ITraceable<_T, Trace>; readonly bimap: <_T>(mapper: ITraceableMapper, Trace>) => ITraceable<_T, Trace>; readonly coExtend: <_T>( - mapper: ITraceableMapper, Trace>, - ) => ReadonlyArray>; + mapper: ITraceableMapper, Trace>, + ) => Array>; readonly peek: (peek: ITraceableMapper) => ITraceable; readonly traceScope: (mapper: ITraceableMapper) => ITraceable; @@ -51,7 +51,7 @@ export class TraceableImpl implements ITraceable { return new TraceableImpl(result, this.trace); } - public coExtend<_T>(mapper: ITraceableMapper, Trace>): ReadonlyArray> { + public coExtend<_T>(mapper: ITraceableMapper, Trace>): Array> { const results = mapper(this); return Array.from(results).map((result) => this.move(result)); } diff --git a/u/types/fn/callable.ts b/u/types/fn/callable.ts index 51756d7..60d747b 100644 --- a/u/types/fn/callable.ts +++ b/u/types/fn/callable.ts @@ -10,6 +10,8 @@ export interface Mapper extends Callable { (t: T): U; } +export interface Predicate extends Mapper {} + export interface BiMapper extends Callable { (t: T, u: U): R; } diff --git a/u/types/fn/either.ts b/u/types/fn/either.ts index aa67d41..80f32b4 100644 --- a/u/types/fn/either.ts +++ b/u/types/fn/either.ts @@ -1,18 +1,36 @@ -import { BiMapper, IOptional, type Mapper, Optional, type Supplier, Tagged, isTagged } from '@emprespresso/pengueno'; +import { + BiMapper, + IOptional, + type Mapper, + Optional, + Predicate, + type Supplier, + Tagged, + isTagged, +} from '@emprespresso/pengueno'; export const IEitherTag = 'IEither' as const; export type IEitherTag = typeof IEitherTag; export const isEither = (o: unknown): o is IEither => isTagged(o, IEitherTag); export interface IEither extends Tagged { - readonly mapBoth: <_E, _T>(errBranch: Mapper, okBranch: Mapper) => IEither<_E, _T>; - readonly fold: <_T>(leftFolder: Mapper, rightFolder: Mapper) => _T; readonly left: Supplier>; readonly right: Supplier>; - readonly moveRight: <_T>(t: _T) => IEither; + readonly mapRight: <_T>(mapper: Mapper) => IEither; + readonly filter: (mapper: Predicate) => IEither; readonly mapLeft: <_E>(mapper: Mapper) => IEither<_E, T>; + readonly mapBoth: <_E, _T>(errBranch: Mapper, okBranch: Mapper) => IEither<_E, _T>; + readonly flatMap: <_T>(mapper: Mapper>) => IEither; readonly flatMapAsync: <_T>(mapper: Mapper>>) => Promise>; + + readonly moveRight: <_T>(t: _T) => IEither; + readonly fold: <_T>(leftFolder: Mapper, rightFolder: Mapper) => _T; + readonly joinRight: (other: IEither, mapper: BiMapper) => IEither; + readonly joinRightAsync: ( + other: Supplier>> | Promise>, + mapper: BiMapper, + ) => Promise>; } const ELeftTag = 'E.Left' as const; @@ -62,6 +80,11 @@ export class Either extends _Tagged implements IEither { return Either.left(this.self.err); } + public filter(mapper: Predicate): IEither { + if (isLeft(this.self)) return Either.left(this.self.err); + return Either.fromFailable(() => this.right().filter(mapper).get()); + } + public async flatMapAsync<_T>(mapper: Mapper>>): Promise> { if (isLeft(this.self)) return Promise.resolve(Either.left(this.self.err)); return await mapper(this.self.ok).catch((err) => Either.left(err)); @@ -82,23 +105,18 @@ export class Either extends _Tagged implements IEither { return Optional.none(); } - static joinRight( - arr: Array, - mapper: BiMapper>, - init: IEither, - ): IEither { - return arr.reduce((acc: IEither, x: K) => acc.flatMap((t) => mapper(x, t)), init); + public joinRight(other: IEither, mapper: BiMapper) { + return this.flatMap((t) => other.mapRight((o) => mapper(o, t))); } - static joinRightAsync( - arr: Array, - mapper: BiMapper>>, - init: IEither, - ): Promise> { - return arr.reduce( - (acc: Promise>, x: K) => acc.then((res) => res.flatMapAsync((t) => mapper(x, t))), - Promise.resolve(init), - ); + public joinRightAsync( + other: Supplier>> | Promise>, + mapper: BiMapper, + ) { + return this.flatMapAsync(async (t) => { + const o = typeof other === 'function' ? other() : other; + return o.then((other) => other.mapRight((o) => mapper(o, t))); + }); } static left(e: E): IEither { diff --git a/u/types/fn/optional.ts b/u/types/fn/optional.ts index 3396a45..504e496 100644 --- a/u/types/fn/optional.ts +++ b/u/types/fn/optional.ts @@ -1,4 +1,4 @@ -import { type Mapper, type Supplier, Tagged, isTagged } from '@emprespresso/pengueno'; +import { type Mapper, Predicate, type Supplier, Tagged, isTagged } from '@emprespresso/pengueno'; export type MaybeGiven = T | undefined | null; @@ -9,7 +9,7 @@ export class IOptionalEmptyError extends Error {} export interface IOptional = NonNullable> extends Tagged, Iterable { readonly move: <_T>(t: MaybeGiven<_T>) => IOptional<_T>; readonly map: <_T>(mapper: Mapper>) => IOptional<_T>; - readonly filter: (mapper: Mapper) => IOptional; + readonly filter: (mapper: Predicate) => IOptional; readonly flatMap: <_T>(mapper: Mapper>>) => IOptional<_T>; readonly orSome: (supplier: Supplier>) => IOptional; readonly get: Supplier; @@ -48,11 +48,11 @@ export class Optional = NonNullable> extends _Tag } public get(): T { - if (isNone(this.self)) throw new IOptionalEmptyError('empty value'); + if (isNone(this.self)) throw new IOptionalEmptyError('called get() on None optional'); return this.self.value; } - public filter(mapper: Mapper): IOptional { + public filter(mapper: Predicate): IOptional { if (isNone(this.self) || !mapper(this.self.value)) return Optional.none(); return Optional.some(this.self.value); } diff --git a/u/types/index.ts b/u/types/index.ts index c68cedc..fc1b15a 100644 --- a/u/types/index.ts +++ b/u/types/index.ts @@ -1,3 +1,5 @@ +export * from './misc.js'; + export * from './object.js'; export * from './tagged.js'; diff --git a/u/types/misc.ts b/u/types/misc.ts new file mode 100644 index 0000000..77833c4 --- /dev/null +++ b/u/types/misc.ts @@ -0,0 +1,3 @@ +export type ObjectFromList, V = string> = { + [K in T extends ReadonlyArray ? U : never]: V; +}; diff --git a/worker/executor.ts b/worker/executor.ts index 47b337c..11c24f6 100644 --- a/worker/executor.ts +++ b/worker/executor.ts @@ -28,7 +28,7 @@ export const executeJob = (tJob: ITraceable) => { .flatMapAsync((args) => getStdout(tJob.move(tJob.get().type), { env: args })), ) .flatMapAsync(TraceUtil.promiseify(TraceUtil.traceResultingEither(metric))) - .peek(TraceUtil.promiseify(t => t.traceScope(() => LogLevel.DEBUG).trace.trace(JSON.stringify(t.get())))) + .peek(TraceUtil.promiseify((t) => t.traceScope(() => LogLevel.DEBUG).trace.trace(JSON.stringify(t.get())))) .get(); }; // -- -- diff --git a/worker/jobs/ci_pipeline.run b/worker/jobs/ci_pipeline.run index 87bcc84..6bee929 100755 --- a/worker/jobs/ci_pipeline.run +++ b/worker/jobs/ci_pipeline.run @@ -1,5 +1,16 @@ #!/bin/sh +# add scripts executed by the pipeline export PATH=$PATH:$PIPELINE_PATH +containerid=$(cat /etc/hostname) +isindocker=$(docker ps -q -f "id=$containerid") +if [ -n "$isindocker" ]; then + executorLaminarPath=$(docker inspect "$containerid" | jq -r '.[0].Mounts[] | select(.Destination == "/var/lib/laminar") | .Source') +else + executorLaminarPath=$(pwd) +fi + +export executorLaminarPath + checkout_ci.js diff --git a/worker/scripts/checkout_ci.ts b/worker/scripts/checkout_ci.ts index d71df6e..cba6a2f 100755 --- a/worker/scripts/checkout_ci.ts +++ b/worker/scripts/checkout_ci.ts @@ -5,23 +5,22 @@ import { Either, LogTraceable, getRequiredEnvVars, - getStdout, isObject, LogMetricTraceable, Metric, prependWith, TraceUtil, - IEither, + getStdoutMany, } from '@emprespresso/pengueno'; import { mkdir, readFile, rm } from 'fs/promises'; -import { basename, join } from 'path'; +import { join } from 'path'; import { type CheckoutCiJob, type FetchCodeJob, PipelineImpl } from '@emprespresso/ci_model'; import { executeJob, executePipeline } from '@emprespresso/ci_worker'; -export interface CiWorkflow { +interface CiWorkflow { workflow: string; } -export function isCiWorkflow(t: unknown): t is CiWorkflow { +function isCiWorkflow(t: unknown): t is CiWorkflow { return isObject(t) && 'workflow' in t && typeof t.workflow === 'string' && !t.workflow.includes('..'); } @@ -30,8 +29,19 @@ const OCI_REGISTRY = 'oci.liz.coffee'; const PIPELINE_IMAGE = OCI_REGISTRY + '/emprespresso/ci_worker:release'; const READONLY_CREDENTIALS = { username: 'readonly', password: 'readonly' }; -const run = Date.now().toString(); -const eitherJob = getRequiredEnvVars(['remote', 'refname', 'rev']).mapRight( +// use a different directory per job run +// even though the Laminar run is unique per job, there's potential for "children" we spawn +// to be a checkout_ci job as well. in which case, we don't want any conflicts with whatever +// the "parent" was doing, so we create a unique directory for each. +// i.e. +// Laminar Run: 57, CWD=/var/lib/laminar/run/57 +// ci_pipeline (1000.uuidA) +// -> checkout_ci +// -> [...children] +// -> checkout_ci (1000.uuidB) +const run = `${Date.now()}.${crypto.randomUUID().replaceAll('-', '')}`; + +const eitherJob = getRequiredEnvVars(['remote', 'refname', 'rev', 'executorLaminarPath']).mapRight( (baseArgs) => { type: 'checkout_ci.js', @@ -46,9 +56,9 @@ const afterJob = eitherJob.flatMapAsync((job) => Either.fromFailableAsync(() => rm(getWorkingDirectoryForCiJob(job), { recursive: true })), ); +const logTraceableJob = LogTraceable.of(eitherJob).flatMap(TraceUtil.withTrace(`checkout_ci.run.${run}`)); const ciRunMetric = Metric.fromName('checkout_ci.run'); -const _logJob = LogTraceable.of(eitherJob).flatMap(TraceUtil.withTrace(`checkout_ci.${run}`)); -await LogMetricTraceable.ofLogTraceable(_logJob) +await LogMetricTraceable.ofLogTraceable(logTraceableJob) .flatMap(TraceUtil.withMetricTrace(ciRunMetric)) .map((tEitherJob) => tEitherJob.get().flatMapAsync((ciJob) => { @@ -84,32 +94,25 @@ await LogMetricTraceable.ofLogTraceable(_logJob) ), ); return repoCiFileContents - .flatMap((fileText) => Either.fromFailable(() => JSON.parse(fileText))) - .flatMap((json) => - eitherCiJob.flatMap((ciJob): IEither; job: CheckoutCiJob }> => { - if (!isCiWorkflow(json)) { - const e = new Error("couldn't find any valid ci configuration (。•́︿•̀。), that's okay~"); - return Either.left(e); - } - return Either.right({ - commands: getPipelineGenerationCommand(ciJob, json.workflow), - job: ciJob, - }); - }), - ); + .flatMap((fileText) => + Either.fromFailable(() => JSON.parse(fileText)).filter((json) => isCiWorkflow(json)), + ) + .joinRight(eitherCiJob, (job: CheckoutCiJob, { workflow }) => ({ + job, + commands: getPipelineGenerationCommand(job, workflow), + })); }) .map(async (tEitherPipelineGenerationCommand) => { const eitherJobCommand = await tEitherPipelineGenerationCommand.get(); - const pipelineSerialized = await eitherJobCommand.flatMapAsync(({ commands }) => { - return Either.joinRightAsync( - commands, - (command) => tEitherPipelineGenerationCommand.move(command).map(getStdout).get(), - Either.right(''), - ); - }); + const pipelineSerialized = await eitherJobCommand.flatMapAsync(({ commands }) => + getStdoutMany(tEitherPipelineGenerationCommand.move(commands)), + ); return pipelineSerialized - .flatMap((s) => PipelineImpl.from(s)) - .flatMap((pipeline) => eitherJobCommand.mapRight(({ job }) => ({ job, pipeline }))); + .flatMap((results) => { + const pipeline = results.at(-1)!; + return PipelineImpl.from(pipeline); + }) + .joinRight(eitherJobCommand, (job, pipeline) => ({ job, pipeline })); }) .peek( TraceUtil.promiseify((tEitherPipeline) => @@ -147,7 +150,18 @@ function getWorkingDirectoryForCiJob(job: CheckoutCiJob) { } function getSrcDirectoryForCiJob(job: CheckoutCiJob) { - return `${job.arguments.returnPath}/${job.arguments.run}/src`; + return `${getWorkingDirectoryForCiJob(job)}/src`; +} + +function getSrcDirectoryForChildContainer(job: CheckoutCiJob) { + // the container which runs the pipeline synthesizer (A) might be spawned by another container + // (B) ((which should be the one running this job)) by talking to the host's docker daemon + // (mounting /var/run/docker.sock) and executing the {@link getPipelineGenerationCommand}. + // + // so mounting {@link getSrcDirectoryForCiJob} has no meaning as it doesn't exist on the host; + // here we replace the path in (B) with the actual volume source on the host, where the src + // exists. + return getSrcDirectoryForCiJob(job).replace('/var/lib/laminar', job.arguments.executorLaminarPath); } function getPipelineGenerationCommand( @@ -170,7 +184,7 @@ function getPipelineGenerationCommand( '-e', ), '-v', - `${getSrcDirectoryForCiJob(job)}:/src`, + `${getSrcDirectoryForChildContainer(job)}:/src`, image, `/src/${pipelineGeneratorPath}`, ], -- cgit v1.2.3-70-g09d2