@intentius/chant 0.124.31 → 0.124.32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/chant +7 -2
- package/dist/cli/commands/check-lexicon-plugin.d.ts +7 -4
- package/dist/cli/commands/check-lexicon-plugin.d.ts.map +1 -1
- package/dist/cli/commands/check-lexicon-plugin.js +10 -7
- package/dist/cli/commands/check-lexicon.d.ts.map +1 -1
- package/dist/cli/commands/check-lexicon.js +18 -12
- package/dist/cli/commands/import.d.ts +10 -0
- package/dist/cli/commands/import.d.ts.map +1 -1
- package/dist/cli/commands/import.js +37 -0
- package/dist/cli/commands/init-lexicon/templates/project.d.ts.map +1 -1
- package/dist/cli/commands/init-lexicon/templates/project.js +4 -3
- package/dist/cli/commands/onboard.d.ts.map +1 -1
- package/dist/cli/commands/onboard.js +8 -4
- package/dist/cli/commands/update.d.ts.map +1 -1
- package/dist/cli/commands/update.js +2 -1
- package/dist/cli/dependencies.d.ts +18 -0
- package/dist/cli/dependencies.d.ts.map +1 -0
- package/dist/cli/dependencies.js +120 -0
- package/dist/cli/handlers/build.d.ts.map +1 -1
- package/dist/cli/handlers/build.js +23 -4
- package/dist/cli/handlers/fan-out.d.ts.map +1 -1
- package/dist/cli/handlers/fan-out.js +12 -4
- package/dist/cli/handlers/graph.d.ts.map +1 -1
- package/dist/cli/handlers/graph.js +16 -6
- package/dist/cli/handlers/lint.d.ts.map +1 -1
- package/dist/cli/handlers/lint.js +6 -0
- package/dist/cli/handlers/operator.d.ts +2 -2
- package/dist/cli/handlers/operator.js +3 -3
- package/dist/cli/handlers/pr.d.ts +14 -0
- package/dist/cli/handlers/pr.d.ts.map +1 -1
- package/dist/cli/handlers/pr.js +488 -32
- package/dist/cli/handlers/promote.d.ts.map +1 -1
- package/dist/cli/handlers/promote.js +3 -1
- package/dist/cli/handlers/run.d.ts +13 -1
- package/dist/cli/handlers/run.d.ts.map +1 -1
- package/dist/cli/handlers/run.js +38 -15
- package/dist/cli/main.d.ts.map +1 -1
- package/dist/cli/main.js +44 -16
- package/dist/cli/parse-args.d.ts.map +1 -1
- package/dist/cli/parse-args.js +15 -0
- package/dist/cli/registry.d.ts +13 -4
- package/dist/cli/registry.d.ts.map +1 -1
- package/dist/cli/registry.js +6 -2
- package/dist/codegen/generate-post-synth-barrel.js +2 -2
- package/dist/codegen/lexicon-regen.d.ts +4 -0
- package/dist/codegen/lexicon-regen.d.ts.map +1 -1
- package/dist/codegen/lexicon-regen.js +1 -1
- package/dist/codegen/pinned-upgrade.js +2 -2
- package/dist/components/capability.d.ts +44 -3
- package/dist/components/capability.d.ts.map +1 -1
- package/dist/components/capability.js +21 -0
- package/dist/components/changed-files.d.ts +7 -1
- package/dist/components/changed-files.d.ts.map +1 -1
- package/dist/components/changed-files.js +9 -1
- package/dist/components/driver.d.ts +38 -0
- package/dist/components/driver.d.ts.map +1 -1
- package/dist/components/driver.js +47 -6
- package/dist/components/fan-out-record.d.ts +6 -1
- package/dist/components/fan-out-record.d.ts.map +1 -1
- package/dist/components/fan-out-record.js +24 -0
- package/dist/components/fan-out-run.d.ts +20 -0
- package/dist/components/fan-out-run.d.ts.map +1 -1
- package/dist/components/fan-out-run.js +21 -1
- package/dist/components/pr-pipeline.d.ts +65 -2
- package/dist/components/pr-pipeline.d.ts.map +1 -1
- package/dist/components/pr-pipeline.js +107 -15
- package/dist/components/pr-run.d.ts +127 -29
- package/dist/components/pr-run.d.ts.map +1 -1
- package/dist/components/pr-run.js +290 -70
- package/dist/components/wave-plan.d.ts +5 -2
- package/dist/components/wave-plan.d.ts.map +1 -1
- package/dist/components/wave-plan.js +12 -1
- package/dist/discovery/fold-import.d.ts.map +1 -1
- package/dist/discovery/fold-import.js +16 -6
- package/dist/discovery/sandbox/bundle.d.ts.map +1 -1
- package/dist/discovery/sandbox/bundle.js +6 -0
- package/dist/discovery/specifier-resolve.d.ts.map +1 -1
- package/dist/discovery/specifier-resolve.js +8 -2
- package/dist/fold/fold.d.ts.map +1 -1
- package/dist/fold/fold.js +68 -1
- package/dist/fold/subset.d.ts +37 -0
- package/dist/fold/subset.d.ts.map +1 -1
- package/dist/fold/subset.js +88 -0
- package/dist/lexicon-module.d.ts +10 -0
- package/dist/lexicon-module.d.ts.map +1 -1
- package/dist/lexicon-module.js +53 -8
- package/dist/lexicon-plugin-helpers.d.ts.map +1 -1
- package/dist/lexicon-plugin-helpers.js +6 -4
- package/dist/lexicon.d.ts +2 -0
- package/dist/lexicon.d.ts.map +1 -1
- package/dist/lifecycle/affected.d.ts.map +1 -1
- package/dist/lifecycle/affected.js +7 -1
- package/dist/lifecycle/release-ledger.d.ts +21 -0
- package/dist/lifecycle/release-ledger.d.ts.map +1 -1
- package/dist/lifecycle/release-ledger.js +24 -0
- package/dist/lifecycle/work-dir.d.ts +45 -0
- package/dist/lifecycle/work-dir.d.ts.map +1 -0
- package/dist/lifecycle/work-dir.js +78 -0
- package/dist/lint/discover.d.ts +17 -9
- package/dist/lint/discover.d.ts.map +1 -1
- package/dist/lint/discover.js +62 -54
- package/dist/module-source.d.ts +57 -0
- package/dist/module-source.d.ts.map +1 -0
- package/dist/module-source.js +104 -0
- package/dist/op/activities/factory.js +1 -1
- package/dist/op/discover.d.ts.map +1 -1
- package/dist/op/discover.js +13 -3
- package/dist/op/gate.d.ts.map +1 -1
- package/dist/op/gate.js +3 -0
- package/dist/op/op-waves-run.d.ts.map +1 -1
- package/dist/op/op-waves-run.js +20 -5
- package/dist/op/operator.d.ts +2 -2
- package/dist/op/operator.js +2 -2
- package/dist/op/steward.d.ts +6 -4
- package/dist/op/steward.d.ts.map +1 -1
- package/dist/op/steward.js +5 -3
- package/dist/op/work-lease-run.d.ts +20 -7
- package/dist/op/work-lease-run.d.ts.map +1 -1
- package/dist/op/work-lease-run.js +56 -12
- package/dist/pr-forge.d.ts +10 -0
- package/dist/pr-forge.d.ts.map +1 -1
- package/dist/pr-forge.js +39 -0
- package/dist/pr-loop.d.ts +141 -4
- package/dist/pr-loop.d.ts.map +1 -1
- package/dist/pr-loop.js +122 -6
- package/dist/pr-report-url.d.ts +2 -2
- package/dist/pr-report-url.d.ts.map +1 -1
- package/dist/pr-report-url.js +5 -5
- package/dist/ts-loader.d.ts +38 -4
- package/dist/ts-loader.d.ts.map +1 -1
- package/dist/ts-loader.js +190 -16
- package/dist/wave-runtime.mjs +208 -208
- package/dist/workspace/chant-migrations/fountain-retired-kinds.d.ts +34 -0
- package/dist/workspace/chant-migrations/fountain-retired-kinds.d.ts.map +1 -0
- package/dist/workspace/chant-migrations/fountain-retired-kinds.js +176 -0
- package/dist/workspace/chant-migrations.d.ts.map +1 -1
- package/dist/workspace/chant-migrations.js +2 -0
- package/dist/workspace/checks/boxes.d.ts +6 -6
- package/dist/workspace/checks/boxes.js +9 -9
- package/dist/workspace/ci-green-forge.d.ts +6 -4
- package/dist/workspace/ci-green-forge.d.ts.map +1 -1
- package/dist/workspace/ci-green.d.ts +28 -12
- package/dist/workspace/ci-green.d.ts.map +1 -1
- package/dist/workspace/ci-green.js +49 -21
- package/dist/workspace/ci-list.schema.json +12 -6
- package/dist/workspace/ci-report-url.d.ts +18 -0
- package/dist/workspace/ci-report-url.d.ts.map +1 -0
- package/dist/workspace/ci-report-url.js +33 -0
- package/dist/workspace/ci-run-index.d.ts +12 -0
- package/dist/workspace/ci-run-index.d.ts.map +1 -1
- package/dist/workspace/ci-run-index.js +6 -4
- package/dist/workspace/ci-run-index.schema.json +5 -3
- package/dist/workspace/ci-run.d.ts +75 -2
- package/dist/workspace/ci-run.d.ts.map +1 -1
- package/dist/workspace/ci-run.js +195 -53
- package/dist/workspace/ci-runner-key.d.ts +16 -3
- package/dist/workspace/ci-runner-key.d.ts.map +1 -1
- package/dist/workspace/ci-runner-key.js +30 -10
- package/dist/workspace/ci-serve.d.ts +54 -4
- package/dist/workspace/ci-serve.d.ts.map +1 -1
- package/dist/workspace/ci-serve.js +175 -26
- package/dist/workspace/declaration.d.ts +8 -0
- package/dist/workspace/declaration.d.ts.map +1 -1
- package/dist/workspace/declaration.js +11 -2
- package/dist/workspace/declaration.schema.json +7 -2
- package/dist/workspace/export.d.ts +1 -1
- package/dist/workspace/export.d.ts.map +1 -1
- package/dist/workspace/export.js +7 -3
- package/dist/workspace/ls.d.ts +6 -1
- package/dist/workspace/ls.d.ts.map +1 -1
- package/dist/workspace/ls.js +1 -0
- package/dist/workspace/ls.schema.json +7 -1
- package/dist/workspace/pin-integrity.d.ts.map +1 -1
- package/dist/workspace/pin-integrity.js +3 -2
- package/dist/workspace/pr-report.schema.json +74 -7
- package/dist/workspace/reason-codes.d.ts +1 -1
- package/dist/workspace/reason-codes.d.ts.map +1 -1
- package/dist/workspace/reason-codes.js +2 -2
- package/dist/workspace/run-event.schema.json +3 -1
- package/dist/workspace/status.schema.json +3 -1
- package/dist/workspace/write-scope.d.ts +9 -0
- package/dist/workspace/write-scope.d.ts.map +1 -1
- package/dist/workspace/write-scope.js +11 -1
- package/package.json +1 -1
- package/src/__fixtures__/pr-loop/pr-apply-wave-waiting.note.golden.md +48 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/package.json +3 -3
- package/src/cli/commands/audit.test.ts +2 -2
- package/src/cli/commands/check-lexicon-plugin.test.ts +9 -0
- package/src/cli/commands/check-lexicon-plugin.ts +11 -8
- package/src/cli/commands/check-lexicon.test.ts +1 -1
- package/src/cli/commands/check-lexicon.ts +16 -11
- package/src/cli/commands/import.ts +40 -0
- package/src/cli/commands/init-lexicon/templates/project.ts +4 -3
- package/src/cli/commands/init-lexicon.test.ts +8 -5
- package/src/cli/commands/onboard.test.ts +45 -0
- package/src/cli/commands/onboard.ts +8 -4
- package/src/cli/commands/update.test.ts +6 -0
- package/src/cli/commands/update.ts +4 -1
- package/src/cli/compiled-cli.e2e.test.ts +76 -11
- package/src/cli/dependencies.e2e.test.ts +101 -0
- package/src/cli/dependencies.test.ts +93 -0
- package/src/cli/dependencies.ts +122 -0
- package/src/cli/handlers/build.test.ts +69 -0
- package/src/cli/handlers/build.ts +20 -4
- package/src/cli/handlers/fan-out.test.ts +73 -0
- package/src/cli/handlers/fan-out.ts +11 -4
- package/src/cli/handlers/graph.test.ts +76 -0
- package/src/cli/handlers/graph.ts +19 -12
- package/src/cli/handlers/lint.test.ts +42 -0
- package/src/cli/handlers/lint.ts +6 -0
- package/src/cli/handlers/operator.ts +3 -3
- package/src/cli/handlers/pr-waves.test.ts +449 -0
- package/src/cli/handlers/pr.test.ts +134 -0
- package/src/cli/handlers/pr.ts +523 -31
- package/src/cli/handlers/promote.test.ts +26 -1
- package/src/cli/handlers/promote.ts +3 -2
- package/src/cli/handlers/run-components-exit.test.ts +99 -0
- package/src/cli/handlers/run-signer.test.ts +79 -0
- package/src/cli/handlers/run.ts +34 -16
- package/src/cli/main.test.ts +18 -0
- package/src/cli/main.ts +43 -16
- package/src/cli/parse-args.ts +12 -0
- package/src/cli/registry.ts +19 -6
- package/src/codegen/generate-post-synth-barrel.ts +2 -2
- package/src/codegen/lexicon-regen.ts +1 -1
- package/src/codegen/pinned-upgrade.ts +2 -2
- package/src/codegen/post-synth-barrel-guards.test.ts +6 -11
- package/src/components/capability.ts +51 -3
- package/src/components/changed-files.test.ts +16 -1
- package/src/components/changed-files.ts +9 -1
- package/src/components/driver.test.ts +36 -1
- package/src/components/driver.ts +76 -4
- package/src/components/fan-out-record.ts +29 -1
- package/src/components/fan-out-run.test.ts +35 -0
- package/src/components/fan-out-run.ts +40 -1
- package/src/components/pr-pipeline.test.ts +100 -0
- package/src/components/pr-pipeline.ts +132 -17
- package/src/components/pr-run.test.ts +323 -18
- package/src/components/pr-run.ts +354 -75
- package/src/components/wave-plan.ts +16 -4
- package/src/discovery/fold-host-method.test.ts +87 -0
- package/src/discovery/fold-import.ts +15 -5
- package/src/discovery/fold-package-composite.test.ts +27 -0
- package/src/discovery/fold-specifiers.test.ts +8 -0
- package/src/discovery/sandbox/bundle.ts +6 -0
- package/src/discovery/specifier-resolve.ts +8 -2
- package/src/fold/fold.test.ts +72 -0
- package/src/fold/fold.ts +71 -0
- package/src/fold/subset.test.ts +31 -0
- package/src/fold/subset.ts +99 -0
- package/src/lexicon-dist.e2e.test.ts +254 -0
- package/src/lexicon-module.test.ts +47 -0
- package/src/lexicon-module.ts +49 -7
- package/src/lexicon-plugin-helpers.ts +6 -4
- package/src/lexicon.ts +2 -0
- package/src/lifecycle/affected.test.ts +7 -0
- package/src/lifecycle/affected.ts +6 -1
- package/src/lifecycle/release-ledger.ts +34 -0
- package/src/lifecycle/work-dir.test.ts +77 -0
- package/src/lifecycle/work-dir.ts +82 -0
- package/src/lint/discover.test.ts +46 -2
- package/src/lint/discover.ts +55 -49
- package/src/module-source.test.ts +85 -0
- package/src/module-source.ts +112 -0
- package/src/op/activities/factory.ts +1 -1
- package/src/op/discover-root.test.ts +31 -2
- package/src/op/discover.ts +14 -4
- package/src/op/gate.test.ts +30 -0
- package/src/op/gate.ts +3 -0
- package/src/op/op-waves-run.ts +20 -5
- package/src/op/op-waves.test.ts +49 -0
- package/src/op/operator.ts +2 -2
- package/src/op/steward.ts +6 -4
- package/src/op/work-lease-run.test.ts +88 -2
- package/src/op/work-lease-run.ts +52 -12
- package/src/pr-forge-bundle.test.ts +2 -2
- package/src/pr-forge.test.ts +42 -0
- package/src/pr-forge.ts +40 -0
- package/src/pr-loop-bundle.test.ts +2 -1
- package/src/pr-loop.test.ts +117 -1
- package/src/pr-loop.ts +252 -10
- package/src/pr-report-url.ts +5 -5
- package/src/terraform/__fixtures__/advise.test.ts +21 -2
- package/src/ts-loader.test.ts +89 -1
- package/src/ts-loader.ts +180 -13
- package/src/workspace/changes.test.ts +1 -1
- package/src/workspace/chant-migrations/fountain-retired-kinds.test.ts +33 -0
- package/src/workspace/chant-migrations/fountain-retired-kinds.ts +190 -0
- package/src/workspace/chant-migrations.e2e.test.ts +5 -0
- package/src/workspace/chant-migrations.ts +2 -0
- package/src/workspace/checks/boxes.ts +9 -9
- package/src/workspace/ci-green-forge.ts +6 -4
- package/src/workspace/ci-green-runs.test.ts +53 -11
- package/src/workspace/ci-green.test.ts +1 -1
- package/src/workspace/ci-green.ts +50 -22
- package/src/workspace/ci-list.schema.json +12 -6
- package/src/workspace/ci-report-url.test.ts +220 -0
- package/src/workspace/ci-report-url.ts +33 -0
- package/src/workspace/ci-run-index.schema.json +5 -3
- package/src/workspace/ci-run-index.test.ts +42 -3
- package/src/workspace/ci-run-index.ts +18 -4
- package/src/workspace/ci-run.test.ts +185 -7
- package/src/workspace/ci-run.ts +224 -54
- package/src/workspace/ci-runner-key.test.ts +55 -17
- package/src/workspace/ci-runner-key.ts +36 -11
- package/src/workspace/ci-serve.test.ts +150 -6
- package/src/workspace/ci-serve.ts +169 -25
- package/src/workspace/declaration.schema.json +7 -2
- package/src/workspace/declaration.test.ts +23 -0
- package/src/workspace/declaration.ts +19 -3
- package/src/workspace/export.ts +6 -3
- package/src/workspace/ls.schema.json +7 -1
- package/src/workspace/ls.ts +7 -2
- package/src/workspace/pin-integrity.test.ts +5 -0
- package/src/workspace/pin-integrity.ts +3 -2
- package/src/workspace/pr-report.schema.json +74 -7
- package/src/workspace/reason-codes.ts +2 -2
- package/src/workspace/run-event.schema.json +3 -1
- package/src/workspace/status.schema.json +3 -1
- package/src/workspace/write-scope.test.ts +29 -1
- package/src/workspace/write-scope.ts +12 -1
package/dist/workspace/ci-run.js
CHANGED
|
@@ -44,6 +44,30 @@
|
|
|
44
44
|
* With a forge client (#4097) the job also sets a commit status at its
|
|
45
45
|
* commit: `pending` when it starts, `success` or `failure` when it settles.
|
|
46
46
|
* A status the forge refuses is a warning and never fails the job.
|
|
47
|
+
*
|
|
48
|
+
* Signing is opt-in (#4156). With no runner key the record is unsigned, and
|
|
49
|
+
* a reader counts it with no signer. A key `.chant/trust.json` does not list
|
|
50
|
+
* leaves it unsigned with a note `--verbose` prints, unless the declaration
|
|
51
|
+
* sets `ci.green.signedSince`; then a run left unsigned is a warning.
|
|
52
|
+
*
|
|
53
|
+
* In place (`--in-place`, #4156) the job runs in the checkout it is started
|
|
54
|
+
* in, as a generated forge job does: no worktree, no setup phase and no
|
|
55
|
+
* commit status of its own (the forge shows the job). The steps get the
|
|
56
|
+
* job's whole environment less `CHANT_RUNNER_KEY`, since the forge job is
|
|
57
|
+
* already the boundary, and the record's `runOrigin` names the forge's run,
|
|
58
|
+
* job and attempt (`resolveCiJob`).
|
|
59
|
+
*
|
|
60
|
+
* Local (`--local`, #4161) runs the job the same way on this machine and
|
|
61
|
+
* leaves nothing outside it. It implies `--no-push` and `--no-status`, and
|
|
62
|
+
* writes nothing under a report URL, given or declared: the command gets no
|
|
63
|
+
* `--report-url`, and gets `--no-status`, so pr-plan and pr-apply post no
|
|
64
|
+
* status or note either. The record is still written to the local
|
|
65
|
+
* `chant/lifecycle`, where a reader on this machine finds it beside CI's for
|
|
66
|
+
* the same commit. It says `runOrigin: { forge: "local" }` and its events say
|
|
67
|
+
* `runtime: "local"`. `ci tick --source runs` and `ci list` never count it.
|
|
68
|
+
* A pr-apply still pushes its own release records and gate facts, as the
|
|
69
|
+
* command always does, since a local apply changes the same infrastructure
|
|
70
|
+
* a CI one does.
|
|
47
71
|
*/
|
|
48
72
|
import { execFileSync } from "node:child_process";
|
|
49
73
|
import { copyFileSync, cpSync, existsSync, mkdirSync, mkdtempSync, readdirSync, readFileSync, realpathSync, rmSync, writeFileSync } from "node:fs";
|
|
@@ -53,14 +77,17 @@ import { PR_APPLY_RECORD, PR_LOOP_REPORT_DIR, prLoopJobs, prLoopSetup, PR_LOOP_I
|
|
|
53
77
|
import { detectForge, ForgeEnvironmentError, forgeFromEnv, FORGE_KINDS } from "../pr-forge.js";
|
|
54
78
|
import { PR_APPLY_GATE } from "../pr-loop.js";
|
|
55
79
|
import { formatError, formatWarning } from "../cli/format.js";
|
|
56
|
-
import {
|
|
80
|
+
import { resolveCiJob } from "../lifecycle/release-ledger.js";
|
|
81
|
+
import { fetchLifecycleStatus, pushLifecycleReport } from "../lifecycle/git.js";
|
|
57
82
|
import { ACTIVITY_PROFILES } from "../op/activity-profiles.js";
|
|
58
83
|
import { shellCmd } from "../op/activities/shell.js";
|
|
59
84
|
import { OpRunFailure, runOpLocally } from "../op/local-executor.js";
|
|
60
85
|
import { redactText } from "../op/redact.js";
|
|
61
86
|
import { findWorkspaceRoot } from "../project-root.js";
|
|
62
87
|
import { parseReportUrl, reportKey } from "../pr-report-url.js";
|
|
63
|
-
import {
|
|
88
|
+
import { reportUrlOf } from "./ci-report-url.js";
|
|
89
|
+
import { ciTagSigner, RUNNER_KEY_ENV, signedRunsRequired } from "./ci-green.js";
|
|
90
|
+
import { loadRunnerKey } from "./trust/dsse.js";
|
|
64
91
|
import { ciRedactor, ciSecretsFor, CI_SECRETS_ENV, readCiSecrets } from "./ci-secrets.js";
|
|
65
92
|
import { updateCiRunIndex } from "./ci-run-index.js";
|
|
66
93
|
import { resolveMemberContext } from "./member-pipeline.js";
|
|
@@ -72,6 +99,8 @@ export const CI_JOB_NAMES = ["plan", "apply"];
|
|
|
72
99
|
export const CI_JOB_OPS = { plan: "pr-plan", apply: "pr-apply" };
|
|
73
100
|
/** Where a job the chant runner ran says it ran (#4094). */
|
|
74
101
|
export const CHANT_RUNNER_ORIGIN = { forge: "chant" };
|
|
102
|
+
/** Where a job `chant ci run --local` ran says it ran (#4161): this machine, outside CI. */
|
|
103
|
+
export const CHANT_LOCAL_ORIGIN = { forge: "local" };
|
|
75
104
|
/** The runner's variables every job gets, when the runner has them. */
|
|
76
105
|
export const CI_RUN_BASE_ENV = ["PATH", "HOME", "USER", "LOGNAME", "SHELL", "LANG", "LC_ALL", "LC_CTYPE", "TMPDIR", "TZ"];
|
|
77
106
|
/**
|
|
@@ -190,6 +219,17 @@ function exitCodeOf(error) {
|
|
|
190
219
|
const m = /command exited (\d+)/.exec(error ?? "");
|
|
191
220
|
return m ? Number(m[1]) : undefined;
|
|
192
221
|
}
|
|
222
|
+
/**
|
|
223
|
+
* The code `chant ci run` exits with for a settled job: 0 when it passed,
|
|
224
|
+
* else its command's own code when that is 1 to 255, such as 3 for a moved
|
|
225
|
+
* plan or a waiting wave (#4156), else 1. The run index records the same
|
|
226
|
+
* code (#4165).
|
|
227
|
+
*/
|
|
228
|
+
export function ciJobExitCode(result) {
|
|
229
|
+
if (result.status === "ok")
|
|
230
|
+
return 0;
|
|
231
|
+
return result.exitCode && result.exitCode > 0 && result.exitCode < 256 ? result.exitCode : 1;
|
|
232
|
+
}
|
|
193
233
|
/** The first line of a step's error, for a reason. */
|
|
194
234
|
const firstLine = (text) => text.split("\n").find((l) => l.trim() !== "")?.trim() ?? text;
|
|
195
235
|
/**
|
|
@@ -396,7 +436,9 @@ async function recordInIndex(input) {
|
|
|
396
436
|
ended: input.record.ended,
|
|
397
437
|
runId: clean(input.record.id),
|
|
398
438
|
key: clean(reportKey(pr, input.commit, input.member)),
|
|
399
|
-
runOrigin:
|
|
439
|
+
runOrigin: input.runOrigin,
|
|
440
|
+
signer: input.signer,
|
|
441
|
+
exitCode: input.exitCode,
|
|
400
442
|
};
|
|
401
443
|
await updateCiRunIndex({ target, entry, ...(input.limit ? { limit: input.limit } : {}), warn: input.warn });
|
|
402
444
|
}
|
|
@@ -413,12 +455,21 @@ export async function ciRun(req) {
|
|
|
413
455
|
if (!top)
|
|
414
456
|
throw new CiRunError("a job runs in a fresh checkout of a commit, and this directory is not in a git repository");
|
|
415
457
|
const commit = resolveJobCommit(top, req.commit ?? "HEAD");
|
|
458
|
+
if (req.inPlace && commit !== resolveJobCommit(top, "HEAD"))
|
|
459
|
+
throw new CiRunError(`--in-place runs the checkout's HEAD, and --commit names ${commit.slice(0, 12)}`);
|
|
460
|
+
if (req.local && req.inPlace)
|
|
461
|
+
throw new CiRunError("--in-place runs a forge job in the forge's checkout, and --local runs one on this machine; pass one of them");
|
|
416
462
|
const place = ciJobPlace(cwd, top);
|
|
417
|
-
const jobs = prLoopJobs(req.forge, req.env, req.loop ?? {}, place.member);
|
|
418
|
-
const
|
|
419
|
-
if (!
|
|
463
|
+
const jobs = prLoopJobs(req.forge, req.env, req.local ? localLoop(req.loop) : (req.loop ?? {}), place.member);
|
|
464
|
+
const found = jobs.find((j) => j.jobName === req.job);
|
|
465
|
+
if (!found)
|
|
420
466
|
throw new CiRunError(`the pull-request pipeline has no ${req.job} job; it has ${jobs.map((j) => j.jobName).join(" and ")}`);
|
|
421
|
-
|
|
467
|
+
if (req.wave !== undefined && (req.job !== "apply" || !req.loop?.waveGate))
|
|
468
|
+
throw new CiRunError("--wave names the one wave an apply runs, and takes --wave-gate: ci run apply --wave-gate <gate> --wave <k>");
|
|
469
|
+
// A generated wave job's command, as prLoopWaveJobs writes it. #4161: a local job's command posts no status or note.
|
|
470
|
+
const command = `${found.command}${req.wave !== undefined ? ` --wave ${req.wave}` : ""}${req.local ? " --no-status" : ""}`;
|
|
471
|
+
const job = { ...found, command, ...(req.wave !== undefined ? { wave: req.wave } : {}) };
|
|
472
|
+
const own = ciJobEnv(job, {
|
|
422
473
|
forge: req.forge,
|
|
423
474
|
env: req.env,
|
|
424
475
|
values: { base: req.base, pr: req.pr, commit },
|
|
@@ -426,11 +477,15 @@ export async function ciRun(req) {
|
|
|
426
477
|
passEnv: req.passEnv,
|
|
427
478
|
secrets: ciSecretsFor(req.secrets, req.job),
|
|
428
479
|
});
|
|
480
|
+
// #4156: in place, the forge job's environment is the job's, less the runner key.
|
|
481
|
+
const jobEnv = req.inPlace ? { ...definedEnv(hostEnv), ...own } : own;
|
|
482
|
+
delete jobEnv[RUNNER_KEY_ENV];
|
|
429
483
|
// #4096: the secrets and the job's secret-looking variables, redacted from everything the run writes and returns.
|
|
430
|
-
|
|
484
|
+
// In place the forge's own variables come too; one whose value is short, such as a flag, is left out, so it can't blank common words.
|
|
485
|
+
const redact = ciRedactor(req.inPlace ? { ...longValues(jobEnv), ...own } : jobEnv, req.secrets);
|
|
431
486
|
try {
|
|
432
487
|
const result = await runCiJob(req, { cwd, top, commit, place, job, jobEnv, ...(redact ? { redact } : {}) });
|
|
433
|
-
return redact ? { ...result, warnings: result.warnings.map(redact), ...(result.pushWarning ? { pushWarning: redact(result.pushWarning) } : {}) } : result;
|
|
488
|
+
return redact ? { ...result, warnings: result.warnings.map(redact), notes: result.notes.map(redact), ...(result.pushWarning ? { pushWarning: redact(result.pushWarning) } : {}) } : result;
|
|
434
489
|
}
|
|
435
490
|
catch (err) {
|
|
436
491
|
if (redact && err instanceof Error)
|
|
@@ -438,23 +493,72 @@ export async function ciRun(req) {
|
|
|
438
493
|
throw err;
|
|
439
494
|
}
|
|
440
495
|
}
|
|
496
|
+
/** The generator's options for a `--local` job (#4161): the same, with no report URL. */
|
|
497
|
+
function localLoop(loop) {
|
|
498
|
+
const { reportUrl: _dropped, ...rest } = loop ?? {};
|
|
499
|
+
return rest;
|
|
500
|
+
}
|
|
501
|
+
/** The variables of `env` whose value is long enough to be a credential, 8 characters or more. */
|
|
502
|
+
function longValues(env) {
|
|
503
|
+
return Object.fromEntries(Object.entries(env).filter(([, value]) => value.length >= 8));
|
|
504
|
+
}
|
|
505
|
+
/** The variables of `env` that have a value. */
|
|
506
|
+
function definedEnv(env) {
|
|
507
|
+
const out = {};
|
|
508
|
+
for (const [name, value] of Object.entries(env))
|
|
509
|
+
if (value !== undefined)
|
|
510
|
+
out[name] = value;
|
|
511
|
+
return out;
|
|
512
|
+
}
|
|
513
|
+
/**
|
|
514
|
+
* Who signs the run (#4156): the runner key, checked against
|
|
515
|
+
* `.chant/trust.json` at the commit. A key that isn't an Ed25519 PEM is an
|
|
516
|
+
* error, and so is a `--key` the file does not list. A key from the
|
|
517
|
+
* environment that it does not list, and no key at all, leave the run
|
|
518
|
+
* unsigned: a warning when the declaration's `ci.green.signedSince` asks
|
|
519
|
+
* for signed runs, a note for `--verbose` otherwise.
|
|
520
|
+
*/
|
|
521
|
+
function ciRunSigner(req, at) {
|
|
522
|
+
// #4161: a local run never counts, signed or not, so nothing about its signature warns.
|
|
523
|
+
const required = req.local ? null : signedRunsRequired(at.cwd);
|
|
524
|
+
const say = (text) => (required ? { warnings: [`${text}, and ci.green.signedSince is ${required}, so it will not count`], notes: [] } : { warnings: [], notes: [text] });
|
|
525
|
+
if (req.runnerKey === undefined)
|
|
526
|
+
return say("there is no runner key, so the run's record is unsigned");
|
|
527
|
+
try {
|
|
528
|
+
loadRunnerKey(req.runnerKey);
|
|
529
|
+
}
|
|
530
|
+
catch (err) {
|
|
531
|
+
throw new CiRunError(`the runner key${req.runnerKeyFromEnv ? ` in ${RUNNER_KEY_ENV}` : ""} is not an Ed25519 private key in PEM: ${message(err)}`);
|
|
532
|
+
}
|
|
533
|
+
try {
|
|
534
|
+
return { signer: ciTagSigner(at.top, at.commit, req.runnerKey, "the runs it signed"), warnings: [], notes: [] };
|
|
535
|
+
}
|
|
536
|
+
catch (err) {
|
|
537
|
+
if (!req.runnerKeyFromEnv)
|
|
538
|
+
throw new CiRunError(`the run would not be signed: ${message(err)}`);
|
|
539
|
+
return say(`${RUNNER_KEY_ENV} is set, and the run will not be signed: ${message(err)}`);
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
/** Where the job runs: a fresh worktree of the commit, or with `inPlace` the checkout itself, which is left as it is. */
|
|
543
|
+
function jobCheckout(top, commit, inPlace) {
|
|
544
|
+
return inPlace ? { dir: top, remove() { } } : freshCheckout(top, commit);
|
|
545
|
+
}
|
|
441
546
|
/** {@link ciRun} once the job and its environment are known. */
|
|
442
547
|
async function runCiJob(req, at) {
|
|
443
548
|
const { cwd, top, commit, place, job, jobEnv, redact } = at;
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
const
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
const checkout = freshCheckout(top, commit);
|
|
549
|
+
const local = req.local === true;
|
|
550
|
+
const status = local ? undefined : req.status;
|
|
551
|
+
const reportUrl = local ? undefined : req.loop?.reportUrl;
|
|
552
|
+
// The record is signed against .chant/trust.json at the commit the job runs, so a --key that commit does not list is refused before anything runs.
|
|
553
|
+
const signing = ciRunSigner(req, { cwd, top, commit });
|
|
554
|
+
const signer = signing.signer;
|
|
555
|
+
const early = signing.warnings;
|
|
556
|
+
const notes = signing.notes;
|
|
557
|
+
const origin = local ? CHANT_LOCAL_ORIGIN : req.inPlace ? (resolveCiJob(req.hostEnv ?? process.env)?.runOrigin ?? CHANT_RUNNER_ORIGIN) : CHANT_RUNNER_ORIGIN;
|
|
558
|
+
// In place, the job writes its record on the checkout's chant/lifecycle, which a forge checkout may not have fetched.
|
|
559
|
+
if (req.inPlace && req.push !== false)
|
|
560
|
+
await fetchLifecycleStatus({ cwd: top }).catch(() => undefined);
|
|
561
|
+
const checkout = jobCheckout(top, commit, req.inPlace);
|
|
458
562
|
try {
|
|
459
563
|
const jobDir = place.dir === "." ? checkout.dir : join(checkout.dir, place.dir);
|
|
460
564
|
const op = ciJobOp({
|
|
@@ -462,7 +566,7 @@ async function runCiJob(req, at) {
|
|
|
462
566
|
env: req.env,
|
|
463
567
|
cwd: jobDir,
|
|
464
568
|
jobEnv,
|
|
465
|
-
setup: prLoopSetup(req.image ?? PR_LOOP_IMAGE),
|
|
569
|
+
setup: req.inPlace ? [] : prLoopSetup(req.image ?? PR_LOOP_IMAGE),
|
|
466
570
|
build: [...(req.beforeScript ?? []), ...(job.build ?? [])],
|
|
467
571
|
...(req.chant ? { chant: req.chant } : {}),
|
|
468
572
|
});
|
|
@@ -474,14 +578,14 @@ async function runCiJob(req, at) {
|
|
|
474
578
|
copyFileSync(resume.host, resume.job);
|
|
475
579
|
}
|
|
476
580
|
const statusContext = ciStatusContext(req.job, req.loop?.gate);
|
|
477
|
-
const statusUrl =
|
|
478
|
-
? ciStatusUrl({ ...(
|
|
581
|
+
const statusUrl = status
|
|
582
|
+
? ciStatusUrl({ ...(status.urlTemplate ? { urlTemplate: status.urlTemplate } : {}), ...(reportUrl ? { reportUrl } : {}), ...(req.pr !== undefined ? { pr: req.pr } : {}), commit, ...(place.member ? { member: place.member } : {}) })
|
|
479
583
|
: undefined;
|
|
480
584
|
const postStatus = async (state, description) => {
|
|
481
|
-
if (!
|
|
585
|
+
if (!status)
|
|
482
586
|
return;
|
|
483
587
|
try {
|
|
484
|
-
await
|
|
588
|
+
await status.client.setCommitStatus(commit, { context: statusContext, state, description: cut(redact ? redact(description) : description), ...(statusUrl ? { targetUrl: statusUrl } : {}) });
|
|
485
589
|
}
|
|
486
590
|
catch (err) {
|
|
487
591
|
warnings.push(`could not set the ${statusContext} status: ${message(err)}`);
|
|
@@ -498,7 +602,7 @@ async function runCiJob(req, at) {
|
|
|
498
602
|
cwd: jobDir,
|
|
499
603
|
commit,
|
|
500
604
|
ledger: { cwd: jobDir },
|
|
501
|
-
runner: { kind: "pipeline", runtime: "ci", runOrigin:
|
|
605
|
+
runner: { kind: "pipeline", runtime: local ? "local" : "ci", runOrigin: origin },
|
|
502
606
|
describeFailure: (records) => ciJobFailure({ job: req.job, op, records, reportDir: join(jobDir, PR_LOOP_REPORT_DIR) }),
|
|
503
607
|
...(req.runEvents ? { runEvents: { write: req.runEvents } } : {}),
|
|
504
608
|
...(req.stepOutput ? { stepOutput: req.stepOutput } : {}),
|
|
@@ -533,28 +637,40 @@ async function runCiJob(req, at) {
|
|
|
533
637
|
let report = null;
|
|
534
638
|
if (existsSync(written)) {
|
|
535
639
|
report = resolve(cwd, req.output ?? PR_LOOP_REPORT_DIR);
|
|
536
|
-
|
|
537
|
-
if (
|
|
538
|
-
|
|
640
|
+
// In place the report is already where the forge keeps it, and stays as the command wrote it.
|
|
641
|
+
if (!(existsSync(report) && realpathSync(report) === realpathSync(written))) {
|
|
642
|
+
cpSync(written, report, { recursive: true });
|
|
643
|
+
if (redact)
|
|
644
|
+
redactTree(report, redact);
|
|
645
|
+
}
|
|
539
646
|
}
|
|
540
|
-
|
|
541
|
-
|
|
647
|
+
const failed = result.records.find((r) => r.status === "fail");
|
|
648
|
+
const exitCode = failed?.phase === req.job ? exitCodeOf(failed.error) : undefined;
|
|
649
|
+
if (reportUrl) {
|
|
650
|
+
await recordInIndex({ url: reportUrl, job: req.job, commit, member: place.member, pr: req.pr, record: result.record, runOrigin: origin, signer: result.record.statement && signer ? signer.principal : null, exitCode: ciJobExitCode({ status: result.status, ...(exitCode !== undefined ? { exitCode } : {}) }), reportDir: written, limit: req.runIndexLimit, ...(redact ? { redact } : {}), warn: (w) => warnings.push(w) });
|
|
542
651
|
}
|
|
543
|
-
|
|
652
|
+
// In place the forge job's outcome is the command's, so a record that could not be written is a warning there.
|
|
653
|
+
if (ledgerError && req.inPlace)
|
|
654
|
+
warnings.push(ledgerError);
|
|
655
|
+
else if (ledgerError)
|
|
544
656
|
throw new CiRunError(ledgerError);
|
|
545
|
-
const pushed =
|
|
546
|
-
|
|
657
|
+
const pushed = local
|
|
658
|
+
? { pushed: false }
|
|
659
|
+
: req.push === false ? { pushed: false, pushWarning: "chant/lifecycle was not pushed" } : await pushLifecycleReport({ cwd: top });
|
|
547
660
|
return {
|
|
548
661
|
op: op.name,
|
|
549
662
|
status: result.status,
|
|
550
663
|
record: result.record,
|
|
551
664
|
...(failed ? { failure: { phase: failed.phase, error: failed.error ?? "the step failed" } } : {}),
|
|
665
|
+
...(exitCode !== undefined ? { exitCode } : {}),
|
|
552
666
|
report,
|
|
553
|
-
signer: signer
|
|
667
|
+
signer: result.record.statement && signer ? signer.principal : null,
|
|
554
668
|
pushed: pushed.pushed,
|
|
555
669
|
...(pushed.pushWarning ? { pushWarning: pushed.pushWarning } : {}),
|
|
556
670
|
warnings,
|
|
671
|
+
notes,
|
|
557
672
|
checkout: checkout.dir,
|
|
673
|
+
...(local ? { local: true } : {}),
|
|
558
674
|
};
|
|
559
675
|
}
|
|
560
676
|
finally {
|
|
@@ -592,18 +708,39 @@ export function formatCiRun(r) {
|
|
|
592
708
|
lines.push(` failure: ${why.code}${why.phase ? ` in ${why.phase}` : ""}${why.exitCode !== undefined ? `, exit ${why.exitCode}` : ""}: ${why.reason}`);
|
|
593
709
|
else if (r.failure)
|
|
594
710
|
lines.push(` ${r.failure.phase}: ${r.failure.error}`);
|
|
595
|
-
lines.push(` record: ${r.record.env}/runs__${r.op}.jsonl on chant/lifecycle, ${r.signer ? `signed by ${r.signer}` : "unsigned
|
|
711
|
+
lines.push(` record: ${r.record.env}/runs__${r.op}.jsonl on chant/lifecycle, ${r.signer ? `signed by ${r.signer}` : "unsigned"}`);
|
|
596
712
|
if (r.report)
|
|
597
713
|
lines.push(` report: ${r.report}`);
|
|
598
|
-
if (
|
|
714
|
+
if (r.local)
|
|
715
|
+
lines.push(" local run: the record stays on this machine, with no push, commit status or report upload");
|
|
716
|
+
else if (!r.pushed)
|
|
599
717
|
lines.push(` ${r.pushWarning ?? "chant/lifecycle was not pushed"}`);
|
|
600
718
|
return lines.join("\n");
|
|
601
719
|
}
|
|
602
720
|
// ── The command ──────────────────────────────────────────────────────────────
|
|
721
|
+
/**
|
|
722
|
+
* The generator's options from `ci run`'s and `ci serve`'s flags, as `chant
|
|
723
|
+
* build --components --generate <lexicon> --pr-loop` takes them: `--gate`,
|
|
724
|
+
* `--branch`, `--events-json`, `--report-url`, `--pr-build`,
|
|
725
|
+
* `--pr-build-command`, `--wave-gate` (#4003) and `--no-require-review`
|
|
726
|
+
* (#4156).
|
|
727
|
+
*/
|
|
728
|
+
export function ciLoopOptions(args) {
|
|
729
|
+
return {
|
|
730
|
+
...(args.gate ? { gate: args.gate } : {}),
|
|
731
|
+
...(args.branch ? { branch: args.branch } : {}),
|
|
732
|
+
...(args.eventsJson ? { events: true } : {}),
|
|
733
|
+
...(args.reportUrl ? { reportUrl: args.reportUrl } : {}),
|
|
734
|
+
...(args.prBuild ? { build: true } : {}),
|
|
735
|
+
...(args.prBuildCommand ? { buildCommand: args.prBuildCommand } : {}),
|
|
736
|
+
...(args.waveGate ? { waveGate: args.waveGate } : {}),
|
|
737
|
+
...(args.requireReview === false ? { requireReview: false } : {}),
|
|
738
|
+
};
|
|
739
|
+
}
|
|
603
740
|
const USAGE = "chant ci run plan|apply --env <env> --base <sha> [--pr <n>] [--commit <sha>] [--forge github|gitlab|forgejo] " +
|
|
604
|
-
"[--gate <name>] [--branch <name>] [--events-json] [--report-url <url>] [--run-index-limit <n>] [--image <image>] [--before-script <line>]... " +
|
|
605
|
-
"[--pr-build] [--pr-build-command <line>] [--no-status] [--status-url <url template>] " +
|
|
606
|
-
"[--chant <command>] [--pass-env <NAME>]... [--secrets <file>] [--key <runner key.pem>] [-o <dir>] | chant ci run --setup-key [--trust [--principal <name>]] [--state <dir>] [--branch <name>]";
|
|
741
|
+
"[--gate <name>] [--wave-gate <name>] [--wave <k>] [--branch <name>] [--events-json] [--report-url <url>] [--run-index-limit <n>] [--image <image>] [--before-script <line>]... " +
|
|
742
|
+
"[--pr-build] [--pr-build-command <line>] [--no-require-review] [--no-status] [--status-url <url template>] " +
|
|
743
|
+
"[--chant <command>] [--pass-env <NAME>]... [--secrets <file>] [--key <runner key.pem>] [-o <dir>] [--in-place | --local] [--no-push] [--verbose] | chant ci run --setup-key [--trust [--principal <name>]] [--state <dir>] [--branch <name>]";
|
|
607
744
|
const ENV_NAME = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
608
745
|
/** The host's runner key in `--state`, or in ci serve's default state directory, when `--setup-key` made one there (#4099). */
|
|
609
746
|
function hostRunnerKeyAt(cwd, statePath) {
|
|
@@ -647,10 +784,14 @@ export async function runCiRun(ctx) {
|
|
|
647
784
|
const hostKey = fromFlag || process.env[RUNNER_KEY_ENV] ? undefined : hostRunnerKeyAt(process.cwd(), args.statePath);
|
|
648
785
|
const runnerKey = fromFlag ? readFileSync(resolve(args.key), "utf-8") : process.env[RUNNER_KEY_ENV] || hostKey?.pem;
|
|
649
786
|
const hostEnv = process.env;
|
|
650
|
-
|
|
787
|
+
// #4156: in place the forge shows the job, so ci run sets no status of its own.
|
|
788
|
+
// #4161: a local run sets no status, so it builds no client and warns about none.
|
|
789
|
+
const { status, warning } = ciStatusFromEnv({ forge: forge, hostEnv, noStatus: args.noStatus || args.ciInPlace || args.local, statusUrl: args.statusUrl });
|
|
651
790
|
if (warning)
|
|
652
791
|
console.error(formatWarning({ message: warning }));
|
|
653
792
|
const secrets = ciSecretsOf(args.ciSecrets);
|
|
793
|
+
// #4160: without --report-url, the declaration's ci.reportUrl.
|
|
794
|
+
const reportUrl = await reportUrlOf(args.reportUrl, process.cwd());
|
|
654
795
|
const result = await ciRun({
|
|
655
796
|
cwd: process.cwd(),
|
|
656
797
|
...(status ? { status } : {}),
|
|
@@ -660,14 +801,11 @@ export async function runCiRun(ctx) {
|
|
|
660
801
|
...(args.base ? { base: args.base } : {}),
|
|
661
802
|
...(args.pr !== undefined ? { pr: args.pr } : {}),
|
|
662
803
|
...(args.commit ? { commit: args.commit } : {}),
|
|
663
|
-
loop: {
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
...(args.prBuild ? { build: true } : {}),
|
|
669
|
-
...(args.prBuildCommand ? { buildCommand: args.prBuildCommand } : {}),
|
|
670
|
-
},
|
|
804
|
+
loop: ciLoopOptions(reportUrl ? { ...args, reportUrl } : args),
|
|
805
|
+
...(args.ciInPlace ? { inPlace: true } : {}),
|
|
806
|
+
...(args.local ? { local: true } : {}),
|
|
807
|
+
...(args.noPush ? { push: false } : {}),
|
|
808
|
+
...(args.wave !== undefined ? { wave: args.wave } : {}),
|
|
671
809
|
...(args.runIndexLimit ? { runIndexLimit: args.runIndexLimit } : {}),
|
|
672
810
|
...(args.image ? { image: args.image } : {}),
|
|
673
811
|
...(args.beforeScript?.length ? { beforeScript: args.beforeScript } : {}),
|
|
@@ -681,8 +819,12 @@ export async function runCiRun(ctx) {
|
|
|
681
819
|
});
|
|
682
820
|
for (const w of result.warnings)
|
|
683
821
|
console.error(formatWarning({ message: w }));
|
|
822
|
+
if (args.verbose)
|
|
823
|
+
for (const n of result.notes)
|
|
824
|
+
console.error(`ci run: ${n}`);
|
|
684
825
|
console.error(formatCiRun(result));
|
|
685
|
-
|
|
826
|
+
// #4156: a failed command's own exit code, such as 3 for a moved plan, is the job's, as it was before the job ran through ci run.
|
|
827
|
+
return ciJobExitCode(result);
|
|
686
828
|
}
|
|
687
829
|
catch (err) {
|
|
688
830
|
console.error(formatError({ message: `chant ci run: ${message(err)}`, hint: USAGE }));
|
|
@@ -101,9 +101,18 @@ export declare function formatRunnerKeySetup(s: RunnerKeySetup, branch: string):
|
|
|
101
101
|
/**
|
|
102
102
|
* The key `ci serve` signs with, checked before its first poll (#4099): the
|
|
103
103
|
* `--key` file, else `CHANT_RUNNER_KEY`, else the host's key in the state
|
|
104
|
-
* directory.
|
|
105
|
-
*
|
|
106
|
-
*
|
|
104
|
+
* directory. Signing is opt-in (#4156):
|
|
105
|
+
*
|
|
106
|
+
* - With no key, `ci serve` serves unsigned and says so only in `note`.
|
|
107
|
+
* - A key `.chant/trust.json` at the target's head does not list with the
|
|
108
|
+
* runner role, or a target branch it can't find to check against, leaves
|
|
109
|
+
* it serving unsigned, with one `warning`.
|
|
110
|
+
* - With `required` (the declaration's `ci.green.signedSince`) each of
|
|
111
|
+
* those refuses to start, as before #4156.
|
|
112
|
+
*
|
|
113
|
+
* A key that is not an Ed25519 private key in PEM, or a `--key` file that
|
|
114
|
+
* can't be read, refuses either way. `unsigned` (`--unsigned`) skips the
|
|
115
|
+
* key without `required` and is ignored with it.
|
|
107
116
|
*/
|
|
108
117
|
export declare function ciServeRunnerKey(req: {
|
|
109
118
|
top: string;
|
|
@@ -112,9 +121,13 @@ export declare function ciServeRunnerKey(req: {
|
|
|
112
121
|
keyFile?: string;
|
|
113
122
|
envKey?: string;
|
|
114
123
|
unsigned?: boolean;
|
|
124
|
+
/** The declaration's `ci.green.signedSince`, when it sets one: every run must then be signed. */
|
|
125
|
+
required?: string | null;
|
|
115
126
|
}): {
|
|
116
127
|
pem?: string;
|
|
117
128
|
fromFlag: boolean;
|
|
118
129
|
source: string;
|
|
130
|
+
warning?: string;
|
|
131
|
+
note?: string;
|
|
119
132
|
};
|
|
120
133
|
//# sourceMappingURL=ci-runner-key.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ci-runner-key.d.ts","sourceRoot":"","sources":["../../src/workspace/ci-runner-key.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAYH,oDAAoD;AACpD,eAAO,MAAM,eAAe,mBAAmB,CAAC;AAEhD,2FAA2F;AAC3F,wBAAgB,sBAAsB,IAAI,MAAM,CAE/C;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,8BAA8B;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,iCAAiC;IACjC,OAAO,EAAE,OAAO,CAAC;CAClB;AA4BD,4EAA4E;AAC5E,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS,CAGzE;AAED,yHAAyH;AACzH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,aAAa,CAWnE;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,SAAW,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAOtH;AAED,gEAAgE;AAChE,wBAAgB,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAEzE;AAED,4HAA4H;AAC5H,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAMzI;AAED,MAAM,WAAW,mBAAmB;IAClC,mGAAmG;IACnG,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,mDAAmD;IACnD,MAAM,EAAE,OAAO,CAAC;IAChB,+CAA+C;IAC/C,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB;AAED;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,EAAE,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,mBAAmB,CAmChK;AAED,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,aAAa,CAAC;IACnB,4FAA4F;IAC5F,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,gEAAgE;IAChE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,mBAAmB,CAAC;CAChC;AAED,iIAAiI;AACjI,wBAAgB,cAAc,CAAC,GAAG,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,cAAc,CAQvJ;AAED,gFAAgF;AAChF,wBAAgB,oBAAoB,CAAC,CAAC,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAe9E;AAED
|
|
1
|
+
{"version":3,"file":"ci-runner-key.d.ts","sourceRoot":"","sources":["../../src/workspace/ci-runner-key.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAYH,oDAAoD;AACpD,eAAO,MAAM,eAAe,mBAAmB,CAAC;AAEhD,2FAA2F;AAC3F,wBAAgB,sBAAsB,IAAI,MAAM,CAE/C;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,8BAA8B;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,iCAAiC;IACjC,OAAO,EAAE,OAAO,CAAC;CAClB;AA4BD,4EAA4E;AAC5E,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS,CAGzE;AAED,yHAAyH;AACzH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,aAAa,CAWnE;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,SAAW,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAOtH;AAED,gEAAgE;AAChE,wBAAgB,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAEzE;AAED,4HAA4H;AAC5H,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAMzI;AAED,MAAM,WAAW,mBAAmB;IAClC,mGAAmG;IACnG,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,mDAAmD;IACnD,MAAM,EAAE,OAAO,CAAC;IAChB,+CAA+C;IAC/C,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB;AAED;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,EAAE,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,mBAAmB,CAmChK;AAED,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,aAAa,CAAC;IACnB,4FAA4F;IAC5F,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,gEAAgE;IAChE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,mBAAmB,CAAC;CAChC;AAED,iIAAiI;AACjI,wBAAgB,cAAc,CAAC,GAAG,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,cAAc,CAQvJ;AAED,gFAAgF;AAChF,wBAAgB,oBAAoB,CAAC,CAAC,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAe9E;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE;IACpC,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,iGAAiG;IACjG,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B,GAAG;IAAE,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CA+BvF"}
|
|
@@ -184,18 +184,31 @@ export function formatRunnerKeySetup(s, branch) {
|
|
|
184
184
|
/**
|
|
185
185
|
* The key `ci serve` signs with, checked before its first poll (#4099): the
|
|
186
186
|
* `--key` file, else `CHANT_RUNNER_KEY`, else the host's key in the state
|
|
187
|
-
* directory.
|
|
188
|
-
*
|
|
189
|
-
*
|
|
187
|
+
* directory. Signing is opt-in (#4156):
|
|
188
|
+
*
|
|
189
|
+
* - With no key, `ci serve` serves unsigned and says so only in `note`.
|
|
190
|
+
* - A key `.chant/trust.json` at the target's head does not list with the
|
|
191
|
+
* runner role, or a target branch it can't find to check against, leaves
|
|
192
|
+
* it serving unsigned, with one `warning`.
|
|
193
|
+
* - With `required` (the declaration's `ci.green.signedSince`) each of
|
|
194
|
+
* those refuses to start, as before #4156.
|
|
195
|
+
*
|
|
196
|
+
* A key that is not an Ed25519 private key in PEM, or a `--key` file that
|
|
197
|
+
* can't be read, refuses either way. `unsigned` (`--unsigned`) skips the
|
|
198
|
+
* key without `required` and is ignored with it.
|
|
190
199
|
*/
|
|
191
200
|
export function ciServeRunnerKey(req) {
|
|
192
|
-
|
|
193
|
-
|
|
201
|
+
const required = req.required ?? null;
|
|
202
|
+
const optIn = `ci.green.signedSince is ${required}, so every run must be signed`;
|
|
203
|
+
if (req.unsigned && !required)
|
|
204
|
+
return { fromFlag: false, source: "none (--unsigned)", note: "runs are not signed (--unsigned)" };
|
|
194
205
|
const host = req.keyFile === undefined && !req.envKey ? hostRunnerKey(req.stateDir) : undefined;
|
|
195
206
|
const pem = req.keyFile !== undefined ? readFileSync(req.keyFile, "utf-8") : req.envKey || host?.pem;
|
|
196
207
|
const source = req.keyFile !== undefined ? req.keyFile : req.envKey ? "CHANT_RUNNER_KEY" : (host?.path ?? join(req.stateDir, RUNNER_KEY_FILE));
|
|
197
208
|
if (pem === undefined) {
|
|
198
|
-
|
|
209
|
+
if (!required)
|
|
210
|
+
return { fromFlag: false, source: "none", note: "runs are not signed: there is no runner key. To verify who ran a job, add one with chant ci serve --setup-key --trust" };
|
|
211
|
+
throw new CiRunError(`${optIn}, and there is no runner key: no --key, no CHANT_RUNNER_KEY and no ${source}. Make one with chant ci serve --setup-key and list it with --trust`);
|
|
199
212
|
}
|
|
200
213
|
let publicKey;
|
|
201
214
|
try {
|
|
@@ -205,12 +218,19 @@ export function ciServeRunnerKey(req) {
|
|
|
205
218
|
throw new CiRunError(`the runner key from ${source} is not an Ed25519 private key in PEM: ${message(err)}`);
|
|
206
219
|
}
|
|
207
220
|
const head = targetHead(req.top, req.branch);
|
|
208
|
-
if (!head)
|
|
209
|
-
|
|
221
|
+
if (!head) {
|
|
222
|
+
const why = `there is no branch ${req.branch} here or on origin, so the runner key can't be checked against its .chant/trust.json`;
|
|
223
|
+
if (required)
|
|
224
|
+
throw new CiRunError(`${optIn}, and ${why}; fetch it`);
|
|
225
|
+
return { fromFlag: false, source, warning: `${why}, so runs are not signed; fetch it to sign them` };
|
|
226
|
+
}
|
|
210
227
|
const listed = runnerKeyListed(req.top, head.commit, pem);
|
|
211
228
|
if (!listed.ok) {
|
|
212
|
-
|
|
213
|
-
|
|
229
|
+
const why = `the runner key from ${source} is not trusted at ${branchAt(req.branch, head.commit)}: ${listed.reason}`;
|
|
230
|
+
const fix = `List it on a branch for review with chant ci serve --setup-key --trust, or run ${trustCommand(defaultRunnerPrincipal(), publicKey)} in a checkout of ${req.branch} and merge the change.`;
|
|
231
|
+
if (required)
|
|
232
|
+
throw new CiRunError(`${optIn}, and ${why}\n ${fix}`);
|
|
233
|
+
return { fromFlag: false, source, warning: `${why}. Runs are not signed until it is. ${fix}` };
|
|
214
234
|
}
|
|
215
235
|
return { pem, fromFlag: req.keyFile !== undefined, source };
|
|
216
236
|
}
|
|
@@ -23,8 +23,26 @@
|
|
|
23
23
|
* as its `--resume` record, so a re-run of a failed apply at the same
|
|
24
24
|
* commit finishes under the approval it has. This replaces the forge's CI
|
|
25
25
|
* cache.
|
|
26
|
+
* - A gate per wave (#4003). With `--wave-gate` the apply job runs
|
|
27
|
+
* `pr-apply --wave-gate`, which applies the waves in order and stops at
|
|
28
|
+
* the first whose moved plan waits on its own gate. The job fails, and the
|
|
29
|
+
* resume record keeps the waves that applied; once the wave's plan is
|
|
30
|
+
* approved, `rerunFailed` runs the apply again and it carries on from
|
|
31
|
+
* that wave.
|
|
26
32
|
* - Capacity. At most `maxJobs` jobs run at once. A job over the limit is not
|
|
27
33
|
* recorded, so a later poll starts it.
|
|
34
|
+
* - Fast-forwards only (#4122). An apply runs only when the target's new head
|
|
35
|
+
* descends from the head seen before it. Any other move, such as a force
|
|
36
|
+
* push back to an older commit, is recorded as seen and not applied.
|
|
37
|
+
* - `[skip ci]` (#4122). A target head or pull request head whose commit
|
|
38
|
+
* message says `[skip ci]` or `[ci skip]` is recorded as seen and not run,
|
|
39
|
+
* as Actions does.
|
|
40
|
+
* - The forge's ledger (#4121). Before each job, when no other job of this
|
|
41
|
+
* serve runs, the clone's `chant/lifecycle` follows the forge's: it is
|
|
42
|
+
* deleted when the forge has none, and reset to the forge's when neither
|
|
43
|
+
* contains the other. Each is a warning. A local branch ahead of the
|
|
44
|
+
* forge's keeps its facts, and the job's push sends them. Nothing here
|
|
45
|
+
* pushes the ledger.
|
|
28
46
|
*
|
|
29
47
|
* Host mode only: each job runs in a fresh worktree on this host as this
|
|
30
48
|
* user, with the clean environment `ciRun` gives it. A pull request from a
|
|
@@ -85,9 +103,11 @@ export interface CiServeState {
|
|
|
85
103
|
env: string;
|
|
86
104
|
branch: string;
|
|
87
105
|
member?: string;
|
|
88
|
-
/** The target branch's head the last apply was measured to, or the head
|
|
106
|
+
/** The target branch's head the last apply was measured to, or the last head seen and not applied. */
|
|
89
107
|
target?: string;
|
|
90
108
|
jobs: Record<string, CiServeJobState>;
|
|
109
|
+
/** Plans seen and not run, by state key, with why (#4122). */
|
|
110
|
+
skipped?: Record<string, "skip-ci">;
|
|
91
111
|
}
|
|
92
112
|
/** What a pass did, in order, for the log. */
|
|
93
113
|
export type CiServeEvent = {
|
|
@@ -125,6 +145,19 @@ export type CiServeEvent = {
|
|
|
125
145
|
status: "ok" | "fail";
|
|
126
146
|
failure?: string;
|
|
127
147
|
report?: string;
|
|
148
|
+
} | {
|
|
149
|
+
type: "not-run";
|
|
150
|
+
job: CiJobName;
|
|
151
|
+
commit: string;
|
|
152
|
+
pr?: number;
|
|
153
|
+
reason: "not-fast-forward" | "skip-ci";
|
|
154
|
+
branch?: string;
|
|
155
|
+
from?: string;
|
|
156
|
+
} | {
|
|
157
|
+
type: "ledger";
|
|
158
|
+
action: "deleted" | "reset";
|
|
159
|
+
local: string;
|
|
160
|
+
forge?: string;
|
|
128
161
|
} | {
|
|
129
162
|
type: "error";
|
|
130
163
|
message: string;
|
|
@@ -168,17 +201,34 @@ export declare function ciServeKey(t: {
|
|
|
168
201
|
commit: string;
|
|
169
202
|
pr?: number;
|
|
170
203
|
}): string;
|
|
204
|
+
/**
|
|
205
|
+
* Make the clone's `chant/lifecycle` follow the forge's (#4121). The clone's
|
|
206
|
+
* branch outlives the forge's, so a ledger the forge deleted or rewrote would
|
|
207
|
+
* make the next pr-apply find the two diverged and stall.
|
|
208
|
+
*
|
|
209
|
+
* - The forge has none: the local branch and its remote-tracking ref go.
|
|
210
|
+
* - Neither contains the other: the local branch and its remote-tracking ref
|
|
211
|
+
* are reset to the forge's tip.
|
|
212
|
+
* - The local branch is ahead of the forge's, or behind it, or the same: it
|
|
213
|
+
* is left, and an apply's push sends what it holds that the forge lacks.
|
|
214
|
+
*
|
|
215
|
+
* Nothing is pushed, so a rewritten ledger never goes back to the forge. A
|
|
216
|
+
* forge that can't be reached leaves everything as it is; the job's own
|
|
217
|
+
* fetch says why.
|
|
218
|
+
*/
|
|
219
|
+
export declare function reconcileCiServeLedger(top: string): CiServeEvent | undefined;
|
|
171
220
|
/** Where a serve keeps its state when `--state` names nowhere: beside the run events, in the repository's common git directory. */
|
|
172
221
|
export declare function defaultCiServeStateDir(cwd: string): string;
|
|
173
222
|
/** The state file, or a new one. */
|
|
174
223
|
export declare function readCiServeState(file: string, fresh: Omit<CiServeState, "schema" | "jobs">): CiServeState;
|
|
175
224
|
/**
|
|
176
225
|
* One poll of the forge, as triggers: an apply when the target's head moved
|
|
177
|
-
* since the last apply (none on the first poll, which only
|
|
226
|
+
* since the last apply by a fast-forward (none on the first poll, which only
|
|
227
|
+
* notes the head, and none for a head whose message says `[skip ci]`),
|
|
178
228
|
* then a plan for each open pull request head not yet planned, oldest pull
|
|
179
229
|
* request first. A job already in the state file is left out unless it
|
|
180
|
-
* failed and `rerunFailed` is set. The first poll's head
|
|
181
|
-
* state file.
|
|
230
|
+
* failed and `rerunFailed` is set. The first poll's head, and a head seen
|
|
231
|
+
* and not applied, are written to the state file.
|
|
182
232
|
*/
|
|
183
233
|
export declare function ciServeObserve(opts: CiServeOptions, rt?: CiServeRuntime): Promise<{
|
|
184
234
|
triggers: CiServeTrigger[];
|