@kungfu-tech/buildchain 4.1.0-alpha.0 → 4.1.0-alpha.2
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/AGENTS.md +30 -41
- package/CONTRIBUTING.md +6 -7
- package/architecture/action-taxonomy.json +5 -21
- package/architecture/agent-change-map.md +74 -56
- package/architecture/build-orchestration.json +8 -1
- package/architecture/ci-lane-change-budget.json +179 -69
- package/architecture/floating-consumer-policy.json +7 -17
- package/architecture/internal-capabilities.json +58 -72
- package/architecture/maintainability-debt.json +138 -52
- package/architecture/maintainability-policy.json +6 -15
- package/architecture/promotion-job-topology.json +39 -20
- package/architecture/release-topology.json +26 -37
- package/architecture/runtime-entry.json +26 -0
- package/architecture/runtime-resume-lineage.json +23 -0
- package/architecture/stage-capsule-qualification.json +2 -2
- package/architecture/universal-workflow-bootstrap.json +10 -25
- package/architecture/universal-workflow-capability-policy.json +61 -0
- package/architecture/universal-workflow-fault-campaign.json +18 -24
- package/architecture/workflow-taxonomy.json +0 -11
- package/contracts/fixtures/{v4-runtime-ref-resume-authority-v1 → runtime-resume-lineage-v2}/scenario.json +2 -3
- package/contracts/fixtures/v4-floating-consumer-policy-v1/cases.json +2 -2
- package/contracts/fixtures/v4-tail-reseal-v1/valid.json +1 -1
- package/contracts/promotion-invocation-v1.schema.json +0 -44
- package/contracts/promotion-request-v1.schema.json +0 -39
- package/contracts/runtime-resume-lineage-v2.schema.json +182 -0
- package/dist/site/buildchain-contract.json +472 -361
- package/dist/site/buildchain-site.json +99 -99
- package/dist/site/capability-registry.json +3 -3
- package/dist/site/cli-registry.json +4 -4
- package/dist/site/controller-registry.json +94 -78
- package/dist/site/kfd-claims.json +224 -343
- package/dist/site/kfd-upstream-aggregate.json +1 -1
- package/dist/site/manual-registry.json +15 -15
- package/dist/site/node-api-registry.json +239 -354
- package/dist/site/page-registry.json +80 -80
- package/dist/site/public-surface-audit.json +203 -254
- package/dist/site/publication-authority-registry.json +1 -24
- package/dist/site/publication-registry.json +4 -4
- package/dist/site/release-provenance.json +1 -1
- package/dist/site/site-manifest.json +19 -19
- package/dist/site/workflow-registry.json +197 -357
- package/docs/MAP.md +3 -3
- package/docs/auditable-demo.md +4 -4
- package/docs/cli-reference.md +2 -2
- package/docs/code-organization.md +5 -4
- package/docs/controller-evidence.md +2 -2
- package/docs/dev-alpha-candidate-patrol.md +5 -5
- package/docs/dev-delivery-warrant.md +14 -24
- package/docs/engineering-housekeeper.md +3 -3
- package/docs/github-artifact-attestation.md +9 -12
- package/docs/github-governance-authority.md +4 -4
- package/docs/node-api-reference.md +153 -167
- package/docs/publication-artifacts.md +3 -4
- package/docs/publication-authority.md +2 -2
- package/docs/release-candidate.md +21 -27
- package/docs/release-governance.md +10 -10
- package/docs/release-promotion-request.md +11 -18
- package/docs/release-propagation.md +5 -6
- package/docs/release-tail-provider-plane.md +2 -3
- package/docs/reusable-build-surface.md +13 -7
- package/docs/runtime-entry.md +122 -0
- package/docs/runtime-train-validation.md +30 -41
- package/docs/v4-publication-rehearsal.md +5 -5
- package/docs/v4-stage-capsule.md +9 -10
- package/docs/web-surface-deployments.md +16 -20
- package/docs/workflow-catalog.md +0 -1
- package/package.json +6 -6
- package/packages/core/adoption/commands/init-repo.mjs +2 -2
- package/packages/core/adoption/cross-platform-adopter-qualification.js +2 -13
- package/packages/core/adoption/qualification/actions.js +3 -5
- package/packages/core/adoption/qualification/admission.js +2 -3
- package/packages/core/build/artifact/attestation.js +0 -1
- package/packages/core/build/artifact/contracts.js +1 -4
- package/packages/core/build/artifact/store.js +6 -5
- package/packages/core/build/artifact-signing.js +4 -10
- package/packages/core/build/binary/actions.js +15 -9
- package/packages/core/build/binary-publication-dispatch.js +2 -6
- package/packages/core/build/demo/action-context.js +2 -11
- package/packages/core/build/demo/binary-action.js +8 -10
- package/packages/core/build/gate/controller.js +0 -2
- package/packages/core/build/github-attestation/action.js +2 -7
- package/packages/core/build/github-attestation/admission.js +0 -8
- package/packages/core/build/github-attestation/transaction.js +1 -2
- package/packages/core/build/lifecycle/stage-action.js +2 -0
- package/packages/core/build/lifecycle/stage.js +6 -3
- package/packages/core/build/macos-credential-island/dmg-assembly.js +0 -2
- package/packages/core/build/macos-credential-island/lib.js +2 -6
- package/packages/core/build/plan/action-context.js +0 -7
- package/packages/core/build/plan/action.js +7 -11
- package/packages/core/build/plan/admission.js +6 -73
- package/packages/core/build/plan/configuration.js +1 -1
- package/packages/core/build/plan/environment.js +1 -1
- package/packages/core/build/plan/identity.js +1 -5
- package/packages/core/build/plan/resolve.js +7 -1
- package/packages/core/build/recovery/checkpoint.js +158 -0
- package/packages/core/build/recovery/transaction.js +51 -0
- package/packages/core/build/signing/authority-actions.js +2 -11
- package/packages/core/build/signing/control.js +0 -5
- package/packages/core/build/signing/controller-transaction.js +0 -1
- package/packages/core/build/signing/delegation.js +2 -4
- package/packages/core/build/signing/dispatch.js +10 -56
- package/packages/core/build/signing/intake.js +0 -4
- package/packages/core/build/signing/prepare.js +1 -1
- package/packages/core/build/signing/request.js +3 -8
- package/packages/core/build/source/checkout-action.js +6 -10
- package/packages/core/build/source/controller.js +1 -4
- package/packages/core/build/source/lifecycle.js +3 -13
- package/packages/core/build/stage-capsule/actions.js +3 -13
- package/packages/core/build/stage-capsule/campaign/resume.js +10 -5
- package/packages/core/build/stage-capsule/rehearsal/verify.js +3 -4
- package/packages/core/build/stage-capsule-resume-planner.js +0 -1
- package/packages/core/build/summary/aggregation.js +5 -5
- package/packages/core/build/summary/finalization.js +1 -1
- package/packages/core/build/verification/actions.js +3 -12
- package/packages/core/build/verification/runner.js +22 -20
- package/packages/core/consumer/floating-consumer-evidence.js +3 -21
- package/packages/core/consumer/floating-consumer-policy.js +2 -93
- package/packages/core/consumer/floating-consumer-release-passport.js +1 -34
- package/packages/core/consumer/policy-admission.js +1 -3
- package/packages/core/contracts/buildchain-contract.js +12 -146
- package/packages/core/contracts/workflow-yaml-contract.js +11 -1
- package/packages/core/dev-delivery/candidate/admission.js +3 -4
- package/packages/core/dev-delivery/candidate/coordinates.js +0 -8
- package/packages/core/dev-delivery/commands/dev-delivery-request.sh +5 -13
- package/packages/core/dev-delivery/commands/dev-delivery-source-proof-reuse.mjs +0 -3
- package/packages/core/dev-delivery/dev-delivery-contract-surface.js +4 -52
- package/packages/core/dev-delivery/dev-delivery-execution-transfer.js +2 -15
- package/packages/core/dev-delivery/native/action-context.js +4 -32
- package/packages/core/dev-delivery/native/provider-boundary.js +0 -2
- package/packages/core/dev-delivery/native/transactions.js +1 -2
- package/packages/core/dev-delivery/queue/reconciliation-action.js +6 -10
- package/packages/core/dev-delivery/source-proof/predicates.js +0 -6
- package/packages/core/dev-delivery/source-proof/reuse.js +0 -2
- package/packages/core/dev-delivery/source-proof/seal.js +0 -2
- package/packages/core/governance/alpha-candidate/action.js +1 -10
- package/packages/core/governance/audit/actions.js +1 -3
- package/packages/core/governance/audit/collection.js +1 -3
- package/packages/core/governance/audit/identity.js +3 -31
- package/packages/core/governance/audit/transactions.js +2 -4
- package/packages/core/governance/buildchain-publication-authority.js +0 -1
- package/packages/core/governance/commands/audit-github-governance.mjs +3 -2
- package/packages/core/governance/github-governance-authority.js +0 -5
- package/packages/core/governance/receipt-admission.js +0 -15
- package/packages/core/index.js +1 -1
- package/packages/core/observability/controller/identity.js +4 -5
- package/packages/core/observability/controller-evidence.js +5 -10
- package/packages/core/paper/commands/paper.mjs +6 -6
- package/packages/core/paper/operations/plans.js +2 -2
- package/packages/core/paper/operations/provisioning-validation.js +1 -1
- package/packages/core/paper/operations/scaffold-workflows.js +29 -12
- package/packages/core/paper/operations/scaffold.js +97 -69
- package/packages/core/paper/paper-agent-entry.js +0 -11
- package/packages/core/paper/paper-runtime-channels.js +21 -66
- package/packages/core/paper/paper-scaffold-content.js +1 -1
- package/packages/core/paper/paper.js +9 -5
- package/packages/core/paper/publication/candidate.js +0 -9
- package/packages/core/paper/publication-controller.js +2 -4
- package/packages/core/providers/github/artifact-attestation.js +1 -1
- package/packages/core/publication/authority/actions.js +9 -8
- package/packages/core/publication/authority/admission.js +0 -11
- package/packages/core/publication/authority/assembly.js +2 -7
- package/packages/core/publication/authority/control-plane.js +2 -2
- package/packages/core/publication/authority/governance.js +2 -9
- package/packages/core/publication/authority/request.js +7 -2
- package/packages/core/publication/authority/verification.js +3 -18
- package/packages/core/publication/binary/action.js +5 -7
- package/packages/core/publication/candidate/qualification-action.js +1 -6
- package/packages/core/publication/candidate/stage-capsules.js +1 -1
- package/packages/core/publication/npm/preview-action.js +9 -15
- package/packages/core/publication/oci/preview-actions.js +1 -10
- package/packages/core/publication/publication-artifact-candidate.js +2 -2
- package/packages/core/publication/publication-authority.js +2 -2
- package/packages/core/publication/publication-rehearsal-projection.js +3 -3
- package/packages/core/publication/settlement/actions.js +2 -11
- package/packages/core/release/binary/coordinates-action.js +0 -13
- package/packages/core/release/binary/coordinates.js +2 -19
- package/packages/core/release/commands/promotion-channel-router.mjs +12 -56
- package/packages/core/release/commands/resume-from-candidate-run.mjs +2 -3
- package/packages/core/release/dev-alpha-active-release-train.js +0 -5
- package/packages/core/release/line/action.js +2 -13
- package/packages/core/release/next-development/actions.js +2 -5
- package/packages/core/release/passport/consumer-validation.js +1 -28
- package/packages/core/release/promote-candidate/development-source.js +7 -8
- package/packages/core/release/promotion/actions.js +7 -38
- package/packages/core/release/promotion/admission.js +3 -50
- package/packages/core/release/promotion/candidate.js +2 -4
- package/packages/core/release/promotion/channel.js +13 -101
- package/packages/core/release/promotion/qualification-action.js +2 -13
- package/packages/core/release/promotion/routing.js +16 -81
- package/packages/core/release/promotion/selection.js +1 -9
- package/packages/core/release/promotion-request.js +1 -40
- package/packages/core/release/qualification/actions.js +0 -8
- package/packages/core/release/qualification/public-build.js +1 -1
- package/packages/core/release/recovery/candidate.js +15 -21
- package/packages/core/release/recovery/capsules.js +2 -10
- package/packages/core/release/recovery/lineage.js +305 -0
- package/packages/core/release/recovery/provenance.js +1 -2
- package/packages/core/release/recovery/publication.js +4 -14
- package/packages/core/release/recovery/readback.js +19 -32
- package/packages/core/release/recovery/runtime.js +4 -95
- package/packages/core/release/release-candidate-recovery.js +4 -9
- package/packages/core/release/release-candidate.js +2 -5
- package/packages/core/release/reseal/actions.js +3 -22
- package/packages/core/release/reseal/admission.js +0 -2
- package/packages/core/release/reseal/finalize-platform.js +0 -2
- package/packages/core/release/tail-reseal-github.js +1 -2
- package/packages/core/release/tail-reseal-receipt.js +0 -1
- package/packages/core/release/tail-reseal.js +2 -20
- package/packages/core/release/version-state.js +16 -13
- package/packages/core/runtime/buildchain-domain.wasm +0 -0
- package/packages/core/runtime/checkout-identity.js +0 -13
- package/packages/core/runtime/domain-wasm-artifact.js +2 -2
- package/packages/core/runtime/entry/actions.js +121 -0
- package/packages/core/runtime/entry/activation.js +86 -0
- package/packages/core/runtime/entry/github.js +55 -0
- package/packages/core/runtime/entry/recovery.js +37 -0
- package/packages/core/runtime/entry/selection.js +110 -0
- package/packages/core/web/controller.js +1 -1
- package/packages/core/web/publication-admission-action.js +1 -8
- package/packages/core/web/web-surface-publication-candidate.js +2 -5
- package/packages/core/workflow/admission/actions.js +6 -26
- package/packages/core/workflow/admission/recovery.js +3 -43
- package/packages/core/workflow/admission/transactions.js +8 -24
- package/packages/core/workflow/commands/buildchain-cli-help.mjs +1 -1
- package/packages/core/workflow/commands/universal-workflow-engine.mjs +2 -7
- package/packages/core/workflow/dogfood/actions.js +0 -17
- package/packages/core/workflow/engine/action.js +0 -5
- package/packages/core/workflow/engine/conformance.js +1 -1
- package/packages/core/workflow/engine/execution.js +2 -6
- package/packages/core/workflow/engine/identity.js +1 -6
- package/packages/core/workflow/engine/release-environment.js +11 -35
- package/packages/core/workflow/engine/release-promotion.js +2 -2
- package/packages/core/workflow/settlement/actions.js +0 -12
- package/packages/core/workflow/universal-self-dogfood.js +2 -16
- package/packages/core/workflow/universal-workflow-bootstrap.js +16 -241
- package/packages/core/workflow/workflow-taxonomy.mjs +2 -2
- package/scripts/check-dev-delivery-actions.mjs +17 -8
- package/scripts/check-floating-consumer-policy-contract.mjs +6 -6
- package/scripts/check-inventory.mjs +1 -10
- package/scripts/check-public-dogfood-contract.mjs +34 -21
- package/scripts/check-release-topology.mjs +2 -6
- package/scripts/check-runtime-entry.mjs +133 -0
- package/scripts/check-universal-workflow-bootstrap.mjs +45 -281
- package/scripts/check-workflows.sh +0 -7
- package/scripts/generate-bootstrap-admission.mjs +1 -1
- package/scripts/generate-channel-promotion-workflow.mjs +4 -4
- package/scripts/inventory/binary.mjs +4 -4
- package/scripts/inventory/promotion.mjs +5 -3
- package/scripts/runtime-source-boundaries.mjs +70 -0
- package/scripts/site-capability-metadata.mjs +2 -2
- package/scripts/workflow-action-graph.mjs +5 -18
- package/scripts/workflow-self-reference.mjs +20 -0
- package/templates/native-dev-delivery.yml +4 -4
- package/templates/universal-buildchain-bootstrap.yml +12 -2
- package/architecture/runtime-ref-resume-authority.json +0 -64
- package/architecture/universal-workflow-train-admission.json +0 -76
- package/contracts/v4-runtime-ref-resume-authority-v1.schema.json +0 -235
- package/docs/v4-runtime-ref-resume-authority.md +0 -69
- package/packages/core/consumer/channel-selection.js +0 -211
- package/packages/core/consumer/commands/buildchain-channel-router.mjs +0 -81
- package/packages/core/consumer/invocation-selector.js +0 -4
- package/packages/core/consumer/runtime-ref-resume-authority.js +0 -621
- package/packages/core/consumer/workflow-definition-authority.js +0 -64
- package/packages/core/contracts/runtime-contract-action.js +0 -48
- package/packages/core/release/commands/promotion-identity-resolver.mjs +0 -77
- package/packages/core/release/commands/verify-promotion-router-binding.sh +0 -36
- package/packages/core/release/promotion/identities.js +0 -56
- package/packages/core/release/runtime-override-authorization.js +0 -242
- package/packages/core/runtime/commands/runtime-ref-core.mjs +0 -171
- package/packages/core/runtime/provider-closure.js +0 -60
- package/packages/core/runtime/runtime-selection-action.js +0 -34
- package/packages/core/runtime/runtime-selection.js +0 -136
- package/packages/core/runtime/workflow-runtime.js +0 -11
- package/packages/core/workflow/admission/review.js +0 -188
- package/packages/core/workflow/commands/universal-workflow-backflow.mjs +0 -26
- package/packages/core/workflow/installation.js +0 -33
- package/packages/core/workflow/settlement/backflow.js +0 -187
- package/scripts/generate-bootstrap-recovery.mjs +0 -94
- package/templates/bootstrap-recovery/actions/build/source/admit/action.yml +0 -17
- package/templates/bootstrap-recovery/actions/build/source/admit/dist/index.js +0 -63
- package/templates/bootstrap-recovery/actions/runtime/dependencies/install/action.yml +0 -18
- package/templates/bootstrap-recovery/actions/runtime/dependencies/install/dist/index.js +0 -63
- package/templates/bootstrap-recovery/actions/runtime/environment/prepare/action.yml +0 -21
- package/templates/bootstrap-recovery/actions/workflow/admission/inspect/action.yml +0 -27
- package/templates/bootstrap-recovery/actions/workflow/admission/inspect/dist/index.js +0 -64
- package/templates/bootstrap-recovery/actions/workflow/admission/review/action.yml +0 -15
- package/templates/bootstrap-recovery/actions/workflow/admission/review/dist/index.js +0 -65
- package/templates/bootstrap-recovery/actions/workflow/recovery/admit/action.yml +0 -47
- package/templates/bootstrap-recovery/actions/workflow/recovery/execute/action.yml +0 -48
- package/templates/bootstrap-recovery/actions/workflow/recovery/settle/action.yml +0 -54
- package/templates/bootstrap-recovery/actions/workflow/settlement/seal/action.yml +0 -17
- package/templates/bootstrap-recovery/actions/workflow/settlement/seal/dist/index.js +0 -65
- package/templates/bootstrap-recovery/manifest.json +0 -68
- package/templates/bootstrap-recovery/package.json +0 -9
- package/templates/universal-buildchain-bootstrap-recovery.yml +0 -115
|
@@ -11,7 +11,7 @@ const DEFAULT_ROOT = path.resolve(
|
|
|
11
11
|
);
|
|
12
12
|
const CALLER_PATH = ".github/workflows/self-build-public-consumer-dogfood.yml";
|
|
13
13
|
const REUSABLE_PATH = ".github/workflows/public-build-stage-capsule-canary.yml";
|
|
14
|
-
export const
|
|
14
|
+
export const PUBLIC_DOGFOOD_ENTRY_REF = "v4";
|
|
15
15
|
const PRIVATE_CONSUMER = ["buildchain", "self", "dogfood"].join("-");
|
|
16
16
|
const PRIVATE_SHADOW = ["kungfu", "shadow"].join("-");
|
|
17
17
|
|
|
@@ -21,6 +21,11 @@ export function expectedPublicDogfoodWorkflow(validationRef) {
|
|
|
21
21
|
on:
|
|
22
22
|
pull_request:
|
|
23
23
|
workflow_dispatch:
|
|
24
|
+
inputs:
|
|
25
|
+
runtime-ref:
|
|
26
|
+
description: Transient runtime selected by the public entry.
|
|
27
|
+
type: string
|
|
28
|
+
default: ""
|
|
24
29
|
|
|
25
30
|
permissions:
|
|
26
31
|
contents: read
|
|
@@ -29,6 +34,7 @@ jobs:
|
|
|
29
34
|
dogfood:
|
|
30
35
|
uses: kungfu-systems/buildchain/.github/workflows/public-build-stage-capsule-canary.yml@${validationRef}
|
|
31
36
|
with:
|
|
37
|
+
runtime-ref: \${{ inputs.runtime-ref }}
|
|
32
38
|
consumer: buildchain
|
|
33
39
|
node-version: "24"
|
|
34
40
|
go-version: "1.25.x"
|
|
@@ -83,7 +89,7 @@ function assertReusableWorkflow(root) {
|
|
|
83
89
|
const nodePath = `actions/build/stage-capsule/${CANARY_NODES[phase]}`;
|
|
84
90
|
const call = workflow.jobs?.[phase]?.steps?.at(-1);
|
|
85
91
|
if (
|
|
86
|
-
call?.uses !== `./.buildchain/
|
|
92
|
+
call?.uses !== `./.buildchain/runtime/${nodePath}` ||
|
|
87
93
|
call.with?.["workflow-sha"] !== "${{ job.workflow_sha }}" ||
|
|
88
94
|
call.with?.["request-json"] !== "${{ toJSON(inputs) }}"
|
|
89
95
|
)
|
|
@@ -94,18 +100,19 @@ function assertReusableWorkflow(root) {
|
|
|
94
100
|
});
|
|
95
101
|
const qualification = YAML.parse(nodes[1]);
|
|
96
102
|
const steps = qualification.runs.steps;
|
|
97
|
-
const prepare = steps.findIndex((step) => step.id === "buildchain-runtime");
|
|
98
103
|
const consumerNode = steps.findIndex((step) =>
|
|
99
104
|
step.uses?.startsWith("actions/setup-node@"),
|
|
100
105
|
);
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
106
|
+
const jobSteps = workflow.jobs.qualify.steps;
|
|
107
|
+
const prepare = jobSteps.findIndex(
|
|
108
|
+
(step) => step.uses === "$/actions/runtime/environment/prepare",
|
|
109
|
+
);
|
|
110
|
+
const node = jobSteps.findIndex((step) =>
|
|
111
|
+
step.uses?.endsWith("/stage-capsule/qualify"),
|
|
112
|
+
);
|
|
113
|
+
if (prepare < 0 || prepare >= node || consumerNode < 0)
|
|
107
114
|
fail(
|
|
108
|
-
"Canary must
|
|
115
|
+
"Canary must prepare its selected runtime before the consumer toolchain",
|
|
109
116
|
);
|
|
110
117
|
if (
|
|
111
118
|
steps[consumerNode].with?.["node-version"] !==
|
|
@@ -118,7 +125,7 @@ function assertReusableWorkflow(root) {
|
|
|
118
125
|
"./.buildchain/runtime/actions/build/stage-capsule/qualify-consumer",
|
|
119
126
|
);
|
|
120
127
|
if (
|
|
121
|
-
campaign?.with?.["workflow-sha"] !== "${{
|
|
128
|
+
campaign?.with?.["workflow-sha"] !== "${{ env.BUILDCHAIN_RUNTIME_SHA }}" ||
|
|
122
129
|
campaign?.with?.["request-json"] !== "${{ inputs.request-json }}" ||
|
|
123
130
|
campaign?.with?.platform !== "${{ fromJSON(inputs.matrix-json).platform }}"
|
|
124
131
|
)
|
|
@@ -235,9 +242,9 @@ function assertProtectedVerify(root) {
|
|
|
235
242
|
const parsed = YAML.parse(verify);
|
|
236
243
|
const nodePath = "actions/build/verification/repository/action.yml";
|
|
237
244
|
if (
|
|
238
|
-
parsed.jobs.check.needs
|
|
245
|
+
![parsed.jobs.check.needs].flat().includes("stage-capsule-checkpoints") ||
|
|
239
246
|
parsed.jobs.check.steps.at(-1).uses !==
|
|
240
|
-
`./.buildchain/
|
|
247
|
+
`./.buildchain/runtime/${nodePath.replace(/\/action.yml$/, "")}`
|
|
241
248
|
)
|
|
242
249
|
fail(
|
|
243
250
|
"Verify must bind the protected check node after Stage Capsule checkpoints",
|
|
@@ -245,7 +252,10 @@ function assertProtectedVerify(root) {
|
|
|
245
252
|
const implementation = read(root, nodePath);
|
|
246
253
|
const steps = YAML.parse(implementation).runs.steps;
|
|
247
254
|
const qualify = steps.find((step) => step.id === "source-verification");
|
|
248
|
-
if (
|
|
255
|
+
if (
|
|
256
|
+
qualify?.uses !==
|
|
257
|
+
"./.buildchain/runtime/actions/build/verification/qualify-source"
|
|
258
|
+
)
|
|
249
259
|
fail("Verify is missing its owned source qualification action");
|
|
250
260
|
const transaction = read(root, "packages/core/build/verification/source.js");
|
|
251
261
|
for (const required of [
|
|
@@ -271,10 +281,13 @@ function assertArchitecture(root) {
|
|
|
271
281
|
);
|
|
272
282
|
const dogfood = architecture.publicConsumerDogfood;
|
|
273
283
|
const validationRef = dogfood?.validationRef;
|
|
274
|
-
if (validationRef !==
|
|
275
|
-
fail("architecture validationRef must use the
|
|
284
|
+
if (validationRef !== PUBLIC_DOGFOOD_ENTRY_REF)
|
|
285
|
+
fail("architecture validationRef must use the public v4 entry");
|
|
276
286
|
const caller = read(root, CALLER_PATH);
|
|
277
|
-
if (
|
|
287
|
+
if (
|
|
288
|
+
JSON.stringify(YAML.parse(caller)) !==
|
|
289
|
+
JSON.stringify(YAML.parse(expectedPublicDogfoodWorkflow(validationRef)))
|
|
290
|
+
)
|
|
278
291
|
fail(`${CALLER_PATH} must remain the exact thin public consumer caller`);
|
|
279
292
|
if (JSON.stringify(architecture.campaign?.consumers) !== '["buildchain"]')
|
|
280
293
|
fail(
|
|
@@ -285,7 +298,7 @@ function assertArchitecture(root) {
|
|
|
285
298
|
dogfood.callerWorkflow !== CALLER_PATH ||
|
|
286
299
|
dogfood.reusableWorkflow !==
|
|
287
300
|
"kungfu-systems/buildchain/.github/workflows/public-build-stage-capsule-canary.yml" ||
|
|
288
|
-
dogfood.runtimeBinding !== "
|
|
301
|
+
dogfood.runtimeBinding !== "entry-selected-runtime" ||
|
|
289
302
|
dogfood.consumerSourceBinding !== "github.sha" ||
|
|
290
303
|
JSON.stringify(dogfood.executableStages) !==
|
|
291
304
|
'["install","build","verify"]' ||
|
|
@@ -351,9 +364,9 @@ function assertPolicySources(root) {
|
|
|
351
364
|
const agents = read(root, "AGENTS.md");
|
|
352
365
|
for (const invariant of [
|
|
353
366
|
"same public reusable-workflow contract as every other consumer",
|
|
354
|
-
"No
|
|
355
|
-
"
|
|
356
|
-
"
|
|
367
|
+
"No repository-specific runtime selection or recovery exception",
|
|
368
|
+
"Normal self workflows",
|
|
369
|
+
"central entry resolves it once",
|
|
357
370
|
"source-persisted exact commit SHA",
|
|
358
371
|
"v4-alpha",
|
|
359
372
|
])
|
|
@@ -150,11 +150,7 @@ export function discoverReleaseAuthorityClosure() {
|
|
|
150
150
|
[".js", ".mjs", ".cjs"],
|
|
151
151
|
/(?:release-tail-provider-(?:adapters|plane)|create(?:GitHubReleaseAssets|SignedStaticChannel|SiteReleaseActivation|ReleasedEvidence)Adapter)/u,
|
|
152
152
|
);
|
|
153
|
-
const runtimeSelectors =
|
|
154
|
-
[".github/workflows", "scripts", "packages/core"],
|
|
155
|
-
[".yml", ".yaml", ".js", ".mjs", ".cjs"],
|
|
156
|
-
/(?:promotion-runtime-sha|resume-buildchain-runtime-sha|BUILDCHAIN_(?:CURRENT_RUNTIME_SHA|RUNTIME_REF|RESUME_CANDIDATE_RUN_ID)|authorizeRuntimeSelection|scanRuntimeSelectorPersistence|release-candidate-adapter)/u,
|
|
157
|
-
);
|
|
153
|
+
const runtimeSelectors = productionFiles("packages/core/runtime/entry", [".js"]).sort();
|
|
158
154
|
const terminalProjections = matchingProductionFiles(
|
|
159
155
|
[".github/workflows", "actions", "packages/core", "scripts"],
|
|
160
156
|
[".yml", ".yaml", ".js", ".mjs", ".cjs"],
|
|
@@ -560,7 +556,7 @@ export function checkReleaseTopology() {
|
|
|
560
556
|
);
|
|
561
557
|
assert.deepEqual(
|
|
562
558
|
publisher.jobs.map((job) => job.split("|", 1)[0]),
|
|
563
|
-
["apply", "qualify", "settle"],
|
|
559
|
+
["apply", "execution-runtime", "qualify", "settle"],
|
|
564
560
|
);
|
|
565
561
|
}
|
|
566
562
|
return actual;
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import YAML from "yaml";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { auditRuntimeSourceBoundaries } from "./runtime-source-boundaries.mjs";
|
|
6
|
+
|
|
7
|
+
const prepare = "actions/runtime/environment/prepare/action.yml";
|
|
8
|
+
const preparation = "$/actions/runtime/environment/prepare";
|
|
9
|
+
const entryActions = new Set([
|
|
10
|
+
"$/actions/runtime/selection/resolve",
|
|
11
|
+
preparation,
|
|
12
|
+
"$/actions/runtime/environment/activate",
|
|
13
|
+
]);
|
|
14
|
+
function yamlFiles(root, directory) {
|
|
15
|
+
return fs
|
|
16
|
+
.readdirSync(path.join(root, directory), { withFileTypes: true })
|
|
17
|
+
.flatMap((entry) => {
|
|
18
|
+
const file = `${directory}/${entry.name}`;
|
|
19
|
+
if (entry.isDirectory())
|
|
20
|
+
return ["node_modules", "dist"].includes(entry.name)
|
|
21
|
+
? []
|
|
22
|
+
: yamlFiles(root, file);
|
|
23
|
+
return /\.ya?ml$/u.test(file) ? [file] : [];
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
function auditCheckout(step, { file, at, prepared }, issues) {
|
|
27
|
+
if (!step.uses?.startsWith("actions/checkout")) return 0;
|
|
28
|
+
const directory = step.with?.path || ".";
|
|
29
|
+
const acquired =
|
|
30
|
+
/^\.buildchain\/(?:runtime|workflow-shell|bootstrap|attester-runtime|authority-runtime|policy-runtime|release-tail-runtime)$/u.test(
|
|
31
|
+
directory,
|
|
32
|
+
);
|
|
33
|
+
if (acquired && (file !== prepare || directory !== ".buildchain/runtime"))
|
|
34
|
+
issues.push(`${at}: execution code acquisition belongs only to ${prepare}`);
|
|
35
|
+
if (prepared && directory === ".")
|
|
36
|
+
issues.push(`${at}: source checkout erases the prepared runtime`);
|
|
37
|
+
if (
|
|
38
|
+
file !== prepare &&
|
|
39
|
+
/(?:runtime[-_.]sha|job\.workflow_sha|BUILDCHAIN_RUNTIME_SHA)/u.test(
|
|
40
|
+
JSON.stringify(step.with || {}),
|
|
41
|
+
)
|
|
42
|
+
)
|
|
43
|
+
issues.push(`${at}: source checkout must not select execution code`);
|
|
44
|
+
return Number(acquired);
|
|
45
|
+
}
|
|
46
|
+
function auditAction(step, { file, at, prepared }, issues) {
|
|
47
|
+
const uses = step.uses || "";
|
|
48
|
+
if (uses.startsWith("$/") && !entryActions.has(uses))
|
|
49
|
+
issues.push(`${at}: business code must load from the selected runtime`);
|
|
50
|
+
if (
|
|
51
|
+
uses.startsWith("./actions/") ||
|
|
52
|
+
/^\.\/\.buildchain\/(?!runtime\/).*\/actions\//u.test(uses)
|
|
53
|
+
)
|
|
54
|
+
issues.push(`${at}: noncanonical business action location`);
|
|
55
|
+
if (
|
|
56
|
+
file.startsWith(".github/workflows/") &&
|
|
57
|
+
uses.startsWith("./.buildchain/runtime/actions/") &&
|
|
58
|
+
!prepared
|
|
59
|
+
)
|
|
60
|
+
issues.push(`${at}: business action runs before preparation`);
|
|
61
|
+
if (file.startsWith("actions/") && uses === preparation)
|
|
62
|
+
issues.push(`${at}: business composites cannot prepare a second runtime`);
|
|
63
|
+
}
|
|
64
|
+
function auditBusinessAcquisition(root, directory, issues) {
|
|
65
|
+
const absolute = path.join(root, directory);
|
|
66
|
+
if (!fs.existsSync(absolute)) return;
|
|
67
|
+
for (const entry of fs.readdirSync(absolute, { withFileTypes: true })) {
|
|
68
|
+
const file = `${directory}/${entry.name}`;
|
|
69
|
+
if (entry.isDirectory()) {
|
|
70
|
+
if (!["node_modules", "dist"].includes(entry.name))
|
|
71
|
+
auditBusinessAcquisition(root, file, issues);
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
if (
|
|
75
|
+
!/\.(?:[cm]?js|sh|py|ps1)$/u.test(file) ||
|
|
76
|
+
file.startsWith("packages/core/runtime/entry/")
|
|
77
|
+
)
|
|
78
|
+
continue;
|
|
79
|
+
const source = fs.readFileSync(path.join(root, file), "utf8");
|
|
80
|
+
if (/\.[cm]?js$/u.test(file))
|
|
81
|
+
issues.push(...auditRuntimeSourceBoundaries(source, file));
|
|
82
|
+
const runtime =
|
|
83
|
+
/\.buildchain\/(?:runtime|workflow-shell|promotion-router)|BUILDCHAIN_RUNTIME_(?:ROOT|SHA)/u.test(
|
|
84
|
+
source,
|
|
85
|
+
);
|
|
86
|
+
const acquisition =
|
|
87
|
+
/["'](?:clone|checkout)["']|git\s+(?:clone|checkout)/u.test(source);
|
|
88
|
+
if (runtime && acquisition)
|
|
89
|
+
issues.push(`${file}: business code cannot acquire an execution runtime`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
export function auditRuntimeEntry(root) {
|
|
93
|
+
const issues = [];
|
|
94
|
+
const files = [
|
|
95
|
+
...yamlFiles(root, "actions"),
|
|
96
|
+
...yamlFiles(root, ".github/workflows"),
|
|
97
|
+
];
|
|
98
|
+
auditBusinessAcquisition(root, "packages/core", issues);
|
|
99
|
+
let acquisitions = 0;
|
|
100
|
+
for (const file of files) {
|
|
101
|
+
const doc = YAML.parse(fs.readFileSync(path.join(root, file), "utf8"));
|
|
102
|
+
for (const [job, node] of Object.entries(
|
|
103
|
+
doc.jobs || { action: doc.runs },
|
|
104
|
+
)) {
|
|
105
|
+
let preparations = 0;
|
|
106
|
+
for (const step of node?.steps || []) {
|
|
107
|
+
if (step.uses === preparation) preparations++;
|
|
108
|
+
const context = {
|
|
109
|
+
file,
|
|
110
|
+
at: `${file}:${job}:${step.id || step.name || step.uses}`,
|
|
111
|
+
prepared: preparations > 0,
|
|
112
|
+
};
|
|
113
|
+
auditAction(step, context, issues);
|
|
114
|
+
acquisitions += auditCheckout(step, context, issues);
|
|
115
|
+
}
|
|
116
|
+
if (preparations > 1)
|
|
117
|
+
issues.push(`${file}:${job}: runtime is prepared more than once`);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
if (acquisitions !== 1)
|
|
121
|
+
issues.push(
|
|
122
|
+
`Expected exactly one execution-code acquisition owner; found ${acquisitions}`,
|
|
123
|
+
);
|
|
124
|
+
return { files: files.length, acquisitions, issues };
|
|
125
|
+
}
|
|
126
|
+
if (
|
|
127
|
+
process.argv[1] &&
|
|
128
|
+
path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)
|
|
129
|
+
) {
|
|
130
|
+
const result = auditRuntimeEntry(path.resolve(import.meta.dirname, ".."));
|
|
131
|
+
console.log(JSON.stringify(result));
|
|
132
|
+
if (result.issues.length) process.exitCode = 1;
|
|
133
|
+
}
|
|
@@ -1,288 +1,52 @@
|
|
|
1
1
|
import assert from "node:assert/strict";
|
|
2
|
-
import YAML from "yaml";
|
|
3
|
-
|
|
4
|
-
import crypto from "node:crypto";
|
|
5
2
|
import fs from "node:fs";
|
|
6
3
|
import path from "node:path";
|
|
7
|
-
import
|
|
8
|
-
import
|
|
9
|
-
|
|
10
|
-
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
11
|
-
const contract = JSON.parse(
|
|
12
|
-
fs.readFileSync(
|
|
13
|
-
path.join(root, "architecture/universal-workflow-bootstrap.json"),
|
|
14
|
-
"utf8",
|
|
15
|
-
),
|
|
16
|
-
);
|
|
17
|
-
const admissionPolicy = JSON.parse(
|
|
18
|
-
fs.readFileSync(
|
|
19
|
-
path.join(root, "architecture/universal-workflow-train-admission.json"),
|
|
20
|
-
"utf8",
|
|
21
|
-
),
|
|
22
|
-
);
|
|
23
|
-
const admissionObservedAt = Date.now();
|
|
24
|
-
assert.ok(
|
|
25
|
-
admissionObservedAt >= Date.parse(admissionPolicy.validFrom) &&
|
|
26
|
-
admissionObservedAt < Date.parse(admissionPolicy.expiresAt),
|
|
27
|
-
`release admission policy is outside its validity window (${admissionPolicy.validFrom} to ${admissionPolicy.expiresAt}); renew it through protected review before publishing`,
|
|
28
|
-
);
|
|
29
|
-
const fileRoot = (relative) =>
|
|
30
|
-
`sha256:${crypto
|
|
31
|
-
.createHash("sha256")
|
|
32
|
-
.update(fs.readFileSync(path.join(root, relative)))
|
|
33
|
-
.digest("hex")}`;
|
|
34
|
-
|
|
35
|
-
const workflowRoot = path.join(root, ".github/workflows");
|
|
36
|
-
const discovered = fs
|
|
37
|
-
.readdirSync(workflowRoot)
|
|
38
|
-
.filter((name) => /\.ya?ml$/u.test(name))
|
|
39
|
-
.map((name) => ({
|
|
40
|
-
path: `.github/workflows/${name}`,
|
|
41
|
-
text: fs.readFileSync(path.join(workflowRoot, name), "utf8"),
|
|
42
|
-
}))
|
|
43
|
-
.filter((entry) => /(?:^|\n)\s*workflow_call:\s*(?:\n|$)/u.test(entry.text))
|
|
44
|
-
.map((entry) => entry.path)
|
|
45
|
-
.sort();
|
|
46
|
-
|
|
47
|
-
assert.equal(
|
|
48
|
-
contract.schema,
|
|
49
|
-
"kungfu-buildchain-v4-universal-workflow-bootstrap/v1",
|
|
50
|
-
);
|
|
51
|
-
assert.deepEqual(
|
|
52
|
-
contract.inventoryWorkflows,
|
|
53
|
-
[...new Set(contract.inventoryWorkflows)].sort(),
|
|
54
|
-
"inventoried public workflows must be sorted and duplicate-free",
|
|
55
|
-
);
|
|
56
|
-
assert.deepEqual(
|
|
57
|
-
contract.inventoryWorkflows,
|
|
58
|
-
discovered,
|
|
59
|
-
"every active public workflow_call surface must be inventoried",
|
|
60
|
-
);
|
|
61
|
-
for (const relative of contract.inventoryWorkflows)
|
|
62
|
-
assert.ok(
|
|
63
|
-
fs.statSync(path.join(root, relative)).isFile(),
|
|
64
|
-
`inventoried workflow is unavailable: ${relative}`,
|
|
65
|
-
);
|
|
66
|
-
assert.deepEqual(
|
|
67
|
-
contract.bootstrapGovernedWorkflows,
|
|
68
|
-
[...new Set(contract.bootstrapGovernedWorkflows)].sort(),
|
|
69
|
-
"Bootstrap-governed workflows must be sorted and duplicate-free",
|
|
70
|
-
);
|
|
71
|
-
assert.equal(
|
|
72
|
-
Object.hasOwn(contract, "migration"),
|
|
73
|
-
false,
|
|
74
|
-
"no historical facade regeneration authority",
|
|
75
|
-
);
|
|
76
|
-
assert.equal(
|
|
77
|
-
Object.hasOwn(contract, "retiredWorkflowSurfaces"),
|
|
78
|
-
false,
|
|
79
|
-
"retired workflows must be absent",
|
|
80
|
-
);
|
|
81
|
-
const activeWorkflows = contract.inventoryWorkflows;
|
|
82
|
-
assert.deepEqual(contract.bootstrapGovernedWorkflows, [
|
|
83
|
-
contract.bootstrap.publicWorkflow,
|
|
84
|
-
]);
|
|
85
|
-
assert.deepEqual(
|
|
86
|
-
contract.directCapabilityWorkflows,
|
|
87
|
-
discovered.filter(
|
|
88
|
-
(relative) =>
|
|
89
|
-
relative !== contract.bootstrap.publicWorkflow &&
|
|
90
|
-
relative !== contract.bootstrap.consumerRecoveryWorkflow,
|
|
91
|
-
),
|
|
92
|
-
);
|
|
93
|
-
for (const relative of contract.directCapabilityWorkflows) {
|
|
94
|
-
assert.doesNotMatch(
|
|
95
|
-
fs.readFileSync(path.join(root, relative), "utf8"),
|
|
96
|
-
/universal-request-json|universal-bootstrap:/u,
|
|
97
|
-
`${relative}: use the independent Bootstrap API instead of a dual execution path`,
|
|
98
|
-
);
|
|
99
|
-
}
|
|
100
|
-
assert.deepEqual(contract.configurationGovernedWorkflows, [
|
|
101
|
-
".github/workflows/.build.yml",
|
|
102
|
-
".github/workflows/build.yml",
|
|
103
|
-
]);
|
|
104
|
-
for (const relative of contract.configurationGovernedWorkflows) {
|
|
105
|
-
const source = fs.readFileSync(path.join(root, relative), "utf8");
|
|
106
|
-
const inputBlock = source
|
|
107
|
-
.split(" inputs:\n")[1]
|
|
108
|
-
.split(" secrets:\n")[0];
|
|
109
|
-
assert.deepEqual(
|
|
110
|
-
[...inputBlock.matchAll(/^ ([a-z0-9-]+):$/gmu)].map(
|
|
111
|
-
(match) => match[1],
|
|
112
|
-
),
|
|
113
|
-
["config-path"],
|
|
114
|
-
);
|
|
115
|
-
assert.doesNotMatch(source, /universal-request-json/u);
|
|
116
|
-
}
|
|
117
|
-
for (const relative of contract.bootstrapGovernedWorkflows) {
|
|
118
|
-
assert.ok(
|
|
119
|
-
contract.inventoryWorkflows.includes(relative),
|
|
120
|
-
`Bootstrap-governed workflow is outside the inventory: ${relative}`,
|
|
121
|
-
);
|
|
122
|
-
const source = fs.readFileSync(path.join(root, relative), "utf8");
|
|
123
|
-
assert.ok(
|
|
124
|
-
relative === contract.bootstrap.publicWorkflow ||
|
|
125
|
-
/uses:\s+(?:\.\/)?\.github\/workflows\/public-ops-bootstrap\.yml/u.test(
|
|
126
|
-
source,
|
|
127
|
-
) ||
|
|
128
|
-
/uses:\s+kungfu-systems\/buildchain\/\.github\/workflows\/public-ops-bootstrap\.yml@/u.test(
|
|
129
|
-
source,
|
|
130
|
-
),
|
|
131
|
-
`workflow is declared governed without a Bootstrap edge: ${relative}`,
|
|
132
|
-
);
|
|
133
|
-
}
|
|
4
|
+
import crypto from "node:crypto";
|
|
5
|
+
import YAML from "yaml";
|
|
6
|
+
import { auditRuntimeEntry } from "./check-runtime-entry.mjs";
|
|
134
7
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
assert.equal(contract.bootstrap.
|
|
146
|
-
assert.equal(contract.bootstrap.
|
|
147
|
-
assert.equal(contract.bootstrap.
|
|
148
|
-
assert.
|
|
149
|
-
|
|
150
|
-
assert.equal(
|
|
151
|
-
assert.
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
);
|
|
155
|
-
assert.deepEqual(
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
assert.
|
|
162
|
-
assert.ok(
|
|
8
|
+
const root = path.resolve(import.meta.dirname, "..");
|
|
9
|
+
const read = file => fs.readFileSync(path.join(root, file), "utf8");
|
|
10
|
+
const json = file => JSON.parse(read(file));
|
|
11
|
+
const parse = file => YAML.parse(read(file));
|
|
12
|
+
const contract = json("architecture/universal-workflow-bootstrap.json");
|
|
13
|
+
const policy = json("architecture/universal-workflow-capability-policy.json");
|
|
14
|
+
const files = fs.readdirSync(path.join(root, ".github/workflows")).filter(f => /\.ya?ml$/u.test(f)).map(f => `.github/workflows/${f}`);
|
|
15
|
+
const reusable = files.filter(f => parse(f).on?.workflow_call).sort();
|
|
16
|
+
assert.deepEqual(contract.inventoryWorkflows, reusable, "Every reusable API must have an owner");
|
|
17
|
+
assert.deepEqual([...contract.directCapabilityWorkflows, ...contract.bootstrapGovernedWorkflows].sort(), reusable, "Capabilities and bootstrap must partition the APIs");
|
|
18
|
+
assert.equal(contract.bootstrap.runtimeEntry, "architecture/runtime-entry.json");
|
|
19
|
+
assert.equal(contract.bootstrap.runtimeRecovery, "same-public-entry-with-transient-runtime");
|
|
20
|
+
assert.equal(contract.bootstrap.entryRecovery, "upgrade-public-entry-and-full-rerun");
|
|
21
|
+
assert.deepEqual(auditRuntimeEntry(root).issues, []);
|
|
22
|
+
for (const retired of ["templates/bootstrap-recovery", "templates/universal-buildchain-bootstrap-recovery.yml", ".github/workflows/public-ops-bootstrap-recovery.yml"])
|
|
23
|
+
assert.equal(fs.existsSync(path.join(root, retired)), false, `Copied recovery code must remain retired: ${retired}`);
|
|
24
|
+
assert.deepEqual(contract.capabilityAdapters.map(a => a.id).sort(), [...policy.allowedCapabilities].sort());
|
|
25
|
+
assert.ok(!policy.allowedCapabilities.includes("workflow-contract"), "Validation alone is not capability execution");
|
|
26
|
+
const now = Date.now();
|
|
27
|
+
assert.ok(now >= Date.parse(policy.validFrom) && now < Date.parse(policy.expiresAt), "Capability admission policy requires protected renewal");
|
|
28
|
+
assert.deepEqual(policy.contractRoots, contract.bootstrap.admissionPolicySources.map(f => `sha256:${crypto.createHash("sha256").update(read(f)).digest("hex")}`).sort());
|
|
29
|
+
const workflow = parse(contract.bootstrap.publicWorkflow);
|
|
30
|
+
assert.deepEqual(Object.keys(workflow.jobs), ["execution-runtime", "admit", "execute", "settle"]);
|
|
31
|
+
for (const phase of ["admit", "execute", "settle"]) {
|
|
32
|
+
const job = workflow.jobs[phase];
|
|
33
|
+
assert.ok(job.needs.includes("execution-runtime"));
|
|
34
|
+
assert.ok(job.steps.some(s => s.uses === `./.buildchain/runtime/actions/workflow/bootstrap/${phase}`));
|
|
35
|
+
assert.ok(job.steps.every(s => s.uses && !s.run && !s.shell));
|
|
163
36
|
}
|
|
164
|
-
assert.ok(
|
|
165
|
-
assert.
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
);
|
|
169
|
-
assert.doesNotMatch(
|
|
170
|
-
fs.readFileSync(
|
|
171
|
-
path.join(root, contract.bootstrap.candidateEnginePath),
|
|
172
|
-
"utf8",
|
|
173
|
-
),
|
|
174
|
-
/request\.capability\.id\s*===\s*["']workflow-contract["']/u,
|
|
175
|
-
"contract-only validation must not be admitted as successful execution",
|
|
176
|
-
);
|
|
177
|
-
const bootstrapSource = fs.readFileSync(
|
|
178
|
-
path.join(root, contract.bootstrap.publicWorkflow),
|
|
179
|
-
"utf8",
|
|
180
|
-
);
|
|
181
|
-
assert.doesNotMatch(
|
|
182
|
-
bootstrapSource,
|
|
183
|
-
/admission-policy-json:/u,
|
|
184
|
-
"callers must not supply their own admission authority",
|
|
185
|
-
);
|
|
186
|
-
assert.doesNotMatch(bootstrapSource, /^ permissions:/mu);
|
|
187
|
-
const parse = (relative) =>
|
|
188
|
-
YAML.parse(fs.readFileSync(path.join(root, relative), "utf8"));
|
|
189
|
-
const bootstrap = YAML.parse(bootstrapSource);
|
|
190
|
-
assert.deepEqual(bootstrap.jobs.settle.needs, ["admit", "execute"]);
|
|
191
|
-
const execute = parse("actions/workflow/bootstrap/execute/action.yml");
|
|
192
|
-
const executor = execute.runs.steps.find((step) => step.id === "execute");
|
|
193
|
-
assert.equal(
|
|
194
|
-
executor.with["mutation-token"],
|
|
195
|
-
"${{ inputs.secrets-buildchain-promotion-token }}",
|
|
196
|
-
);
|
|
37
|
+
assert.ok(workflow.jobs.execute.needs.includes("admit"));
|
|
38
|
+
assert.ok(workflow.jobs.settle.needs.includes("execute"));
|
|
39
|
+
const executor = parse("actions/workflow/bootstrap/execute/action.yml").runs.steps.find(s => s.id === "execute");
|
|
40
|
+
assert.equal(executor.with["mutation-token"], "${{ inputs.secrets-buildchain-promotion-token }}");
|
|
197
41
|
assert.equal(executor.with.token, "${{ github.token }}");
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
])
|
|
204
|
-
|
|
205
|
-
assert.equal(
|
|
206
|
-
assert.equal(
|
|
207
|
-
recovery.on.workflow_call.outputs["result-json"].value,
|
|
208
|
-
"${{ jobs.recovery-execute.outputs.result-json }}",
|
|
209
|
-
);
|
|
210
|
-
for (const [id, job] of Object.entries(recovery.jobs)) {
|
|
211
|
-
assert.ok(
|
|
212
|
-
job.steps.length <= 3,
|
|
213
|
-
`${id}: recovery workflow contains implementation details`,
|
|
214
|
-
);
|
|
215
|
-
const node = job.steps.find((step) => step.id === "node");
|
|
216
|
-
assert.equal(
|
|
217
|
-
node.uses,
|
|
218
|
-
`./.buildchain/workflow-shell/.buildchain/bootstrap-recovery/actions/workflow/recovery/${id.replace("recovery-", "")}`,
|
|
219
|
-
);
|
|
220
|
-
for (const step of job.steps) {
|
|
221
|
-
assert.equal(
|
|
222
|
-
Object.hasOwn(step, "run"),
|
|
223
|
-
false,
|
|
224
|
-
"Recovery workflow must dispatch owned nodes",
|
|
225
|
-
);
|
|
226
|
-
assert.ok(
|
|
227
|
-
!step.uses?.startsWith("kungfu-systems/buildchain/"),
|
|
228
|
-
"Recovery cannot depend on published Buildchain",
|
|
229
|
-
);
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
const recoveryAdmit = parse("actions/workflow/recovery/admit/action.yml").runs
|
|
233
|
-
.steps;
|
|
234
|
-
const reviewIndex = recoveryAdmit.findIndex((step) =>
|
|
235
|
-
/independent review/.test(step.name || ""),
|
|
236
|
-
);
|
|
237
|
-
const installationIndex = recoveryAdmit.findIndex((step) =>
|
|
238
|
-
step.uses?.endsWith("actions/runtime/environment/prepare"),
|
|
239
|
-
);
|
|
240
|
-
const admissionIndex = recoveryAdmit.findIndex((step) => step.id === "admit");
|
|
241
|
-
assert.ok(
|
|
242
|
-
reviewIndex >= 0 &&
|
|
243
|
-
installationIndex > reviewIndex &&
|
|
244
|
-
admissionIndex > installationIndex,
|
|
245
|
-
"No candidate dependency or code may execute before independent review",
|
|
246
|
-
);
|
|
247
|
-
execFileSync(
|
|
248
|
-
process.execPath,
|
|
249
|
-
[path.join(root, "scripts/generate-bootstrap-recovery.mjs"), "--check"],
|
|
250
|
-
{ cwd: root, stdio: "pipe" },
|
|
251
|
-
);
|
|
252
|
-
const primaryTemplate = parse(contract.bootstrap.consumerTemplate);
|
|
253
|
-
assert.ok(
|
|
254
|
-
Object.values(primaryTemplate.jobs).some(
|
|
255
|
-
(job) =>
|
|
256
|
-
job.uses ===
|
|
257
|
-
"kungfu-systems/buildchain/.github/workflows/public-ops-bootstrap.yml@v4",
|
|
258
|
-
),
|
|
259
|
-
);
|
|
260
|
-
const selfDogfood = parse(contract.bootstrap.selfDogfoodWorkflow);
|
|
261
|
-
for (const channel of ["conformance", "alpha", "stable"]) {
|
|
262
|
-
assert.equal(
|
|
263
|
-
selfDogfood.jobs[`primary-${channel}`].uses,
|
|
264
|
-
"./.github/workflows/public-ops-bootstrap.yml",
|
|
265
|
-
);
|
|
266
|
-
assert.equal(
|
|
267
|
-
selfDogfood.jobs[`recovery-${channel}`].uses,
|
|
268
|
-
"./.github/workflows/public-ops-bootstrap-recovery.yml",
|
|
269
|
-
);
|
|
42
|
+
assert.doesNotMatch(read(contract.bootstrap.publicWorkflow), /admission-policy-json:/u);
|
|
43
|
+
const template = parse(contract.bootstrap.consumerTemplate);
|
|
44
|
+
assert.equal(template.jobs.bootstrap.uses, "kungfu-systems/buildchain/.github/workflows/public-ops-bootstrap.yml@v4");
|
|
45
|
+
assert.equal(template.jobs.bootstrap.with["runtime-ref"], "${{ inputs.runtime-ref }}");
|
|
46
|
+
const dogfood = parse(contract.bootstrap.selfDogfoodWorkflow);
|
|
47
|
+
for (const channel of ["conformance", "alpha", "stable"]) for (const mode of ["primary", "recovery"]) {
|
|
48
|
+
const job = dogfood.jobs[`${mode}-${channel}`];
|
|
49
|
+
assert.equal(job.uses, "kungfu-systems/buildchain/.github/workflows/public-ops-bootstrap.yml@v4");
|
|
50
|
+
assert.equal(job.with["runtime-ref"], mode === "primary" ? "${{ inputs.runtime-ref }}" : "${{ inputs.recovery-runtime-ref }}");
|
|
270
51
|
}
|
|
271
|
-
|
|
272
|
-
console.log(
|
|
273
|
-
JSON.stringify({
|
|
274
|
-
ok: true,
|
|
275
|
-
schema: contract.schema,
|
|
276
|
-
publicWorkflowCount: discovered.length,
|
|
277
|
-
activePublicWorkflowCount: activeWorkflows.length,
|
|
278
|
-
inventoriedWorkflowCount: contract.inventoryWorkflows.length,
|
|
279
|
-
inventoryCoveragePercent: 100,
|
|
280
|
-
governedWorkflowCount: contract.bootstrapGovernedWorkflows.length,
|
|
281
|
-
governedCoveragePercent: Number(
|
|
282
|
-
(
|
|
283
|
-
(contract.bootstrapGovernedWorkflows.length / activeWorkflows.length) *
|
|
284
|
-
100
|
|
285
|
-
).toFixed(2),
|
|
286
|
-
),
|
|
287
|
-
}),
|
|
288
|
-
);
|
|
52
|
+
console.log(JSON.stringify({ ok: true, reusableWorkflowCount: reusable.length, runtimeAcquisitionOwners: 1 }));
|
|
@@ -1,11 +1,4 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
2
|
set -euo pipefail
|
|
3
3
|
|
|
4
|
-
if command -v actionlint >/dev/null 2>&1; then
|
|
5
|
-
find .github/workflows -maxdepth 1 -name '*.yml' -print0 | xargs -0 actionlint -color=false
|
|
6
|
-
else
|
|
7
|
-
find .github/workflows -maxdepth 1 -name '*.yml' -print0 | \
|
|
8
|
-
xargs -0 go run github.com/rhysd/actionlint/cmd/actionlint@v1.7.12 -color=false
|
|
9
|
-
fi
|
|
10
|
-
|
|
11
4
|
node scripts/check-dev-delivery-actions.mjs
|
|
@@ -13,7 +13,7 @@ const contract = JSON.parse(
|
|
|
13
13
|
);
|
|
14
14
|
const target = path.join(
|
|
15
15
|
root,
|
|
16
|
-
"architecture/universal-workflow-
|
|
16
|
+
"architecture/universal-workflow-capability-policy.json",
|
|
17
17
|
);
|
|
18
18
|
const policy = JSON.parse(fs.readFileSync(target, "utf8"));
|
|
19
19
|
policy.contractRoots = contract.bootstrap.admissionPolicySources
|