@intentius/chant 0.124.32 → 0.124.33
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/ci-jobs.d.ts +4 -0
- package/dist/ci-jobs.d.ts.map +1 -1
- package/dist/ci-jobs.js +9 -2
- package/dist/cli/handlers/build.d.ts.map +1 -1
- package/dist/cli/handlers/build.js +18 -3
- package/dist/cli/handlers/lifecycle-health.d.ts +16 -0
- package/dist/cli/handlers/lifecycle-health.d.ts.map +1 -0
- package/dist/cli/handlers/lifecycle-health.js +150 -0
- package/dist/cli/handlers/lifecycle.d.ts +26 -0
- package/dist/cli/handlers/lifecycle.d.ts.map +1 -1
- package/dist/cli/handlers/lifecycle.js +1 -1
- package/dist/cli/handlers/pr.d.ts +2 -0
- package/dist/cli/handlers/pr.d.ts.map +1 -1
- package/dist/cli/handlers/pr.js +124 -2
- package/dist/cli/handlers/promote.d.ts.map +1 -1
- package/dist/cli/handlers/promote.js +16 -1
- package/dist/cli/handlers/release.d.ts +16 -1
- package/dist/cli/handlers/release.d.ts.map +1 -1
- package/dist/cli/handlers/release.js +83 -11
- package/dist/cli/main.d.ts.map +1 -1
- package/dist/cli/main.js +81 -21
- package/dist/cli/parse-args.d.ts.map +1 -1
- package/dist/cli/parse-args.js +23 -1
- package/dist/cli/registry.d.ts +18 -4
- package/dist/cli/registry.d.ts.map +1 -1
- package/dist/components/deploy-inputs.d.ts +91 -0
- package/dist/components/deploy-inputs.d.ts.map +1 -0
- package/dist/components/deploy-inputs.js +184 -0
- package/dist/components/driver.d.ts.map +1 -1
- package/dist/components/driver.js +9 -0
- package/dist/components/gate-plan.d.ts +8 -0
- package/dist/components/gate-plan.d.ts.map +1 -1
- package/dist/components/gate-plan.js +6 -0
- package/dist/components/pr-pipeline.d.ts +33 -10
- package/dist/components/pr-pipeline.d.ts.map +1 -1
- package/dist/components/pr-pipeline.js +42 -11
- package/dist/components/pr-run.d.ts +1 -1
- package/dist/components/pr-run.d.ts.map +1 -1
- package/dist/components/release-compile.d.ts +37 -2
- package/dist/components/release-compile.d.ts.map +1 -1
- package/dist/components/release-compile.js +64 -8
- package/dist/components/release-promote.d.ts +17 -3
- package/dist/components/release-promote.d.ts.map +1 -1
- package/dist/components/release-promote.js +79 -18
- package/dist/components/verbs/bom-writer.d.ts +1 -1
- package/dist/components/verbs/bom-writer.d.ts.map +1 -1
- package/dist/components/verbs/lockfile-sbom-generator.d.ts +20 -0
- package/dist/components/verbs/lockfile-sbom-generator.d.ts.map +1 -1
- package/dist/components/verbs/lockfile-sbom-generator.js +41 -4
- package/dist/components/wave-plan.d.ts +10 -0
- package/dist/components/wave-plan.d.ts.map +1 -1
- package/dist/components/wave-plan.js +32 -4
- package/dist/graph-detail.d.ts.map +1 -1
- package/dist/graph-detail.js +32 -7
- package/dist/health.d.ts +110 -0
- package/dist/health.d.ts.map +1 -0
- package/dist/health.js +105 -0
- package/dist/health.schema.json +191 -0
- package/dist/lexicon.d.ts +27 -0
- package/dist/lexicon.d.ts.map +1 -1
- package/dist/lifecycle/merge-record.d.ts +104 -0
- package/dist/lifecycle/merge-record.d.ts.map +1 -0
- package/dist/lifecycle/merge-record.js +102 -0
- package/dist/lifecycle/publish-attachments.d.ts +8 -5
- package/dist/lifecycle/publish-attachments.d.ts.map +1 -1
- package/dist/lifecycle/publish-attachments.js +27 -13
- package/dist/lifecycle/publish-npm.d.ts +3 -2
- package/dist/lifecycle/publish-npm.d.ts.map +1 -1
- package/dist/lifecycle/publish-npm.js +4 -3
- package/dist/lifecycle/release-at.d.ts +40 -0
- package/dist/lifecycle/release-at.d.ts.map +1 -0
- package/dist/lifecycle/release-at.js +83 -0
- package/dist/lifecycle/release-cut.d.ts.map +1 -1
- package/dist/lifecycle/release-cut.js +23 -12
- package/dist/lifecycle/release-publish.d.ts +28 -12
- package/dist/lifecycle/release-publish.d.ts.map +1 -1
- package/dist/lifecycle/release-publish.js +29 -17
- package/dist/lifecycle/release-store.d.ts +25 -2
- package/dist/lifecycle/release-store.d.ts.map +1 -1
- package/dist/lifecycle/release-store.js +40 -13
- package/dist/lifecycle/run-ledger.d.ts.map +1 -1
- package/dist/lifecycle/run-ledger.js +1 -0
- package/dist/lint/rules/op/index.d.ts +2 -1
- package/dist/lint/rules/op/index.d.ts.map +1 -1
- package/dist/lint/rules/op/index.js +4 -2
- package/dist/lint/rules/op/ops018-shell-image-digest.d.ts +10 -0
- package/dist/lint/rules/op/ops018-shell-image-digest.d.ts.map +1 -0
- package/dist/lint/rules/op/ops018-shell-image-digest.js +38 -0
- package/dist/op/activities/activity-contracts.d.ts +5 -0
- package/dist/op/activities/activity-contracts.d.ts.map +1 -1
- package/dist/op/activities/activity-contracts.js +3 -0
- package/dist/op/activities/shell.d.ts +38 -0
- package/dist/op/activities/shell.d.ts.map +1 -1
- package/dist/op/activities/shell.js +89 -3
- package/dist/op/builders.d.ts +7 -0
- package/dist/op/builders.d.ts.map +1 -1
- package/dist/op/builders.js +7 -0
- package/dist/op/runtime.d.ts +2 -0
- package/dist/op/runtime.d.ts.map +1 -1
- package/dist/pr-loop.d.ts +14 -3
- package/dist/pr-loop.d.ts.map +1 -1
- package/dist/pr-loop.js +15 -5
- package/dist/pr-report-url.d.ts +2 -2
- package/dist/pr-report-url.d.ts.map +1 -1
- package/dist/runner-start.d.ts +179 -0
- package/dist/runner-start.d.ts.map +1 -0
- package/dist/runner-start.js +277 -0
- package/dist/wave-runtime.mjs +235 -228
- package/dist/workspace/check.schema.json +70 -2
- package/dist/workspace/checks/security.d.ts +35 -0
- package/dist/workspace/checks/security.d.ts.map +1 -0
- package/dist/workspace/checks/security.js +305 -0
- package/dist/workspace/checks.d.ts +2 -0
- package/dist/workspace/checks.d.ts.map +1 -1
- package/dist/workspace/checks.js +5 -0
- package/dist/workspace/ci-builds.d.ts +163 -0
- package/dist/workspace/ci-builds.d.ts.map +1 -0
- package/dist/workspace/ci-builds.js +315 -0
- package/dist/workspace/ci-green-cli.d.ts +2 -1
- package/dist/workspace/ci-green-cli.d.ts.map +1 -1
- package/dist/workspace/ci-green-cli.js +25 -5
- package/dist/workspace/ci-green-forge.d.ts +12 -0
- package/dist/workspace/ci-green-forge.d.ts.map +1 -1
- package/dist/workspace/ci-green-forge.js +12 -0
- package/dist/workspace/ci-green-workflow.d.ts +55 -1
- package/dist/workspace/ci-green-workflow.d.ts.map +1 -1
- package/dist/workspace/ci-green-workflow.js +128 -1
- package/dist/workspace/ci-green.d.ts +89 -4
- package/dist/workspace/ci-green.d.ts.map +1 -1
- package/dist/workspace/ci-green.js +310 -23
- package/dist/workspace/ci-last-green.schema.json +49 -0
- package/dist/workspace/ci-list.schema.json +35 -1
- package/dist/workspace/ci-run-index.d.ts +1 -1
- package/dist/workspace/ci-run-index.d.ts.map +1 -1
- package/dist/workspace/ci-run-index.schema.json +1 -1
- package/dist/workspace/ci-run.d.ts +12 -6
- package/dist/workspace/ci-run.d.ts.map +1 -1
- package/dist/workspace/ci-run.js +72 -16
- package/dist/workspace/ci-runner-start.d.ts +77 -0
- package/dist/workspace/ci-runner-start.d.ts.map +1 -0
- package/dist/workspace/ci-runner-start.js +185 -0
- package/dist/workspace/ci-serve.d.ts +76 -7
- package/dist/workspace/ci-serve.d.ts.map +1 -1
- package/dist/workspace/ci-serve.js +153 -19
- package/dist/workspace/declaration.d.ts +17 -0
- package/dist/workspace/declaration.d.ts.map +1 -1
- package/dist/workspace/declaration.js +3 -0
- package/dist/workspace/declaration.schema.json +23 -0
- package/dist/workspace/ls.d.ts +2 -0
- package/dist/workspace/ls.d.ts.map +1 -1
- package/dist/workspace/ls.js +1 -1
- package/dist/workspace/ls.schema.json +2 -1
- package/dist/workspace/member-pipeline.d.ts +1 -0
- package/dist/workspace/member-pipeline.d.ts.map +1 -1
- package/dist/workspace/member-pipeline.js +2 -0
- package/dist/workspace/merge-record.schema.json +49 -0
- package/dist/workspace/pr-report.schema.json +3 -3
- package/dist/workspace/reason-codes.d.ts +17 -0
- package/dist/workspace/reason-codes.d.ts.map +1 -1
- package/dist/workspace/reason-codes.js +18 -0
- package/dist/workspace/release-candidates.d.ts +129 -0
- package/dist/workspace/release-candidates.d.ts.map +1 -0
- package/dist/workspace/release-candidates.js +233 -0
- package/dist/workspace/release-candidates.schema.json +353 -0
- package/dist/workspace/security.d.ts +40 -0
- package/dist/workspace/security.d.ts.map +1 -0
- package/dist/workspace/security.js +99 -0
- package/package.json +8 -1
- package/src/__fixtures__/pr-loop/pr-plan.note.golden.md +1 -1
- package/src/ci-jobs.ts +11 -2
- package/src/cli/handlers/build.test.ts +36 -0
- package/src/cli/handlers/build.ts +18 -3
- package/src/cli/handlers/lifecycle-health.test.ts +123 -0
- package/src/cli/handlers/lifecycle-health.ts +157 -0
- package/src/cli/handlers/lifecycle.ts +1 -1
- package/src/cli/handlers/pr-deploy-inputs.test.ts +209 -0
- package/src/cli/handlers/pr-record.test.ts +201 -0
- package/src/cli/handlers/pr.test.ts +22 -0
- package/src/cli/handlers/pr.ts +127 -4
- package/src/cli/handlers/promote.test.ts +17 -0
- package/src/cli/handlers/promote.ts +16 -1
- package/src/cli/handlers/release-cut-at.e2e.test.ts +115 -0
- package/src/cli/handlers/release-publish-targets.e2e.test.ts +2 -2
- package/src/cli/handlers/release-verify.e2e.test.ts +12 -10
- package/src/cli/handlers/release.e2e.test.ts +19 -28
- package/src/cli/handlers/release.ts +81 -11
- package/src/cli/main.ts +81 -21
- package/src/cli/parse-args.ts +17 -1
- package/src/cli/registry.ts +18 -4
- package/src/components/deploy-inputs.test.ts +108 -0
- package/src/components/deploy-inputs.ts +225 -0
- package/src/components/driver.ts +11 -0
- package/src/components/gate-plan.ts +9 -0
- package/src/components/pr-pipeline.ts +56 -15
- package/src/components/pr-run.ts +1 -1
- package/src/components/release-compile.test.ts +34 -0
- package/src/components/release-compile.ts +89 -8
- package/src/components/release-promote.test.ts +55 -2
- package/src/components/release-promote.ts +93 -23
- package/src/components/verbs/bom-writer.ts +1 -1
- package/src/components/verbs/lockfile-sbom-generator.test.ts +36 -0
- package/src/components/verbs/lockfile-sbom-generator.ts +43 -5
- package/src/components/wave-plan.ts +32 -4
- package/src/graph-detail.test.ts +57 -0
- package/src/graph-detail.ts +33 -7
- package/src/health.schema.json +191 -0
- package/src/health.test.ts +62 -0
- package/src/health.ts +178 -0
- package/src/lexicon.ts +41 -1
- package/src/lifecycle/merge-record.test.ts +111 -0
- package/src/lifecycle/merge-record.ts +154 -0
- package/src/lifecycle/publish-attachments.ts +25 -13
- package/src/lifecycle/publish-npm.ts +4 -3
- package/src/lifecycle/publish-targets.test.ts +34 -4
- package/src/lifecycle/release-at.test.ts +48 -0
- package/src/lifecycle/release-at.ts +100 -0
- package/src/lifecycle/release-cut.test.ts +41 -0
- package/src/lifecycle/release-cut.ts +22 -11
- package/src/lifecycle/release-publish.test.ts +43 -13
- package/src/lifecycle/release-publish.ts +38 -18
- package/src/lifecycle/release-store.ts +57 -13
- package/src/lifecycle/run-ledger.test.ts +13 -0
- package/src/lifecycle/run-ledger.ts +1 -0
- package/src/lint/rules/op/index.ts +4 -2
- package/src/lint/rules/op/ops018-shell-image-digest.test.ts +60 -0
- package/src/lint/rules/op/ops018-shell-image-digest.ts +48 -0
- package/src/op/activities/activity-contracts.ts +3 -0
- package/src/op/activities/shell-container.e2e.test.ts +67 -0
- package/src/op/activities/shell.test.ts +44 -1
- package/src/op/activities/shell.ts +110 -3
- package/src/op/builders.ts +7 -0
- package/src/op/runtime.ts +2 -0
- package/src/op/step-console.test.ts +4 -2
- package/src/pr-loop.test.ts +29 -0
- package/src/pr-loop.ts +26 -7
- package/src/pr-report-url.ts +2 -2
- package/src/runner-start-bundle.test.ts +29 -0
- package/src/runner-start.test.ts +179 -0
- package/src/runner-start.ts +329 -0
- package/src/workspace/check-contract.test.ts +3 -2
- package/src/workspace/check.schema.json +70 -2
- package/src/workspace/checks/security.test.ts +138 -0
- package/src/workspace/checks/security.ts +305 -0
- package/src/workspace/checks.ts +5 -0
- package/src/workspace/ci-builds.e2e.test.ts +208 -0
- package/src/workspace/ci-builds.test.ts +174 -0
- package/src/workspace/ci-builds.ts +385 -0
- package/src/workspace/ci-green-builds.test.ts +180 -0
- package/src/workspace/ci-green-cli.test.ts +54 -0
- package/src/workspace/ci-green-cli.ts +26 -5
- package/src/workspace/ci-green-forge.ts +24 -0
- package/src/workspace/ci-green-workflow.test.ts +84 -1
- package/src/workspace/ci-green-workflow.ts +139 -2
- package/src/workspace/ci-green.test.ts +5 -2
- package/src/workspace/ci-green.ts +359 -27
- package/src/workspace/ci-last-green.schema.json +49 -0
- package/src/workspace/ci-list.schema.json +35 -1
- package/src/workspace/ci-run-index.schema.json +1 -1
- package/src/workspace/ci-run-index.ts +1 -1
- package/src/workspace/ci-run.test.ts +24 -7
- package/src/workspace/ci-run.ts +78 -16
- package/src/workspace/ci-runner-start.test.ts +151 -0
- package/src/workspace/ci-runner-start.ts +193 -0
- package/src/workspace/ci-serve-builds.test.ts +86 -0
- package/src/workspace/ci-serve.test.ts +59 -0
- package/src/workspace/ci-serve.ts +177 -22
- package/src/workspace/declaration.schema.json +23 -0
- package/src/workspace/declaration.test.ts +17 -0
- package/src/workspace/declaration.ts +22 -1
- package/src/workspace/ls.schema.json +2 -1
- package/src/workspace/ls.ts +3 -2
- package/src/workspace/member-pipeline.ts +2 -1
- package/src/workspace/merge-record.schema.json +49 -0
- package/src/workspace/pr-report.schema.json +3 -3
- package/src/workspace/reason-codes.test.ts +2 -0
- package/src/workspace/reason-codes.ts +18 -0
- package/src/workspace/release-candidates.schema.json +353 -0
- package/src/workspace/release-candidates.test.ts +106 -0
- package/src/workspace/release-candidates.ts +315 -0
- package/src/workspace/security.test.ts +70 -0
- package/src/workspace/security.ts +118 -0
|
@@ -298,7 +298,41 @@
|
|
|
298
298
|
"flag-stale",
|
|
299
299
|
"diagram-source-missing",
|
|
300
300
|
"diagram-render-missing",
|
|
301
|
-
"diagram-render-drift"
|
|
301
|
+
"diagram-render-drift",
|
|
302
|
+
|
|
303
|
+
"security-signers-missing",
|
|
304
|
+
|
|
305
|
+
"security-release-gate-missing",
|
|
306
|
+
|
|
307
|
+
"security-publish-allows-unsigned",
|
|
308
|
+
|
|
309
|
+
"security-sbom-disabled",
|
|
310
|
+
|
|
311
|
+
"security-sbom-unsourced",
|
|
312
|
+
|
|
313
|
+
"security-publish-no-provenance",
|
|
314
|
+
|
|
315
|
+
"security-component-no-sbom",
|
|
316
|
+
|
|
317
|
+
"security-component-no-verify",
|
|
318
|
+
|
|
319
|
+
"security-signed-since-unset",
|
|
320
|
+
|
|
321
|
+
"security-runner-untrusted",
|
|
322
|
+
|
|
323
|
+
"security-tick-no-key",
|
|
324
|
+
|
|
325
|
+
"security-review-not-required",
|
|
326
|
+
|
|
327
|
+
"security-attribution-any",
|
|
328
|
+
|
|
329
|
+
"security-gate-unsigned",
|
|
330
|
+
|
|
331
|
+
"security-runner-start-no-key",
|
|
332
|
+
|
|
333
|
+
"security-runner-start-no-role",
|
|
334
|
+
|
|
335
|
+
"security-trust-unreadable"
|
|
302
336
|
],
|
|
303
337
|
"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)."
|
|
304
338
|
}
|
|
@@ -377,7 +411,41 @@
|
|
|
377
411
|
"flag-stale",
|
|
378
412
|
"diagram-source-missing",
|
|
379
413
|
"diagram-render-missing",
|
|
380
|
-
"diagram-render-drift"
|
|
414
|
+
"diagram-render-drift",
|
|
415
|
+
|
|
416
|
+
"security-signers-missing",
|
|
417
|
+
|
|
418
|
+
"security-release-gate-missing",
|
|
419
|
+
|
|
420
|
+
"security-publish-allows-unsigned",
|
|
421
|
+
|
|
422
|
+
"security-sbom-disabled",
|
|
423
|
+
|
|
424
|
+
"security-sbom-unsourced",
|
|
425
|
+
|
|
426
|
+
"security-publish-no-provenance",
|
|
427
|
+
|
|
428
|
+
"security-component-no-sbom",
|
|
429
|
+
|
|
430
|
+
"security-component-no-verify",
|
|
431
|
+
|
|
432
|
+
"security-signed-since-unset",
|
|
433
|
+
|
|
434
|
+
"security-runner-untrusted",
|
|
435
|
+
|
|
436
|
+
"security-tick-no-key",
|
|
437
|
+
|
|
438
|
+
"security-review-not-required",
|
|
439
|
+
|
|
440
|
+
"security-attribution-any",
|
|
441
|
+
|
|
442
|
+
"security-gate-unsigned",
|
|
443
|
+
|
|
444
|
+
"security-runner-start-no-key",
|
|
445
|
+
|
|
446
|
+
"security-runner-start-no-role",
|
|
447
|
+
|
|
448
|
+
"security-trust-unreadable"
|
|
381
449
|
],
|
|
382
450
|
"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)."
|
|
383
451
|
},
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The security group checklists, WSP161 to WSP165 (#4168 step 7).
|
|
3
|
+
*
|
|
4
|
+
* A workspace opts into a security group with `security` in the
|
|
5
|
+
* declaration, at the top level or on a member (../security.ts). Each group
|
|
6
|
+
* has one check here, which reports what the group still lacks in the tree
|
|
7
|
+
* read. A group the declaration does not name is never checked, so a
|
|
8
|
+
* workspace that opts into nothing gets no finding from this file (#4168:
|
|
9
|
+
* security features are opt-in).
|
|
10
|
+
*
|
|
11
|
+
* | Id | Group | Codes |
|
|
12
|
+
* |---|---|---|
|
|
13
|
+
* | WSP161 | `verified-releases` | `security-signers-missing`, `security-release-gate-missing`, `security-publish-allows-unsigned` |
|
|
14
|
+
* | WSP162 | `supply-chain` | `security-sbom-disabled`, `security-sbom-unsourced`, `security-publish-no-provenance`, `security-component-no-sbom`, `security-component-no-verify` |
|
|
15
|
+
* | WSP163 | `who-ran-it` | `security-signed-since-unset`, `security-runner-untrusted`, `security-tick-no-key` |
|
|
16
|
+
* | WSP164 | `review-rules` | `security-review-not-required`, `security-attribution-any`, `security-gate-unsigned` |
|
|
17
|
+
* | WSP165 | `isolated-runners` | `security-runner-start-no-key`, `security-runner-start-no-role`, `security-trust-unreadable` |
|
|
18
|
+
*
|
|
19
|
+
* The checks read files, never run them: a member's `chant.config.ts` and
|
|
20
|
+
* `*.component.ts` are scanned as text, and the pipeline files are the
|
|
21
|
+
* forge files at the workspace root and in each member (`.github/workflows`,
|
|
22
|
+
* `.forgejo/workflows`, `.gitlab-ci.yml`). A command line is one line of a
|
|
23
|
+
* pipeline file that runs `chant release publish`, `chant ci tick` or
|
|
24
|
+
* `chant components pr-apply`.
|
|
25
|
+
*/
|
|
26
|
+
import type { WorkspaceCheck } from "../checks.js";
|
|
27
|
+
/** The read contract's codes for the security findings, carried as `code` on each. */
|
|
28
|
+
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
|
+
export declare const WSP_SECURITY_VERIFIED_RELEASES = "WSP161";
|
|
30
|
+
export declare const WSP_SECURITY_SUPPLY_CHAIN = "WSP162";
|
|
31
|
+
export declare const WSP_SECURITY_WHO_RAN_IT = "WSP163";
|
|
32
|
+
export declare const WSP_SECURITY_REVIEW_RULES = "WSP164";
|
|
33
|
+
export declare const WSP_SECURITY_ISOLATED_RUNNERS = "WSP165";
|
|
34
|
+
export declare const SECURITY_CHECKS: readonly WorkspaceCheck[];
|
|
35
|
+
//# sourceMappingURL=security.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"security.d.ts","sourceRoot":"","sources":["../../../src/workspace/checks/security.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,KAAK,EAAE,cAAc,EAA8C,MAAM,WAAW,CAAC;AAU5F,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;AA0GtD,eAAO,MAAM,eAAe,EAAE,SAAS,cAAc,EAuIpD,CAAC"}
|
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The security group checklists, WSP161 to WSP165 (#4168 step 7).
|
|
3
|
+
*
|
|
4
|
+
* A workspace opts into a security group with `security` in the
|
|
5
|
+
* declaration, at the top level or on a member (../security.ts). Each group
|
|
6
|
+
* has one check here, which reports what the group still lacks in the tree
|
|
7
|
+
* read. A group the declaration does not name is never checked, so a
|
|
8
|
+
* workspace that opts into nothing gets no finding from this file (#4168:
|
|
9
|
+
* security features are opt-in).
|
|
10
|
+
*
|
|
11
|
+
* | Id | Group | Codes |
|
|
12
|
+
* |---|---|---|
|
|
13
|
+
* | WSP161 | `verified-releases` | `security-signers-missing`, `security-release-gate-missing`, `security-publish-allows-unsigned` |
|
|
14
|
+
* | WSP162 | `supply-chain` | `security-sbom-disabled`, `security-sbom-unsourced`, `security-publish-no-provenance`, `security-component-no-sbom`, `security-component-no-verify` |
|
|
15
|
+
* | WSP163 | `who-ran-it` | `security-signed-since-unset`, `security-runner-untrusted`, `security-tick-no-key` |
|
|
16
|
+
* | WSP164 | `review-rules` | `security-review-not-required`, `security-attribution-any`, `security-gate-unsigned` |
|
|
17
|
+
* | WSP165 | `isolated-runners` | `security-runner-start-no-key`, `security-runner-start-no-role`, `security-trust-unreadable` |
|
|
18
|
+
*
|
|
19
|
+
* The checks read files, never run them: a member's `chant.config.ts` and
|
|
20
|
+
* `*.component.ts` are scanned as text, and the pipeline files are the
|
|
21
|
+
* forge files at the workspace root and in each member (`.github/workflows`,
|
|
22
|
+
* `.forgejo/workflows`, `.gitlab-ci.yml`). A command line is one line of a
|
|
23
|
+
* pipeline file that runs `chant release publish`, `chant ci tick` or
|
|
24
|
+
* `chant components pr-apply`.
|
|
25
|
+
*/
|
|
26
|
+
import { securityGroupsOf } from "../security.js";
|
|
27
|
+
import { joinPath } from "../tree.js";
|
|
28
|
+
import { runnerStartReadiness } from "../ci-runner-start.js";
|
|
29
|
+
import { readTrustPolicy } from "../trust/policy.js";
|
|
30
|
+
import { DEFAULT_SIGNERS_PATH, TRUST_CONFIG_PATH } from "../trust/trust-files.js";
|
|
31
|
+
/** The read contract's codes for the security findings, carried as `code` on each. */
|
|
32
|
+
export const SECURITY_FINDING_CODES = [
|
|
33
|
+
"security-signers-missing",
|
|
34
|
+
"security-release-gate-missing",
|
|
35
|
+
"security-publish-allows-unsigned",
|
|
36
|
+
"security-sbom-disabled",
|
|
37
|
+
"security-sbom-unsourced",
|
|
38
|
+
"security-publish-no-provenance",
|
|
39
|
+
"security-component-no-sbom",
|
|
40
|
+
"security-component-no-verify",
|
|
41
|
+
"security-signed-since-unset",
|
|
42
|
+
"security-runner-untrusted",
|
|
43
|
+
"security-tick-no-key",
|
|
44
|
+
"security-review-not-required",
|
|
45
|
+
"security-attribution-any",
|
|
46
|
+
"security-gate-unsigned",
|
|
47
|
+
"security-runner-start-no-key",
|
|
48
|
+
"security-runner-start-no-role",
|
|
49
|
+
"security-trust-unreadable",
|
|
50
|
+
];
|
|
51
|
+
export const WSP_SECURITY_VERIFIED_RELEASES = "WSP161";
|
|
52
|
+
export const WSP_SECURITY_SUPPLY_CHAIN = "WSP162";
|
|
53
|
+
export const WSP_SECURITY_WHO_RAN_IT = "WSP163";
|
|
54
|
+
export const WSP_SECURITY_REVIEW_RULES = "WSP164";
|
|
55
|
+
export const WSP_SECURITY_ISOLATED_RUNNERS = "WSP165";
|
|
56
|
+
const memberDir = (dir) => (dir === "." ? "" : dir);
|
|
57
|
+
/** The members under `group`: every member when the top level names it, else those that name it themselves. */
|
|
58
|
+
function membersUnder(declaration, group) {
|
|
59
|
+
return declaration.members.filter((m) => securityGroupsOf(declaration, m.name).has(group));
|
|
60
|
+
}
|
|
61
|
+
/** Whether the declaration names `group` anywhere. */
|
|
62
|
+
function declares(declaration, group) {
|
|
63
|
+
return declaration.security.includes(group) || declaration.members.some((m) => m.security.includes(group));
|
|
64
|
+
}
|
|
65
|
+
/** Where the group is named, for the finding's pointer: the top level, else the first member naming it. */
|
|
66
|
+
function groupPointer(declaration, group) {
|
|
67
|
+
const top = declaration.security.indexOf(group);
|
|
68
|
+
if (top >= 0)
|
|
69
|
+
return `/security/${top}`;
|
|
70
|
+
const m = declaration.members.find((e) => e.security.includes(group));
|
|
71
|
+
return m ? `${m.pointer}/security/${m.security.indexOf(group)}` : "/security";
|
|
72
|
+
}
|
|
73
|
+
function read(tree, path) {
|
|
74
|
+
if (tree.stat(path) !== "file")
|
|
75
|
+
return null;
|
|
76
|
+
try {
|
|
77
|
+
return tree.read(path);
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
/** The forge pipeline files under `dir`, each as its path and lines. */
|
|
84
|
+
function pipelineFiles(tree, dir) {
|
|
85
|
+
const out = [];
|
|
86
|
+
for (const folder of [".github/workflows", ".forgejo/workflows", ".gitea/workflows"]) {
|
|
87
|
+
for (const e of tree.list(joinPath(dir, folder)) ?? []) {
|
|
88
|
+
if (e.type !== "file" || !/\.ya?ml$/.test(e.name))
|
|
89
|
+
continue;
|
|
90
|
+
const path = joinPath(dir, folder, e.name);
|
|
91
|
+
const text = read(tree, path);
|
|
92
|
+
if (text !== null)
|
|
93
|
+
out.push({ path, lines: text.split("\n") });
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
const gitlab = read(tree, joinPath(dir, ".gitlab-ci.yml"));
|
|
97
|
+
if (gitlab !== null)
|
|
98
|
+
out.push({ path: joinPath(dir, ".gitlab-ci.yml"), lines: gitlab.split("\n") });
|
|
99
|
+
return out;
|
|
100
|
+
}
|
|
101
|
+
/** Every pipeline file of the workspace: its root's, and each member's. */
|
|
102
|
+
function allPipelineFiles(ctx) {
|
|
103
|
+
const dirs = new Set(["", ...ctx.declaration.members.map((m) => memberDir(m.dir))]);
|
|
104
|
+
return [...dirs].flatMap((d) => pipelineFiles(ctx.tree, d));
|
|
105
|
+
}
|
|
106
|
+
/** The lines of the pipeline files that run a chant command, `command` naming it, such as `release publish`. */
|
|
107
|
+
function commandLines(ctx, command) {
|
|
108
|
+
return allPipelineFiles(ctx).flatMap((f) => f.lines.flatMap((text, i) => (command.test(text) ? [{ path: f.path, line: i + 1, text }] : [])));
|
|
109
|
+
}
|
|
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) {
|
|
112
|
+
const out = [];
|
|
113
|
+
for (const e of tree.list(dir) ?? []) {
|
|
114
|
+
if (e.type === "dir") {
|
|
115
|
+
if (depth > 0 && !["node_modules", "dist", ".git", ".chant", "target"].includes(e.name))
|
|
116
|
+
out.push(...componentFiles(tree, joinPath(dir, e.name), depth - 1));
|
|
117
|
+
}
|
|
118
|
+
else if (e.name.endsWith(".component.ts"))
|
|
119
|
+
out.push(joinPath(dir, e.name));
|
|
120
|
+
}
|
|
121
|
+
return out;
|
|
122
|
+
}
|
|
123
|
+
/** Whether a lockfile chant writes an SBOM from is in `dir` or a directory above it in the tree. */
|
|
124
|
+
function lockfileAbove(tree, dir) {
|
|
125
|
+
let at = dir;
|
|
126
|
+
for (;;) {
|
|
127
|
+
if (["package-lock.json", "pom.xml", "Cargo.lock"].some((f) => tree.stat(joinPath(at, f)) === "file"))
|
|
128
|
+
return true;
|
|
129
|
+
if (at === "")
|
|
130
|
+
return false;
|
|
131
|
+
at = at.includes("/") ? at.slice(0, at.lastIndexOf("/")) : "";
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
/** The trust policy in the tree read: `.chant/trust.json` and the signers file it names. */
|
|
135
|
+
function treePolicy(tree) {
|
|
136
|
+
const source = {
|
|
137
|
+
label: tree.label,
|
|
138
|
+
list: (dir) => tree.list(dir)?.filter((e) => e.type === "file").map((e) => e.name),
|
|
139
|
+
read: (path) => tree.read(path),
|
|
140
|
+
bytes: (path) => (tree.bytes ? tree.bytes(path) : new Uint8Array(Buffer.from(tree.read(path), "utf-8"))),
|
|
141
|
+
};
|
|
142
|
+
return readTrustPolicy(source, null);
|
|
143
|
+
}
|
|
144
|
+
function finding(check, code, message, at) {
|
|
145
|
+
return {
|
|
146
|
+
checkId: check.id,
|
|
147
|
+
severity: check.severity,
|
|
148
|
+
code,
|
|
149
|
+
message: `${code}: ${message}`,
|
|
150
|
+
pointer: at.pointer,
|
|
151
|
+
...(at.entity !== undefined ? { entity: at.entity } : {}),
|
|
152
|
+
...(at.treeFile !== undefined ? { treeFile: at.treeFile, line: at.line ?? 1, column: 1 } : {}),
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
const RELEASE_PUBLISH = /\bchant\b.*\brelease\s+publish\b/;
|
|
156
|
+
const CI_TICK = /\bchant\b.*\bci\s+tick\b|main\.ts\s+ci\s+tick\b/;
|
|
157
|
+
const PR_APPLY = /\bcomponents\s+pr-apply\b|\bci\s+run\s+apply\b/;
|
|
158
|
+
export const SECURITY_CHECKS = [
|
|
159
|
+
{
|
|
160
|
+
id: WSP_SECURITY_VERIFIED_RELEASES,
|
|
161
|
+
name: "security-verified-releases",
|
|
162
|
+
description: "Under the verified-releases group: a signers file, a release gate naming a class, and no publish job that passes --allow-unsigned.",
|
|
163
|
+
severity: "warning",
|
|
164
|
+
configurable: true,
|
|
165
|
+
check(ctx) {
|
|
166
|
+
const d = ctx.declaration;
|
|
167
|
+
if (!declares(d, "verified-releases"))
|
|
168
|
+
return [];
|
|
169
|
+
const pointer = groupPointer(d, "verified-releases");
|
|
170
|
+
const out = [];
|
|
171
|
+
if (ctx.tree.stat(DEFAULT_SIGNERS_PATH) !== "file") {
|
|
172
|
+
out.push(finding(this, "security-signers-missing", `the workspace opts into verified-releases, and there is no ${DEFAULT_SIGNERS_PATH}${ctx.tree.label} to verify a release's signature against; add the keys of the people who cut releases to it, in ssh-keygen's allowed_signers format`, { pointer }));
|
|
173
|
+
}
|
|
174
|
+
const gate = d.identity?.gates.release;
|
|
175
|
+
if (!gate?.class) {
|
|
176
|
+
out.push(finding(this, "security-release-gate-missing", gate ? `identity.gates.release names no class, so any signer in ${DEFAULT_SIGNERS_PATH} may cut a release; name the class that may` : "the workspace opts into verified-releases, and identity.gates has no release entry, so a cut is not held to a class of signers", { pointer: gate?.pointer ?? pointer }));
|
|
177
|
+
}
|
|
178
|
+
for (const l of commandLines(ctx, RELEASE_PUBLISH)) {
|
|
179
|
+
if (!/--allow-unsigned\b/.test(l.text))
|
|
180
|
+
continue;
|
|
181
|
+
out.push(finding(this, "security-publish-allows-unsigned", `${l.path} runs release publish with --allow-unsigned, which publishes a release whose signature does not verify`, { pointer, treeFile: l.path, line: l.line }));
|
|
182
|
+
}
|
|
183
|
+
return out;
|
|
184
|
+
},
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
id: WSP_SECURITY_SUPPLY_CHAIN,
|
|
188
|
+
name: "security-supply-chain",
|
|
189
|
+
description: "Under the supply-chain group: every publish target attaches an SBOM it has a source for, publish jobs require OIDC provenance, and components generate an SBOM and verify what they deploy.",
|
|
190
|
+
severity: "warning",
|
|
191
|
+
configurable: true,
|
|
192
|
+
check(ctx) {
|
|
193
|
+
const d = ctx.declaration;
|
|
194
|
+
if (!declares(d, "supply-chain"))
|
|
195
|
+
return [];
|
|
196
|
+
const pointer = groupPointer(d, "supply-chain");
|
|
197
|
+
const out = [];
|
|
198
|
+
for (const m of membersUnder(d, "supply-chain")) {
|
|
199
|
+
const configPath = joinPath(memberDir(m.dir), "chant.config.ts");
|
|
200
|
+
const config = read(ctx.tree, configPath);
|
|
201
|
+
if (config !== null && /\bpublish\s*:/.test(config)) {
|
|
202
|
+
const lines = config.split("\n");
|
|
203
|
+
const off = lines.findIndex((l) => /\bsbom\s*:\s*false\b/.test(l));
|
|
204
|
+
if (off >= 0)
|
|
205
|
+
out.push(finding(this, "security-sbom-disabled", `member ${m.name}'s chant.config.ts sets sbom: false on a publish target, so what it publishes carries no SBOM`, { pointer, entity: m.name, treeFile: configPath, line: off + 1 }));
|
|
206
|
+
else if (!/\bsbom\s*:\s*["'`]/.test(config) && !lockfileAbove(ctx.tree, memberDir(m.dir))) {
|
|
207
|
+
out.push(finding(this, "security-sbom-unsourced", `member ${m.name} publishes, names no sbom file and has no package-lock.json, pom.xml or Cargo.lock in its directory or above it, so publish would refuse it as publish-sbom-missing`, { pointer, entity: m.name, treeFile: configPath }));
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
const components = componentFiles(ctx.tree, memberDir(m.dir));
|
|
211
|
+
if (components.length === 0)
|
|
212
|
+
continue;
|
|
213
|
+
const text = components.map((f) => read(ctx.tree, f) ?? "").join("\n");
|
|
214
|
+
if (!/\bgenerateSbom\s*\(|["']generate-sbom["']/.test(text)) {
|
|
215
|
+
out.push(finding(this, "security-component-no-sbom", `member ${m.name}'s components have no generate-sbom step, so what they build carries no SBOM`, { pointer, entity: m.name, treeFile: components[0] }));
|
|
216
|
+
}
|
|
217
|
+
if (!/\bverify\s*\(|["']verify["']/.test(text)) {
|
|
218
|
+
out.push(finding(this, "security-component-no-verify", `member ${m.name}'s components have no verify step, so a signature or attestation is never checked before a deploy`, { pointer, entity: m.name, treeFile: components[0] }));
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
for (const l of commandLines(ctx, RELEASE_PUBLISH)) {
|
|
222
|
+
if (/--require-provenance\b/.test(l.text))
|
|
223
|
+
continue;
|
|
224
|
+
out.push(finding(this, "security-publish-no-provenance", `${l.path} runs release publish without --require-provenance, so a run with no OIDC token publishes with only a warning`, { pointer, treeFile: l.path, line: l.line }));
|
|
225
|
+
}
|
|
226
|
+
return out;
|
|
227
|
+
},
|
|
228
|
+
},
|
|
229
|
+
{
|
|
230
|
+
id: WSP_SECURITY_WHO_RAN_IT,
|
|
231
|
+
name: "security-who-ran-it",
|
|
232
|
+
description: "Under the who-ran-it group: ci.green.signedSince is set, .chant/trust.json lists a runner key, and the tick signs its tags.",
|
|
233
|
+
severity: "warning",
|
|
234
|
+
configurable: true,
|
|
235
|
+
check(ctx) {
|
|
236
|
+
const d = ctx.declaration;
|
|
237
|
+
if (!declares(d, "who-ran-it"))
|
|
238
|
+
return [];
|
|
239
|
+
const pointer = groupPointer(d, "who-ran-it");
|
|
240
|
+
const out = [];
|
|
241
|
+
if (!d.ci?.green?.signedSince) {
|
|
242
|
+
out.push(finding(this, "security-signed-since-unset", d.ci?.green ? "ci.green sets no signedSince, so an unsigned green tag still counts" : "the workspace opts into who-ran-it, and has no ci.green block, so no commit's green tag is signed", { pointer: d.ci?.green?.pointer ?? pointer }));
|
|
243
|
+
}
|
|
244
|
+
const runners = treePolicy(ctx.tree).runners.filter((r) => r.class === "runner").length;
|
|
245
|
+
if (runners === 0) {
|
|
246
|
+
out.push(finding(this, "security-runner-untrusted", `${TRUST_CONFIG_PATH}${ctx.tree.label} lists no runner key, so no run or tag can be traced to a runner; add one with chant ci serve --setup-key --trust`, { pointer }));
|
|
247
|
+
}
|
|
248
|
+
for (const l of commandLines(ctx, CI_TICK)) {
|
|
249
|
+
const file = allPipelineFiles(ctx).find((f) => f.path === l.path);
|
|
250
|
+
if (/--key\b/.test(l.text) || file?.lines.some((t) => /CHANT_RUNNER_KEY/.test(t)))
|
|
251
|
+
continue;
|
|
252
|
+
out.push(finding(this, "security-tick-no-key", `${l.path} runs ci tick with no runner key, so its tags are unsigned; regenerate it with chant ci workflow --runner-key-secret <NAME>`, { pointer, treeFile: l.path, line: l.line }));
|
|
253
|
+
}
|
|
254
|
+
return out;
|
|
255
|
+
},
|
|
256
|
+
},
|
|
257
|
+
{
|
|
258
|
+
id: WSP_SECURITY_REVIEW_RULES,
|
|
259
|
+
name: "security-review-rules",
|
|
260
|
+
description: "Under the review-rules group: no apply job turns forge review off, attribution is identified, and every gate an apply job names needs a signed approval.",
|
|
261
|
+
severity: "warning",
|
|
262
|
+
configurable: true,
|
|
263
|
+
check(ctx) {
|
|
264
|
+
const d = ctx.declaration;
|
|
265
|
+
if (!declares(d, "review-rules"))
|
|
266
|
+
return [];
|
|
267
|
+
const pointer = groupPointer(d, "review-rules");
|
|
268
|
+
const out = [];
|
|
269
|
+
const applies = commandLines(ctx, PR_APPLY);
|
|
270
|
+
for (const l of applies) {
|
|
271
|
+
if (!/--no-require-review\b/.test(l.text))
|
|
272
|
+
continue;
|
|
273
|
+
out.push(finding(this, "security-review-not-required", `${l.path} passes --no-require-review, which the review-rules group overrides; drop it so the file says what runs`, { pointer, treeFile: l.path, line: l.line }));
|
|
274
|
+
}
|
|
275
|
+
if (d.identity?.attribution !== "identified") {
|
|
276
|
+
out.push(finding(this, "security-attribution-any", "identity.attribution is not identified, so a person-attributed write may name anyone", { pointer: d.identity ? "/identity" : pointer }));
|
|
277
|
+
}
|
|
278
|
+
const signed = new Set(Object.keys(d.identity?.gates ?? {}));
|
|
279
|
+
const named = new Set(applies.flatMap((l) => [...l.text.matchAll(/--(?:wave-)?gate\s+["']?([A-Za-z0-9_.-]+)/g)].map((m) => m[1])));
|
|
280
|
+
const unsigned = [...named].filter((g) => !signed.has(g));
|
|
281
|
+
if (unsigned.length > 0) {
|
|
282
|
+
out.push(finding(this, "security-gate-unsigned", `the apply jobs name the gate${unsigned.length > 1 ? "s" : ""} ${unsigned.join(", ")}, which identity.gates does not list, so an unsigned approval passes ${unsigned.length > 1 ? "them" : "it"}`, { pointer }));
|
|
283
|
+
}
|
|
284
|
+
else if (named.size === 0 && signed.size === 0) {
|
|
285
|
+
out.push(finding(this, "security-gate-unsigned", "identity.gates lists no gate, so every approval may be unsigned", { pointer }));
|
|
286
|
+
}
|
|
287
|
+
return out;
|
|
288
|
+
},
|
|
289
|
+
},
|
|
290
|
+
{
|
|
291
|
+
id: WSP_SECURITY_ISOLATED_RUNNERS,
|
|
292
|
+
name: "security-isolated-runners",
|
|
293
|
+
description: "Under the isolated-runners group: .chant/trust.json lists a runner key whose principal holds the runner role, so an apply job can require a runner-start statement (#4147).",
|
|
294
|
+
severity: "warning",
|
|
295
|
+
configurable: true,
|
|
296
|
+
check(ctx) {
|
|
297
|
+
const d = ctx.declaration;
|
|
298
|
+
if (!declares(d, "isolated-runners"))
|
|
299
|
+
return [];
|
|
300
|
+
const pointer = groupPointer(d, "isolated-runners");
|
|
301
|
+
const codes = { "no-runner-key": "security-runner-start-no-key", "no-runner-role": "security-runner-start-no-role", "trust-unreadable": "security-trust-unreadable" };
|
|
302
|
+
return runnerStartReadiness(treePolicy(ctx.tree)).gaps.map((g) => finding(this, codes[g.gap], `the workspace opts into isolated-runners, so every apply job requires a runner-start statement, and ${g.reason}`, { pointer }));
|
|
303
|
+
},
|
|
304
|
+
},
|
|
305
|
+
];
|
|
@@ -37,6 +37,8 @@
|
|
|
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 | reserved for the `ci.workflow` preset (#4168 step 6) |
|
|
41
|
+
* | WSP161 to WSP165 | the security groups the declaration opts into: what each still lacks (#4168 step 7) |
|
|
40
42
|
*/
|
|
41
43
|
import type { LintDiagnostic, LintRule, Severity } from "../lint/rule.js";
|
|
42
44
|
import type { PostSynthDiagnostic } from "../lint/post-synth.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"checks.d.ts","sourceRoot":"","sources":["../../src/workspace/checks.ts"],"names":[],"mappings":"AAAA
|
|
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,6 +37,8 @@
|
|
|
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 | reserved for the `ci.workflow` preset (#4168 step 6) |
|
|
41
|
+
* | WSP161 to WSP165 | the security groups the declaration opts into: what each still lacks (#4168 step 7) |
|
|
40
42
|
*/
|
|
41
43
|
import { realpathSync } from "node:fs";
|
|
42
44
|
import { relative, sep } from "node:path";
|
|
@@ -53,6 +55,7 @@ import { gatherLiveLinkFacts, LIVE_CHECKS } from "./checks/live.js";
|
|
|
53
55
|
import { RECORD_CHECKS } from "./checks/records.js";
|
|
54
56
|
import { BOX_CHECKS } from "./checks/boxes.js";
|
|
55
57
|
import { DIAGRAM_CHECKS } from "./checks/diagrams.js";
|
|
58
|
+
import { SECURITY_CHECKS } from "./checks/security.js";
|
|
56
59
|
import { loadDeclaredKinds } from "./declared-kinds.js";
|
|
57
60
|
import { resolveBoxIntents } from "./box-intent.js";
|
|
58
61
|
import { gatherLexiconOutputs } from "./lexicon-outputs.js";
|
|
@@ -270,6 +273,8 @@ export const WORKSPACE_CHECKS = [
|
|
|
270
273
|
...DIAGRAM_CHECKS,
|
|
271
274
|
// Links resolved against a live read, with --live (#2549), WSP141 and WSP142.
|
|
272
275
|
...LIVE_CHECKS,
|
|
276
|
+
// The security groups the declaration opts into (#4168 step 7), WSP161 to WSP165.
|
|
277
|
+
...SECURITY_CHECKS,
|
|
273
278
|
];
|
|
274
279
|
const BY_ID = new Map(WORKSPACE_CHECKS.map((c) => [c.id, c]));
|
|
275
280
|
function settingProblem(id) {
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which commits get built, and what each one ends with (#4169).
|
|
3
|
+
*
|
|
4
|
+
* A `ci.green` phase declares `build: "every"` (the default) or
|
|
5
|
+
* `build: "newest"`. Under `newest` the phase's workflow keeps one run
|
|
6
|
+
* waiting and drops the rest, so only the newest commit is built when a
|
|
7
|
+
* runner frees. This module does the tick's part of that, given the
|
|
8
|
+
* verdicts {@link planTick} (./ci-green.ts) judged:
|
|
9
|
+
*
|
|
10
|
+
* - Superseded. A commit with no run of a `newest` phase, below a newer
|
|
11
|
+
* commit that has one, is `unbuilt` for that phase, covered by the newer
|
|
12
|
+
* commit. A cancelled run makes any phase `unbuilt` (`phaseVerdictOf`).
|
|
13
|
+
* - Retry. A failed run with one attempt is run again once before the
|
|
14
|
+
* commit counts red: through the forge's re-run (`CiForge.rerun`) for
|
|
15
|
+
* check runs, or a build request for chant runs. A retry that has not
|
|
16
|
+
* started an hour on counts as the failure it was.
|
|
17
|
+
* - Bisect. When the newest built commit of a `newest` phase is red, the
|
|
18
|
+
* unbuilt commits back to the last one the phase passed on are built, one
|
|
19
|
+
* request per tick: the commit halfway when more than four are left, else
|
|
20
|
+
* the one just below the red. The first red commit found is the culprit,
|
|
21
|
+
* and it and every commit after it name it.
|
|
22
|
+
* - Requests. A build is asked for with an annotated tag,
|
|
23
|
+
* `ci/build/<sha>`, whose record names the phases and why. The tick
|
|
24
|
+
* starts a workflow at that tag on GitHub (`CiForge.dispatch`), and `ci
|
|
25
|
+
* serve --build-command` serves it for chant runs. A phase a request
|
|
26
|
+
* waits on is `pending`. A request no runner served in six hours leaves
|
|
27
|
+
* the phase `unbuilt`, and once its phases are decided the tick deletes
|
|
28
|
+
* the tag.
|
|
29
|
+
* - Records. Every first-parent commit the tick judged gets
|
|
30
|
+
* `_ci/<sha>.json` on `chant/lifecycle`, rewritten only when what it says
|
|
31
|
+
* changes: `green`, `red`, `unbuilt` or `pending`, with each required
|
|
32
|
+
* phase's verdict, runs, reason, the commit that covers it and the culprit.
|
|
33
|
+
* A commit that leaves the window still `pending` is rewritten `unbuilt`,
|
|
34
|
+
* for the reason `aged-out`. The `ci/green` and `ci/revoked` tags stay
|
|
35
|
+
* what decides a release; the records say what happened to the rest.
|
|
36
|
+
*/
|
|
37
|
+
import type { CiBuild, CiGreen } from "./declaration.js";
|
|
38
|
+
import type { CheckRun } from "./ci-green-forge.js";
|
|
39
|
+
import { type PhaseReason, type TickCommit, type Verdict } from "./ci-green.js";
|
|
40
|
+
export declare const CI_BUILD_TAG_PREFIX = "ci/build/";
|
|
41
|
+
/** The directory on `chant/lifecycle` holding one record per commit. */
|
|
42
|
+
export declare const CI_RECORDS_DIR = "_ci";
|
|
43
|
+
/** How long a build request may wait for a runner before its phases count as unbuilt. */
|
|
44
|
+
export declare const BUILD_REQUEST_TTL_MS: number;
|
|
45
|
+
/** How long a retry may take to start before the failure it was counts. */
|
|
46
|
+
export declare const RETRY_TTL_MS = 3600000;
|
|
47
|
+
/** Above this many unbuilt commits a bisect halves the range; at or below it, it steps back one commit at a time. */
|
|
48
|
+
export declare const BISECT_HALVE_ABOVE = 4;
|
|
49
|
+
export type BuildRequestReason = "bisect" | "retry" | "requested";
|
|
50
|
+
/** A `ci/build/<sha>` tag's record: the phases to build at the commit, and why. */
|
|
51
|
+
export interface BuildRequest {
|
|
52
|
+
schema: 1;
|
|
53
|
+
tag: string;
|
|
54
|
+
commit: string;
|
|
55
|
+
branch: string;
|
|
56
|
+
phases: string[];
|
|
57
|
+
reason: BuildRequestReason;
|
|
58
|
+
/** When it was asked for, ISO 8601. */
|
|
59
|
+
at: string;
|
|
60
|
+
}
|
|
61
|
+
/** One record, `_ci/<sha>.json` on `chant/lifecycle`. */
|
|
62
|
+
export interface CiRecord {
|
|
63
|
+
schema: 1;
|
|
64
|
+
commit: string;
|
|
65
|
+
branch: string;
|
|
66
|
+
/** `green` and `red` follow the tags: a green tag, or a failed phase or a revoked tag. */
|
|
67
|
+
state: "green" | "red" | "unbuilt" | "pending";
|
|
68
|
+
/** When this content was written, ISO 8601. Not compared when deciding whether to write again. */
|
|
69
|
+
at: string;
|
|
70
|
+
/** Set when the commit has a revoked tag. */
|
|
71
|
+
revoked?: true;
|
|
72
|
+
phases: Record<string, CiRecordPhase>;
|
|
73
|
+
/** The first required phase that failed, and its run, when the state is red. */
|
|
74
|
+
failure?: {
|
|
75
|
+
phase: string;
|
|
76
|
+
run: CiRecordRun;
|
|
77
|
+
};
|
|
78
|
+
/** The retries asked for, by run name: the run that failed and when. */
|
|
79
|
+
retried?: Record<string, {
|
|
80
|
+
run: number;
|
|
81
|
+
at: string;
|
|
82
|
+
}>;
|
|
83
|
+
/** The build request waiting on this commit, when there is one. */
|
|
84
|
+
request?: {
|
|
85
|
+
tag: string;
|
|
86
|
+
reason: BuildRequestReason;
|
|
87
|
+
phases: string[];
|
|
88
|
+
at: string;
|
|
89
|
+
dispatched?: true;
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
export interface CiRecordRun {
|
|
93
|
+
name: string;
|
|
94
|
+
id: number | null;
|
|
95
|
+
conclusion: string | null;
|
|
96
|
+
url: string | null;
|
|
97
|
+
}
|
|
98
|
+
export interface CiRecordPhase {
|
|
99
|
+
verdict: Verdict;
|
|
100
|
+
build: CiBuild;
|
|
101
|
+
runs: CiRecordRun[];
|
|
102
|
+
reason?: PhaseReason | "aged-out";
|
|
103
|
+
coveredBy?: string;
|
|
104
|
+
culprit?: string;
|
|
105
|
+
}
|
|
106
|
+
/** How a tick can run a failed job again: the forge's re-run, a build request, or not at all. */
|
|
107
|
+
export type RetryVia = "forge" | "request" | null;
|
|
108
|
+
export interface BuildsInput {
|
|
109
|
+
green: CiGreen;
|
|
110
|
+
/** Newest first, as {@link planTick} returns them. */
|
|
111
|
+
commits: TickCommit[];
|
|
112
|
+
/** Every attempt of every run, by commit. */
|
|
113
|
+
runs: ReadonlyMap<string, readonly CheckRun[]>;
|
|
114
|
+
requests: ReadonlyMap<string, BuildRequest>;
|
|
115
|
+
records: ReadonlyMap<string, CiRecord>;
|
|
116
|
+
now: Date;
|
|
117
|
+
retryVia: RetryVia;
|
|
118
|
+
}
|
|
119
|
+
export interface BuildsPlan {
|
|
120
|
+
/** Requests to make: bisect steps, and retries of chant runs. */
|
|
121
|
+
requests: BuildRequest[];
|
|
122
|
+
/** Failed check runs to run again through the forge, by commit. */
|
|
123
|
+
retries: {
|
|
124
|
+
sha: string;
|
|
125
|
+
run: CheckRun;
|
|
126
|
+
}[];
|
|
127
|
+
/** Request tags whose phases are decided, or that waited too long, or whose commit left the window. */
|
|
128
|
+
resolved: string[];
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Apply supersession, retries, requests and bisection to the judged commits,
|
|
132
|
+
* in place, and say what the tick has to ask for. Pure: it reads and writes
|
|
133
|
+
* nothing but its input.
|
|
134
|
+
*/
|
|
135
|
+
export declare function planBuilds(input: BuildsInput): BuildsPlan;
|
|
136
|
+
/** The record a judged commit gets, carrying the retries asked for before and now. */
|
|
137
|
+
export declare function recordOf(input: {
|
|
138
|
+
green: CiGreen;
|
|
139
|
+
commit: TickCommit;
|
|
140
|
+
previous?: CiRecord;
|
|
141
|
+
retries: readonly {
|
|
142
|
+
sha: string;
|
|
143
|
+
run: CheckRun;
|
|
144
|
+
}[];
|
|
145
|
+
requests: ReadonlyMap<string, BuildRequest>;
|
|
146
|
+
now: Date;
|
|
147
|
+
}): CiRecord;
|
|
148
|
+
/** The record a commit that left the window still pending gets: each pending phase `unbuilt`, `aged-out`. */
|
|
149
|
+
export declare function agedOut(record: CiRecord, now: Date): CiRecord;
|
|
150
|
+
/** Whether two records say the same thing, the time each was written aside. */
|
|
151
|
+
export declare function sameRecord(a: CiRecord | undefined, b: CiRecord): boolean;
|
|
152
|
+
export declare const recordPath: (sha: string) => string;
|
|
153
|
+
/** One commit's record, from the local `chant/lifecycle`. */
|
|
154
|
+
export declare function readCiRecord(top: string, sha: string): CiRecord | undefined;
|
|
155
|
+
/** Every record on the local `chant/lifecycle`, by commit. Empty when the branch or the directory is missing. */
|
|
156
|
+
export declare function readCiRecords(top: string): Map<string, CiRecord>;
|
|
157
|
+
/** Every `ci/build/<sha>` tag with a record this reads, from local refs, by commit. */
|
|
158
|
+
export declare function readBuildRequests(top: string): Map<string, BuildRequest>;
|
|
159
|
+
/** The annotation of a request tag: a subject line, then the record. */
|
|
160
|
+
export declare function buildRequestAnnotation(r: BuildRequest): string;
|
|
161
|
+
/** A request for `ci run --at <sha>`: every required phase, asked for by a person. */
|
|
162
|
+
export declare function requestedBuild(green: CiGreen, commit: string, phases: readonly string[], now: Date): BuildRequest;
|
|
163
|
+
//# sourceMappingURL=ci-builds.d.ts.map
|