#!/usr/bin/env node "use strict"; // ../u/leftpadesque/debug.ts var _hasEnv = true; var _env = _hasEnv && (process.env.ENVIRONMENT ?? "").toLowerCase().includes("prod") ? "production" : "development"; var isProd = () => _env === "production"; var _debug = !isProd() || _hasEnv && ["y", "t"].some((process.env.DEBUG ?? "").toLowerCase().startsWith); var isDebug = () => _debug; // ../u/leftpadesque/memoize.ts var memoize = (fn) => { const cache = /* @__PURE__ */ new Map(); return (...args) => { const key = JSON.stringify(args); if (cache.has(key)) { return cache.get(key); } const res = fn.apply(args); cache.set(key, res); return res; }; }; // ../u/trace/itrace.ts var TraceableImpl = class _TraceableImpl { constructor(item, trace) { this.item = item; this.trace = trace; } map(mapper) { const result = mapper(this); return new _TraceableImpl(result, this.trace); } coExtend(mapper) { const results = mapper(this); return Array.from(results).map((result) => this.move(result)); } flatMap(mapper) { return mapper(this); } flatMapAsync(mapper) { return new _TraceableImpl( mapper(this).then((t) => t.get()), this.trace ); } traceScope(mapper) { return new _TraceableImpl(this.get(), this.trace.traceScope(mapper(this))); } peek(peek) { peek(this); return this; } move(t) { return this.map(() => t); } bimap(mapper) { const { item, trace: _trace } = mapper(this); return this.move(item).traceScope(() => _trace); } get() { return this.item; } }; // ../u/trace/metric/emittable.ts var EmittableMetric = class { constructor(name, unit) { this.name = name; this.unit = unit; } withValue(value) { return { name: this.name, unit: this.unit, emissionTimestamp: Date.now(), value, _tag: MetricValueTag }; } }; // ../u/trace/metric/metric.ts var _Tagged = class { constructor(_tag = IMetricTag) { this._tag = _tag; } }; var Metric = class _Metric extends _Tagged { constructor(name, parent = void 0, count = new EmittableMetric(_Metric.join(name, "count"), "COUNT" /* COUNT */), time = new EmittableMetric(_Metric.join(name, "time"), "MILLISECONDS" /* MILLISECONDS */)) { super(); this.name = name; this.parent = parent; this.count = count; this.time = time; } static DELIM = "."; child(_name) { const childName = _Metric.join(this.name, _name); return new _Metric(childName, this); } asResult() { return ResultMetric.from(this); } static join(...name) { return name.join(_Metric.DELIM); } static fromName(name) { return new _Metric(name); } }; var ResultMetric = class _ResultMetric extends Metric { constructor(name, parent = void 0, failure, success, warn) { super(name, parent); this.name = name; this.parent = parent; this.failure = failure; this.success = success; this.warn = warn; } static from(metric) { const failure = metric.child("failure"); const success = metric.child("success"); const warn = metric.child("warn"); return new _ResultMetric(metric.name, metric.parent, failure, success, warn); } }; // ../u/trace/metric/trace.ts var MetricsTrace = class _MetricsTrace { constructor(metricConsumer, activeTraces = /* @__PURE__ */ new Map(), completedTraces = /* @__PURE__ */ new Set()) { this.metricConsumer = metricConsumer; this.activeTraces = activeTraces; this.completedTraces = completedTraces; } traceScope(trace) { const now = Date.now(); const metricsToTrace = (Array.isArray(trace) ? trace : [trace]).filter(isIMetric); const initialTraces = new Map(metricsToTrace.map((metric) => [metric, now])); return new _MetricsTrace(this.metricConsumer, initialTraces); } trace(metrics) { if (!metrics || typeof metrics === "string") { return this; } const now = Date.now(); const allMetrics = Array.isArray(metrics) ? metrics : [metrics]; const valuesToEmit = allMetrics.filter(isMetricValue); const traceableMetrics = allMetrics.filter(isIMetric); const metricsToStart = traceableMetrics.filter((m) => !this.activeTraces.has(m)); const metricsToEnd = traceableMetrics.filter((m) => this.activeTraces.has(m) && !this.completedTraces.has(m)); const endedMetricValues = metricsToEnd.flatMap((metric) => [ metric.count.withValue(1), metric.time.withValue(now - this.activeTraces.get(metric)) ]); const allMetricsToEmit = [...valuesToEmit, ...endedMetricValues]; if (allMetricsToEmit.length > 0) { this.metricConsumer(allMetricsToEmit); } const nextActiveTraces = new Map([ ...this.activeTraces, ...metricsToStart.map((m) => [m, now]) ]); const nextCompletedTraces = /* @__PURE__ */ new Set([...this.completedTraces, ...metricsToEnd]); return new _MetricsTrace(this.metricConsumer, nextActiveTraces, nextCompletedTraces); } }; // ../u/trace/metric/index.ts var MetricValueTag = "MetricValue"; var isMetricValue = (t) => isTagged(t, MetricValueTag); var IMetricTag = "IMetric"; var isIMetric = (t) => isTagged(t, IMetricTag); // ../u/trace/log/ansi.ts var ANSI = { RESET: "\x1B[0m", BOLD: "\x1B[1m", DIM: "\x1B[2m", RED: "\x1B[31m", GREEN: "\x1B[32m", YELLOW: "\x1B[33m", BLUE: "\x1B[34m", MAGENTA: "\x1B[35m", CYAN: "\x1B[36m", WHITE: "\x1B[37m", BRIGHT_RED: "\x1B[91m", BRIGHT_YELLOW: "\x1B[93m", GRAY: "\x1B[90m" }; // ../u/trace/log/level.ts var logLevelOrder = [ "DEBUG" /* DEBUG */, "INFO" /* INFO */, "WARN" /* WARN */, "ERROR" /* ERROR */, "SYS" /* SYS */ ]; var isLogLevel = (l) => typeof l === "string" && logLevelOrder.some((level) => level === l); // ../u/trace/log/pretty_json_console.ts var PrettyJsonConsoleLogger = class { log(level, ...trace) { const message = JSON.stringify( { level, trace }, null, 4 ); const styled = `${this.getStyle(level)}${message}${ANSI.RESET} `; this.getStream(level)(styled); } getStream(level) { if (level === "ERROR" /* ERROR */) { return console.error; } return console.log; } getStyle(level) { switch (level) { case "UNKNOWN" /* UNKNOWN */: case "INFO" /* INFO */: return `${ANSI.MAGENTA}`; case "DEBUG" /* DEBUG */: return `${ANSI.CYAN}`; case "WARN" /* WARN */: return `${ANSI.BRIGHT_YELLOW}`; case "ERROR" /* ERROR */: return `${ANSI.BRIGHT_RED}`; case "SYS" /* SYS */: return `${ANSI.DIM}${ANSI.BLUE}`; } } }; // ../u/trace/log/trace.ts var LogTrace = class _LogTrace { constructor(logger = new PrettyJsonConsoleLogger(), traces = [defaultTrace], defaultLevel = "INFO" /* INFO */, allowedLevels = defaultAllowedLevelsSupplier) { this.logger = logger; this.traces = traces; this.defaultLevel = defaultLevel; this.allowedLevels = allowedLevels; } traceScope(trace) { return new _LogTrace(this.logger, this.traces.concat(trace), this.defaultLevel, this.allowedLevels); } trace(trace) { const { traces, level: _level } = this.foldTraces(this.traces.concat(trace)); if (!this.allowedLevels().has(_level)) return; const level = _level === "UNKNOWN" /* UNKNOWN */ ? this.defaultLevel : _level; this.logger.log(level, ...traces); } foldTraces(_traces) { const _logTraces = _traces.map((trace) => typeof trace === "function" ? trace() : trace); const _level = _logTraces.filter((trace) => isLogLevel(trace)).reduce((acc, level2) => Math.max(logLevelOrder.indexOf(level2), acc), -1); const level = logLevelOrder[_level] ?? "UNKNOWN" /* UNKNOWN */; const traces = _logTraces.filter((trace) => !isLogLevel(trace)).map((trace) => { if (typeof trace === "object") { return `TracedException.Name = ${trace.name}, TracedException.Message = ${trace.message}, TracedException.Stack = ${trace.stack}`; } return trace; }); return { level, traces }; } }; var defaultTrace = () => `TimeStamp = ${(/* @__PURE__ */ new Date()).toISOString()}`; var defaultAllowedLevels = memoize( (isDebug2) => /* @__PURE__ */ new Set([ "UNKNOWN" /* UNKNOWN */, ...isDebug2 ? ["DEBUG" /* DEBUG */] : [], "INFO" /* INFO */, "WARN" /* WARN */, "ERROR" /* ERROR */, "SYS" /* SYS */ ]) ); var defaultAllowedLevelsSupplier = () => defaultAllowedLevels(isDebug()); // ../u/trace/trace.ts var LogTraceable = class _LogTraceable extends TraceableImpl { static LogTrace = new LogTrace(); static of(t) { return new _LogTraceable(t, _LogTraceable.LogTrace); } }; var getEmbeddedMetricConsumer = (logTrace) => (metrics) => { if (metrics.length === 0) return; logTrace.traceScope("SYS" /* SYS */).trace(`Metrics = ${JSON.stringify(metrics)}`); }; var EmbeddedMetricsTraceable = class _EmbeddedMetricsTraceable extends TraceableImpl { static MetricsTrace = new MetricsTrace(getEmbeddedMetricConsumer(LogTraceable.LogTrace)); static of(t, metricsTrace = _EmbeddedMetricsTraceable.MetricsTrace) { return new _EmbeddedMetricsTraceable(t, metricsTrace); } }; // ../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); var CmdMetric = Metric.fromName("Exec").asResult(); // ../u/process/signals.ts var SigIntMetric = Metric.fromName("SigInt").asResult(); var SigTermMetric = Metric.fromName("SigTerm").asResult(); // ../u/server/response/pengueno.ts var ResponseCodeMetrics = [0, 1, 2, 3, 4, 5].map((x) => Metric.fromName(`response.${x}xx`).asResult()); // ../u/server/activity/health.ts var healthCheckMetric = Metric.fromName("Health").asResult(); // ../u/server/filter/json.ts var ParseJsonMetric = Metric.fromName("JsonParse").asResult(); // ../u/server/filter/index.ts var ErrorSource = ((ErrorSource2) => { ErrorSource2[ErrorSource2["USER"] = "WARN" /* WARN */] = "USER"; ErrorSource2[ErrorSource2["SYSTEM"] = "ERROR" /* ERROR */] = "SYSTEM"; return ErrorSource2; })(ErrorSource || {}); // ../u/types/object.ts var isObject = (o) => typeof o === "object" && !Array.isArray(o) && !!o; // ../u/types/tagged.ts var isTagged = (o, tag) => !!(isObject(o) && "_tag" in o && o._tag === tag); // ../u/types/fn/either.ts var IEitherTag = "IEither"; var ELeftTag = "E.Left"; var isLeft = (o) => isTagged(o, ELeftTag); var ERightTag = "E.Right"; var isRight = (o) => isTagged(o, ERightTag); var _Tagged2 = class { constructor(_tag = IEitherTag) { this._tag = _tag; } }; var Either = class _Either extends _Tagged2 { constructor(self) { super(); this.self = self; } moveRight(t) { return this.mapRight(() => t); } mapBoth(errBranch, okBranch) { if (isLeft(this.self)) return _Either.left(errBranch(this.self.err)); return _Either.right(okBranch(this.self.ok)); } mapRight(mapper) { if (isRight(this.self)) return _Either.right(mapper(this.self.ok)); return _Either.left(this.self.err); } mapLeft(mapper) { if (isLeft(this.self)) return _Either.left(mapper(this.self.err)); return _Either.right(this.self.ok); } flatMap(mapper) { 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)); } fold(leftFolder, rightFolder) { if (isLeft(this.self)) return leftFolder(this.self.err); return rightFolder(this.self.ok); } left() { if (isLeft(this.self)) return Optional.from(this.self.err); return Optional.none(); } right() { 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 }); } static right(t) { return new _Either({ ok: t, _tag: ERightTag }); } static fromFailable(s) { try { return _Either.right(s()); } catch (e) { return _Either.left(e); } } static async fromFailableAsync(s) { return await (typeof s === "function" ? s() : s).then((t) => _Either.right(t)).catch((e) => _Either.left(e)); } }; // ../u/types/fn/optional.ts var IOptionalTag = "IOptional"; var IOptionalEmptyError = class extends Error { }; var OSomeTag = "O.Some"; var ONoneTag = "O.None"; var isNone = (o) => isTagged(o, ONoneTag); var isSome = (o) => isTagged(o, OSomeTag); var _Tagged3 = class { constructor(_tag = IOptionalTag) { this._tag = _tag; } }; var Optional = class _Optional extends _Tagged3 { constructor(self) { super(); this.self = self; } move(t) { return this.map(() => t); } orSome(supplier) { if (isNone(this.self)) return _Optional.from(supplier()); return this; } get() { if (isNone(this.self)) throw new IOptionalEmptyError("called get() on None optional"); return this.self.value; } filter(mapper) { if (isNone(this.self) || !mapper(this.self.value)) return _Optional.none(); return _Optional.some(this.self.value); } map(mapper) { if (isNone(this.self)) return _Optional.none(); return _Optional.from(mapper(this.self.value)); } flatMap(mapper) { if (isNone(this.self)) return _Optional.none(); return _Optional.from(mapper(this.self.value)).orSome(() => _Optional.none()).get(); } present() { return isSome(this.self); } *[Symbol.iterator]() { if (isSome(this.self)) yield this.self.value; } static some(value) { return new _Optional({ value, _tag: OSomeTag }); } static _none = new _Optional({ _tag: ONoneTag }); static none() { return this._none; } static from(value) { if (value === null || value === void 0) return _Optional.none(); return _Optional.some(value); } }; // ../model/job/index.ts var isJob = (j) => !!(isObject(j) && "arguments" in j && isObject(j.arguments) && "type" in j && typeof j.type === "string" && j); // ../model/pipeline/builder.ts var BasePipelineBuilder = class { stages = []; addStage(stage) { this.stages.push(stage); return this; } build() { return new PipelineImpl(this.stages); } }; var DefaultGitHookPipelineBuilder = class extends BasePipelineBuilder { constructor(remoteUrl = process.env.remote, rev = process.env.rev, refname = process.env.refname) { super(); this.remoteUrl = remoteUrl; this.refname = refname; this.addStage({ parallelJobs: [ { type: "fetch_code", arguments: { remoteUrl, checkout: rev, path: this.getSourceDestination() } } ] }); } getSourceDestination() { return this.remoteUrl.split("/").at(-1) ?? "src"; } getBranch() { const branchRefPrefix = "refs/heads/"; return this.refname.split(branchRefPrefix).at(1); } }; // ../model/pipeline/impl.ts var PipelineImpl = class _PipelineImpl { constructor(serialJobs) { this.serialJobs = serialJobs; } serialize() { return JSON.stringify({ serialJobs: this.serialJobs }); } static from(s) { return Either.fromFailable(() => JSON.parse(s)).flatMap( (eitherPipelineJson) => isPipeline(eitherPipelineJson) ? Either.right(eitherPipelineJson) : Either.left(new Error("oh noes D: its a bad pipewine :((")) ).mapRight((pipeline) => new _PipelineImpl(pipeline.serialJobs)); } }; // ../model/pipeline/index.ts var isPipelineStage = (t) => isObject(t) && "parallelJobs" in t && Array.isArray(t.parallelJobs) && t.parallelJobs.every((j) => isJob(j)); var isPipeline = (t) => isObject(t) && "serialJobs" in t && Array.isArray(t.serialJobs) && t.serialJobs.every((p) => isPipelineStage(p)); // dist/ci.js var REGISTRY = "oci.liz.coffee"; var NAMESPACE = "emprespresso"; var IMG = "ci"; var REMOTE = "ssh://src.liz.coffee:2222"; var getPipeline = () => { const gitHookPipeline = new DefaultGitHookPipelineBuilder(); const branch = gitHookPipeline.getBranch(); if (!branch) return gitHookPipeline.build(); const commonBuildArgs = { registry: REGISTRY, namespace: NAMESPACE, imageTag: branch }; const baseCiPackageBuild = { type: "build_docker_image.js", arguments: { ...commonBuildArgs, context: gitHookPipeline.getSourceDestination(), repository: IMG + "_base", buildTarget: IMG + "_base", dockerfile: "Dockerfile" } }; gitHookPipeline.addStage({ parallelJobs: [baseCiPackageBuild] }); const subPackages = ["worker", "hooks"].map((_package) => ({ type: "build_docker_image.js", arguments: { ...commonBuildArgs, repository: `${IMG}_${_package}`, buildTarget: _package, dockerfile: `${_package}/Dockerfile` } })); gitHookPipeline.addStage({ parallelJobs: subPackages }); const isRelease = branch === "release"; if (!isRelease) { return gitHookPipeline.build(); } const fetchAnsibleCode = { type: "fetch_code", arguments: { remoteUrl: `${REMOTE}/infra`, checkout: "main", path: "infra" } }; const thenDeploy = { type: "ansible_playbook.js", arguments: { path: "infra", playbooks: "playbooks/ci.yml" } }; [fetchAnsibleCode, thenDeploy].forEach((deploymentStage) => gitHookPipeline.addStage({ parallelJobs: [deploymentStage] })); return gitHookPipeline.build(); }; var main = () => { const data = getPipeline().serialize(); process.stdout.write(data); }; main();