@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
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contract-repo.d.ts","sourceRoot":"","sources":["../../../src/workspace/__fixtures__/contract-repo.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAMH,OAAO,OAAO,MAAM,eAAe,CAAC;AAGpC,6CAA6C;AAC7C,eAAO,MAAM,IAAI,QAAwE,CAAC;AAI1F,gGAAgG;AAChG,wBAAgB,YAAY,IAAI,IAAI,CAEnC;AAED,wBAAgB,UAAU,CAAC,MAAM,SAAoB,GAAG,MAAM,CAI7D;AAED,wBAAgB,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,GAAG,MAAM,CAM1D;AAED,iFAAiF;AACjF,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,GAAG,IAAI,CAO7G;AAED,uFAAuF;AACvF,wBAAgB,IAAI,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,EAAE,MAAM,UAAQ,GAAG,MAAM,CAS3G;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,MAAM,EAAE,MAAM,SAAyB,GAAG,MAAM,MAAM,
|
|
1
|
+
{"version":3,"file":"contract-repo.d.ts","sourceRoot":"","sources":["../../../src/workspace/__fixtures__/contract-repo.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAMH,OAAO,OAAO,MAAM,eAAe,CAAC;AAGpC,6CAA6C;AAC7C,eAAO,MAAM,IAAI,QAAwE,CAAC;AAI1F,gGAAgG;AAChG,wBAAgB,YAAY,IAAI,IAAI,CAEnC;AAED,wBAAgB,UAAU,CAAC,MAAM,SAAoB,GAAG,MAAM,CAI7D;AAED,wBAAgB,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,GAAG,MAAM,CAM1D;AAED,iFAAiF;AACjF,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,GAAG,IAAI,CAO7G;AAED,uFAAuF;AACvF,wBAAgB,IAAI,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,EAAE,MAAM,UAAQ,GAAG,MAAM,CAS3G;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,MAAM,EAAE,MAAM,SAAyB,GAAG,MAAM,MAAM,CAiB9F;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,SAAS,GAAG,MAAM,CAIhE;AAED,QAAA,MAAM,GAAG,SAAiD,CAAC;AAE3D,4EAA4E;AAC5E,wBAAgB,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG;IAAE,QAAQ,EAAE,UAAU,CAAC,OAAO,GAAG,CAAC,OAAO,CAAC,CAAC;IAAC,WAAW,CAAC,GAAG,EAAE,OAAO,GAAG,IAAI,CAAA;CAAE,CAStH;AAED,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAEnD;AAED,eAAO,MAAM,WAAW,YAAa,OAAO,EAAE,UAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,WACL,CAAC;AAE1E;;;GAGG;AACH,eAAO,MAAM,gBAAgB,6XAU5B,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,sZASjC,CAAC"}
|
|
@@ -22,7 +22,7 @@ export function scratchDir(prefix = "chant-contract-") {
|
|
|
22
22
|
return d;
|
|
23
23
|
}
|
|
24
24
|
export function git(cwd, ...args) {
|
|
25
|
-
return execFileSync("git", ["-c", "user.name=t", "-c", "user.email=t@example.com", "-c", "commit.gpgsign=false", ...args], {
|
|
25
|
+
return execFileSync("git", ["-c", "user.name=t", "-c", "user.email=t@example.com", "-c", "commit.gpgsign=false", "-c", "gc.auto=0", "-c", "maintenance.auto=false", ...args], {
|
|
26
26
|
cwd,
|
|
27
27
|
encoding: "utf-8",
|
|
28
28
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -61,7 +61,18 @@ export function repoFixture(build, prefix = "chant-contract-copy-") {
|
|
|
61
61
|
if (template === undefined || !existsSync(template))
|
|
62
62
|
template = build();
|
|
63
63
|
const root = scratchDir(prefix);
|
|
64
|
-
|
|
64
|
+
// A commit can leave a detached `git gc --auto` or maintenance run moving
|
|
65
|
+
// the template's objects, and a copy that meets it fails with ENOENT on
|
|
66
|
+
// .git/objects. Copy once more after it settles.
|
|
67
|
+
try {
|
|
68
|
+
cpSync(template, root, { recursive: true });
|
|
69
|
+
}
|
|
70
|
+
catch (err) {
|
|
71
|
+
if (err.code !== "ENOENT")
|
|
72
|
+
throw err;
|
|
73
|
+
rmSync(root, { recursive: true, force: true });
|
|
74
|
+
cpSync(template, root, { recursive: true });
|
|
75
|
+
}
|
|
65
76
|
return root;
|
|
66
77
|
};
|
|
67
78
|
}
|
|
@@ -332,7 +332,16 @@
|
|
|
332
332
|
|
|
333
333
|
"security-runner-start-no-role",
|
|
334
334
|
|
|
335
|
-
"security-trust-unreadable"
|
|
335
|
+
"security-trust-unreadable",
|
|
336
|
+
"ci-workflow-green-undeclared",
|
|
337
|
+
"ci-workflow-no-pre-merge",
|
|
338
|
+
"ci-workflow-apply-in-green",
|
|
339
|
+
"ci-workflow-no-components",
|
|
340
|
+
"ci-workflow-no-environments",
|
|
341
|
+
"ci-workflow-ungated-env",
|
|
342
|
+
"ci-workflow-pipeline-missing",
|
|
343
|
+
"ci-workflow-pipeline-differs",
|
|
344
|
+
"ci-workflow-publish-missing"
|
|
336
345
|
],
|
|
337
346
|
"description": "For WSP001: why the declaration can't be read. For WSP121 to WSP127, the box finding (#2726, #2727, #2780, #2850). For WSP117, work-acceptance-unmet (#2772). For WSP118, flag-stale (#4024). For WSP131 to WSP133, the diagram finding (#2764)."
|
|
338
347
|
}
|
|
@@ -445,7 +454,16 @@
|
|
|
445
454
|
|
|
446
455
|
"security-runner-start-no-role",
|
|
447
456
|
|
|
448
|
-
"security-trust-unreadable"
|
|
457
|
+
"security-trust-unreadable",
|
|
458
|
+
"ci-workflow-green-undeclared",
|
|
459
|
+
"ci-workflow-no-pre-merge",
|
|
460
|
+
"ci-workflow-apply-in-green",
|
|
461
|
+
"ci-workflow-no-components",
|
|
462
|
+
"ci-workflow-no-environments",
|
|
463
|
+
"ci-workflow-ungated-env",
|
|
464
|
+
"ci-workflow-pipeline-missing",
|
|
465
|
+
"ci-workflow-pipeline-differs",
|
|
466
|
+
"ci-workflow-publish-missing"
|
|
449
467
|
],
|
|
450
468
|
"description": "For WSP001: why the declaration can't be read. For WSP121 to WSP127, the box finding (#2726, #2727, #2780, #2850). For WSP117, work-acceptance-unmet (#2772). For WSP118, flag-stale (#4024). For WSP131 to WSP133, the diagram finding (#2764)."
|
|
451
469
|
},
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WSP151 (#4168 step 6): what a member under `ci.workflow` still lacks.
|
|
3
|
+
*
|
|
4
|
+
* A declaration that names no workflow gets no finding from this file. The
|
|
5
|
+
* requirements and their codes are {@link CI_WORKFLOW_REQUIREMENTS}
|
|
6
|
+
* (../ci-workflow.ts). Like the security checklists (./security.ts), the
|
|
7
|
+
* check reads files and never runs them: `chant.config.ts` for the
|
|
8
|
+
* environments (its literal `environments`, as `workspace status` reads it),
|
|
9
|
+
* `*.component.ts` for components and gates, and the forge pipeline files for
|
|
10
|
+
* the jobs. A generated file that differs from what its generator writes is
|
|
11
|
+
* WSP101 to WSP106's to report; this check asks only whether the jobs the
|
|
12
|
+
* workflow needs are there.
|
|
13
|
+
*/
|
|
14
|
+
import type { WorkspaceCheck } from "../checks.js";
|
|
15
|
+
export declare const WSP_CI_WORKFLOW = "WSP151";
|
|
16
|
+
export declare const CI_WORKFLOW_CHECKS: readonly WorkspaceCheck[];
|
|
17
|
+
//# sourceMappingURL=ci-workflow.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ci-workflow.d.ts","sourceRoot":"","sources":["../../../src/workspace/checks/ci-workflow.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAuB,MAAM,WAAW,CAAC;AAQrE,eAAO,MAAM,eAAe,WAAW,CAAC;AAaxC,eAAO,MAAM,kBAAkB,EAAE,SAAS,cAAc,EAmGvD,CAAC"}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WSP151 (#4168 step 6): what a member under `ci.workflow` still lacks.
|
|
3
|
+
*
|
|
4
|
+
* A declaration that names no workflow gets no finding from this file. The
|
|
5
|
+
* requirements and their codes are {@link CI_WORKFLOW_REQUIREMENTS}
|
|
6
|
+
* (../ci-workflow.ts). Like the security checklists (./security.ts), the
|
|
7
|
+
* check reads files and never runs them: `chant.config.ts` for the
|
|
8
|
+
* environments (its literal `environments`, as `workspace status` reads it),
|
|
9
|
+
* `*.component.ts` for components and gates, and the forge pipeline files for
|
|
10
|
+
* the jobs. A generated file that differs from what its generator writes is
|
|
11
|
+
* WSP101 to WSP106's to report; this check asks only whether the jobs the
|
|
12
|
+
* workflow needs are there.
|
|
13
|
+
*/
|
|
14
|
+
import { appliesOnMerge, STUB_MARKER, workflowOf } from "../ci-workflow.js";
|
|
15
|
+
import { matchesCheckRun } from "../ci-green.js";
|
|
16
|
+
import { staticPromotionOrder } from "../promotion-order.js";
|
|
17
|
+
import { joinPath } from "../tree.js";
|
|
18
|
+
import { allPipelineFiles, commandLines, componentFiles, read } from "./security.js";
|
|
19
|
+
export const WSP_CI_WORKFLOW = "WSP151";
|
|
20
|
+
const memberDir = (dir) => (dir === "." ? "" : dir);
|
|
21
|
+
const PLAN = /\bci\s+run\s+plan\b|\bcomponents\s+pr-plan\b/;
|
|
22
|
+
const RECORD = /\bci\s+run\s+record\b|\bcomponents\s+pr-record\b/;
|
|
23
|
+
const APPLY = /\bci\s+run\s+apply\b|\bcomponents\s+pr-apply\b/;
|
|
24
|
+
const TICK = /\bci\s+tick\b/;
|
|
25
|
+
const PUBLISH = /\brelease\s+publish\b/;
|
|
26
|
+
/** The apply job names a required phase could match. */
|
|
27
|
+
const APPLY_JOB_NAMES = ["apply", "pr-apply"];
|
|
28
|
+
export const CI_WORKFLOW_CHECKS = [
|
|
29
|
+
{
|
|
30
|
+
id: WSP_CI_WORKFLOW,
|
|
31
|
+
name: "ci-workflow",
|
|
32
|
+
description: "Under ci.workflow: ci.green with pre-merge phases and no apply job required, each member with a component, environments and a gate on each component, and pipeline files that plan, record (or apply under apply on merge), tick and publish.",
|
|
33
|
+
severity: "warning",
|
|
34
|
+
configurable: true,
|
|
35
|
+
check(ctx) {
|
|
36
|
+
const d = ctx.declaration;
|
|
37
|
+
const under = d.members
|
|
38
|
+
.map((m) => ({ member: m, workflow: workflowOf(d, m) }))
|
|
39
|
+
.filter((u) => u.workflow !== null)
|
|
40
|
+
// The top level's workflow covers the members that build with chant; a member that names one itself is held to it.
|
|
41
|
+
.filter((u) => u.member.ciWorkflow !== null || ctx.tree.stat(joinPath(memberDir(u.member.dir), "chant.config.ts")) === "file");
|
|
42
|
+
if (under.length === 0 && !d.ci?.workflow)
|
|
43
|
+
return [];
|
|
44
|
+
const pointer = d.ci?.workflow ? "/ci/workflow" : `${under[0].member.pointer}/ci/workflow`;
|
|
45
|
+
const out = [];
|
|
46
|
+
const finding = (code, message, at = {}) => {
|
|
47
|
+
out.push({
|
|
48
|
+
checkId: this.id,
|
|
49
|
+
severity: this.severity,
|
|
50
|
+
code,
|
|
51
|
+
message: `${code}: ${message}`,
|
|
52
|
+
pointer: at.pointer ?? pointer,
|
|
53
|
+
...(at.entity !== undefined ? { entity: at.entity } : {}),
|
|
54
|
+
...(at.treeFile !== undefined ? { treeFile: at.treeFile, line: at.line ?? 1, column: 1 } : {}),
|
|
55
|
+
});
|
|
56
|
+
};
|
|
57
|
+
const green = d.ci?.green ?? null;
|
|
58
|
+
if (!green) {
|
|
59
|
+
finding("ci-workflow-green-undeclared", "the workspace follows a ci.workflow and declares no ci.green, so no commit gets a verdict and no release can be cut; run chant ci init or add a ci.green block");
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
if ((green.preMerge ?? []).length === 0) {
|
|
63
|
+
finding("ci-workflow-no-pre-merge", "ci.green.preMerge names no phase, so nothing says which checks run before the branch moves; name the quick ones", { pointer: green.pointer });
|
|
64
|
+
}
|
|
65
|
+
for (const name of green.require) {
|
|
66
|
+
const phase = green.phases.find((p) => p.name === name);
|
|
67
|
+
const hit = phase?.runs.find((pattern) => APPLY_JOB_NAMES.some((job) => matchesCheckRun(pattern, job)));
|
|
68
|
+
if (phase && hit) {
|
|
69
|
+
finding("ci-workflow-apply-in-green", `the required phase ${name} matches the apply job with ${JSON.stringify(hit)}, so an apply that waits for a green commit would wait on itself; drop the pattern`, { pointer: phase.pointer });
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
for (const { member } of under) {
|
|
74
|
+
const dir = memberDir(member.dir);
|
|
75
|
+
const components = componentFiles(ctx.tree, dir);
|
|
76
|
+
if (components.length === 0) {
|
|
77
|
+
finding("ci-workflow-no-components", `member ${member.name} declares no component, so nothing of it can be cut or promoted; chant ci init writes a stub`, { pointer: member.pointer, entity: member.name });
|
|
78
|
+
}
|
|
79
|
+
else if (components.every((f) => (read(ctx.tree, f) ?? "").includes(STUB_MARKER))) {
|
|
80
|
+
finding("ci-workflow-no-components", `member ${member.name}'s only component is the stub chant ci init wrote (${components[0]}); say how it is built and deployed, then delete its ${STUB_MARKER} line`, { pointer: member.pointer, entity: member.name, treeFile: components[0] });
|
|
81
|
+
}
|
|
82
|
+
const configPath = joinPath(dir, "chant.config.ts");
|
|
83
|
+
const config = read(ctx.tree, configPath);
|
|
84
|
+
// A config whose environments aren't literals can't be read without running it; say nothing rather than guess.
|
|
85
|
+
const order = config === null ? [] : staticPromotionOrder(config);
|
|
86
|
+
if (order !== undefined && order.length === 0) {
|
|
87
|
+
finding("ci-workflow-no-environments", `member ${member.name}'s chant.config.ts declares no environments, so a release has nowhere to be promoted to`, { pointer: member.pointer, entity: member.name, ...(config !== null ? { treeFile: configPath } : {}) });
|
|
88
|
+
}
|
|
89
|
+
for (const file of components) {
|
|
90
|
+
const text = read(ctx.tree, file) ?? "";
|
|
91
|
+
if (/\bgate\s*\(|kind\s*:\s*["']gate["']/.test(text))
|
|
92
|
+
continue;
|
|
93
|
+
const last = order && order.length > 0 ? order[order.length - 1].name : "the last environment";
|
|
94
|
+
finding("ci-workflow-ungated-env", `${file} has no gate, so a promote to ${last} waits on no approval; add a gate() step`, { pointer: member.pointer, entity: member.name, treeFile: file });
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
const files = allPipelineFiles(ctx);
|
|
98
|
+
const has = (re) => files.some((f) => f.lines.some((l) => re.test(l)));
|
|
99
|
+
const applyOnMerge = under.some((u) => appliesOnMerge(u.workflow)) || (d.ci?.workflow ? appliesOnMerge(d.ci.workflow) : false);
|
|
100
|
+
const missing = [
|
|
101
|
+
...(has(PLAN) ? [] : ["a plan job (ci run plan)"]),
|
|
102
|
+
// #4168 step 9: every merge is recorded, apply on merge or not, since the apply reads the merge records.
|
|
103
|
+
...(has(RECORD) ? [] : ["a record job (ci run record)"]),
|
|
104
|
+
...(has(TICK) ? [] : ["chant ci tick"]),
|
|
105
|
+
];
|
|
106
|
+
if (missing.length > 0) {
|
|
107
|
+
finding("ci-workflow-pipeline-missing", `no pipeline file runs ${missing.join(", ")}; generate them with chant build --components --generate <forge> --pr-loop and chant ci workflow`);
|
|
108
|
+
}
|
|
109
|
+
if (!applyOnMerge) {
|
|
110
|
+
for (const l of commandLines(ctx, APPLY)) {
|
|
111
|
+
finding("ci-workflow-pipeline-differs", `${l.path} applies on merge, and the workflow is trunk-curated, where a merge records the plan and deploys nothing; regenerate without --apply-on-merge, or set ci.workflow to trunk-curated+apply-on-merge`, { treeFile: l.path, line: l.line });
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
else if (!has(APPLY) && has(RECORD)) {
|
|
115
|
+
const l = commandLines(ctx, RECORD)[0];
|
|
116
|
+
finding("ci-workflow-pipeline-differs", `${l.path} records on merge, and the workflow is trunk-curated+apply-on-merge; regenerate with --apply-on-merge`, { treeFile: l.path, line: l.line });
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
// #4168 step 9: an apply measured from a push's base applies each merge; the workflow applies green commits.
|
|
120
|
+
for (const l of commandLines(ctx, APPLY).filter((c) => /--base\b/.test(c.text) || (/\bcomponents\s+pr-apply\b/.test(c.text) && !/--green\b/.test(c.text)))) {
|
|
121
|
+
finding("ci-workflow-pipeline-differs", `${l.path} applies each merge, and under trunk-curated+apply-on-merge the apply applies the newest green commit; regenerate with --apply-on-merge`, { treeFile: l.path, line: l.line });
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
if (!has(PUBLISH)) {
|
|
125
|
+
finding("ci-workflow-publish-missing", "no pipeline file runs chant release publish, so a cut release is never published; generate one with chant release workflow");
|
|
126
|
+
}
|
|
127
|
+
return out;
|
|
128
|
+
},
|
|
129
|
+
},
|
|
130
|
+
];
|
|
@@ -23,7 +23,8 @@
|
|
|
23
23
|
* pipeline file that runs `chant release publish`, `chant ci tick` or
|
|
24
24
|
* `chant components pr-apply`.
|
|
25
25
|
*/
|
|
26
|
-
import type { WorkspaceCheck } from "../checks.js";
|
|
26
|
+
import type { WorkspaceCheck, WorkspaceCheckContext } from "../checks.js";
|
|
27
|
+
import { type WorkspaceTree } from "../tree.js";
|
|
27
28
|
/** The read contract's codes for the security findings, carried as `code` on each. */
|
|
28
29
|
export declare const SECURITY_FINDING_CODES: readonly ["security-signers-missing", "security-release-gate-missing", "security-publish-allows-unsigned", "security-sbom-disabled", "security-sbom-unsourced", "security-publish-no-provenance", "security-component-no-sbom", "security-component-no-verify", "security-signed-since-unset", "security-runner-untrusted", "security-tick-no-key", "security-review-not-required", "security-attribution-any", "security-gate-unsigned", "security-runner-start-no-key", "security-runner-start-no-role", "security-trust-unreadable"];
|
|
29
30
|
export declare const WSP_SECURITY_VERIFIED_RELEASES = "WSP161";
|
|
@@ -31,5 +32,19 @@ export declare const WSP_SECURITY_SUPPLY_CHAIN = "WSP162";
|
|
|
31
32
|
export declare const WSP_SECURITY_WHO_RAN_IT = "WSP163";
|
|
32
33
|
export declare const WSP_SECURITY_REVIEW_RULES = "WSP164";
|
|
33
34
|
export declare const WSP_SECURITY_ISOLATED_RUNNERS = "WSP165";
|
|
35
|
+
export declare function read(tree: WorkspaceTree, path: string): string | null;
|
|
36
|
+
/** Every pipeline file of the workspace: its root's, and each member's. */
|
|
37
|
+
export declare function allPipelineFiles(ctx: WorkspaceCheckContext): {
|
|
38
|
+
path: string;
|
|
39
|
+
lines: string[];
|
|
40
|
+
}[];
|
|
41
|
+
/** The lines of the pipeline files that run a chant command, `command` naming it, such as `release publish`. */
|
|
42
|
+
export declare function commandLines(ctx: WorkspaceCheckContext, command: RegExp): {
|
|
43
|
+
path: string;
|
|
44
|
+
line: number;
|
|
45
|
+
text: string;
|
|
46
|
+
}[];
|
|
47
|
+
/** The member's `*.component.ts` files, walked a few levels down, past node_modules and build output. */
|
|
48
|
+
export declare function componentFiles(tree: WorkspaceTree, dir: string, depth?: number): string[];
|
|
34
49
|
export declare const SECURITY_CHECKS: readonly WorkspaceCheck[];
|
|
35
50
|
//# sourceMappingURL=security.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"security.d.ts","sourceRoot":"","sources":["../../../src/workspace/checks/security.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,KAAK,EAAE,cAAc,
|
|
1
|
+
{"version":3,"file":"security.d.ts","sourceRoot":"","sources":["../../../src/workspace/checks/security.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,qBAAqB,EAAuB,MAAM,WAAW,CAAC;AAI5F,OAAO,EAAY,KAAK,aAAa,EAAE,MAAM,SAAS,CAAC;AAMvD,sFAAsF;AACtF,eAAO,MAAM,sBAAsB,ygBAkBO,CAAC;AAI3C,eAAO,MAAM,8BAA8B,WAAW,CAAC;AACvD,eAAO,MAAM,yBAAyB,WAAW,CAAC;AAClD,eAAO,MAAM,uBAAuB,WAAW,CAAC;AAChD,eAAO,MAAM,yBAAyB,WAAW,CAAC;AAClD,eAAO,MAAM,6BAA6B,WAAW,CAAC;AAsBtD,wBAAgB,IAAI,CAAC,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAOrE;AAkBD,2EAA2E;AAC3E,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,qBAAqB,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,EAAE,CAAA;CAAE,EAAE,CAGhG;AAED,gHAAgH;AAChH,wBAAgB,YAAY,CAAC,GAAG,EAAE,qBAAqB,EAAE,OAAO,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,EAAE,CAExH;AAED,yGAAyG;AACzG,wBAAgB,cAAc,CAAC,IAAI,EAAE,aAAa,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,SAAI,GAAG,MAAM,EAAE,CAQpF;AAuCD,eAAO,MAAM,eAAe,EAAE,SAAS,cAAc,EAuIpD,CAAC"}
|
|
@@ -70,7 +70,7 @@ function groupPointer(declaration, group) {
|
|
|
70
70
|
const m = declaration.members.find((e) => e.security.includes(group));
|
|
71
71
|
return m ? `${m.pointer}/security/${m.security.indexOf(group)}` : "/security";
|
|
72
72
|
}
|
|
73
|
-
function read(tree, path) {
|
|
73
|
+
export function read(tree, path) {
|
|
74
74
|
if (tree.stat(path) !== "file")
|
|
75
75
|
return null;
|
|
76
76
|
try {
|
|
@@ -99,16 +99,16 @@ function pipelineFiles(tree, dir) {
|
|
|
99
99
|
return out;
|
|
100
100
|
}
|
|
101
101
|
/** Every pipeline file of the workspace: its root's, and each member's. */
|
|
102
|
-
function allPipelineFiles(ctx) {
|
|
102
|
+
export function allPipelineFiles(ctx) {
|
|
103
103
|
const dirs = new Set(["", ...ctx.declaration.members.map((m) => memberDir(m.dir))]);
|
|
104
104
|
return [...dirs].flatMap((d) => pipelineFiles(ctx.tree, d));
|
|
105
105
|
}
|
|
106
106
|
/** The lines of the pipeline files that run a chant command, `command` naming it, such as `release publish`. */
|
|
107
|
-
function commandLines(ctx, command) {
|
|
107
|
+
export function commandLines(ctx, command) {
|
|
108
108
|
return allPipelineFiles(ctx).flatMap((f) => f.lines.flatMap((text, i) => (command.test(text) ? [{ path: f.path, line: i + 1, text }] : [])));
|
|
109
109
|
}
|
|
110
110
|
/** The member's `*.component.ts` files, walked a few levels down, past node_modules and build output. */
|
|
111
|
-
function componentFiles(tree, dir, depth = 4) {
|
|
111
|
+
export function componentFiles(tree, dir, depth = 4) {
|
|
112
112
|
const out = [];
|
|
113
113
|
for (const e of tree.list(dir) ?? []) {
|
|
114
114
|
if (e.type === "dir") {
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
* | WSP126, WSP127 | box intent: the decision record a box names exists, and constrains a member or path of this workspace (#2850, #2857) |
|
|
38
38
|
* | WSP141, WSP142 | member links resolved against a live read, with `--live` (#2549) |
|
|
39
39
|
* | WSP131 to WSP133 | diagram artifacts: source and render exist, a recorded source hash still matches (#2764) |
|
|
40
|
-
* | WSP151 |
|
|
40
|
+
* | WSP151 | what a member under `ci.workflow` still lacks (#4168 step 6) |
|
|
41
41
|
* | WSP161 to WSP165 | the security groups the declaration opts into: what each still lacks (#4168 step 7) |
|
|
42
42
|
*/
|
|
43
43
|
import type { LintDiagnostic, LintRule, Severity } from "../lint/rule.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"checks.d.ts","sourceRoot":"","sources":["../../src/workspace/checks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AAIH,OAAO,KAAK,EAAE,cAAc,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AACvE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAC9D,OAAO,EAAsD,KAAK,WAAW,EAAkC,KAAK,aAAa,EAAE,MAAM,eAAe,CAAC;AACzJ,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAEjD,OAAO,EAAiE,KAAK,eAAe,EAAE,KAAK,YAAY,EAAE,MAAM,SAAS,CAAC;AACjI,OAAO,EAA6D,KAAK,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpH,OAAO,EAAuB,KAAK,aAAa,EAAE,MAAM,QAAQ,CAAC;AAEjE,OAAO,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACrD,OAAO,EAA0C,KAAK,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACrG,OAAO,EAAoC,KAAK,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAC5F,OAAO,EAAwC,KAAK,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACpG,OAAO,EAAoC,KAAK,aAAa,EAAE,MAAM,eAAe,CAAC;AACrF,OAAO,EAAiB,KAAK,WAAW,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"checks.d.ts","sourceRoot":"","sources":["../../src/workspace/checks.ts"],"names":[],"mappings":"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"}
|
package/dist/workspace/checks.js
CHANGED
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
* | WSP126, WSP127 | box intent: the decision record a box names exists, and constrains a member or path of this workspace (#2850, #2857) |
|
|
38
38
|
* | WSP141, WSP142 | member links resolved against a live read, with `--live` (#2549) |
|
|
39
39
|
* | WSP131 to WSP133 | diagram artifacts: source and render exist, a recorded source hash still matches (#2764) |
|
|
40
|
-
* | WSP151 |
|
|
40
|
+
* | WSP151 | what a member under `ci.workflow` still lacks (#4168 step 6) |
|
|
41
41
|
* | WSP161 to WSP165 | the security groups the declaration opts into: what each still lacks (#4168 step 7) |
|
|
42
42
|
*/
|
|
43
43
|
import { realpathSync } from "node:fs";
|
|
@@ -56,6 +56,7 @@ import { RECORD_CHECKS } from "./checks/records.js";
|
|
|
56
56
|
import { BOX_CHECKS } from "./checks/boxes.js";
|
|
57
57
|
import { DIAGRAM_CHECKS } from "./checks/diagrams.js";
|
|
58
58
|
import { SECURITY_CHECKS } from "./checks/security.js";
|
|
59
|
+
import { CI_WORKFLOW_CHECKS } from "./checks/ci-workflow.js";
|
|
59
60
|
import { loadDeclaredKinds } from "./declared-kinds.js";
|
|
60
61
|
import { resolveBoxIntents } from "./box-intent.js";
|
|
61
62
|
import { gatherLexiconOutputs } from "./lexicon-outputs.js";
|
|
@@ -273,6 +274,8 @@ export const WORKSPACE_CHECKS = [
|
|
|
273
274
|
...DIAGRAM_CHECKS,
|
|
274
275
|
// Links resolved against a live read, with --live (#2549), WSP141 and WSP142.
|
|
275
276
|
...LIVE_CHECKS,
|
|
277
|
+
// What a member under ci.workflow still lacks (#4168 step 6), WSP151.
|
|
278
|
+
...CI_WORKFLOW_CHECKS,
|
|
276
279
|
// The security groups the declaration opts into (#4168 step 7), WSP161 to WSP165.
|
|
277
280
|
...SECURITY_CHECKS,
|
|
278
281
|
];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ci-green-cli.d.ts","sourceRoot":"","sources":["../../src/workspace/ci-green-cli.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAKH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEtD,OAAO,EAAmE,cAAc,EAAE,MAAM,YAAY,CAAC;AAW7G,OAAO,EAAE,cAAc,EAAE,CAAC;AAe1B,wBAAsB,cAAc,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAiBzE;AAED,wBAAsB,SAAS,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAsBpE;AAID,wBAAsB,SAAS,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAoBpE;AAeD,wBAAsB,aAAa,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,
|
|
1
|
+
{"version":3,"file":"ci-green-cli.d.ts","sourceRoot":"","sources":["../../src/workspace/ci-green-cli.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAKH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEtD,OAAO,EAAmE,cAAc,EAAE,MAAM,YAAY,CAAC;AAW7G,OAAO,EAAE,cAAc,EAAE,CAAC;AAe1B,wBAAsB,cAAc,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAiBzE;AAED,wBAAsB,SAAS,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAsBpE;AAID,wBAAsB,SAAS,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAoBpE;AAeD,wBAAsB,aAAa,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAiExE"}
|
|
@@ -153,7 +153,9 @@ export async function runCiWorkflow(ctx) {
|
|
|
153
153
|
const sources = readWorkflowSources(repoRoot, fromRepo);
|
|
154
154
|
const scan = workflowsHolding(green, sources);
|
|
155
155
|
// #4169: the triggers and concurrency of the workflows holding a phase that sets build. Refused before anything is written.
|
|
156
|
-
|
|
156
|
+
// #4220: under a ci.workflow, composed releases' branches run the phases and tick too.
|
|
157
|
+
const compose = declaration.ci?.workflow !== undefined || declaration.members.some((m) => m.ciWorkflow !== null);
|
|
158
|
+
const edits = buildTriggerEdits(green, sources, { compose });
|
|
157
159
|
const extra = ctx.args.ciWorkflows ?? [];
|
|
158
160
|
const workflows = [...new Set([...scan.workflows, ...extra])].sort();
|
|
159
161
|
for (const f of scan.unreadable)
|
|
@@ -186,11 +188,11 @@ export async function runCiWorkflow(ctx) {
|
|
|
186
188
|
const workspaceRoot = relative(repoRoot, root).split(sep).join("/") || ".";
|
|
187
189
|
// The install runs in the workspace root, so its lockfile there keys setup-node's npm cache.
|
|
188
190
|
const npmLock = ctx.args.ciInstall && existsSync(join(root, "package-lock.json")) ? (workspaceRoot === "." ? "package-lock.json" : `${workspaceRoot}/package-lock.json`) : undefined;
|
|
189
|
-
const yaml = renderCiGreenWorkflow({ green, workflows, chantVersion: readerVersion(), workspaceRoot, chant: ctx.args.ciChant, install: ctx.args.ciInstall, npmLock, push, ...(runnerKeySecret ? { runnerKeySecret } : {}) });
|
|
191
|
+
const yaml = renderCiGreenWorkflow({ green, workflows, chantVersion: readerVersion(), workspaceRoot, chant: ctx.args.ciChant, install: ctx.args.ciInstall, npmLock, push, ...(runnerKeySecret ? { runnerKeySecret } : {}), ...(compose ? { compose } : {}) });
|
|
190
192
|
const written = writeWorkflowFile(target, ciGreenHeader(command, member ? "member" : "root") + yaml);
|
|
191
193
|
if (member)
|
|
192
194
|
recordGeneratedFiles(member.memberRoot, [{ path: fromRepo, command }]);
|
|
193
|
-
console.log(`Wrote ${written}: ticks when ${workflows.join(", ")} complete${workflows.length === 1 ? "s" : ""} on ${green.branch}, and every 15 minutes.`);
|
|
195
|
+
console.log(`Wrote ${written}: ticks when ${workflows.join(", ")} complete${workflows.length === 1 ? "s" : ""} on ${green.branch}${compose ? " or a chant/compose/ branch" : ""}, and every 15 minutes.`);
|
|
194
196
|
for (const e of edits) {
|
|
195
197
|
if (!e.changed)
|
|
196
198
|
continue;
|
|
@@ -106,7 +106,15 @@ export interface WorkflowInput {
|
|
|
106
106
|
* The key's principal needs the `runner` role in `.chant/trust.json`.
|
|
107
107
|
*/
|
|
108
108
|
runnerKeySecret?: string;
|
|
109
|
+
/**
|
|
110
|
+
* The workspace declares a `ci.workflow` (#4220), so composed releases
|
|
111
|
+
* live on `chant/compose/<name>` branches (#4168 4.3): the tick also runs
|
|
112
|
+
* when a phase workflow completes on one of them.
|
|
113
|
+
*/
|
|
114
|
+
compose?: boolean;
|
|
109
115
|
}
|
|
116
|
+
/** The branch filter for composed releases' branches (#4220). */
|
|
117
|
+
export declare const COMPOSE_BRANCHES = "chant/compose/**";
|
|
110
118
|
/**
|
|
111
119
|
* A token the checkout persists for the tick's `git push`: a repository
|
|
112
120
|
* secret, or a GitHub App's installation token minted from its id (an
|
|
@@ -153,12 +161,20 @@ export interface BuildTriggerEdit {
|
|
|
153
161
|
* branch, so no push cancels or replaces another's run; elsewhere, such as
|
|
154
162
|
* a pull request, a new push cancels the run it supersedes.
|
|
155
163
|
*
|
|
156
|
-
*
|
|
164
|
+
* With `compose` (#4220, a workspace declaring `ci.workflow`), both builds
|
|
165
|
+
* also run on pushes to `chant/compose/**`: `newest` adds the filter to the
|
|
166
|
+
* branches it writes, `every` adds it to an existing `push.branches` list,
|
|
167
|
+
* and `every`'s group keys a compose branch's runs by commit, as the trunk
|
|
168
|
+
* branch's.
|
|
169
|
+
*
|
|
170
|
+
* Only the top-level `on:` (for `newest`, or `every` with `compose`) and `concurrency:` blocks change,
|
|
157
171
|
* each under {@link BUILD_BLOCK_MARKER}; the rest of the file is kept as it
|
|
158
172
|
* is. Throws when one workflow holds a phase of each build, which no one
|
|
159
173
|
* concurrency group can serve.
|
|
160
174
|
*/
|
|
161
|
-
export declare function buildTriggerEdits(green: CiGreen, sources: readonly WorkflowSource[]
|
|
175
|
+
export declare function buildTriggerEdits(green: CiGreen, sources: readonly WorkflowSource[], options?: {
|
|
176
|
+
compose?: boolean;
|
|
177
|
+
}): BuildTriggerEdit[];
|
|
162
178
|
/**
|
|
163
179
|
* Replace the top-level `key:` block of a YAML file with `value` (or with
|
|
164
180
|
* `value`'s lines, the key's own line first), under
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ci-green-workflow.d.ts","sourceRoot":"","sources":["../../src/workspace/ci-green-workflow.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AAMH,OAAO,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"ci-green-workflow.d.ts","sourceRoot":"","sources":["../../src/workspace/ci-green-workflow.ts"],"names":[],"mappings":"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"}
|
|
@@ -50,6 +50,7 @@ import { GENERATED_MARKER } from "../discovery/files.js";
|
|
|
50
50
|
import { emitYAMLEntry, parseYAMLDocument } from "../yaml.js";
|
|
51
51
|
import { matchesCheckRun } from "./ci-green.js";
|
|
52
52
|
import { githubSecretOrJobToken } from "../ci-jobs.js";
|
|
53
|
+
import { COMPOSE_BRANCH_PREFIX } from "../lifecycle/compose-refs.js";
|
|
53
54
|
/** Where the workflow goes, relative to the repository root. */
|
|
54
55
|
export const CI_GREEN_WORKFLOW_FILE = ".github/workflows/chant-ci-green.yml";
|
|
55
56
|
export const CI_GREEN_WORKFLOW_NAME = "chant-ci-green";
|
|
@@ -149,6 +150,8 @@ export function readWorkflowSources(repoRoot, skip) {
|
|
|
149
150
|
.map((path) => ({ path, text: readFileSync(join(repoRoot, path), "utf-8") }));
|
|
150
151
|
}
|
|
151
152
|
const q = (s) => JSON.stringify(s);
|
|
153
|
+
/** The branch filter for composed releases' branches (#4220). */
|
|
154
|
+
export const COMPOSE_BRANCHES = `${COMPOSE_BRANCH_PREFIX}**`;
|
|
152
155
|
/** A secret or variable name GitHub accepts: letters, digits and underscores, not starting with a digit. */
|
|
153
156
|
export const ACTIONS_NAME = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
154
157
|
/** The workflow's YAML, without the generated-file header. Deterministic: the same input writes the same bytes. */
|
|
@@ -184,6 +187,8 @@ export function renderCiGreenWorkflow(input) {
|
|
|
184
187
|
` - ${q(green.branch)}`,
|
|
185
188
|
// #4169: a build the tick asked for runs at a ci/build/<sha> tag, and its completion ticks too.
|
|
186
189
|
...(newestPhases(green) ? [` - ${q("ci/build/**")}`] : []),
|
|
190
|
+
// #4220: a composed commit's phases run on its chant/compose/<name> branch, and their completion ticks too.
|
|
191
|
+
...(input.compose ? [` - ${q(COMPOSE_BRANCHES)}`] : []),
|
|
187
192
|
" schedule:",
|
|
188
193
|
" - cron: \"*/15 * * * *\"",
|
|
189
194
|
" workflow_dispatch: {}",
|
|
@@ -248,12 +253,19 @@ export const BUILD_BLOCK_MARKER = "# Written by chant ci workflow from ci.green'
|
|
|
248
253
|
* branch, so no push cancels or replaces another's run; elsewhere, such as
|
|
249
254
|
* a pull request, a new push cancels the run it supersedes.
|
|
250
255
|
*
|
|
251
|
-
*
|
|
256
|
+
* With `compose` (#4220, a workspace declaring `ci.workflow`), both builds
|
|
257
|
+
* also run on pushes to `chant/compose/**`: `newest` adds the filter to the
|
|
258
|
+
* branches it writes, `every` adds it to an existing `push.branches` list,
|
|
259
|
+
* and `every`'s group keys a compose branch's runs by commit, as the trunk
|
|
260
|
+
* branch's.
|
|
261
|
+
*
|
|
262
|
+
* Only the top-level `on:` (for `newest`, or `every` with `compose`) and `concurrency:` blocks change,
|
|
252
263
|
* each under {@link BUILD_BLOCK_MARKER}; the rest of the file is kept as it
|
|
253
264
|
* is. Throws when one workflow holds a phase of each build, which no one
|
|
254
265
|
* concurrency group can serve.
|
|
255
266
|
*/
|
|
256
|
-
export function buildTriggerEdits(green, sources) {
|
|
267
|
+
export function buildTriggerEdits(green, sources, options = {}) {
|
|
268
|
+
const compose = options.compose === true;
|
|
257
269
|
const out = [];
|
|
258
270
|
for (const s of sources) {
|
|
259
271
|
let doc;
|
|
@@ -280,18 +292,28 @@ export function buildTriggerEdits(green, sources) {
|
|
|
280
292
|
delete on.schedule;
|
|
281
293
|
const push = isObject(on.push) ? { ...on.push } : {};
|
|
282
294
|
const branches = Array.isArray(push.branches) ? push.branches.filter((b) => typeof b === "string") : [];
|
|
283
|
-
|
|
295
|
+
const withBranch = branches.includes(green.branch) ? branches : [...branches, green.branch];
|
|
296
|
+
push.branches = compose && !withBranch.includes(COMPOSE_BRANCHES) ? [...withBranch, COMPOSE_BRANCHES] : withBranch;
|
|
284
297
|
const { push: _push, workflow_dispatch: dispatch, ...rest } = on;
|
|
285
298
|
text = writeTopLevel(text, "on", { push, workflow_dispatch: dispatch ?? {}, ...rest });
|
|
286
299
|
text = writeTopLevel(text, "concurrency", { group: `chant-build-${tag}-\${{ github.ref }}`, "cancel-in-progress": false });
|
|
287
300
|
}
|
|
288
301
|
else {
|
|
289
302
|
const main = `refs/heads/${green.branch}`;
|
|
303
|
+
// A push filter that lists branches gains the compose branches; one with no list already runs on every branch.
|
|
304
|
+
const on = isObject(doc) && isObject(doc.on) ? doc.on : undefined;
|
|
305
|
+
const pushOn = on && isObject(on.push) ? on.push : undefined;
|
|
306
|
+
if (compose && on && pushOn && Array.isArray(pushOn.branches) && !pushOn.branches.includes(COMPOSE_BRANCHES)) {
|
|
307
|
+
text = writeTopLevel(text, "on", { ...on, push: { ...pushOn, branches: [...pushOn.branches, COMPOSE_BRANCHES] } });
|
|
308
|
+
}
|
|
309
|
+
// #4220: a compose branch's commits each get a verdict, so its runs are keyed by commit, as the trunk branch's are.
|
|
310
|
+
const keyed = compose ? `(github.ref == '${main}' || startsWith(github.ref, 'refs/heads/${COMPOSE_BRANCH_PREFIX}'))` : `github.ref == '${main}'`;
|
|
311
|
+
const unkeyed = compose ? `!${keyed}` : `github.ref != '${main}'`;
|
|
290
312
|
// Written as lines: the emitter would quote the expression, and GitHub reads it the same either way.
|
|
291
313
|
text = writeTopLevel(text, "concurrency", [
|
|
292
314
|
"concurrency:",
|
|
293
|
-
` group: chant-build-${tag}-\${{
|
|
294
|
-
` cancel-in-progress: \${{
|
|
315
|
+
` group: chant-build-${tag}-\${{ ${keyed} && github.sha || github.ref }}`,
|
|
316
|
+
` cancel-in-progress: \${{ ${unkeyed} }}`,
|
|
295
317
|
]);
|
|
296
318
|
}
|
|
297
319
|
out.push({ path: s.path, build, phases, text, changed: text !== s.text });
|
|
@@ -248,6 +248,16 @@ export declare function readCiTags(top: string, green?: CiGreen | null, base?: s
|
|
|
248
248
|
* and `ci run` and `chant run` warn about a run they leave unsigned.
|
|
249
249
|
*/
|
|
250
250
|
export declare function signedRunsRequired(cwd: string): string | null;
|
|
251
|
+
/** The declaration's `ci.green`, read where `cwd` is, and the git top. */
|
|
252
|
+
export declare function readGreen(cwd: string): {
|
|
253
|
+
green: CiGreen;
|
|
254
|
+
top: string;
|
|
255
|
+
workspace: {
|
|
256
|
+
name: string;
|
|
257
|
+
root: string;
|
|
258
|
+
file: string;
|
|
259
|
+
};
|
|
260
|
+
};
|
|
251
261
|
export interface TickRequest {
|
|
252
262
|
cwd: string;
|
|
253
263
|
/**
|
|
@@ -271,12 +281,20 @@ export interface TickRequest {
|
|
|
271
281
|
*/
|
|
272
282
|
runnerKey?: string;
|
|
273
283
|
}
|
|
284
|
+
/** One compose branch a tick walked (#4168 4.3): its composed commits within the window. */
|
|
285
|
+
export interface ComposedBranchTick {
|
|
286
|
+
branch: string;
|
|
287
|
+
ref: string;
|
|
288
|
+
commits: TickCommit[];
|
|
289
|
+
}
|
|
274
290
|
export interface TickResult {
|
|
275
291
|
branch: string;
|
|
276
292
|
window: string;
|
|
277
293
|
/** The ref the commits were read from. */
|
|
278
294
|
ref: string;
|
|
279
295
|
commits: TickCommit[];
|
|
296
|
+
/** The compose branches walked (#4168 4.3), each with its composed commits judged. Their tags are in `made`. */
|
|
297
|
+
composed: ComposedBranchTick[];
|
|
280
298
|
/** The tags made and pushed, or that would be under `dryRun`. */
|
|
281
299
|
made: string[];
|
|
282
300
|
/** The builds asked for (#4169), as `ci/build/<sha>` tags made and pushed, or that would be under `dryRun`. */
|
|
@@ -355,6 +373,19 @@ export declare function opRunCheckRun(record: OpRunRecord, signer: string | null
|
|
|
355
373
|
* elsewhere. Reads local refs only.
|
|
356
374
|
*/
|
|
357
375
|
export declare function readOpRunSource(cwd: string, top: string, base: string, signedSince?: string | null): Promise<OpRunSource>;
|
|
376
|
+
/**
|
|
377
|
+
* Judge `ci.green.preMerge` on `commit` from the Op run records on the ledger
|
|
378
|
+
* (#4168 4.4), as `ci serve --land` does before it moves the target. The
|
|
379
|
+
* trust policy is read at `base`, the target's head. An unsigned run counts
|
|
380
|
+
* as the tick counts one: always, unless `ci.green.signedSince` asks for a
|
|
381
|
+
* signature. Throws {@link CiGreenError} when the declaration has no
|
|
382
|
+
* `ci.green` or names no pre-merge phase.
|
|
383
|
+
*/
|
|
384
|
+
export declare function judgePreMerge(req: {
|
|
385
|
+
cwd: string;
|
|
386
|
+
commit: string;
|
|
387
|
+
base: string;
|
|
388
|
+
}): Promise<CommitVerdict>;
|
|
358
389
|
/**
|
|
359
390
|
* What `chant run <op> --key` signs a run's record with (#4044): the runner
|
|
360
391
|
* key `.chant/trust.json` at the checkout's `HEAD` lists for a principal
|