@intentius/chant 0.124.39 → 0.124.41
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/dist/__fixtures__/oidc-emulator.d.ts +49 -0
- package/dist/__fixtures__/oidc-emulator.d.ts.map +1 -0
- package/dist/__fixtures__/oidc-emulator.js +89 -0
- package/dist/ci-handover.d.ts +117 -0
- package/dist/ci-handover.d.ts.map +1 -0
- package/dist/ci-handover.js +217 -0
- package/dist/ci-identity.d.ts +190 -0
- package/dist/ci-identity.d.ts.map +1 -0
- package/dist/ci-identity.js +219 -0
- package/dist/cli/component-endpoint.d.ts +20 -0
- package/dist/cli/component-endpoint.d.ts.map +1 -0
- package/dist/cli/component-endpoint.js +36 -0
- package/dist/cli/handlers/approve.d.ts +2 -1
- package/dist/cli/handlers/approve.d.ts.map +1 -1
- package/dist/cli/handlers/approve.js +20 -6
- package/dist/cli/handlers/build.d.ts.map +1 -1
- package/dist/cli/handlers/build.js +15 -1
- package/dist/cli/handlers/fan-out.d.ts.map +1 -1
- package/dist/cli/handlers/fan-out.js +26 -4
- package/dist/cli/handlers/gate-resume.d.ts +3 -0
- package/dist/cli/handlers/gate-resume.d.ts.map +1 -1
- package/dist/cli/handlers/gate-resume.js +15 -2
- package/dist/cli/handlers/lock.d.ts +8 -0
- package/dist/cli/handlers/lock.d.ts.map +1 -0
- package/dist/cli/handlers/lock.js +197 -0
- package/dist/cli/handlers/pr-env.d.ts +34 -0
- package/dist/cli/handlers/pr-env.d.ts.map +1 -0
- package/dist/cli/handlers/pr-env.js +436 -0
- package/dist/cli/handlers/pr.d.ts +74 -0
- package/dist/cli/handlers/pr.d.ts.map +1 -1
- package/dist/cli/handlers/pr.js +514 -58
- package/dist/cli/handlers/promote.d.ts.map +1 -1
- package/dist/cli/handlers/promote.js +16 -2
- package/dist/cli/handlers/query.d.ts +6 -0
- package/dist/cli/handlers/query.d.ts.map +1 -0
- package/dist/cli/handlers/query.js +80 -0
- package/dist/cli/handlers/run.d.ts +4 -1
- package/dist/cli/handlers/run.d.ts.map +1 -1
- package/dist/cli/handlers/run.js +17 -6
- package/dist/cli/main.d.ts.map +1 -1
- package/dist/cli/main.js +41 -2
- package/dist/cli/parse-args.d.ts.map +1 -1
- package/dist/cli/parse-args.js +31 -2
- package/dist/cli/registry.d.ts +12 -2
- package/dist/cli/registry.d.ts.map +1 -1
- package/dist/components/auto-release.d.ts +35 -3
- package/dist/components/auto-release.d.ts.map +1 -1
- package/dist/components/auto-release.js +29 -2
- package/dist/components/cli-support.d.ts +2 -0
- package/dist/components/cli-support.d.ts.map +1 -1
- package/dist/components/cli-support.js +2 -0
- package/dist/components/deploy-units.d.ts.map +1 -1
- package/dist/components/deploy-units.js +5 -0
- package/dist/components/fan-out-run.d.ts +28 -0
- package/dist/components/fan-out-run.d.ts.map +1 -1
- package/dist/components/fan-out-run.js +27 -3
- package/dist/components/pr-environment.d.ts +298 -0
- package/dist/components/pr-environment.d.ts.map +1 -0
- package/dist/components/pr-environment.js +406 -0
- package/dist/components/pr-hooks.d.ts +66 -0
- package/dist/components/pr-hooks.d.ts.map +1 -0
- package/dist/components/pr-hooks.js +119 -0
- package/dist/components/pr-pipeline.d.ts +72 -2
- package/dist/components/pr-pipeline.d.ts.map +1 -1
- package/dist/components/pr-pipeline.js +96 -5
- package/dist/components/pr-policy.d.ts +109 -0
- package/dist/components/pr-policy.d.ts.map +1 -0
- package/dist/components/pr-policy.js +150 -0
- package/dist/components/pr-run.d.ts.map +1 -1
- package/dist/components/pr-run.js +5 -3
- package/dist/components/promote.d.ts.map +1 -1
- package/dist/components/promote.js +39 -16
- package/dist/gate-decision.d.ts +15 -2
- package/dist/gate-decision.d.ts.map +1 -1
- package/dist/gate-decision.js +17 -2
- package/dist/gate-policy.d.ts +140 -0
- package/dist/gate-policy.d.ts.map +1 -0
- package/dist/gate-policy.js +242 -0
- package/dist/lifecycle/__fixtures__/publish-fakes.d.ts.map +1 -1
- package/dist/lifecycle/__fixtures__/publish-fakes.js +6 -1
- package/dist/lifecycle/gate-checkpoint.d.ts +1 -1
- package/dist/lifecycle/gate-checkpoint.js +1 -1
- package/dist/{workspace/trust → lifecycle}/gate-seal.d.ts +1 -1
- package/dist/lifecycle/gate-seal.d.ts.map +1 -0
- package/dist/lifecycle/publish-declaration.d.ts.map +1 -1
- package/dist/lifecycle/publish-declaration.js +4 -3
- package/dist/lifecycle/publish-npm.d.ts +2 -2
- package/dist/lifecycle/publish-npm.d.ts.map +1 -1
- package/dist/lifecycle/publish-npm.js +31 -8
- package/dist/lifecycle/query.d.ts +68 -0
- package/dist/lifecycle/query.d.ts.map +1 -0
- package/dist/lifecycle/query.js +246 -0
- package/dist/lifecycle/release-ledger.d.ts +31 -0
- package/dist/lifecycle/release-ledger.d.ts.map +1 -1
- package/dist/lifecycle/release-ledger.js +19 -1
- package/dist/lifecycle/run-ledger.d.ts +13 -0
- package/dist/lifecycle/run-ledger.d.ts.map +1 -1
- package/dist/lifecycle/run-ledger.js +16 -0
- package/dist/lifecycle/status.d.ts +5 -1
- package/dist/lifecycle/status.d.ts.map +1 -1
- package/dist/lifecycle/status.js +10 -5
- package/dist/lifecycle/target-lock.d.ts +97 -0
- package/dist/lifecycle/target-lock.d.ts.map +1 -0
- package/dist/lifecycle/target-lock.js +286 -0
- package/dist/live-endpoint.d.ts +18 -0
- package/dist/live-endpoint.d.ts.map +1 -1
- package/dist/live-endpoint.js +27 -0
- package/dist/op/activities/activity-contracts.d.ts +30 -0
- package/dist/op/activities/activity-contracts.d.ts.map +1 -1
- package/dist/op/activities/activity-contracts.js +26 -0
- package/dist/op/activities/index.d.ts +2 -0
- package/dist/op/activities/index.d.ts.map +1 -1
- package/dist/op/activities/index.js +4 -0
- package/dist/op/activities/review-agent.d.ts +137 -0
- package/dist/op/activities/review-agent.d.ts.map +1 -0
- package/dist/op/activities/review-agent.js +266 -0
- package/dist/op/builders.d.ts +9 -0
- package/dist/op/builders.d.ts.map +1 -1
- package/dist/op/builders.js +11 -0
- package/dist/op/cron.d.ts +8 -0
- package/dist/op/cron.d.ts.map +1 -1
- package/dist/op/cron.js +51 -0
- package/dist/op/gate-approval.d.ts +20 -13
- package/dist/op/gate-approval.d.ts.map +1 -1
- package/dist/op/gate-approval.js +13 -18
- package/dist/op/gate-quorum.d.ts +131 -0
- package/dist/op/gate-quorum.d.ts.map +1 -0
- package/dist/op/gate-quorum.js +183 -0
- package/dist/op/gate-resume.d.ts +4 -2
- package/dist/op/gate-resume.d.ts.map +1 -1
- package/dist/op/gate-resume.js +5 -4
- package/dist/op/gate-review.js +1 -1
- package/dist/op/gate.d.ts +32 -4
- package/dist/op/gate.d.ts.map +1 -1
- package/dist/op/gate.js +40 -24
- package/dist/op/index.d.ts +6 -4
- package/dist/op/index.d.ts.map +1 -1
- package/dist/op/index.js +4 -3
- package/dist/op/local-executor.d.ts.map +1 -1
- package/dist/op/local-executor.js +9 -3
- package/dist/op/op-waves-run.d.ts +62 -1
- package/dist/op/op-waves-run.d.ts.map +1 -1
- package/dist/op/op-waves-run.js +190 -11
- package/dist/op/op-waves.d.ts +55 -3
- package/dist/op/op-waves.d.ts.map +1 -1
- package/dist/op/op-waves.js +63 -8
- package/dist/op/schedule-overdue.d.ts +57 -0
- package/dist/op/schedule-overdue.d.ts.map +1 -0
- package/dist/op/schedule-overdue.js +138 -0
- package/dist/override-gate.d.ts +2 -2
- package/dist/override-gate.d.ts.map +1 -1
- package/dist/override-gate.js +1 -1
- package/dist/pr-forge.d.ts +36 -0
- package/dist/pr-forge.d.ts.map +1 -1
- package/dist/pr-forge.js +82 -0
- package/dist/pr-loop.d.ts +90 -1
- package/dist/pr-loop.d.ts.map +1 -1
- package/dist/pr-loop.js +77 -3
- package/dist/pr-post.d.ts +72 -0
- package/dist/pr-post.d.ts.map +1 -0
- package/dist/pr-post.js +134 -0
- package/dist/wave-blocked.d.ts +79 -0
- package/dist/wave-blocked.d.ts.map +1 -0
- package/dist/wave-blocked.js +245 -0
- package/dist/wave-engine.d.ts +6 -0
- package/dist/wave-engine.d.ts.map +1 -1
- package/dist/wave-engine.js +2 -0
- package/dist/wave-runtime.mjs +239 -239
- package/dist/workspace/__fixtures__/two-member-workspace.d.ts +1 -0
- package/dist/workspace/__fixtures__/two-member-workspace.d.ts.map +1 -1
- package/dist/workspace/__fixtures__/two-member-workspace.js +1 -1
- package/dist/workspace/ci-branches.d.ts +51 -0
- package/dist/workspace/ci-branches.d.ts.map +1 -0
- package/dist/workspace/ci-branches.js +69 -0
- package/dist/workspace/ci-cloud-identity.d.ts +11 -0
- package/dist/workspace/ci-cloud-identity.d.ts.map +1 -0
- package/dist/workspace/ci-cloud-identity.js +24 -0
- package/dist/workspace/ci-locks.d.ts +12 -0
- package/dist/workspace/ci-locks.d.ts.map +1 -0
- package/dist/workspace/ci-locks.js +6 -0
- package/dist/workspace/ci-run.d.ts +24 -1
- package/dist/workspace/ci-run.d.ts.map +1 -1
- package/dist/workspace/ci-run.js +85 -6
- package/dist/workspace/declaration.d.ts +63 -0
- package/dist/workspace/declaration.d.ts.map +1 -1
- package/dist/workspace/declaration.js +72 -1
- package/dist/workspace/declaration.schema.json +147 -0
- package/dist/workspace/identity-gate-rule.d.ts +1 -1
- package/dist/workspace/identity-gate-rule.d.ts.map +1 -1
- package/dist/workspace/identity-gate-rule.js +1 -1
- package/dist/workspace/identity.d.ts +1 -1
- package/dist/workspace/identity.d.ts.map +1 -1
- package/dist/workspace/lineage-upgrade.d.ts.map +1 -1
- package/dist/workspace/lineage-upgrade.js +8 -5
- package/dist/workspace/member-pipeline.d.ts +6 -0
- package/dist/workspace/member-pipeline.d.ts.map +1 -1
- package/dist/workspace/member-pipeline.js +23 -0
- package/dist/workspace/pr-report.schema.json +73 -2
- package/dist/workspace/security.d.ts.map +1 -1
- package/dist/workspace/security.js +4 -0
- package/dist/workspace/status-gates.d.ts +3 -2
- package/dist/workspace/status-gates.d.ts.map +1 -1
- package/dist/workspace/status-gates.js +8 -7
- package/dist/workspace/status.d.ts +3 -1
- package/dist/workspace/status.d.ts.map +1 -1
- package/dist/workspace/status.js +7 -3
- package/dist/workspace/status.schema.json +1 -0
- package/dist/workspace/trust/seal.d.ts +2 -2
- package/dist/workspace/trust/seal.d.ts.map +1 -1
- package/dist/workspace/trust/seal.js +2 -2
- package/package.json +1 -1
- package/src/__fixtures__/oidc-emulator.ts +118 -0
- package/src/ci-handover.test.ts +209 -0
- package/src/ci-handover.ts +237 -0
- package/src/ci-identity.test.ts +201 -0
- package/src/ci-identity.ts +310 -0
- package/src/cli/component-endpoint.ts +43 -0
- package/src/cli/handlers/approve.ts +18 -5
- package/src/cli/handlers/build.ts +15 -1
- package/src/cli/handlers/fan-out.test.ts +10 -0
- package/src/cli/handlers/fan-out.ts +29 -4
- package/src/cli/handlers/gate-resume.ts +16 -2
- package/src/cli/handlers/lock.test.ts +98 -0
- package/src/cli/handlers/lock.ts +202 -0
- package/src/cli/handlers/operator.test.ts +3 -1
- package/src/cli/handlers/pr-endpoint.test.ts +169 -0
- package/src/cli/handlers/pr-env.ts +462 -0
- package/src/cli/handlers/pr-waves-blocked.test.ts +247 -0
- package/src/cli/handlers/pr.test.ts +184 -0
- package/src/cli/handlers/pr.ts +508 -64
- package/src/cli/handlers/promote.ts +25 -2
- package/src/cli/handlers/query.ts +78 -0
- package/src/cli/handlers/run.test.ts +2 -1
- package/src/cli/handlers/run.ts +16 -5
- package/src/cli/main.ts +41 -2
- package/src/cli/parse-args.ts +23 -2
- package/src/cli/registry.ts +12 -2
- package/src/components/auto-release-infra.test.ts +131 -0
- package/src/components/auto-release.test.ts +40 -1
- package/src/components/auto-release.ts +50 -4
- package/src/components/cli-support.ts +4 -0
- package/src/components/deploy-units.ts +5 -0
- package/src/components/fan-out-run.ts +39 -2
- package/src/components/fan-out-waves.test.ts +45 -1
- package/src/components/pr-environment.test.ts +249 -0
- package/src/components/pr-environment.ts +538 -0
- package/src/components/pr-hooks.test.ts +118 -0
- package/src/components/pr-hooks.ts +157 -0
- package/src/components/pr-pipeline.ts +130 -5
- package/src/components/pr-policy.test.ts +145 -0
- package/src/components/pr-policy.ts +211 -0
- package/src/components/pr-run.ts +5 -2
- package/src/components/promote.test.ts +39 -0
- package/src/components/promote.ts +38 -14
- package/src/gate-decision-bundle.test.ts +4 -3
- package/src/gate-decision.ts +39 -2
- package/src/gate-policy-bundle.test.ts +28 -0
- package/src/gate-policy.test.ts +162 -0
- package/src/gate-policy.ts +298 -0
- package/src/lifecycle/__fixtures__/publish-fakes.ts +6 -1
- package/src/lifecycle/gate-checkpoint.ts +1 -1
- package/src/{workspace/trust → lifecycle}/gate-seal.ts +1 -1
- package/src/lifecycle/publish-declaration.ts +4 -3
- package/src/lifecycle/publish-npm.ts +33 -8
- package/src/lifecycle/publish-targets.test.ts +36 -3
- package/src/lifecycle/query.test.ts +91 -0
- package/src/lifecycle/query.ts +315 -0
- package/src/lifecycle/release-ledger.ts +42 -1
- package/src/lifecycle/run-ledger.ts +18 -0
- package/src/lifecycle/status.test.ts +19 -0
- package/src/lifecycle/status.ts +14 -6
- package/src/lifecycle/target-lock.test.ts +195 -0
- package/src/lifecycle/target-lock.ts +347 -0
- package/src/live-endpoint.ts +31 -0
- package/src/op/activities/activity-contracts.ts +31 -0
- package/src/op/activities/index.ts +6 -0
- package/src/op/activities/review-agent.test.ts +240 -0
- package/src/op/activities/review-agent.ts +355 -0
- package/src/op/builders.ts +13 -0
- package/src/op/cron.ts +44 -0
- package/src/op/gate-alias.test.ts +2 -2
- package/src/op/gate-approval.test.ts +30 -9
- package/src/op/gate-approval.ts +23 -26
- package/src/op/gate-quorum.test.ts +139 -0
- package/src/op/gate-quorum.ts +248 -0
- package/src/op/gate-resume.ts +6 -3
- package/src/op/gate-review.ts +1 -1
- package/src/op/gate.ts +64 -23
- package/src/op/index.ts +13 -1
- package/src/op/local-executor.ts +9 -2
- package/src/op/op-waves-hook-targets.test.ts +126 -0
- package/src/op/op-waves-policy-hold.test.ts +66 -0
- package/src/op/op-waves-quorum.test.ts +101 -0
- package/src/op/op-waves-run.ts +233 -11
- package/src/op/op-waves.ts +83 -10
- package/src/op/schedule-overdue.test.ts +220 -0
- package/src/op/schedule-overdue.ts +161 -0
- package/src/override-gate-bundle.test.ts +1 -1
- package/src/override-gate.ts +2 -2
- package/src/pr-forge-bundle.test.ts +2 -2
- package/src/pr-forge.test.ts +33 -0
- package/src/pr-forge.ts +114 -0
- package/src/pr-loop-bundle.test.ts +2 -2
- package/src/pr-loop.test.ts +58 -0
- package/src/pr-loop.ts +146 -4
- package/src/pr-post.test.ts +141 -0
- package/src/pr-post.ts +159 -0
- package/src/wave-blocked-bundle.test.ts +25 -0
- package/src/wave-blocked.test.ts +146 -0
- package/src/wave-blocked.ts +271 -0
- package/src/wave-engine.ts +8 -0
- package/src/workspace/__fixtures__/two-member-workspace.ts +2 -2
- package/src/workspace/ci-branches.test.ts +62 -0
- package/src/workspace/ci-branches.ts +79 -0
- package/src/workspace/ci-cloud-identity.test.ts +53 -0
- package/src/workspace/ci-cloud-identity.ts +23 -0
- package/src/workspace/ci-locks.ts +16 -0
- package/src/workspace/ci-run-index.test.ts +2 -1
- package/src/workspace/ci-run.test.ts +133 -8
- package/src/workspace/ci-run.ts +92 -7
- package/src/workspace/declaration.schema.json +147 -0
- package/src/workspace/declaration.test.ts +76 -0
- package/src/workspace/declaration.ts +134 -2
- package/src/workspace/identity-gate-rule.ts +1 -1
- package/src/workspace/identity.ts +1 -1
- package/src/workspace/lineage-upgrade.ts +11 -8
- package/src/workspace/member-pipeline.test.ts +29 -0
- package/src/workspace/member-pipeline.ts +30 -0
- package/src/workspace/pr-report.schema.json +73 -2
- package/src/workspace/security.ts +4 -0
- package/src/workspace/status-gates.ts +9 -6
- package/src/workspace/status.schema.json +1 -0
- package/src/workspace/status.ts +11 -4
- package/src/workspace/trust/gate-seal.test.ts +1 -1
- package/src/workspace/trust/seal.ts +2 -2
- package/dist/workspace/trust/gate-seal.d.ts.map +0 -1
- /package/dist/{workspace/trust → lifecycle}/gate-seal.js +0 -0
|
@@ -0,0 +1,436 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `chant components pr-env up|down|sweep` (#4298): an environment per open
|
|
3
|
+
* pull request, `pr-<n>`, derived from `ci.prEnvironment.template`
|
|
4
|
+
* (../../components/pr-environment.ts says what each does and why).
|
|
5
|
+
*
|
|
6
|
+
* ```
|
|
7
|
+
* chant components pr-env up --base <ref> --pr <n> [--forge <kind>] [--member <name>] [--output <dir>] [--no-push]
|
|
8
|
+
* chant approve pr-<n> pr-env --plan <digest> --approver <you> --sign
|
|
9
|
+
* chant components pr-env down --pr <n> [--reason closed|expired] [--forge <kind>] [--member <name>]
|
|
10
|
+
* chant components pr-env post --pr <n> --head <sha> [--result <r>] --forge <kind>
|
|
11
|
+
* chant components pr-env sweep [--forge <kind>]
|
|
12
|
+
* ```
|
|
13
|
+
*
|
|
14
|
+
* In the generated jobs (#4295) `up` runs the change's code with no forge
|
|
15
|
+
* token, so it passes `--no-push` and hands its report and record on, and
|
|
16
|
+
* `post` runs in a fresh job at the target branch's commit, appends the
|
|
17
|
+
* record and posts the note.
|
|
18
|
+
*
|
|
19
|
+
* `up` prepares the change as `pr-plan` does, with `--env pr-<n>` and the
|
|
20
|
+
* build parameter `ownership.env` reads set to `pr-<n>`, so every resource
|
|
21
|
+
* the apply creates carries that environment's marker. It applies only when
|
|
22
|
+
* the plan is empty or an approval of its digest stands under gate `pr-env`,
|
|
23
|
+
* since a non-empty plan never applies with no person approving (ws-080).
|
|
24
|
+
*
|
|
25
|
+
* `down` reads the environment's record on chant/lifecycle, checks it names
|
|
26
|
+
* this pull request's environment, and tears down what `planTeardown` finds
|
|
27
|
+
* under the record's marker, after dropping any entry whose marker is not
|
|
28
|
+
* exactly that one. No gate holds it: closing the pull request is the
|
|
29
|
+
* decision, as is the TTL the base set for `sweep`.
|
|
30
|
+
*/
|
|
31
|
+
import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
32
|
+
import { join, resolve } from "node:path";
|
|
33
|
+
import { loadChantConfig } from "../../config.js";
|
|
34
|
+
import { ENV_VAR } from "../../env.js";
|
|
35
|
+
import { isEmptyChangeSet } from "../../change-set.js";
|
|
36
|
+
import { PR_ENV_EXIT, PR_ENV_GATE, PR_ENV_LEDGER_DIR, PR_ENV_LEDGER_FILE, PR_ENV_RECORD_FILE, PR_ENV_REPORT_DIR, acceptHandedRecord, handedPrEnvReport, PR_ENV_TEMPLATE_VAR, derivePrEnvironment, expired, livePrEnvironments, parsePrEnvRecords, prEnvApproveCommand, prEnvNoteMarker, prEnvStatusContext, prEnvStatusDescription, prEnvStatusState, prEnvironmentName, renderPrEnvNote, teardownComplete, teardownCounts, tearDownPrEnvironment, ttlMs, } from "../../components/pr-environment.js";
|
|
37
|
+
import { applyPrSet, approvalOf, decidePrGate, readOnlyLedger } from "../../components/pr-run.js";
|
|
38
|
+
import { gitGateLedgerPort } from "../../op/gate.js";
|
|
39
|
+
import { prOp } from "../../pr-loop.js";
|
|
40
|
+
import { FORGE_KINDS, forgeFromEnv, forgePrincipalOf } from "../../pr-forge.js";
|
|
41
|
+
import { formatError, formatInfo, formatWarning } from "../format.js";
|
|
42
|
+
import { withComponentEndpoint } from "../component-endpoint.js";
|
|
43
|
+
const USAGE = "chant components pr-env up --base <ref> --pr <n> [--no-push] | post --pr <n> --head <sha> | down --pr <n> [--reason closed|expired] | sweep";
|
|
44
|
+
const fail = (message, hint) => {
|
|
45
|
+
console.error(formatError({ message, ...(hint ? { hint } : {}) }));
|
|
46
|
+
return PR_ENV_EXIT.failed;
|
|
47
|
+
};
|
|
48
|
+
/** chant components pr-env <up|down|sweep> */
|
|
49
|
+
export async function runComponentsPrEnv(ctx) {
|
|
50
|
+
const action = ctx.args.extraPositional;
|
|
51
|
+
if (action === "up")
|
|
52
|
+
return prEnvUp(ctx);
|
|
53
|
+
if (action === "down")
|
|
54
|
+
return prEnvDown(ctx);
|
|
55
|
+
if (action === "sweep")
|
|
56
|
+
return prEnvSweep(ctx);
|
|
57
|
+
if (action === "post")
|
|
58
|
+
return prEnvPost(ctx);
|
|
59
|
+
return fail(action ? `pr-env ${action} is not one of up, post, down or sweep` : "pr-env needs an action: up, post, down or sweep", USAGE);
|
|
60
|
+
}
|
|
61
|
+
function forgeOf(ctx) {
|
|
62
|
+
const kind = ctx.args.forge;
|
|
63
|
+
if (kind === undefined || ctx.args.noStatus)
|
|
64
|
+
return undefined;
|
|
65
|
+
if (!FORGE_KINDS.includes(kind))
|
|
66
|
+
return fail(`--forge ${kind} is not one of ${FORGE_KINDS.join(", ")}`);
|
|
67
|
+
try {
|
|
68
|
+
return forgeFromEnv(kind);
|
|
69
|
+
}
|
|
70
|
+
catch (err) {
|
|
71
|
+
return fail(err instanceof Error ? err.message : String(err));
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
function actor() {
|
|
75
|
+
return process.env.GITHUB_ACTOR || process.env.GITLAB_USER_LOGIN || process.env.USER || "chant";
|
|
76
|
+
}
|
|
77
|
+
function memberOf(ctx) {
|
|
78
|
+
return ctx.args.members?.length === 1 ? ctx.args.members[0] : undefined;
|
|
79
|
+
}
|
|
80
|
+
// ── the ledger ─────────────────────────────────────────────────────────────
|
|
81
|
+
async function readRecords() {
|
|
82
|
+
const { fetchLifecycle, readBlobFromPath } = await import("../../lifecycle/git.js");
|
|
83
|
+
await fetchLifecycle().catch(() => false);
|
|
84
|
+
return parsePrEnvRecords(await readBlobFromPath(PR_ENV_LEDGER_DIR, PR_ENV_LEDGER_FILE));
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Append `record` to chant/lifecycle and push it. With `--no-push` (#4295)
|
|
88
|
+
* the record is written to `pr-env-record.json` under `--output` instead,
|
|
89
|
+
* for `pr-env post` to append from a job that runs none of the change's
|
|
90
|
+
* code. A failure warns: what was applied or torn down stands.
|
|
91
|
+
*/
|
|
92
|
+
async function appendRecord(ctx, record) {
|
|
93
|
+
if (ctx.args.noPush) {
|
|
94
|
+
const dir = resolve(ctx.args.output ?? PR_ENV_REPORT_DIR);
|
|
95
|
+
mkdirSync(dir, { recursive: true });
|
|
96
|
+
writeFileSync(join(dir, PR_ENV_RECORD_FILE), JSON.stringify(record, null, 2) + "\n");
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
const { appendLedgerLine, fetchLifecycle, pushLifecycleReport } = await import("../../lifecycle/git.js");
|
|
100
|
+
try {
|
|
101
|
+
await fetchLifecycle().catch(() => false);
|
|
102
|
+
await appendLedgerLine(PR_ENV_LEDGER_DIR, PR_ENV_LEDGER_FILE, JSON.stringify(record), `Pull request environment ${record.env} ${record.kind} (${record.result})`);
|
|
103
|
+
const pushed = await pushLifecycleReport();
|
|
104
|
+
if (pushed.pushWarning)
|
|
105
|
+
console.error(formatWarning({ message: pushed.pushWarning }));
|
|
106
|
+
}
|
|
107
|
+
catch (err) {
|
|
108
|
+
console.error(formatWarning({ message: `could not record ${record.env}'s ${record.kind} on chant/lifecycle: ${err instanceof Error ? err.message : String(err)}` }));
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
// ── the report and the note ────────────────────────────────────────────────
|
|
112
|
+
async function finish(ctx, forge, report) {
|
|
113
|
+
const dir = resolve(ctx.args.output ?? PR_ENV_REPORT_DIR);
|
|
114
|
+
mkdirSync(dir, { recursive: true });
|
|
115
|
+
const note = renderPrEnvNote(report, forge ? forgePrincipalOf(forge.kind, forge.host, "<your-login>") : "<you>");
|
|
116
|
+
writeFileSync(join(dir, `pr-env-${report.stage}.json`), JSON.stringify(report, null, 2) + "\n");
|
|
117
|
+
writeFileSync(join(dir, "pr-env-note.md"), note);
|
|
118
|
+
if (ctx.args.json)
|
|
119
|
+
console.log(JSON.stringify(report, null, 2));
|
|
120
|
+
if (!forge)
|
|
121
|
+
return;
|
|
122
|
+
if (report.head) {
|
|
123
|
+
try {
|
|
124
|
+
await forge.setStatus(report.head, {
|
|
125
|
+
context: prEnvStatusContext(report.member),
|
|
126
|
+
state: prEnvStatusState(report),
|
|
127
|
+
description: prEnvStatusDescription(report),
|
|
128
|
+
...(forge.runUrl ? { url: forge.runUrl } : {}),
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
catch (err) {
|
|
132
|
+
console.error(formatWarning({ message: `could not set the ${prEnvStatusContext(report.member)} status: ${err instanceof Error ? err.message : String(err)}` }));
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
try {
|
|
136
|
+
await forge.upsertNote(report.pr, prEnvNoteMarker(report.env, report.member), note);
|
|
137
|
+
}
|
|
138
|
+
catch (err) {
|
|
139
|
+
console.error(formatWarning({ message: `could not post the note on #${report.pr}: ${err instanceof Error ? err.message : String(err)}` }));
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
// ── up ─────────────────────────────────────────────────────────────────────
|
|
143
|
+
async function prEnvUp(ctx) {
|
|
144
|
+
const { args } = ctx;
|
|
145
|
+
if (args.pr === undefined)
|
|
146
|
+
return fail("pr-env up needs the pull or merge request: --pr <n>", "The environment is pr-<n>, and its approval is recorded under op pr-<n>.");
|
|
147
|
+
if (!args.base)
|
|
148
|
+
return fail("pr-env up needs the commit the change is measured from: --base <ref>", "On a pull request, the target branch's commit. ci.prEnvironment is read from the declaration there.");
|
|
149
|
+
const pr = args.pr;
|
|
150
|
+
const projectPath = resolve(".");
|
|
151
|
+
// The settings at base, never the pull request's own: a change cannot name another template or a longer TTL.
|
|
152
|
+
const { scopeSource } = await import("../../workspace/write-scope.js");
|
|
153
|
+
const source = scopeSource(projectPath, args.base);
|
|
154
|
+
const setting = source.from === "base" ? source.declaration?.ci?.prEnvironment : undefined;
|
|
155
|
+
if (!setting) {
|
|
156
|
+
return fail(`the workspace declaration at ${args.base.slice(0, 12)} names no ci.prEnvironment, so pull request ${pr} gets no environment`, 'Declare ci.prEnvironment: { "template": "<env>" } in chant.workspace.json and merge it first.');
|
|
157
|
+
}
|
|
158
|
+
const { config } = await loadChantConfig(projectPath).catch(() => ({ config: {} }));
|
|
159
|
+
const derived = derivePrEnvironment(config, setting.template, pr);
|
|
160
|
+
if ("refusal" in derived)
|
|
161
|
+
return fail(derived.refusal);
|
|
162
|
+
const ttl = ttlMs(setting.ttl) ?? ttlMs("72h");
|
|
163
|
+
// The build sees pr-<n> as its environment and the template's name beside it.
|
|
164
|
+
process.env[ENV_VAR] = derived.env;
|
|
165
|
+
process.env[PR_ENV_TEMPLATE_VAR] = derived.template;
|
|
166
|
+
const envCtx = {
|
|
167
|
+
...ctx,
|
|
168
|
+
args: { ...args, env: derived.env, param: [...(args.param ?? []).filter((p) => !p.startsWith(`${derived.param}=`)), `${derived.param}=${derived.env}`] },
|
|
169
|
+
};
|
|
170
|
+
console.error(formatInfo(`pr-env up: ${derived.env}, derived from ${derived.template}${derived.endpoint ? ` at ${derived.endpoint}` : ""}, marker ${derived.stack}/${derived.env}`));
|
|
171
|
+
return withComponentEndpoint(envCtx, () => up(envCtx, pr, derived, ttl), { config: { ...config, environments: derived.environments }, environment: derived.env });
|
|
172
|
+
}
|
|
173
|
+
async function up(ctx, pr, derived, ttl) {
|
|
174
|
+
const { prepare, planAll, placeHooks, runStageHook } = await import("./pr.js");
|
|
175
|
+
const p = await prepare(ctx, "plan");
|
|
176
|
+
if (typeof p === "number")
|
|
177
|
+
return p;
|
|
178
|
+
const forge = p.forge;
|
|
179
|
+
const op = prOp(pr, p.member);
|
|
180
|
+
// ci.hooks (#4288), read at base as the other pull-request jobs read them, around the plan and the apply.
|
|
181
|
+
placeHooks(ctx, p, "apply", op);
|
|
182
|
+
const base = {
|
|
183
|
+
stage: "up",
|
|
184
|
+
pr,
|
|
185
|
+
env: derived.env,
|
|
186
|
+
...(p.member ? { member: p.member } : {}),
|
|
187
|
+
template: derived.template,
|
|
188
|
+
head: p.head,
|
|
189
|
+
op,
|
|
190
|
+
gate: PR_ENV_GATE,
|
|
191
|
+
digest: "",
|
|
192
|
+
status: "nothing",
|
|
193
|
+
...(derived.endpoint ? { endpoint: derived.endpoint } : {}),
|
|
194
|
+
components: [],
|
|
195
|
+
};
|
|
196
|
+
if (p.plan.order.length === 0) {
|
|
197
|
+
await finish(ctx, forge, { ...base, ...(p.routedAway ? { message: p.routedAway } : {}) });
|
|
198
|
+
console.error(formatInfo(`pr-env up: nothing this change touches is deployed by a component, so ${derived.env} gets nothing`));
|
|
199
|
+
return PR_ENV_EXIT.done;
|
|
200
|
+
}
|
|
201
|
+
const set = await planAll(p);
|
|
202
|
+
const rows = (status) => p.plan.order.map((component) => {
|
|
203
|
+
const ms = set.members.filter((m) => m.component === component);
|
|
204
|
+
const sum = (k) => ms.reduce((n, m) => n + m.counts[k], 0);
|
|
205
|
+
return { component, status: status(component), create: sum("create"), update: sum("update"), replace: sum("replace"), delete: sum("delete") };
|
|
206
|
+
});
|
|
207
|
+
const digest = set.doc.digest;
|
|
208
|
+
if (set.failed) {
|
|
209
|
+
await finish(ctx, forge, { ...base, digest, status: "plan-failed", ...(p.hookFailed ? { message: p.hookFailed } : {}), components: rows((c) => (set.members.some((m) => m.component === c && m.status === "plan-failed") ? "plan failed" : "planned")) });
|
|
210
|
+
return PR_ENV_EXIT.failed;
|
|
211
|
+
}
|
|
212
|
+
// ws-080, ws-104: a non-empty plan applies only under a person's approval of its digest. An empty one passes.
|
|
213
|
+
let approvedBy;
|
|
214
|
+
if (!isEmptyChangeSet(set.doc)) {
|
|
215
|
+
let approval;
|
|
216
|
+
try {
|
|
217
|
+
approval = approvalOf(await decidePrGate(readOnlyLedger(gitGateLedgerPort()), { op, gate: PR_ENV_GATE, digest, description: `${derived.env}: ${p.plan.order.join(", ")}` }));
|
|
218
|
+
}
|
|
219
|
+
catch (err) {
|
|
220
|
+
return fail(`could not read the gate ledger, so ${derived.env} does not apply: ${err instanceof Error ? err.message : String(err)}`);
|
|
221
|
+
}
|
|
222
|
+
if (approval.status !== "approved") {
|
|
223
|
+
const status = approval.status === "changed" ? "changed" : "waiting";
|
|
224
|
+
await finish(ctx, forge, { ...base, digest, status, components: rows(() => "planned") });
|
|
225
|
+
console.error(formatInfo(`pr-env up: ${derived.env} waits for an approval of ${digest}`));
|
|
226
|
+
console.error(formatInfo(`approve : ${prEnvApproveCommand(op, digest)}`));
|
|
227
|
+
return PR_ENV_EXIT.waiting;
|
|
228
|
+
}
|
|
229
|
+
approvedBy = approval.approvedBy ?? [];
|
|
230
|
+
}
|
|
231
|
+
const hookStop = runStageHook(p, "before-apply", p.plan.order, set);
|
|
232
|
+
if (hookStop) {
|
|
233
|
+
await finish(ctx, forge, { ...base, digest, status: "failed", message: hookStop, components: rows(() => "planned"), ...(approvedBy ? { approvedBy } : {}) });
|
|
234
|
+
return PR_ENV_EXIT.failed;
|
|
235
|
+
}
|
|
236
|
+
const applied = await applyPrSet({ components: p.components, plan: p.plan, registry: p.registry, env: derived.env, set, gate: PR_ENV_GATE });
|
|
237
|
+
const afterHook = runStageHook(p, "after-apply", p.plan.order, set, "the environment stands as applied");
|
|
238
|
+
const status = (component) => applied.members.find((m) => m.component === component)?.status ?? "planned";
|
|
239
|
+
const ok = applied.status === "applied";
|
|
240
|
+
const now = new Date();
|
|
241
|
+
const expiresAt = new Date(now.getTime() + ttl).toISOString();
|
|
242
|
+
// A copy that failed part way is live too: what applied is torn down on close or at expiry like the rest.
|
|
243
|
+
await appendRecord(ctx, {
|
|
244
|
+
version: 1,
|
|
245
|
+
kind: "up",
|
|
246
|
+
pr,
|
|
247
|
+
env: derived.env,
|
|
248
|
+
...(p.member ? { member: p.member } : {}),
|
|
249
|
+
template: derived.template,
|
|
250
|
+
marker: { stack: derived.stack, env: derived.env },
|
|
251
|
+
commit: p.head,
|
|
252
|
+
components: p.plan.order,
|
|
253
|
+
planDigest: digest,
|
|
254
|
+
result: ok ? "applied" : "failed",
|
|
255
|
+
by: actor(),
|
|
256
|
+
timestamp: now.toISOString(),
|
|
257
|
+
expiresAt,
|
|
258
|
+
...(approvedBy ? { approvedBy } : {}),
|
|
259
|
+
releaseTarget: false,
|
|
260
|
+
...(forge?.runUrl ? { runUrl: forge.runUrl } : {}),
|
|
261
|
+
});
|
|
262
|
+
await finish(ctx, forge, {
|
|
263
|
+
...base,
|
|
264
|
+
digest,
|
|
265
|
+
status: ok ? "applied" : "failed",
|
|
266
|
+
components: rows(status),
|
|
267
|
+
...(approvedBy ? { approvedBy } : {}),
|
|
268
|
+
expiresAt,
|
|
269
|
+
...(ok ? (afterHook ? { message: afterHook } : {}) : { message: applied.status === "waiting" ? "A component read an upstream output that moved when it applied; run the job again to plan it against the new value." : "A component failed to apply; what applied stays in the environment and is torn down with it." }),
|
|
270
|
+
});
|
|
271
|
+
console.error((ok ? formatInfo : (m) => formatWarning({ message: m }))(`pr-env up: ${derived.env} ${ok ? "applied" : applied.status}; it expires ${expiresAt}`));
|
|
272
|
+
return ok && !afterHook ? PR_ENV_EXIT.done : PR_ENV_EXIT.failed;
|
|
273
|
+
}
|
|
274
|
+
// ── post ───────────────────────────────────────────────────────────────────
|
|
275
|
+
/**
|
|
276
|
+
* `pr-env post --pr <n> --head <sha> [--result <r>]` (#4295): from a job at
|
|
277
|
+
* the target branch's commit that runs none of the change's code, take the
|
|
278
|
+
* report and the record `pr-env up --no-push` handed on, check the record
|
|
279
|
+
* as data against the settings at this commit, append it to chant/lifecycle,
|
|
280
|
+
* and post the note and the `chant/pr-env` status with the forge token the
|
|
281
|
+
* up job never held.
|
|
282
|
+
*/
|
|
283
|
+
async function prEnvPost(ctx) {
|
|
284
|
+
const { args } = ctx;
|
|
285
|
+
if (args.pr === undefined || !args.head)
|
|
286
|
+
return fail("pr-env post needs the pull request and the commit its environment was applied from: --pr <n> --head <sha>");
|
|
287
|
+
const forge = forgeOf(ctx);
|
|
288
|
+
if (typeof forge === "number")
|
|
289
|
+
return forge;
|
|
290
|
+
const pr = args.pr;
|
|
291
|
+
const env = prEnvironmentName(pr);
|
|
292
|
+
const member = memberOf(ctx);
|
|
293
|
+
const dir = resolve(args.output ?? PR_ENV_REPORT_DIR);
|
|
294
|
+
const read = (name) => {
|
|
295
|
+
try {
|
|
296
|
+
return JSON.parse(readFileSync(join(dir, name), "utf-8"));
|
|
297
|
+
}
|
|
298
|
+
catch {
|
|
299
|
+
return undefined;
|
|
300
|
+
}
|
|
301
|
+
};
|
|
302
|
+
const { scopeSource } = await import("../../workspace/write-scope.js");
|
|
303
|
+
const setting = scopeSource(resolve("."), "HEAD").declaration?.ci?.prEnvironment;
|
|
304
|
+
const handed = handedPrEnvReport(read("pr-env-up.json"), { pr, head: args.head, ...(member ? { member } : {}) });
|
|
305
|
+
const report = "report" in handed
|
|
306
|
+
? handed.report
|
|
307
|
+
: { stage: "up", pr, env, ...(member ? { member } : {}), template: setting?.template ?? "", head: args.head, op: prOp(pr, member), gate: PR_ENV_GATE, digest: "", status: "failed", components: [], message: handed.refusal + (args.planResult && args.planResult !== "success" ? ` The env-up job's result is ${args.planResult}.` : "") };
|
|
308
|
+
const raw = read(PR_ENV_RECORD_FILE);
|
|
309
|
+
let code = "report" in handed ? (report.status === "waiting" || report.status === "changed" ? PR_ENV_EXIT.waiting : prEnvStatusState(report) === "failure" ? PR_ENV_EXIT.failed : PR_ENV_EXIT.done) : PR_ENV_EXIT.failed;
|
|
310
|
+
if (raw !== undefined) {
|
|
311
|
+
if (!setting) {
|
|
312
|
+
report.message = `${report.message ? `${report.message} ` : ""}The declaration here names no ci.prEnvironment, so the record was not kept.`;
|
|
313
|
+
code = PR_ENV_EXIT.failed;
|
|
314
|
+
}
|
|
315
|
+
else {
|
|
316
|
+
const accepted = acceptHandedRecord(raw, { pr, head: args.head, template: setting.template, ttlMs: ttlMs(setting.ttl) ?? ttlMs("72h"), now: new Date(), by: actor(), ...(member ? { member } : {}), ...(forge?.runUrl ? { runUrl: forge.runUrl } : {}) });
|
|
317
|
+
if ("refusal" in accepted) {
|
|
318
|
+
report.message = `${report.message ? `${report.message} ` : ""}${accepted.refusal}`;
|
|
319
|
+
code = PR_ENV_EXIT.failed;
|
|
320
|
+
}
|
|
321
|
+
else {
|
|
322
|
+
await appendRecord({ ...ctx, args: { ...args, noPush: false } }, accepted.record);
|
|
323
|
+
if (accepted.record.expiresAt)
|
|
324
|
+
report.expiresAt = accepted.record.expiresAt;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
await finish(ctx, forge, report);
|
|
329
|
+
return code;
|
|
330
|
+
}
|
|
331
|
+
// ── down ───────────────────────────────────────────────────────────────────
|
|
332
|
+
async function prEnvDown(ctx) {
|
|
333
|
+
const { args } = ctx;
|
|
334
|
+
if (args.pr === undefined)
|
|
335
|
+
return fail("pr-env down needs the pull or merge request: --pr <n>");
|
|
336
|
+
const reason = args.reason ?? "closed";
|
|
337
|
+
if (reason !== "closed" && reason !== "expired")
|
|
338
|
+
return fail(`--reason ${reason} is not closed or expired`);
|
|
339
|
+
const forge = forgeOf(ctx);
|
|
340
|
+
if (typeof forge === "number")
|
|
341
|
+
return forge;
|
|
342
|
+
const live = livePrEnvironments(await readRecords()).get(prEnvironmentName(args.pr));
|
|
343
|
+
return teardown(ctx, forge, args.pr, live, reason);
|
|
344
|
+
}
|
|
345
|
+
async function teardown(ctx, forge, pr, live, reason) {
|
|
346
|
+
const env = prEnvironmentName(pr);
|
|
347
|
+
const member = memberOf(ctx);
|
|
348
|
+
const report = { stage: "down", pr, env, ...(member ? { member } : {}), template: live?.template ?? "", head: "", op: prOp(pr, member), gate: PR_ENV_GATE, digest: "", status: "absent", components: [] };
|
|
349
|
+
if (!live) {
|
|
350
|
+
console.error(formatInfo(`pr-env down: ${env} has no live record, so there is nothing to tear down`));
|
|
351
|
+
if (forge)
|
|
352
|
+
await finish(ctx, forge, report);
|
|
353
|
+
return PR_ENV_EXIT.done;
|
|
354
|
+
}
|
|
355
|
+
const { config } = await loadChantConfig(resolve(".")).catch(() => ({ config: {} }));
|
|
356
|
+
const torn = await tearDownPrEnvironment({ pr, live, config, plugins: ctx.plugins, warn: (message) => console.error(formatWarning({ message })) });
|
|
357
|
+
if ("refusal" in torn) {
|
|
358
|
+
await finish(ctx, forge, { ...report, template: live.template, status: "refused", message: torn.refusal });
|
|
359
|
+
return fail(torn.refusal);
|
|
360
|
+
}
|
|
361
|
+
const { report: result, dropped, marker } = torn;
|
|
362
|
+
const counts = teardownCounts(result);
|
|
363
|
+
const complete = teardownComplete(result);
|
|
364
|
+
const lexicons = [...new Set(result.outcomes.map((o) => o.lexicon))].sort();
|
|
365
|
+
const head = await headCommit();
|
|
366
|
+
await appendRecord(ctx, {
|
|
367
|
+
version: 1,
|
|
368
|
+
kind: "down",
|
|
369
|
+
pr,
|
|
370
|
+
env,
|
|
371
|
+
...(live.member ? { member: live.member } : {}),
|
|
372
|
+
template: live.template,
|
|
373
|
+
marker,
|
|
374
|
+
commit: head,
|
|
375
|
+
components: lexicons,
|
|
376
|
+
planDigest: "",
|
|
377
|
+
result: complete ? "destroyed" : "incomplete",
|
|
378
|
+
by: actor(),
|
|
379
|
+
timestamp: new Date().toISOString(),
|
|
380
|
+
reason,
|
|
381
|
+
outcomes: counts,
|
|
382
|
+
releaseTarget: false,
|
|
383
|
+
...(forge?.runUrl ? { runUrl: forge.runUrl } : {}),
|
|
384
|
+
});
|
|
385
|
+
const why = [];
|
|
386
|
+
if (dropped > 0)
|
|
387
|
+
why.push(`${dropped} candidate${dropped === 1 ? "" : "s"} whose marker was not ${marker.stack}/${env} left alone`);
|
|
388
|
+
if (result.unimplemented.length > 0)
|
|
389
|
+
why.push(`no teardown execution in ${result.unimplemented.join(", ")}`);
|
|
390
|
+
if (result.plan.holes.length > 0)
|
|
391
|
+
why.push(`${result.plan.holes.length} part${result.plan.holes.length === 1 ? "" : "s"} could not be read`);
|
|
392
|
+
for (const o of result.outcomes.filter((x) => x.outcome === "failed"))
|
|
393
|
+
why.push(`${o.lexicon} ${o.name}: ${o.detail ?? "failed"}`);
|
|
394
|
+
await finish(ctx, forge, { ...report, template: live.template, status: complete ? "destroyed" : "incomplete", outcomes: counts, ...(why.length > 0 ? { message: why.join("; ") } : {}) });
|
|
395
|
+
console.error((complete ? formatInfo : (m) => formatWarning({ message: m }))(`pr-env down: ${env} ${complete ? "torn down" : "not wholly torn down; it stays live and the sweep tries again"} (${counts.deleted} deleted, ${counts.failed} failed, ${counts.skipped} skipped)`));
|
|
396
|
+
return complete ? PR_ENV_EXIT.done : PR_ENV_EXIT.failed;
|
|
397
|
+
}
|
|
398
|
+
async function headCommit() {
|
|
399
|
+
const { spawnSync } = await import("node:child_process");
|
|
400
|
+
const r = spawnSync("git", ["rev-parse", "HEAD"], { encoding: "utf-8" });
|
|
401
|
+
return r.status === 0 ? r.stdout.trim() : "";
|
|
402
|
+
}
|
|
403
|
+
// ── sweep ──────────────────────────────────────────────────────────────────
|
|
404
|
+
async function prEnvSweep(ctx) {
|
|
405
|
+
const forge = forgeOf(ctx);
|
|
406
|
+
if (typeof forge === "number")
|
|
407
|
+
return forge;
|
|
408
|
+
const live = [...livePrEnvironments(await readRecords()).values()];
|
|
409
|
+
if (live.length === 0) {
|
|
410
|
+
console.error(formatInfo("pr-env sweep: no pull request environment is live"));
|
|
411
|
+
return PR_ENV_EXIT.done;
|
|
412
|
+
}
|
|
413
|
+
const now = new Date();
|
|
414
|
+
let code = PR_ENV_EXIT.done;
|
|
415
|
+
for (const e of live) {
|
|
416
|
+
let reason = expired(e, now) ? "expired" : undefined;
|
|
417
|
+
if (!reason && forge?.pullRequestState) {
|
|
418
|
+
try {
|
|
419
|
+
const state = await forge.pullRequestState(e.pr);
|
|
420
|
+
if (state !== "open")
|
|
421
|
+
reason = "closed";
|
|
422
|
+
}
|
|
423
|
+
catch (err) {
|
|
424
|
+
console.error(formatWarning({ message: `could not read whether #${e.pr} is open (${err instanceof Error ? err.message : String(err)}); its TTL still holds` }));
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
if (!reason) {
|
|
428
|
+
console.error(formatInfo(`pr-env sweep: ${e.env} stays; it expires ${e.expiresAt}`));
|
|
429
|
+
continue;
|
|
430
|
+
}
|
|
431
|
+
const c = await teardown(ctx, forge, e.pr, e, reason);
|
|
432
|
+
if (c !== PR_ENV_EXIT.done)
|
|
433
|
+
code = PR_ENV_EXIT.failed;
|
|
434
|
+
}
|
|
435
|
+
return code;
|
|
436
|
+
}
|
|
@@ -63,13 +63,87 @@
|
|
|
63
63
|
* all it uses one for; pr-apply still reads the pull request and its reviews
|
|
64
64
|
* through `--forge`.
|
|
65
65
|
*/
|
|
66
|
+
import { type ChantConfig } from "../../config.js";
|
|
67
|
+
import { type PrPlanSet } from "../../components/pr-run.js";
|
|
68
|
+
import { type RunEventSink } from "../../run-event-stream.js";
|
|
69
|
+
import { type PrPolicyReport } from "../../pr-loop.js";
|
|
70
|
+
import { type PrForge } from "../../pr-forge.js";
|
|
66
71
|
import type { CommandContext } from "../registry.js";
|
|
72
|
+
import type { ComponentChangeSignal, FanOutPlan } from "../../components/fan-out.js";
|
|
73
|
+
import { type DriverComponent } from "../../components/driver.js";
|
|
74
|
+
import type { CapabilityRegistry } from "../../components/capability.js";
|
|
75
|
+
import { type PrHookPlace, type PrLoopHooks } from "../../components/pr-hooks.js";
|
|
76
|
+
import { type PrLoopPolicy } from "../../components/pr-policy.js";
|
|
77
|
+
import type { OpWaveHookMoment } from "../../op/op-waves.js";
|
|
78
|
+
import type { PrHookOutcome } from "../../pr-loop.js";
|
|
67
79
|
/** Where the reports land unless `--output` says. */
|
|
68
80
|
export declare const PR_REPORT_DIR = ".chant/pr";
|
|
81
|
+
export interface Prepared {
|
|
82
|
+
config: ChantConfig;
|
|
83
|
+
env: string;
|
|
84
|
+
/** The workspace member, with `--member` (#3465). */
|
|
85
|
+
member?: string;
|
|
86
|
+
base: string;
|
|
87
|
+
head: string;
|
|
88
|
+
gate: string;
|
|
89
|
+
forge?: PrForge;
|
|
90
|
+
/**
|
|
91
|
+
* Every component in the project, not the selection (`deriveFanOut`), so
|
|
92
|
+
* the plan reads the deployed outputs of a dependency the change did not
|
|
93
|
+
* select (#4104).
|
|
94
|
+
*/
|
|
95
|
+
components: DriverComponent[];
|
|
96
|
+
plan: FanOutPlan;
|
|
97
|
+
signal: ComponentChangeSignal;
|
|
98
|
+
registry: CapabilityRegistry;
|
|
99
|
+
seededOutputs: Record<string, Record<string, unknown>>;
|
|
100
|
+
/** `ci.hooks` from the declaration at base (#4288). */
|
|
101
|
+
hooks: PrLoopHooks;
|
|
102
|
+
/** Every hook the stage ran so far, for the report's `hooks`. */
|
|
103
|
+
hookOutcomes: PrHookOutcome[];
|
|
104
|
+
/** Set when a hook failed: the report's message. */
|
|
105
|
+
hookFailed?: string;
|
|
106
|
+
/** Where the stage's hooks run, once its op is known. */
|
|
107
|
+
hookPlace?: PrHookPlace;
|
|
108
|
+
/**
|
|
109
|
+
* Set when `ci.branches` or `ci.labels` (#4292) leave the member out of this
|
|
110
|
+
* stage: why. The plan is then empty, so the stage reports `nothing`.
|
|
111
|
+
*/
|
|
112
|
+
routedAway?: string;
|
|
113
|
+
/** `ci.policy` from the declaration at base (#4294). */
|
|
114
|
+
policy: PrLoopPolicy;
|
|
115
|
+
/** What the policy said about the plan, for the report's `policy`. */
|
|
116
|
+
policyReport?: PrPolicyReport;
|
|
117
|
+
}
|
|
118
|
+
/** Everything both stages share: the change and the derivation. Each stage plans the members itself. `pr-env up` (#4298) prepares as the plan does. */
|
|
119
|
+
export declare function prepare(ctx: CommandContext, stage: "plan" | "apply" | "record"): Promise<Prepared | number>;
|
|
120
|
+
/** Set where `p`'s hooks run, once the stage knows its op (#4288). */
|
|
121
|
+
export declare function placeHooks(ctx: CommandContext, p: Prepared, stage: PrHookPlace["stage"], op: string): void;
|
|
122
|
+
/**
|
|
123
|
+
* Run one moment's hooks for `components` (#4288), keeping their outcomes
|
|
124
|
+
* for the report. Returns the failure as the report's message, or undefined
|
|
125
|
+
* when no hook failed. A hold is logged: the pull request's gate already
|
|
126
|
+
* waits for a person's approval of the digest.
|
|
127
|
+
*/
|
|
128
|
+
export declare function runStageHook(p: Prepared, moment: OpWaveHookMoment, components: readonly string[], planned?: PrPlanSet, what?: string): string | undefined;
|
|
129
|
+
/** Plan every member the derivation selects, between the plan-side hooks (#4288). */
|
|
130
|
+
export declare function planAll(p: Prepared, onRunEvent?: RunEventSink): Promise<PrPlanSet>;
|
|
131
|
+
export declare function planBare(p: Prepared, onRunEvent?: RunEventSink): Promise<PrPlanSet>;
|
|
69
132
|
/** chant components pr-plan --base <ref> --pr <n> [--env <env>] [--gate <name>] [--forge <kind>] [--output <dir>] [--json] [--events-json] [--report-url <url>] */
|
|
70
133
|
export declare function runComponentsPrPlan(ctx: CommandContext): Promise<number>;
|
|
71
134
|
/** chant components pr-apply --base <ref> [--pr <n>] [--env <env>] [--gate <name>] [--forge <kind>] [--require-review] [--output <dir>] [--json] [--events-json] [--report-url <url>] */
|
|
72
135
|
export declare function runComponentsPrApply(ctx: CommandContext): Promise<number>;
|
|
73
136
|
/** chant components pr-record --base <ref> [--pr <n>] [--env <env>] [--gate <name>] [--forge <kind>] [--output <dir>] [--json] [--events-json] [--report-url <url>] */
|
|
74
137
|
export declare function runComponentsPrRecord(ctx: CommandContext): Promise<number>;
|
|
138
|
+
/**
|
|
139
|
+
* chant components pr-post --pr <n> --head <sha> --env <env> --forge <kind> [--member <name>] [--output <dir>] [--result <result>] (#4295)
|
|
140
|
+
*
|
|
141
|
+
* Post the plan note and the `chant/plan` status from the report a plan job
|
|
142
|
+
* wrote with `pr-plan --no-status`, in a job that runs none of the change's
|
|
143
|
+
* code. The pull request, the head, the environment and the member come from
|
|
144
|
+
* the flags, and a report that names others is refused (`../../pr-post.ts`).
|
|
145
|
+
* It exits 0 once it has posted, whatever the plan said, so the plan job's
|
|
146
|
+
* own result stays the one that fails the run.
|
|
147
|
+
*/
|
|
148
|
+
export declare function runComponentsPrPost(ctx: CommandContext): Promise<number>;
|
|
75
149
|
//# sourceMappingURL=pr.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pr.d.ts","sourceRoot":"","sources":["../../../src/cli/handlers/pr.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgEG;
|
|
1
|
+
{"version":3,"file":"pr.d.ts","sourceRoot":"","sources":["../../../src/cli/handlers/pr.ts"],"names":[],"mappings":"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"}
|