@intentius/chant 0.124.33 → 0.124.35
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/cli/handlers/graph.js +2 -2
- package/dist/cli/handlers/pr.d.ts.map +1 -1
- package/dist/cli/handlers/pr.js +147 -51
- package/dist/cli/handlers/promote.d.ts.map +1 -1
- package/dist/cli/handlers/promote.js +284 -81
- package/dist/cli/handlers/release.d.ts +12 -0
- package/dist/cli/handlers/release.d.ts.map +1 -1
- package/dist/cli/handlers/release.js +125 -10
- package/dist/cli/handlers/run.d.ts +3 -1
- package/dist/cli/handlers/run.d.ts.map +1 -1
- package/dist/cli/handlers/run.js +12 -2
- package/dist/cli/main.d.ts.map +1 -1
- package/dist/cli/main.js +37 -2
- package/dist/cli/parse-args.d.ts.map +1 -1
- package/dist/cli/parse-args.js +24 -0
- package/dist/cli/registry.d.ts +17 -1
- package/dist/cli/registry.d.ts.map +1 -1
- package/dist/components/auto-release.d.ts +8 -1
- package/dist/components/auto-release.d.ts.map +1 -1
- package/dist/components/auto-release.js +3 -2
- package/dist/components/green-cover.d.ts +77 -0
- package/dist/components/green-cover.d.ts.map +1 -0
- package/dist/components/green-cover.js +153 -0
- package/dist/components/pr-pipeline.d.ts +34 -0
- package/dist/components/pr-pipeline.d.ts.map +1 -1
- package/dist/components/pr-pipeline.js +39 -5
- package/dist/components/promote-rounds.d.ts +102 -0
- package/dist/components/promote-rounds.d.ts.map +1 -0
- package/dist/components/promote-rounds.js +174 -0
- package/dist/components/promote.d.ts +4 -1
- package/dist/components/promote.d.ts.map +1 -1
- package/dist/components/promote.js +6 -3
- package/dist/components/release-gate.d.ts +30 -0
- package/dist/components/release-gate.d.ts.map +1 -1
- package/dist/components/release-gate.js +92 -42
- package/dist/components/release-promote.d.ts +9 -0
- package/dist/components/release-promote.d.ts.map +1 -1
- package/dist/components/release-promote.js +15 -0
- package/dist/config.d.ts +2 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/deep-observation.d.ts +11 -0
- package/dist/deep-observation.d.ts.map +1 -1
- package/dist/graph-declared.d.ts +9 -1
- package/dist/graph-declared.d.ts.map +1 -1
- package/dist/graph-declared.js +18 -3
- package/dist/graph-detail.d.ts.map +1 -1
- package/dist/graph-detail.js +30 -1
- package/dist/graph-ir.d.ts.map +1 -1
- package/dist/graph-ir.js +13 -2
- package/dist/lifecycle/__fixtures__/publish-fakes.d.ts.map +1 -1
- package/dist/lifecycle/__fixtures__/publish-fakes.js +5 -1
- package/dist/lifecycle/compose-refs.d.ts +52 -0
- package/dist/lifecycle/compose-refs.d.ts.map +1 -0
- package/dist/lifecycle/compose-refs.js +115 -0
- package/dist/lifecycle/deep-observe.d.ts.map +1 -1
- package/dist/lifecycle/deep-observe.js +3 -2
- package/dist/lifecycle/green-apply.d.ts +39 -0
- package/dist/lifecycle/green-apply.d.ts.map +1 -0
- package/dist/lifecycle/green-apply.js +51 -0
- package/dist/lifecycle/publish-declaration.d.ts +4 -0
- package/dist/lifecycle/publish-declaration.d.ts.map +1 -1
- package/dist/lifecycle/publish-declaration.js +15 -0
- package/dist/lifecycle/publish-npm.d.ts +7 -4
- package/dist/lifecycle/publish-npm.d.ts.map +1 -1
- package/dist/lifecycle/publish-npm.js +60 -13
- package/dist/lifecycle/release-compose.d.ts +91 -0
- package/dist/lifecycle/release-compose.d.ts.map +1 -0
- package/dist/lifecycle/release-compose.js +235 -0
- package/dist/lifecycle/release-cut.d.ts +8 -0
- package/dist/lifecycle/release-cut.d.ts.map +1 -1
- package/dist/lifecycle/release-cut.js +2 -0
- package/dist/lifecycle/release-publish.d.ts +2 -0
- package/dist/lifecycle/release-publish.d.ts.map +1 -1
- package/dist/lifecycle/release-publish.js +2 -1
- package/dist/lifecycle/release-store.d.ts +15 -0
- package/dist/lifecycle/release-store.d.ts.map +1 -1
- package/dist/lifecycle/release-store.js +5 -1
- package/dist/wave-runtime.mjs +226 -224
- package/dist/workspace/__fixtures__/contract-repo.d.ts.map +1 -1
- package/dist/workspace/__fixtures__/contract-repo.js +13 -2
- package/dist/workspace/check.schema.json +20 -2
- package/dist/workspace/checks/ci-workflow.d.ts +17 -0
- package/dist/workspace/checks/ci-workflow.d.ts.map +1 -0
- package/dist/workspace/checks/ci-workflow.js +130 -0
- package/dist/workspace/checks/security.d.ts +16 -1
- package/dist/workspace/checks/security.d.ts.map +1 -1
- package/dist/workspace/checks/security.js +4 -4
- package/dist/workspace/checks.d.ts +1 -1
- package/dist/workspace/checks.d.ts.map +1 -1
- package/dist/workspace/checks.js +4 -1
- package/dist/workspace/ci-green-cli.d.ts.map +1 -1
- package/dist/workspace/ci-green-cli.js +5 -3
- package/dist/workspace/ci-green-workflow.d.ts +18 -2
- package/dist/workspace/ci-green-workflow.d.ts.map +1 -1
- package/dist/workspace/ci-green-workflow.js +27 -5
- package/dist/workspace/ci-green.d.ts +31 -0
- package/dist/workspace/ci-green.d.ts.map +1 -1
- package/dist/workspace/ci-green.js +78 -4
- package/dist/workspace/ci-init.d.ts +100 -0
- package/dist/workspace/ci-init.d.ts.map +1 -0
- package/dist/workspace/ci-init.js +434 -0
- package/dist/workspace/ci-run.d.ts +6 -0
- package/dist/workspace/ci-run.d.ts.map +1 -1
- package/dist/workspace/ci-run.js +2 -1
- package/dist/workspace/ci-serve.d.ts +94 -13
- package/dist/workspace/ci-serve.d.ts.map +1 -1
- package/dist/workspace/ci-serve.js +248 -30
- package/dist/workspace/ci-workflow.d.ts +91 -0
- package/dist/workspace/ci-workflow.d.ts.map +1 -0
- package/dist/workspace/ci-workflow.js +33 -0
- package/dist/workspace/declaration.d.ts +18 -0
- package/dist/workspace/declaration.d.ts.map +1 -1
- package/dist/workspace/declaration.js +9 -2
- package/dist/workspace/declaration.schema.json +28 -0
- package/dist/workspace/delivery-metrics.d.ts +3 -1
- package/dist/workspace/delivery-metrics.d.ts.map +1 -1
- package/dist/workspace/delivery-metrics.js +12 -3
- package/dist/workspace/init.js +2 -2
- package/dist/workspace/member-commands.d.ts.map +1 -1
- package/dist/workspace/member-commands.js +4 -0
- package/dist/workspace/nested-graph.d.ts.map +1 -1
- package/dist/workspace/nested-graph.js +1 -0
- package/dist/workspace/reason-codes.d.ts +9 -0
- package/dist/workspace/reason-codes.d.ts.map +1 -1
- package/dist/workspace/reason-codes.js +10 -0
- package/dist/workspace/release-candidates.d.ts +28 -0
- package/dist/workspace/release-candidates.d.ts.map +1 -1
- package/dist/workspace/release-candidates.js +38 -2
- package/dist/workspace/release-candidates.schema.json +51 -0
- package/dist/workspace/release-workflow.d.ts +31 -0
- package/dist/workspace/release-workflow.d.ts.map +1 -0
- package/dist/workspace/release-workflow.js +150 -0
- package/dist/workspace/releases.schema.json +30 -0
- package/package.json +1 -1
- package/src/cli/handlers/graph.ts +2 -2
- package/src/cli/handlers/pr-green.test.ts +230 -0
- package/src/cli/handlers/pr.ts +148 -66
- package/src/cli/handlers/promote.test.ts +34 -0
- package/src/cli/handlers/promote.ts +302 -91
- package/src/cli/handlers/release-compose.e2e.test.ts +133 -0
- package/src/cli/handlers/release.ts +112 -10
- package/src/cli/handlers/run.ts +12 -1
- package/src/cli/main.ts +37 -2
- package/src/cli/parse-args.ts +18 -0
- package/src/cli/registry.ts +17 -1
- package/src/codegen/release-wiring.test.ts +56 -9
- package/src/components/auto-release.test.ts +22 -0
- package/src/components/auto-release.ts +9 -2
- package/src/components/green-cover.test.ts +170 -0
- package/src/components/green-cover.ts +186 -0
- package/src/components/pr-pipeline.test.ts +14 -4
- package/src/components/pr-pipeline.ts +56 -5
- package/src/components/promote-rounds.test.ts +220 -0
- package/src/components/promote-rounds.ts +232 -0
- package/src/components/promote.test.ts +7 -0
- package/src/components/promote.ts +9 -4
- package/src/components/release-gate.test.ts +47 -0
- package/src/components/release-gate.ts +111 -46
- package/src/components/release-promote.test.ts +24 -1
- package/src/components/release-promote.ts +15 -0
- package/src/deep-observation.ts +8 -0
- package/src/graph-declared-exports.test.ts +40 -0
- package/src/graph-declared.test.ts +18 -0
- package/src/graph-declared.ts +17 -2
- package/src/graph-detail.test.ts +68 -1
- package/src/graph-detail.ts +29 -1
- package/src/graph-ir.ts +12 -4
- package/src/lifecycle/__fixtures__/publish-fakes.ts +5 -1
- package/src/lifecycle/compose-refs.ts +125 -0
- package/src/lifecycle/deep-observe.test.ts +20 -0
- package/src/lifecycle/deep-observe.ts +4 -2
- package/src/lifecycle/green-apply.ts +71 -0
- package/src/lifecycle/publish-declaration.ts +15 -0
- package/src/lifecycle/publish-npm.ts +55 -13
- package/src/lifecycle/publish-targets.test.ts +45 -0
- package/src/lifecycle/release-compose.test.ts +125 -0
- package/src/lifecycle/release-compose.ts +312 -0
- package/src/lifecycle/release-cut.ts +6 -0
- package/src/lifecycle/release-publish.ts +4 -1
- package/src/lifecycle/release-store.ts +16 -1
- package/src/workspace/__fixtures__/contract-repo.ts +11 -2
- package/src/workspace/check-contract.test.ts +3 -2
- package/src/workspace/check.schema.json +20 -2
- package/src/workspace/checks/ci-workflow.test.ts +160 -0
- package/src/workspace/checks/ci-workflow.ts +135 -0
- package/src/workspace/checks/security.ts +4 -4
- package/src/workspace/checks.ts +4 -1
- package/src/workspace/ci-green-cli.ts +5 -3
- package/src/workspace/ci-green-workflow.test.ts +52 -0
- package/src/workspace/ci-green-workflow.ts +34 -5
- package/src/workspace/ci-green.test.ts +33 -0
- package/src/workspace/ci-green.ts +85 -4
- package/src/workspace/ci-init.test.ts +159 -0
- package/src/workspace/ci-init.ts +445 -0
- package/src/workspace/ci-report-url.test.ts +6 -1
- package/src/workspace/ci-run.test.ts +18 -13
- package/src/workspace/ci-run.ts +8 -1
- package/src/workspace/ci-serve-land.e2e.test.ts +138 -0
- package/src/workspace/ci-serve-land.test.ts +176 -0
- package/src/workspace/ci-serve.test.ts +77 -43
- package/src/workspace/ci-serve.ts +268 -34
- package/src/workspace/ci-workflow.ts +71 -0
- package/src/workspace/declaration.schema.json +28 -0
- package/src/workspace/declaration.ts +32 -3
- package/src/workspace/delivery-metrics.ts +12 -3
- package/src/workspace/graph-contract.test.ts +3 -2
- package/src/workspace/init.ts +2 -2
- package/src/workspace/member-commands.test.ts +11 -4
- package/src/workspace/member-commands.ts +3 -0
- package/src/workspace/nested-graph.test.ts +4 -0
- package/src/workspace/nested-graph.ts +1 -0
- package/src/workspace/reason-codes.test.ts +2 -0
- package/src/workspace/reason-codes.ts +10 -0
- package/src/workspace/release-candidates.schema.json +51 -0
- package/src/workspace/release-candidates.test.ts +30 -3
- package/src/workspace/release-candidates.ts +54 -2
- package/src/workspace/release-workflow.ts +167 -0
- package/src/workspace/releases.schema.json +30 -0
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Apply on merge per green commit (#4168 step 9): whether the change sets
|
|
3
|
+
* approved for the merges since the last applied green commit cover the plan
|
|
4
|
+
* at the green one.
|
|
5
|
+
*
|
|
6
|
+
* Each member of the fresh plan is checked against the newest approved change
|
|
7
|
+
* set that planned it, with the same rule `pr-apply` uses for one pull request
|
|
8
|
+
* (`planCoveredBy`, ./pr-run.ts). A member with nothing to do is covered by
|
|
9
|
+
* nothing. Any member left uncovered sends the whole plan to a gate of its own,
|
|
10
|
+
* bound to the combined digest.
|
|
11
|
+
*/
|
|
12
|
+
import type { ChangeSetDocument } from "../change-set.js";
|
|
13
|
+
import type { GateLedgerPort } from "../op/gate.js";
|
|
14
|
+
/** One merge's approved change set, with who approved it. */
|
|
15
|
+
export interface ApprovedMerge {
|
|
16
|
+
commit: string;
|
|
17
|
+
pr?: number;
|
|
18
|
+
doc: ChangeSetDocument;
|
|
19
|
+
approvedBy: readonly string[];
|
|
20
|
+
}
|
|
21
|
+
export interface GreenCover {
|
|
22
|
+
covered: boolean;
|
|
23
|
+
/** Why each uncovered member is not covered, one line each. */
|
|
24
|
+
uncovered: string[];
|
|
25
|
+
/** The approvers of the change sets that covered a member, sorted. */
|
|
26
|
+
approvedBy: string[];
|
|
27
|
+
/** The merges whose approval covered a member, in trunk order. */
|
|
28
|
+
coveredBy: string[];
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Check `fresh` against `merges`, given oldest first, as the first-parent
|
|
32
|
+
* history between the last applied green commit and this one runs.
|
|
33
|
+
*/
|
|
34
|
+
export declare function coverByMerges(fresh: ChangeSetDocument, merges: readonly ApprovedMerge[]): GreenCover;
|
|
35
|
+
/** The green commit `pr-apply --green` applies, what it is measured from, and the approved merges between. */
|
|
36
|
+
export interface GreenTarget {
|
|
37
|
+
commit: string;
|
|
38
|
+
/** The last green commit applied to the environment, else `--base`, else the green commit before this one, else its parent. */
|
|
39
|
+
base: string;
|
|
40
|
+
/** Where `base` came from. */
|
|
41
|
+
baseFrom: "applied" | "flag" | "previous-green" | "parent";
|
|
42
|
+
/** The first-parent commits after `base` up to `commit`, oldest first. */
|
|
43
|
+
merges: string[];
|
|
44
|
+
/** Their pull requests, from the merge records, in trunk order. */
|
|
45
|
+
prs: number[];
|
|
46
|
+
/** The merges whose approved change set still stands, oldest first. */
|
|
47
|
+
approved: ApprovedMerge[];
|
|
48
|
+
}
|
|
49
|
+
export type GreenTargetResult = {
|
|
50
|
+
status: "target";
|
|
51
|
+
target: GreenTarget;
|
|
52
|
+
}
|
|
53
|
+
/** The checkout is the green commit last applied. */
|
|
54
|
+
| {
|
|
55
|
+
status: "applied";
|
|
56
|
+
commit: string;
|
|
57
|
+
} | {
|
|
58
|
+
status: "error";
|
|
59
|
+
message: string;
|
|
60
|
+
hint?: string;
|
|
61
|
+
};
|
|
62
|
+
/**
|
|
63
|
+
* Resolve the green target for `env` at `cwd`'s HEAD (#4168 step 9). HEAD must
|
|
64
|
+
* carry a counting `ci/green` tag and no `ci/revoked` one, judged by the
|
|
65
|
+
* declaration's `ci.green` as `release cut` judges them. Reads the local tags
|
|
66
|
+
* and `chant/lifecycle` and never fetches.
|
|
67
|
+
*/
|
|
68
|
+
export declare function resolveGreenTarget(req: {
|
|
69
|
+
cwd: string;
|
|
70
|
+
env: string;
|
|
71
|
+
base?: string;
|
|
72
|
+
gate: string;
|
|
73
|
+
ledger?: GateLedgerPort;
|
|
74
|
+
}): Promise<GreenTargetResult>;
|
|
75
|
+
/** The op a green commit's own gate is recorded under: `green-<sha>`, or `green-<sha>-<member>` for a workspace member. */
|
|
76
|
+
export declare function greenOp(commit: string, member?: string): string;
|
|
77
|
+
//# sourceMappingURL=green-cover.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"green-cover.d.ts","sourceRoot":"","sources":["../../src/components/green-cover.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAGH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AACvD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAGjD,6DAA6D;AAC7D,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,iBAAiB,CAAC;IACvB,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;CAC/B;AAED,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,+DAA+D;IAC/D,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,sEAAsE;IACtE,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,kEAAkE;IAClE,SAAS,EAAE,MAAM,EAAE,CAAC;CACrB;AAUD;;;GAGG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,iBAAiB,EAAE,MAAM,EAAE,SAAS,aAAa,EAAE,GAAG,UAAU,CAyBpG;AAED,8GAA8G;AAC9G,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,+HAA+H;IAC/H,IAAI,EAAE,MAAM,CAAC;IACb,8BAA8B;IAC9B,QAAQ,EAAE,SAAS,GAAG,MAAM,GAAG,gBAAgB,GAAG,QAAQ,CAAC;IAC3D,0EAA0E;IAC1E,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,mEAAmE;IACnE,GAAG,EAAE,MAAM,EAAE,CAAC;IACd,uEAAuE;IACvE,QAAQ,EAAE,aAAa,EAAE,CAAC;CAC3B;AAED,MAAM,MAAM,iBAAiB,GACzB;IAAE,MAAM,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,WAAW,CAAA;CAAE;AAC3C,qDAAqD;GACnD;IAAE,MAAM,EAAE,SAAS,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACrC;IAAE,MAAM,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAUxD;;;;;GAKG;AACH,wBAAsB,kBAAkB,CAAC,GAAG,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,cAAc,CAAA;CAAE,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAuE5J;AAED,2HAA2H;AAC3H,wBAAgB,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAE/D"}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Apply on merge per green commit (#4168 step 9): whether the change sets
|
|
3
|
+
* approved for the merges since the last applied green commit cover the plan
|
|
4
|
+
* at the green one.
|
|
5
|
+
*
|
|
6
|
+
* Each member of the fresh plan is checked against the newest approved change
|
|
7
|
+
* set that planned it, with the same rule `pr-apply` uses for one pull request
|
|
8
|
+
* (`planCoveredBy`, ./pr-run.ts). A member with nothing to do is covered by
|
|
9
|
+
* nothing. Any member left uncovered sends the whole plan to a gate of its own,
|
|
10
|
+
* bound to the combined digest.
|
|
11
|
+
*/
|
|
12
|
+
import { execFileSync } from "node:child_process";
|
|
13
|
+
import { planCoveredBy } from "./pr-run.js";
|
|
14
|
+
/** Whether `member` has anything to apply in `doc`. */
|
|
15
|
+
function hasWork(doc, member) {
|
|
16
|
+
const m = doc.members.find((x) => x.member === member);
|
|
17
|
+
if (m && m.status === "failed")
|
|
18
|
+
return true;
|
|
19
|
+
if (doc.entries.some((e) => e.member === member && e.action !== "no-op" && e.action !== "read"))
|
|
20
|
+
return true;
|
|
21
|
+
return (doc.sideEffects ?? []).some((s) => s.member === member);
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Check `fresh` against `merges`, given oldest first, as the first-parent
|
|
25
|
+
* history between the last applied green commit and this one runs.
|
|
26
|
+
*/
|
|
27
|
+
export function coverByMerges(fresh, merges) {
|
|
28
|
+
const uncovered = [];
|
|
29
|
+
const approvers = new Set();
|
|
30
|
+
const used = new Set();
|
|
31
|
+
for (const member of fresh.members.map((m) => m.member)) {
|
|
32
|
+
if (!hasWork(fresh, member))
|
|
33
|
+
continue;
|
|
34
|
+
const newest = [...merges].reverse().find((m) => m.doc.members.some((x) => x.member === member));
|
|
35
|
+
if (!newest) {
|
|
36
|
+
uncovered.push(`${member}: no merge since the last applied green commit had an approved plan for it`);
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
const why = planCoveredBy(newest.doc, fresh, member);
|
|
40
|
+
if (why) {
|
|
41
|
+
uncovered.push(`${why} (checked against ${newest.pr !== undefined ? `#${newest.pr}` : newest.commit.slice(0, 12)})`);
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
used.add(newest.commit);
|
|
45
|
+
for (const a of newest.approvedBy)
|
|
46
|
+
approvers.add(a);
|
|
47
|
+
}
|
|
48
|
+
return {
|
|
49
|
+
covered: uncovered.length === 0,
|
|
50
|
+
uncovered,
|
|
51
|
+
approvedBy: [...approvers].sort(),
|
|
52
|
+
coveredBy: merges.filter((m) => used.has(m.commit)).map((m) => m.commit),
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
function gitOut(cwd, args) {
|
|
56
|
+
try {
|
|
57
|
+
return execFileSync("git", args, { cwd, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
return undefined;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Resolve the green target for `env` at `cwd`'s HEAD (#4168 step 9). HEAD must
|
|
65
|
+
* carry a counting `ci/green` tag and no `ci/revoked` one, judged by the
|
|
66
|
+
* declaration's `ci.green` as `release cut` judges them. Reads the local tags
|
|
67
|
+
* and `chant/lifecycle` and never fetches.
|
|
68
|
+
*/
|
|
69
|
+
export async function resolveGreenTarget(req) {
|
|
70
|
+
const { readGreen, CI_GREEN_TAG_PREFIX } = await import("../workspace/ci-green.js");
|
|
71
|
+
const { judgedCiTags } = await import("../lifecycle/release-revocation.js");
|
|
72
|
+
let green;
|
|
73
|
+
let top;
|
|
74
|
+
try {
|
|
75
|
+
({ green, top } = readGreen(req.cwd));
|
|
76
|
+
}
|
|
77
|
+
catch (err) {
|
|
78
|
+
return {
|
|
79
|
+
status: "error",
|
|
80
|
+
message: `apply on merge applies green commits (#4168), and ${err instanceof Error ? err.message : String(err)}`,
|
|
81
|
+
hint: "Declare ci.green in chant.workspace.json, or run chant ci init --workflow trunk-curated+apply-on-merge.",
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
const head = gitOut(req.cwd, ["rev-parse", "HEAD"]);
|
|
85
|
+
if (!head)
|
|
86
|
+
return { status: "error", message: "pr-apply --green applies the checkout's HEAD, and it has none" };
|
|
87
|
+
const tags = await judgedCiTags(top, green);
|
|
88
|
+
const counts = (sha) => tags.green.has(sha) && !tags.revoked.has(sha);
|
|
89
|
+
if (!counts(head)) {
|
|
90
|
+
return {
|
|
91
|
+
status: "error",
|
|
92
|
+
message: `${head.slice(0, 12)} is not a green commit (no counting ${CI_GREEN_TAG_PREFIX}${head.slice(0, 12)}… tag, or a ci/revoked one), so apply on merge does not apply it`,
|
|
93
|
+
hint: "Check out the newest green commit first: git checkout --detach \"$(chant ci last-green)\"",
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
const { readGreenApply } = await import("../lifecycle/green-apply.js");
|
|
97
|
+
const last = await readGreenApply(req.env, { cwd: req.cwd });
|
|
98
|
+
if (last?.commit === head)
|
|
99
|
+
return { status: "applied", commit: head };
|
|
100
|
+
let base;
|
|
101
|
+
let baseFrom = "applied";
|
|
102
|
+
if (last && gitOut(req.cwd, ["merge-base", "--is-ancestor", last.commit, head]) !== undefined)
|
|
103
|
+
base = last.commit;
|
|
104
|
+
if (!base && req.base) {
|
|
105
|
+
base = gitOut(req.cwd, ["rev-parse", "--verify", "--quiet", `${req.base}^{commit}`]);
|
|
106
|
+
baseFrom = "flag";
|
|
107
|
+
if (!base)
|
|
108
|
+
return { status: "error", message: `--base ${req.base} names no commit here` };
|
|
109
|
+
}
|
|
110
|
+
if (!base) {
|
|
111
|
+
const before = (gitOut(req.cwd, ["rev-list", "--first-parent", `${head}^`]) ?? "").split("\n").filter(Boolean);
|
|
112
|
+
base = before.find(counts);
|
|
113
|
+
baseFrom = "previous-green";
|
|
114
|
+
if (!base) {
|
|
115
|
+
base = before[0];
|
|
116
|
+
baseFrom = "parent";
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
if (!base)
|
|
120
|
+
return { status: "error", message: `${head.slice(0, 12)} is a root commit, so there is nothing to measure its change from; pass --base` };
|
|
121
|
+
const merges = (gitOut(req.cwd, ["rev-list", "--first-parent", "--reverse", `${base}..${head}`]) ?? "").split("\n").filter(Boolean);
|
|
122
|
+
const { readMergeRecord } = await import("../lifecycle/merge-record.js");
|
|
123
|
+
const { readStoredChangeSet } = await import("../lifecycle/change-set-store.js");
|
|
124
|
+
const { gitGateLedgerPort } = await import("../op/gate.js");
|
|
125
|
+
const { decidePrGate, readOnlyLedger, approvalOf } = await import("./pr-run.js");
|
|
126
|
+
const prs = [];
|
|
127
|
+
const approved = [];
|
|
128
|
+
for (const commit of merges) {
|
|
129
|
+
const record = await readMergeRecord(commit, { cwd: req.cwd }).catch(() => null);
|
|
130
|
+
if (!record || record.env !== req.env)
|
|
131
|
+
continue;
|
|
132
|
+
const pr = record.source.kind === "pull-request" ? record.source.pr : undefined;
|
|
133
|
+
if (pr !== undefined)
|
|
134
|
+
prs.push(pr);
|
|
135
|
+
// The digest a person approved: the merged plan's when it matched, else the one approved before it moved.
|
|
136
|
+
const digest = record.plan === "approved" ? record.digest : record.plan === "changed" ? record.approvedDigest : undefined;
|
|
137
|
+
if (!digest || !record.op)
|
|
138
|
+
continue;
|
|
139
|
+
const stored = await readStoredChangeSet(digest, { cwd: req.cwd }).catch(() => ({ status: "missing" }));
|
|
140
|
+
if (stored.status !== "found")
|
|
141
|
+
continue;
|
|
142
|
+
// The approval must still stand: one revoked since the merge covers nothing.
|
|
143
|
+
const check = await decidePrGate(readOnlyLedger(req.ledger ?? gitGateLedgerPort()), { op: record.op, gate: record.gate ?? req.gate, digest }).catch(() => undefined);
|
|
144
|
+
if (!check?.satisfied)
|
|
145
|
+
continue;
|
|
146
|
+
approved.push({ commit, ...(pr !== undefined ? { pr } : {}), doc: stored.document, approvedBy: approvalOf(check).approvedBy ?? [] });
|
|
147
|
+
}
|
|
148
|
+
return { status: "target", target: { commit: head, base, baseFrom, merges, prs, approved } };
|
|
149
|
+
}
|
|
150
|
+
/** The op a green commit's own gate is recorded under: `green-<sha>`, or `green-<sha>-<member>` for a workspace member. */
|
|
151
|
+
export function greenOp(commit, member) {
|
|
152
|
+
return member ? `green-${commit.slice(0, 12)}-${member}` : `green-${commit.slice(0, 12)}`;
|
|
153
|
+
}
|
|
@@ -176,10 +176,23 @@ export interface PrLoopJob {
|
|
|
176
176
|
env: Record<string, string>;
|
|
177
177
|
/** Shell lines to run before the command, to settle values the forge may leave unusable. */
|
|
178
178
|
setup?: string[];
|
|
179
|
+
/**
|
|
180
|
+
* Shell lines a generated job runs first, before `setup`, to check out the
|
|
181
|
+
* commit it works on (#4168 step 9): the apply checks out the newest green
|
|
182
|
+
* commit. `chant ci run` and `ci serve` already run the job at that commit,
|
|
183
|
+
* so they skip these.
|
|
184
|
+
*/
|
|
185
|
+
checkout?: string[];
|
|
179
186
|
/** With `build` (#4088): the build line and the lint line, run in this order before the command. */
|
|
180
187
|
build?: string[];
|
|
181
188
|
/** With `waveGate` (#4003): the wave a generated apply job runs, 1-based. */
|
|
182
189
|
wave?: number;
|
|
190
|
+
/**
|
|
191
|
+
* The branch an apply job takes its green commit from (#4168 step 9). A
|
|
192
|
+
* wave after the first checks out the commit wave 1 took, which the job
|
|
193
|
+
* receives as `GREEN_SHA` (#4221).
|
|
194
|
+
*/
|
|
195
|
+
greenBranch?: string;
|
|
183
196
|
/**
|
|
184
197
|
* What the generated job runs in place of `command` (#4156): `chant ci run
|
|
185
198
|
* <job> --in-place` with the same environment, gate, forge, events and
|
|
@@ -202,6 +215,14 @@ export declare function prLoopBuildLines(env: string, options?: PrLoopPipelineOp
|
|
|
202
215
|
* the commands then run in its directory, so `--output` is relative to it.
|
|
203
216
|
*/
|
|
204
217
|
export declare function prLoopJobs(forge: ForgeKind, env: string, options?: PrLoopPipelineOptions, member?: string): PrLoopJob[];
|
|
218
|
+
/**
|
|
219
|
+
* What starts the apply under apply on merge (#4168 step 9): on GitHub the
|
|
220
|
+
* tick's workflow finishing (`workflow_run`, since tags its token pushes start
|
|
221
|
+
* no workflow), elsewhere a pushed `ci/green/*` tag. The name is
|
|
222
|
+
* `CI_GREEN_WORKFLOW_NAME` (../workspace/ci-green-workflow.ts).
|
|
223
|
+
*/
|
|
224
|
+
export declare const GREEN_TICK_WORKFLOW = "chant-ci-green";
|
|
225
|
+
export declare const GREEN_TAG_PATTERN = "ci/green/**";
|
|
205
226
|
/** The job a push to the target branch runs (#4168): `record`, or `apply` when the pipeline opts into apply on merge. */
|
|
206
227
|
export declare function prLoopMergeJob(jobs: readonly PrLoopJob[], options?: PrLoopPipelineOptions): PrLoopJob;
|
|
207
228
|
/**
|
|
@@ -243,6 +264,19 @@ export declare function prRecordGroup(env: string, member?: string): string;
|
|
|
243
264
|
* refuses a record made for another pull request, gate or digest.
|
|
244
265
|
*/
|
|
245
266
|
export declare function prApplyRecordKey(env: string, commit: string, member?: string): string;
|
|
267
|
+
/**
|
|
268
|
+
* The line that checks out the newest green commit of `branch` (#4168 step
|
|
269
|
+
* 9), after fetching the branch and its tags, for the apply job. `chant ci
|
|
270
|
+
* last-green` exits 1 when no commit is green, which fails the job.
|
|
271
|
+
*/
|
|
272
|
+
export declare function greenCheckout(branch: string): string;
|
|
273
|
+
/**
|
|
274
|
+
* The line a wave after the first runs in place of {@link greenCheckout}
|
|
275
|
+
* (#4221): it checks out `GREEN_SHA`, the commit wave 1 took, so a commit
|
|
276
|
+
* that goes green while the waves run waits for a run of its own. An empty
|
|
277
|
+
* `GREEN_SHA` fails the job.
|
|
278
|
+
*/
|
|
279
|
+
export declare function pinnedGreenCheckout(branch: string): string;
|
|
246
280
|
/**
|
|
247
281
|
* GitLab sets `CI_COMMIT_BEFORE_SHA` to forty zeros on a branch's first push.
|
|
248
282
|
* The line replaces it with the merge request's diff base when the pipeline
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pr-pipeline.d.ts","sourceRoot":"","sources":["../../src/components/pr-pipeline.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8EG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAK7C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAChD,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,uBAAuB,CAAC;AAKtE,kEAAkE;AAClE,eAAO,MAAM,kBAAkB,cAAc,CAAC;AAE9C;;;;GAIG;AACH,eAAO,MAAM,eAAe,mCAAmC,CAAC;AAEhE;;;;;GAKG;AACH,eAAO,MAAM,aAAa,YAAW,CAAC;AAEtC,8CAA8C;AAC9C,eAAO,MAAM,aAAa,UAEzB,CAAC;AAEF,mHAAmH;AACnH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAEnD;AAED;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,yBAAqC,CAAC;AAErE,yEAAyE;AACzE,MAAM,WAAW,qBAAqB;IACpC,uDAAuD;IACvD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,sIAAsI;IACtI,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,2OAA2O;IAC3O,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B;;;;OAIG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB;;;OAGG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;;;;;OAKG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;;;OAMG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;OAGG;IACH,KAAK,CAAC,EAAE,QAAQ,GAAG,SAAS,CAAC;CAC9B;AAED,8FAA8F;AAC9F,MAAM,WAAW,SAAS;IACxB,2CAA2C;IAC3C,OAAO,EAAE,MAAM,GAAG,OAAO,GAAG,QAAQ,CAAC;IACrC,2EAA2E;IAC3E,OAAO,EAAE,MAAM,CAAC;IAChB;;;;OAIG;IACH,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5B,4FAA4F;IAC5F,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,oGAAoG;IACpG,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,6EAA6E;IAC7E,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;;;;OAOG;IACH,GAAG,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KAAE,CAAC;CACvD;AAED,iHAAiH;AACjH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,GAAE,qBAA0B,GAAG,MAAM,EAAE,CAG3F;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,SAAS,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,GAAE,qBAA0B,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,EAAE,
|
|
1
|
+
{"version":3,"file":"pr-pipeline.d.ts","sourceRoot":"","sources":["../../src/components/pr-pipeline.ts"],"names":[],"mappings":"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"}
|
|
@@ -142,7 +142,7 @@ export function prLoopJobs(forge, env, options = {}, member) {
|
|
|
142
142
|
// #4156: the flags `chant ci run` rebuilds `command` from. It finds the member from the directory it runs in.
|
|
143
143
|
const ciRun = (job) => [
|
|
144
144
|
"chant", "ci", "run", job, "--in-place", ...(job === "plan" ? ["--no-push"] : []),
|
|
145
|
-
"--base", '"$BASE_SHA"', ...(job === "plan" ? ["--pr", '"$PR_NUMBER"'] : []),
|
|
145
|
+
...(job === "apply" ? [] : ["--base", '"$BASE_SHA"']), ...(job === "plan" ? ["--pr", '"$PR_NUMBER"'] : []),
|
|
146
146
|
"--env", env, "--gate", gate, "--forge", forge,
|
|
147
147
|
...(job !== "record" && options.waveGate ? ["--wave-gate", options.waveGate] : []),
|
|
148
148
|
...(job !== "plan" && options.branch ? ["--branch", JSON.stringify(options.branch)] : []),
|
|
@@ -155,6 +155,7 @@ export function prLoopJobs(forge, env, options = {}, member) {
|
|
|
155
155
|
const planEnv = github
|
|
156
156
|
? { BASE_SHA: "${{ github.event.pull_request.base.sha }}", PR_NUMBER: "${{ github.event.pull_request.number }}", ...jobEnv }
|
|
157
157
|
: { BASE_SHA: "$CI_MERGE_REQUEST_DIFF_BASE_SHA", PR_NUMBER: "$CI_MERGE_REQUEST_IID", ...jobEnv };
|
|
158
|
+
const greenBranch = github ? JSON.stringify(options.branch ?? "main") : options.branch ? JSON.stringify(options.branch) : "$CI_DEFAULT_BRANCH";
|
|
158
159
|
const applyEnv = github ? { BASE_SHA: "${{ github.event.before }}", ...jobEnv } : { BASE_SHA: "$CI_COMMIT_BEFORE_SHA", ...jobEnv };
|
|
159
160
|
return [
|
|
160
161
|
{
|
|
@@ -166,17 +167,19 @@ export function prLoopJobs(forge, env, options = {}, member) {
|
|
|
166
167
|
run: { command: ciRun("plan").join(" "), env: runEnv(planEnv) },
|
|
167
168
|
},
|
|
168
169
|
{
|
|
170
|
+
// #4168 step 9: the apply applies the newest green commit, measured from the one applied before it.
|
|
169
171
|
jobName: "apply",
|
|
170
172
|
command: [
|
|
171
|
-
"chant", "components", "pr-apply", "--
|
|
173
|
+
"chant", "components", "pr-apply", "--green", ...common,
|
|
172
174
|
...(options.requireReview === true ? ["--require-review"] : []),
|
|
173
175
|
...(options.waveGate ? ["--wave-gate", options.waveGate] : []),
|
|
174
176
|
"--resume", PR_APPLY_RECORD,
|
|
175
177
|
].join(" "),
|
|
176
|
-
env:
|
|
177
|
-
|
|
178
|
+
env: jobEnv,
|
|
179
|
+
checkout: [greenCheckout(greenBranch)],
|
|
180
|
+
greenBranch,
|
|
178
181
|
...build,
|
|
179
|
-
run: { command: ciRun("apply").join(" "), env: runEnv(
|
|
182
|
+
run: { command: ciRun("apply").join(" "), env: runEnv(jobEnv) },
|
|
180
183
|
},
|
|
181
184
|
{
|
|
182
185
|
jobName: "record",
|
|
@@ -188,6 +191,14 @@ export function prLoopJobs(forge, env, options = {}, member) {
|
|
|
188
191
|
},
|
|
189
192
|
];
|
|
190
193
|
}
|
|
194
|
+
/**
|
|
195
|
+
* What starts the apply under apply on merge (#4168 step 9): on GitHub the
|
|
196
|
+
* tick's workflow finishing (`workflow_run`, since tags its token pushes start
|
|
197
|
+
* no workflow), elsewhere a pushed `ci/green/*` tag. The name is
|
|
198
|
+
* `CI_GREEN_WORKFLOW_NAME` (../workspace/ci-green-workflow.ts).
|
|
199
|
+
*/
|
|
200
|
+
export const GREEN_TICK_WORKFLOW = "chant-ci-green";
|
|
201
|
+
export const GREEN_TAG_PATTERN = "ci/green/**";
|
|
191
202
|
/** The job a push to the target branch runs (#4168): `record`, or `apply` when the pipeline opts into apply on merge. */
|
|
192
203
|
export function prLoopMergeJob(jobs, options = {}) {
|
|
193
204
|
return jobs.find((j) => j.jobName === (options.applyOnMerge ? "apply" : "record"));
|
|
@@ -226,6 +237,8 @@ export function prLoopWaveJobs(components, apply) {
|
|
|
226
237
|
...apply,
|
|
227
238
|
command: `${apply.command} --wave ${i + 1}`,
|
|
228
239
|
wave: i + 1,
|
|
240
|
+
// #4221: every wave after the first applies the commit wave 1 took.
|
|
241
|
+
...(i > 0 && apply.greenBranch !== undefined ? { checkout: [pinnedGreenCheckout(apply.greenBranch)] } : {}),
|
|
229
242
|
// #4156: the job runs it through chant ci run, which appends the same --wave to pr-apply.
|
|
230
243
|
run: { ...apply.run, command: `${apply.run.command} --wave ${i + 1}` },
|
|
231
244
|
}));
|
|
@@ -273,6 +286,27 @@ function pushBaseFallback(target, commit, diffBase) {
|
|
|
273
286
|
(diffBase ? `BASE_SHA="\${${diffBase}:-}"; if [ -z "$BASE_SHA" ]; then ${fromBranch}fi; ` : fromBranch) +
|
|
274
287
|
"fi; export BASE_SHA");
|
|
275
288
|
}
|
|
289
|
+
/**
|
|
290
|
+
* The line that checks out the newest green commit of `branch` (#4168 step
|
|
291
|
+
* 9), after fetching the branch and its tags, for the apply job. `chant ci
|
|
292
|
+
* last-green` exits 1 when no commit is green, which fails the job.
|
|
293
|
+
*/
|
|
294
|
+
export function greenCheckout(branch) {
|
|
295
|
+
return `${fetchBranch(branch)} && GREEN_SHA="$(chant ci last-green)" && git checkout --quiet --detach "$GREEN_SHA"`;
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* The line a wave after the first runs in place of {@link greenCheckout}
|
|
299
|
+
* (#4221): it checks out `GREEN_SHA`, the commit wave 1 took, so a commit
|
|
300
|
+
* that goes green while the waves run waits for a run of its own. An empty
|
|
301
|
+
* `GREEN_SHA` fails the job.
|
|
302
|
+
*/
|
|
303
|
+
export function pinnedGreenCheckout(branch) {
|
|
304
|
+
return `test -n "$GREEN_SHA" && ${fetchBranch(branch)} && git checkout --quiet --detach "$GREEN_SHA"`;
|
|
305
|
+
}
|
|
306
|
+
function fetchBranch(branch) {
|
|
307
|
+
const name = branch.replace(/^"|"$/g, "");
|
|
308
|
+
return `git fetch --quiet --tags origin "+refs/heads/${name}:refs/remotes/origin/${name}"`;
|
|
309
|
+
}
|
|
276
310
|
/**
|
|
277
311
|
* GitLab sets `CI_COMMIT_BEFORE_SHA` to forty zeros on a branch's first push.
|
|
278
312
|
* The line replaces it with the merge request's diff base when the pipeline
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A promote under a release that plans again once an upstream applied, and a
|
|
3
|
+
* gate per wave (#4168 step 4). Merging deploys nothing since step 1, so what
|
|
4
|
+
* `pr-apply` did on merge happens here, at promote:
|
|
5
|
+
*
|
|
6
|
+
* - **Rounds** (#3999's rule, `../pr-loop.ts`). The release gate approves a
|
|
7
|
+
* change set in which each dependent was planned against its upstream's
|
|
8
|
+
* planned outputs (`planReleaseSet` in ./release-gate.ts). The promote runs
|
|
9
|
+
* a wave at a time. Before a wave runs, a component whose plan read an
|
|
10
|
+
* upstream output that the run left as another value
|
|
11
|
+
* (`movedWiringReferences`) is planned again against what stands. When the
|
|
12
|
+
* approved change set covers the new plan (`waveNotCovered`, the test a
|
|
13
|
+
* pull request's resume uses), it applies under the approval it has.
|
|
14
|
+
* Otherwise its gates are decided as `<gate>-round-<n>` (`prRoundGate`),
|
|
15
|
+
* bound to the new plan's digest.
|
|
16
|
+
* - **A gate per wave** (`--wave-gate <gate>`, #4157's rule). Every wave
|
|
17
|
+
* after the first is planned again against what the waves before it left,
|
|
18
|
+
* and a wave the approved change set does not cover waits at
|
|
19
|
+
* `<gate>-wave-<k>` (`waveGateName`), one gate for the whole wave, bound to
|
|
20
|
+
* that wave's digest.
|
|
21
|
+
*
|
|
22
|
+
* A stage whose components declare no gate has nowhere to wait, so the
|
|
23
|
+
* promote stops there with nothing more deployed.
|
|
24
|
+
*
|
|
25
|
+
* A promote that stops at a round's or a wave's gate writes a resume record
|
|
26
|
+
* ({@link PromoteResume}): the components that applied and the outputs they
|
|
27
|
+
* left. The same promote run again with it skips what applied and carries on
|
|
28
|
+
* from the wave it stopped at, so an approval of the round or wave it waits on
|
|
29
|
+
* is decided against the same plan.
|
|
30
|
+
*/
|
|
31
|
+
import { type DriverComponent, type DriverRunResult, type ReleaseGateBinding } from "./driver.js";
|
|
32
|
+
import type { ChangeSetDocument } from "../change-set.js";
|
|
33
|
+
import type { ReleasePlan } from "./release-gate.js";
|
|
34
|
+
export declare const PROMOTE_RESUME_SCHEMA = "chant-promote-resume/v1";
|
|
35
|
+
/** What a promote that stopped at a round's or a wave's gate leaves for the next attempt. */
|
|
36
|
+
export interface PromoteResume {
|
|
37
|
+
schema: typeof PROMOTE_RESUME_SCHEMA;
|
|
38
|
+
env: string;
|
|
39
|
+
releaseDigest: string;
|
|
40
|
+
/** The change set the release gate approved before anything applied. */
|
|
41
|
+
approvedDigest: string;
|
|
42
|
+
/** Components that applied, in run order. */
|
|
43
|
+
completed: string[];
|
|
44
|
+
/** The outputs standing after them. */
|
|
45
|
+
outputs: Record<string, Record<string, unknown>>;
|
|
46
|
+
/** ISO 8601. */
|
|
47
|
+
at: string;
|
|
48
|
+
}
|
|
49
|
+
/** One round or wave that needed its own approval. */
|
|
50
|
+
export interface PromoteStage {
|
|
51
|
+
kind: "round" | "wave";
|
|
52
|
+
/** The round number (2 and up) or the wave number (1-based). */
|
|
53
|
+
number: number;
|
|
54
|
+
components: string[];
|
|
55
|
+
/** The plan the stage's gates bind. */
|
|
56
|
+
digest: string;
|
|
57
|
+
/** Why the approved change set did not cover it, one line per member. */
|
|
58
|
+
notCovered: string[];
|
|
59
|
+
status: "applied" | "waiting" | "failed";
|
|
60
|
+
/** With `failed` before anything ran: why. */
|
|
61
|
+
message?: string;
|
|
62
|
+
}
|
|
63
|
+
export interface PromoteRoundsOptions {
|
|
64
|
+
/** The promoted compositions, in no particular order. */
|
|
65
|
+
components: DriverComponent[];
|
|
66
|
+
/** Outputs standing before the run: the seeds, and on a resume the record's. */
|
|
67
|
+
outputs: Record<string, Record<string, unknown>>;
|
|
68
|
+
/** The release gate the change set was approved under. */
|
|
69
|
+
binding: ReleaseGateBinding;
|
|
70
|
+
/** The approved change set, and what each component's plan read. */
|
|
71
|
+
approved: ChangeSetDocument;
|
|
72
|
+
reads: ReleasePlan["reads"];
|
|
73
|
+
/** Components an earlier attempt applied. Skipped. */
|
|
74
|
+
completed?: readonly string[];
|
|
75
|
+
/** `--wave-gate <gate>`: check every wave after the first, not only the components whose inputs moved, and name a wave's gate `<gate>-wave-<k>`. */
|
|
76
|
+
waveGate?: string;
|
|
77
|
+
/** Run one subset of the components with `outputs` standing (`runPromotion` with the binding). */
|
|
78
|
+
run: (components: DriverComponent[], outputs: Record<string, Record<string, unknown>>, binding: ReleaseGateBinding) => Promise<DriverRunResult>;
|
|
79
|
+
/** Plan `only` again with `outputs` standing (`planReleaseSet`). */
|
|
80
|
+
replan: (only: ReadonlySet<string>, outputs: Record<string, Record<string, unknown>>) => Promise<ChangeSetDocument>;
|
|
81
|
+
/** Store a stage's plan and bind its gates (empty resolutions included). */
|
|
82
|
+
bind: (doc: ChangeSetDocument, gates: string[]) => Promise<ReleaseGateBinding>;
|
|
83
|
+
}
|
|
84
|
+
export interface PromoteRoundsResult {
|
|
85
|
+
run: DriverRunResult;
|
|
86
|
+
/** The rounds and waves that needed their own approval. */
|
|
87
|
+
stages: PromoteStage[];
|
|
88
|
+
/** Every component that applied, earlier attempts included, in run order. */
|
|
89
|
+
completed: string[];
|
|
90
|
+
outputs: Record<string, Record<string, unknown>>;
|
|
91
|
+
}
|
|
92
|
+
/** `component` with each gate renamed by `rename`, nested phases included. */
|
|
93
|
+
export declare function renameGates(component: DriverComponent, rename: (gate: string) => string): DriverComponent;
|
|
94
|
+
/** The references `component`'s steps make that resolve to another value now than when its plan read them. */
|
|
95
|
+
export declare function movedInputs(component: DriverComponent, read: Record<string, Record<string, unknown>> | undefined, now: Record<string, Record<string, unknown>>): string[];
|
|
96
|
+
/**
|
|
97
|
+
* Run the promote a wave at a time, planning again where an upstream applied
|
|
98
|
+
* as another value, and with `waveGate`, checking each wave. Stops at the
|
|
99
|
+
* first wave that fails or waits on a gate.
|
|
100
|
+
*/
|
|
101
|
+
export declare function runPromotionInRounds(options: PromoteRoundsOptions): Promise<PromoteRoundsResult>;
|
|
102
|
+
//# sourceMappingURL=promote-rounds.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"promote-rounds.d.ts","sourceRoot":"","sources":["../../src/components/promote-rounds.ts"],"names":[],"mappings":"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"}
|