#!/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();