@kungfu-tech/buildchain 4.0.1-alpha.5 → 4.0.1-alpha.55
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/README.md +9 -6
- package/actions/release-tail/README.md +8 -0
- package/actions/v4-release-candidate-promote/README.md +3 -0
- package/architecture/agent-change-map.md +1015 -0
- package/architecture/ci-lane-change-budget.json +256 -0
- package/architecture/decisions/0004-v4-declarative-release-tail.md +47 -0
- package/architecture/internal-capabilities.json +220 -15
- package/architecture/maintainability-debt.json +3229 -0
- package/architecture/maintainability-policy.json +489 -132
- package/architecture/release-tail-contract-inventory.json +5 -2
- package/architecture/v3-core-mechanism-inventory.json +4 -3
- package/architecture/v3-v4-capability-closure.md +59 -0
- package/architecture/v3-v4-live-capability-inventory.json +102404 -0
- package/architecture/v4-adopter-delivery-parity.json +32 -5
- package/architecture/v4-cross-platform-adopter-qualification.json +52 -0
- package/architecture/v4-delivery-warrant-shadow-fixtures.json +3 -3
- package/architecture/v4-floating-consumer-policy.json +6 -0
- package/architecture/v4-publication-rehearsal-parity.json +84 -0
- package/architecture/v4-runtime-semantic-closure.json +710 -0
- package/bin/buildchain.mjs +7 -1
- package/bin/internal/adopter-delivery-cli.mjs +70 -0
- package/bin/internal/command-registry.mjs +1 -0
- package/bin/internal/trust-release-release-handlers.mjs +5 -0
- package/contracts/fixtures/v4-adopter-delivery-v1/archive-template.json +19 -0
- package/contracts/fixtures/v4-adopter-delivery-v1/bootstrap-positive.json +93 -0
- package/contracts/fixtures/v4-adopter-delivery-v1/gate-positive.json +29 -0
- package/contracts/fixtures/v4-adopter-delivery-v1/gate-unknown-selector.json +26 -0
- package/contracts/fixtures/v4-adopter-delivery-v1/offline-vectors.json +50 -0
- package/contracts/fixtures/v4-publication-rehearsal-v1/candidate/artifacts/product.bin +1 -0
- package/contracts/fixtures/v4-publication-rehearsal-v1/candidate/config/buildchain.toml +5 -0
- package/contracts/fixtures/v4-publication-rehearsal-v1/candidate/documents/release-activation.json +1 -0
- package/contracts/fixtures/v4-publication-rehearsal-v1/candidate/documents/signed-channel.json +1 -0
- package/contracts/fixtures/v4-publication-rehearsal-v1/candidate/evidence/qualification.json +1 -0
- package/contracts/fixtures/v4-publication-rehearsal-v1/candidate/manifests/candidate.json +5 -0
- package/contracts/fixtures/v4-publication-rehearsal-v1/candidate/manifests/release-passport.json +1 -0
- package/contracts/fixtures/v4-publication-rehearsal-v1/capsule.json +707 -0
- package/contracts/fixtures/v4-publication-rehearsal-v1/offline-vectors.json +27 -0
- package/contracts/fixtures/v4-tail-reseal-v1/valid.json +1 -1
- package/contracts/v4-adopter-delivery-v1.schema.json +81 -0
- package/contracts/v4-floating-consumer-policy-receipt-v1.schema.json +4 -2
- package/contracts/v4-publication-qualification-v1.schema.json +51 -0
- package/contracts/v4-publication-rehearsal-capsule-v1.schema.json +233 -0
- package/dist/site/agent-index.json +4 -0
- package/dist/site/artifact-schemas.json +8 -0
- package/dist/site/buildchain-contract.json +125 -34
- package/dist/site/buildchain-site.json +308 -33
- package/dist/site/capability-registry.json +13 -12
- package/dist/site/cli-registry.json +57 -2
- package/dist/site/controller-registry.json +41 -5
- package/dist/site/kfd-claims.json +438 -24
- package/dist/site/kfd-upstream-aggregate.json +1 -1
- package/dist/site/manual-registry.json +22 -7
- package/dist/site/node-api-registry.json +12843 -9314
- package/dist/site/page-registry.json +288 -21
- package/dist/site/public-surface-audit.json +281 -21
- package/dist/site/publication-authority-registry.json +66 -8
- package/dist/site/publication-registry.json +4 -4
- package/dist/site/release-provenance.json +20 -0
- package/dist/site/schemas/release-tail-capabilities-v1.schema.json +199 -0
- package/dist/site/schemas/v4-adopter-delivery-v1.schema.json +81 -0
- package/dist/site/schemas/v4-publication-qualification-v1.schema.json +51 -0
- package/dist/site/schemas/v4-publication-rehearsal-capsule-v1.schema.json +233 -0
- package/dist/site/site-manifest.json +19 -11
- package/dist/site/workflow-registry.json +195 -17
- package/docs/MAP.md +4 -2
- package/docs/cli-reference.md +70 -2
- package/docs/cli.md +8 -0
- package/docs/dev-delivery-warrant.md +34 -10
- package/docs/node-api-reference.md +524 -279
- package/docs/publish-transaction.md +11 -4
- package/docs/release-candidate.md +7 -0
- package/docs/release-tail-provider-plane.md +30 -0
- package/docs/v4-adopter-delivery.md +152 -0
- package/docs/v4-declarative-release-tail-migration.md +43 -0
- package/docs/v4-publication-rehearsal.md +130 -0
- package/package.json +24 -4
- package/packages/core/adopter-delivery-passport.js +295 -0
- package/packages/core/adopter-delivery-vectors.js +158 -0
- package/packages/core/adopter-delivery-vectors.json +77 -0
- package/packages/core/buildchain-agent-manuals.js +1 -0
- package/packages/core/buildchain-config.js +84 -0
- package/packages/core/buildchain-contract.js +31 -1
- package/packages/core/buildchain-delivery-bootstrap.js +240 -0
- package/packages/core/buildchain-delivery-infrastructure.js +164 -0
- package/packages/core/buildchain-delivery-self-dogfood.js +395 -0
- package/packages/core/buildchain-publication-authority.js +2 -0
- package/packages/core/ci-lane-change-budget.js +247 -0
- package/packages/core/dev-delivery-native-proof.js +43 -27
- package/packages/core/dev-delivery-warrant-settlement.js +239 -3
- package/packages/core/dev-delivery-warrant-state.js +46 -10
- package/packages/core/dev-delivery-warrant.js +29 -7
- package/packages/core/github-governance-authority.js +5 -4
- package/packages/core/index.js +6 -0
- package/packages/core/legacy-kfd-adopter-driver.js +167 -0
- package/packages/core/paper-scaffold-content.js +401 -0
- package/packages/core/paper.js +12 -378
- package/packages/core/publication-control-plane-audit.js +135 -111
- package/packages/core/publication-rehearsal-projection.js +104 -0
- package/packages/core/publication-rehearsal-runtime.js +190 -0
- package/packages/core/published-delivery-authority.js +266 -0
- package/packages/core/release-candidate-recovery.js +71 -15
- package/packages/core/release-candidate.js +2 -2
- package/packages/core/release-passport.js +95 -170
- package/packages/core/v4-adopter-delivery-parity.js +3 -3
- package/packages/core/v4-adopter-delivery.js +359 -0
- package/packages/core/v4-cross-platform-adopter-qualification.js +343 -0
- package/packages/core/v4-delivery-warrant-read-candidate.js +10 -3
- package/packages/core/v4-floating-consumer-evidence.js +18 -4
- package/packages/core/v4-floating-consumer-policy.js +139 -35
- package/packages/core/v4-floating-consumer-release-passport.js +154 -8
- package/packages/core/v4-publication-qualification.js +222 -0
- package/packages/core/v4-publication-rehearsal-capsule.js +468 -0
- package/packages/core/v4-publication-rehearsal-provider-bindings.js +212 -0
- package/packages/core/v4-publication-rehearsal.js +481 -0
- package/packages/core/workflow-yaml-contract.js +24 -2
- package/scripts/artifact-signing-controller-core.mjs +1 -1
- package/scripts/audit-publication-control-plane.mjs +17 -17
- package/scripts/buildchain-cli-help.mjs +13 -0
- package/scripts/check-ci-lane-change-budget.mjs +100 -0
- package/scripts/check-internal-architecture.mjs +23 -3
- package/scripts/check-inventory.mjs +24 -6
- package/scripts/check-maintainability.mjs +145 -4
- package/scripts/check-project-cut-merge-queue-admission.mjs +87 -0
- package/scripts/check-v3-v4-capability-inventory.mjs +574 -0
- package/scripts/check-v4-floating-consumer-policy-contract.mjs +30 -4
- package/scripts/check-v4-runtime-semantic-closure.mjs +266 -0
- package/scripts/dev-delivery-source-proof-replay.mjs +153 -0
- package/scripts/dev-delivery-source-proof-reuse.mjs +13 -97
- package/scripts/dev-delivery-warrant-options.mjs +27 -11
- package/scripts/dev-delivery-warrant-store.mjs +10 -6
- package/scripts/dev-delivery-warrant.mjs +50 -6
- package/scripts/dev-pr-auto-merge.mjs +25 -11
- package/scripts/dev-pr-delivery-warrant.mjs +45 -0
- package/scripts/dev-pr-prequeue-guard.mjs +0 -18
- package/scripts/dev-qualification-patrol.mjs +9 -9
- package/scripts/dispatch-artifact-signing-authority.mjs +1 -1
- package/scripts/generate-agent-change-map.mjs +118 -0
- package/scripts/generate-channel-promotion-workflow.mjs +2 -2
- package/scripts/generate-release-candidate-passport.mjs +177 -0
- package/scripts/generate-site-bundle.mjs +36 -0
- package/scripts/maintainability-domain-metrics.mjs +237 -0
- package/scripts/maintainability-governance.mjs +473 -0
- package/scripts/maintainability-metrics.mjs +28 -6
- package/scripts/project-cut-merge-queue-admission.mjs +394 -0
- package/scripts/promotion-channel-router.mjs +4 -4
- package/scripts/promotion-routing-evidence.mjs +35 -6
- package/scripts/release-candidate-anchor-provenance.mjs +99 -0
- package/scripts/release-candidate-resolver.mjs +44 -0
- package/scripts/release-line-policy.mjs +25 -15
- package/scripts/release-tail.mjs +68 -3
- package/scripts/resume-from-candidate-run.mjs +595 -65
- package/scripts/run-lifecycle-core.mjs +341 -376
- package/scripts/site-capability-metadata.mjs +21 -0
- package/scripts/stable-candidate-qualification.mjs +9 -10
- package/scripts/v3-v4-capability-catalog.mjs +439 -0
- package/scripts/v4-consumer-policy.mjs +4 -0
- package/scripts/v4-cross-platform-adopter-qualification.mjs +228 -0
- package/scripts/v4-declarative-promotion-admission.mjs +42 -0
- package/scripts/v4-publication-rehearsal-fixture.mjs +144 -0
|
@@ -72,77 +72,72 @@ function fact(id, pass, observed) {
|
|
|
72
72
|
};
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
-
|
|
75
|
+
function evaluatePublisherPolicy({
|
|
76
|
+
publisher,
|
|
77
|
+
publisherMode,
|
|
78
|
+
packageName,
|
|
76
79
|
repository,
|
|
77
80
|
workflowPath,
|
|
78
|
-
publisherWorkflowPath
|
|
81
|
+
publisherWorkflowPath,
|
|
82
|
+
workflowFilename,
|
|
79
83
|
environment,
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
publisherMode
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
const actions = snapshot?.actions || {};
|
|
91
|
-
const branchPolicy = snapshot?.branch || {};
|
|
92
|
-
const environmentPolicy = snapshot?.environment || {};
|
|
93
|
-
const oidc = snapshot?.oidc || {};
|
|
94
|
-
const publisher = snapshot?.publisher || {};
|
|
95
|
-
const runner = snapshot?.runner || {};
|
|
96
|
-
const npmIdentityPass = publisher.packageName === packageName &&
|
|
97
|
-
publisher.provider === "github" &&
|
|
98
|
-
publisher.repository === repository &&
|
|
99
|
-
publisher.workflowFilename === workflowFilename &&
|
|
100
|
-
publisher.environment === providerEnvironment &&
|
|
101
|
-
publisher.longLivedWorkflowCredentialPresent === false;
|
|
102
|
-
const publisherPass = publisherMode === "npm-trusted-publisher"
|
|
103
|
-
? npmIdentityPass && (
|
|
84
|
+
providerEnvironment,
|
|
85
|
+
}) {
|
|
86
|
+
if (publisherMode === "npm-trusted-publisher") {
|
|
87
|
+
const identityPass = publisher.packageName === packageName &&
|
|
88
|
+
publisher.provider === "github" &&
|
|
89
|
+
publisher.repository === repository &&
|
|
90
|
+
publisher.workflowFilename === workflowFilename &&
|
|
91
|
+
publisher.environment === providerEnvironment &&
|
|
92
|
+
publisher.longLivedWorkflowCredentialPresent === false;
|
|
93
|
+
return identityPass && (
|
|
104
94
|
(publisher.enforcement === "audited-control-plane" && publisher.allowPublish === true) ||
|
|
105
95
|
(publisher.enforcement === "provider-at-transaction" &&
|
|
106
96
|
publisher.authorizationDeferred === true &&
|
|
107
97
|
publisher.configurationRead === false)
|
|
108
|
-
)
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
if (publisherMode === "github-token") {
|
|
101
|
+
return publisher.provider === "github-token" &&
|
|
102
|
+
publisher.repository === repository &&
|
|
103
|
+
publisher.workflowPath === workflowPath &&
|
|
104
|
+
publisher.permissionScoped === true &&
|
|
105
|
+
publisher.longLivedWorkflowCredentialPresent === false;
|
|
106
|
+
}
|
|
107
|
+
if (publisherMode !== "oidc-role") return false;
|
|
108
|
+
return publisher.provider !== "" &&
|
|
109
|
+
publisher.repository === repository &&
|
|
110
|
+
publisher.workflowPath === publisherWorkflowPath &&
|
|
111
|
+
publisher.environment === environment &&
|
|
112
|
+
publisher.trustQualifying === true &&
|
|
113
|
+
/^[0-9a-f]{64}$/i.test(String(publisher.roleDigest || "").replace(/^sha256:/, "")) &&
|
|
114
|
+
publisher.longLivedWorkflowCredentialPresent === false;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function evaluateCredentialIsolation({ oidc, publisherMode, publisherWorkflowPath, providerEnvironment }) {
|
|
118
|
+
return publisherMode === "github-token"
|
|
125
119
|
? oidc.githubTokenJobScoped === true && oidc.longLivedCredentialPresent === false
|
|
126
120
|
: oidc.workflowPath === publisherWorkflowPath &&
|
|
127
121
|
oidc.environment === providerEnvironment &&
|
|
128
122
|
oidc.idTokenJobScoped === true &&
|
|
129
123
|
oidc.longLivedCredentialPresent === false;
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function requiredStatusCheckBinding(branchPolicy, requiredStatusCheck) {
|
|
127
|
+
const declared = branchPolicy.declaredRequiredStatusCheck || branchPolicy.requiredStatusCheck;
|
|
128
|
+
const resolved = branchPolicy.requiredStatusCheck;
|
|
129
|
+
return {
|
|
130
|
+
resolved,
|
|
131
|
+
pass: declared === requiredStatusCheck && (
|
|
132
|
+
resolved === declared ||
|
|
133
|
+
(resolved.startsWith(`${declared} / `) && branchPolicy.requiredStatusCheckMatchCount === 1)
|
|
134
|
+
),
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function sourceAuthorizationPass(branchPolicy) {
|
|
144
139
|
const authorizationSha = branchPolicy.authorizationSha || branchPolicy.sourceSha;
|
|
145
|
-
|
|
140
|
+
return branchPolicy.sourceSha === authorizationSha || (
|
|
146
141
|
branchPolicy.releaseReconciliation?.qualifying === true &&
|
|
147
142
|
branchPolicy.releaseReconciliation.parentSha === authorizationSha &&
|
|
148
143
|
/^\d+\.\d+\.\d+$/.test(String(branchPolicy.releaseReconciliation.version || "")) &&
|
|
@@ -150,74 +145,103 @@ export function evaluatePublicationControlPlaneSnapshot({
|
|
|
150
145
|
Array.isArray(branchPolicy.releaseReconciliation.changedPaths) &&
|
|
151
146
|
branchPolicy.releaseReconciliation.changedPaths.length > 0
|
|
152
147
|
);
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function evaluateConfiguredBranchPolicy(branchPolicy, branch) {
|
|
151
|
+
return branchPolicy.ref === branch &&
|
|
152
|
+
branchPolicy.strict === true &&
|
|
153
|
+
Number(branchPolicy.requiredApprovals || 0) >= 1 &&
|
|
154
|
+
branchPolicy.requireConversationResolution === true &&
|
|
155
|
+
branchPolicy.enforceAdmins === true;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function evaluateProviderBranchPolicy(branchPolicy, branch, requiredCheck) {
|
|
159
|
+
return branchPolicy.ref === branch &&
|
|
156
160
|
branchPolicy.policyMode === "provider-enforced-transaction" &&
|
|
157
161
|
branchPolicy.protected === true &&
|
|
158
162
|
branchPolicy.enforcementLevel === "everyone" &&
|
|
159
163
|
Array.isArray(branchPolicy.requiredStatusChecks) &&
|
|
160
|
-
|
|
161
|
-
branchPolicy.requiredStatusChecks.includes(
|
|
164
|
+
requiredCheck.pass &&
|
|
165
|
+
branchPolicy.requiredStatusChecks.includes(requiredCheck.resolved) &&
|
|
162
166
|
branchPolicy.requiredCheckPassed === true &&
|
|
163
167
|
/^[0-9a-f]{40}$/i.test(String(branchPolicy.requiredCheckSha || "")) &&
|
|
164
|
-
branchPolicy.requiredCheckSha === branchPolicy.pullRequestHeadSha
|
|
165
|
-
|
|
166
|
-
|
|
168
|
+
branchPolicy.requiredCheckSha === branchPolicy.pullRequestHeadSha;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function evaluateProviderPullRequest(branchPolicy, branch, repository) {
|
|
172
|
+
return (branchPolicy.sourceSha === branchPolicy.headSha || branchPolicy.sourceContainedInBranch === true) &&
|
|
173
|
+
sourceAuthorizationPass(branchPolicy) &&
|
|
167
174
|
branchPolicy.mergedPullRequest === true &&
|
|
168
175
|
branchPolicy.baseRef === branch &&
|
|
169
176
|
branchPolicy.headRepository === repository &&
|
|
170
177
|
Number(branchPolicy.approvalCount || 0) >= 1 &&
|
|
171
178
|
branchPolicy.independentApproval === true;
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
)
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
publisherPass,
|
|
203
|
-
publisher,
|
|
204
|
-
),
|
|
205
|
-
fact(
|
|
206
|
-
"runner-policy",
|
|
207
|
-
runner.class === "ephemeral" &&
|
|
208
|
-
runner.label === "ubuntu-24.04" &&
|
|
209
|
-
runner.githubHosted === true &&
|
|
210
|
-
runner.selfHostedAuthorized === false,
|
|
211
|
-
runner,
|
|
212
|
-
),
|
|
213
|
-
];
|
|
214
|
-
return createPublicationControlPlaneAudit({
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function evaluateBranchPolicy({ branchPolicy, branch, repository, requiredStatusCheck }) {
|
|
182
|
+
const requiredCheck = requiredStatusCheckBinding(branchPolicy, requiredStatusCheck);
|
|
183
|
+
return evaluateConfiguredBranchPolicy(branchPolicy, branch) || (
|
|
184
|
+
evaluateProviderBranchPolicy(branchPolicy, branch, requiredCheck) &&
|
|
185
|
+
evaluateProviderPullRequest(branchPolicy, branch, repository)
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function evaluateEnvironmentPolicy(environmentPolicy, environment) {
|
|
190
|
+
return environment === "none"
|
|
191
|
+
? environmentPolicy.declared === false && environmentPolicy.exists === false
|
|
192
|
+
: environmentPolicy.name === environment &&
|
|
193
|
+
environmentPolicy.declared === true &&
|
|
194
|
+
environmentPolicy.exists === true &&
|
|
195
|
+
environmentPolicy.protected === true &&
|
|
196
|
+
environmentPolicy.branchAuthorized === true &&
|
|
197
|
+
(environmentPolicy.reviewRequired !== true || environmentPolicy.preventSelfReview === true);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function evaluateRunnerPolicy(runner) {
|
|
201
|
+
return runner.class === "ephemeral" &&
|
|
202
|
+
runner.label === "ubuntu-24.04" &&
|
|
203
|
+
runner.githubHosted === true &&
|
|
204
|
+
runner.selfHostedAuthorized === false;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function evaluatePublicationControlPlaneSnapshotOptions(options) {
|
|
208
|
+
const {
|
|
215
209
|
repository,
|
|
216
210
|
workflowPath,
|
|
217
|
-
publisherWorkflowPath,
|
|
211
|
+
publisherWorkflowPath = workflowPath,
|
|
218
212
|
environment,
|
|
219
|
-
|
|
213
|
+
branch,
|
|
214
|
+
packageName,
|
|
215
|
+
publisherMode = "npm-trusted-publisher",
|
|
216
|
+
requiredStatusCheck = "check",
|
|
217
|
+
snapshot,
|
|
220
218
|
observedAt,
|
|
221
219
|
expiresAt,
|
|
220
|
+
} = options;
|
|
221
|
+
const workflowFilename = String(publisherWorkflowPath || "").split("/").pop();
|
|
222
|
+
const providerEnvironment = environment === "none" ? "" : environment;
|
|
223
|
+
const actions = snapshot?.actions || {};
|
|
224
|
+
const branchPolicy = snapshot?.branch || {};
|
|
225
|
+
const environmentPolicy = snapshot?.environment || {};
|
|
226
|
+
const oidc = snapshot?.oidc || {};
|
|
227
|
+
const publisher = snapshot?.publisher || {};
|
|
228
|
+
const runner = snapshot?.runner || {};
|
|
229
|
+
const facts = [
|
|
230
|
+
fact("actions-policy", actions.defaultWorkflowPermissions === "read" && actions.canApprovePullRequestReviews === false, actions),
|
|
231
|
+
fact("branch-policy", evaluateBranchPolicy({ branchPolicy, branch, repository, requiredStatusCheck }), branchPolicy),
|
|
232
|
+
fact("environment-policy", evaluateEnvironmentPolicy(environmentPolicy, environment), environmentPolicy),
|
|
233
|
+
fact("oidc-policy", evaluateCredentialIsolation({ oidc, publisherMode, publisherWorkflowPath, providerEnvironment }), oidc),
|
|
234
|
+
fact("publisher-policy", evaluatePublisherPolicy({
|
|
235
|
+
publisher, publisherMode, packageName, repository, workflowPath,
|
|
236
|
+
publisherWorkflowPath, workflowFilename, environment, providerEnvironment,
|
|
237
|
+
}), publisher),
|
|
238
|
+
fact("runner-policy", evaluateRunnerPolicy(runner), runner),
|
|
239
|
+
];
|
|
240
|
+
return createPublicationControlPlaneAudit({
|
|
241
|
+
repository, workflowPath, publisherWorkflowPath, environment, facts, observedAt, expiresAt,
|
|
222
242
|
});
|
|
223
243
|
}
|
|
244
|
+
|
|
245
|
+
export function evaluatePublicationControlPlaneSnapshot({ repository, workflowPath, publisherWorkflowPath = workflowPath, environment, branch, packageName, publisherMode = "npm-trusted-publisher", requiredStatusCheck = "check", snapshot, observedAt, expiresAt, } = {}) {
|
|
246
|
+
return evaluatePublicationControlPlaneSnapshotOptions({ repository, workflowPath, publisherWorkflowPath, environment, branch, packageName, publisherMode, requiredStatusCheck, snapshot, observedAt, expiresAt });
|
|
247
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import {
|
|
2
|
+
PUBLICATION_REHEARSAL_CAPSULE_CONTRACT,
|
|
3
|
+
PUBLICATION_REHEARSAL_COMMAND,
|
|
4
|
+
RELEASE_LOCAL_CONSTRUCTIBILITY_ADR,
|
|
5
|
+
RELEASE_LOCAL_CONSTRUCTIBILITY_INVARIANT,
|
|
6
|
+
} from "./publication-rehearsal-runtime.js";
|
|
7
|
+
|
|
8
|
+
export const PUBLICATION_REHEARSAL_AGENT_SECTION_START =
|
|
9
|
+
"<!-- buildchain:publication-rehearsal:v4:start -->";
|
|
10
|
+
export const PUBLICATION_REHEARSAL_AGENT_SECTION_END =
|
|
11
|
+
"<!-- buildchain:publication-rehearsal:v4:end -->";
|
|
12
|
+
export const PUBLICATION_REHEARSAL_WORKFLOW_PATH =
|
|
13
|
+
".github/workflows/publication-rehearsal.yml";
|
|
14
|
+
|
|
15
|
+
export function publicationRehearsalToml() {
|
|
16
|
+
return `[publication_rehearsal]\ncontract = ${JSON.stringify(PUBLICATION_REHEARSAL_CAPSULE_CONTRACT)}\nadr = ${JSON.stringify(RELEASE_LOCAL_CONSTRUCTIBILITY_ADR)}\ninvariant = ${JSON.stringify(RELEASE_LOCAL_CONSTRUCTIBILITY_INVARIANT)}\ncommand = ${JSON.stringify(PUBLICATION_REHEARSAL_COMMAND)}\ncapsule = ".buildchain/publication-rehearsal/capsule.json"\ncandidate_root = ".buildchain/publication-rehearsal/candidate"\nstate = ".buildchain/publication-rehearsal/state.json"\nevidence = ".buildchain/publication-rehearsal/evidence.json"\n`;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function assertPublicationRehearsalConfig(config) {
|
|
20
|
+
const section = config?.publication_rehearsal;
|
|
21
|
+
if (!section || typeof section !== "object" || Array.isArray(section))
|
|
22
|
+
throw new Error("publication_rehearsal table is missing");
|
|
23
|
+
const expected = {
|
|
24
|
+
contract: PUBLICATION_REHEARSAL_CAPSULE_CONTRACT,
|
|
25
|
+
adr: RELEASE_LOCAL_CONSTRUCTIBILITY_ADR,
|
|
26
|
+
invariant: RELEASE_LOCAL_CONSTRUCTIBILITY_INVARIANT,
|
|
27
|
+
command: PUBLICATION_REHEARSAL_COMMAND,
|
|
28
|
+
capsule: ".buildchain/publication-rehearsal/capsule.json",
|
|
29
|
+
candidate_root: ".buildchain/publication-rehearsal/candidate",
|
|
30
|
+
state: ".buildchain/publication-rehearsal/state.json",
|
|
31
|
+
evidence: ".buildchain/publication-rehearsal/evidence.json",
|
|
32
|
+
};
|
|
33
|
+
if (
|
|
34
|
+
JSON.stringify(Object.keys(section).sort()) !==
|
|
35
|
+
JSON.stringify(Object.keys(expected).sort())
|
|
36
|
+
)
|
|
37
|
+
throw new Error(
|
|
38
|
+
`publication_rehearsal fields must be exactly: ${Object.keys(expected).sort().join(", ")}`,
|
|
39
|
+
);
|
|
40
|
+
for (const [key, value] of Object.entries(expected))
|
|
41
|
+
if (section[key] !== value)
|
|
42
|
+
throw new Error(`publication_rehearsal.${key} is stale or unsupported`);
|
|
43
|
+
return structuredClone(section);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function appendPublicationRehearsalToml(source) {
|
|
47
|
+
const current = String(source || "").trimEnd();
|
|
48
|
+
if (/^\[publication_rehearsal\]$/mu.test(current))
|
|
49
|
+
throw new Error(
|
|
50
|
+
"buildchain config already contains a publication_rehearsal table",
|
|
51
|
+
);
|
|
52
|
+
return `${current}\n\n${publicationRehearsalToml()}`;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function projectPublicationRehearsalToml(source) {
|
|
56
|
+
const current = String(source || "");
|
|
57
|
+
const start = current.search(/^\[publication_rehearsal\]\s*$/mu);
|
|
58
|
+
if (start === -1) return appendPublicationRehearsalToml(current);
|
|
59
|
+
const remainder = current.slice(start);
|
|
60
|
+
const newline = remainder.indexOf("\n");
|
|
61
|
+
const nextTable = remainder.slice(newline + 1).search(/^\[[^\n]+\]\s*$/mu);
|
|
62
|
+
const end =
|
|
63
|
+
nextTable === -1 ? current.length : start + newline + 1 + nextTable;
|
|
64
|
+
const prefix = current.slice(0, start).trimEnd();
|
|
65
|
+
const suffix = current.slice(end).trimStart();
|
|
66
|
+
return `${prefix}\n\n${publicationRehearsalToml()}${suffix ? `\n${suffix}` : ""}`;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function publicationRehearsalAgentInstructions() {
|
|
70
|
+
return `${PUBLICATION_REHEARSAL_AGENT_SECTION_START}\n## Publication rehearsal (required)\n\nBuildchain v4 rehearsal follows \`${RELEASE_LOCAL_CONSTRUCTIBILITY_ADR}\`.\n${RELEASE_LOCAL_CONSTRUCTIBILITY_INVARIANT}\n\nRun the source-bound capsule with:\n\n\`\`\`sh\n${PUBLICATION_REHEARSAL_COMMAND}\n\`\`\`\n\nSimulation and replay evidence never claim production publication authority.\n${PUBLICATION_REHEARSAL_AGENT_SECTION_END}`;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function mergePublicationRehearsalAgentInstructions(current = "") {
|
|
74
|
+
const source = String(current || "");
|
|
75
|
+
const section = publicationRehearsalAgentInstructions();
|
|
76
|
+
const start = source.indexOf(PUBLICATION_REHEARSAL_AGENT_SECTION_START);
|
|
77
|
+
const end = source.indexOf(PUBLICATION_REHEARSAL_AGENT_SECTION_END);
|
|
78
|
+
if ((start === -1) !== (end === -1) || end < start)
|
|
79
|
+
throw new Error(
|
|
80
|
+
"AGENTS.md has an incomplete Buildchain publication rehearsal section",
|
|
81
|
+
);
|
|
82
|
+
if (start === -1)
|
|
83
|
+
return source.trim()
|
|
84
|
+
? `${source.trimEnd()}\n\n${section}\n`
|
|
85
|
+
: `# AGENTS.md\n\n${section}\n`;
|
|
86
|
+
if (
|
|
87
|
+
source.indexOf(PUBLICATION_REHEARSAL_AGENT_SECTION_START, start + 1) !==
|
|
88
|
+
-1 ||
|
|
89
|
+
source.indexOf(PUBLICATION_REHEARSAL_AGENT_SECTION_END, end + 1) !== -1
|
|
90
|
+
)
|
|
91
|
+
throw new Error(
|
|
92
|
+
"AGENTS.md has ambiguous Buildchain publication rehearsal sections",
|
|
93
|
+
);
|
|
94
|
+
return `${source.slice(0, start)}${section}${source.slice(
|
|
95
|
+
end + PUBLICATION_REHEARSAL_AGENT_SECTION_END.length,
|
|
96
|
+
)}`;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export function publicationRehearsalWorkflow(buildchainRef) {
|
|
100
|
+
const ref = String(buildchainRef || "").trim();
|
|
101
|
+
if (!ref)
|
|
102
|
+
throw new Error("publication rehearsal workflow requires a Buildchain ref");
|
|
103
|
+
return `name: Publication Rehearsal\n\non:\n workflow_dispatch:\n inputs:\n capsule-path:\n description: "Source-bound v4 publication rehearsal capsule"\n required: true\n default: ".buildchain/publication-rehearsal/capsule.json"\n candidate-root:\n description: "Repository-relative candidate root"\n required: true\n default: ".buildchain/publication-rehearsal/candidate"\n\npermissions:\n contents: read\n\njobs:\n rehearsal:\n uses: kungfu-systems/buildchain/.github/workflows/release-tail.yml@${ref}\n with:\n buildchain-ref: ${ref}\n rehearsal-capsule-path: \${{ inputs.capsule-path }}\n candidate-root: \${{ inputs.candidate-root }}\n rehearsal-mode: simulate\n state-path: ".buildchain/publication-rehearsal/state.json"\n rehearsal-evidence-path: ".buildchain/publication-rehearsal/evidence.json"\n`;
|
|
104
|
+
}
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
|
|
4
|
+
import { releaseTailRoot } from "./release-tail-provider-plane.js";
|
|
5
|
+
import {
|
|
6
|
+
V4_PUBLICATION_REHEARSAL_CAPSULE_CONTRACT,
|
|
7
|
+
V4_PUBLICATION_REHEARSAL_EVIDENCE_CONTRACT,
|
|
8
|
+
createV4PublicationRehearsalCapsule,
|
|
9
|
+
executeV4PublicationRehearsal,
|
|
10
|
+
validateV4PublicationRehearsalCapsule,
|
|
11
|
+
verifyV4PublicationRehearsalCapsule,
|
|
12
|
+
} from "./v4-publication-rehearsal.js";
|
|
13
|
+
|
|
14
|
+
export const PUBLICATION_REHEARSAL_CAPSULE_CONTRACT =
|
|
15
|
+
V4_PUBLICATION_REHEARSAL_CAPSULE_CONTRACT;
|
|
16
|
+
export const PUBLICATION_REHEARSAL_EVIDENCE_CONTRACT =
|
|
17
|
+
V4_PUBLICATION_REHEARSAL_EVIDENCE_CONTRACT;
|
|
18
|
+
export const PUBLICATION_REHEARSAL_DIAGNOSTIC_CONTRACT =
|
|
19
|
+
"buildchain-v4-publication-rehearsal-compatibility-diagnostic/v1";
|
|
20
|
+
export const RELEASE_LOCAL_CONSTRUCTIBILITY_ADR =
|
|
21
|
+
"docs/v4-publication-rehearsal.md";
|
|
22
|
+
export const RELEASE_LOCAL_CONSTRUCTIBILITY_INVARIANT =
|
|
23
|
+
"Publication rehearsal is source-, candidate-, manifest-, config-, policy-, observation-, and shared-core-bound; rehearsal authority is effect-disabled by default and never grants production authority.";
|
|
24
|
+
export const PUBLICATION_REHEARSAL_COMMAND =
|
|
25
|
+
'buildchain release-tail rehearse --capsule "$PWD/.buildchain/publication-rehearsal/capsule.json" --candidate-root "$PWD/.buildchain/publication-rehearsal/candidate" --mode simulate --state "$PWD/.buildchain/publication-rehearsal/state.json" --evidence "$PWD/.buildchain/publication-rehearsal/evidence.json"';
|
|
26
|
+
|
|
27
|
+
const REQUIRED_V4_INPUTS = Object.freeze([
|
|
28
|
+
"source.repository",
|
|
29
|
+
"source.revision",
|
|
30
|
+
"manifest.path",
|
|
31
|
+
"manifest.root",
|
|
32
|
+
"config.path",
|
|
33
|
+
"config.root",
|
|
34
|
+
]);
|
|
35
|
+
|
|
36
|
+
function migrationReceipt(surface, reasonCode) {
|
|
37
|
+
const body = {
|
|
38
|
+
schema: "buildchain-v3-v4-public-surface-migration/v1",
|
|
39
|
+
surface,
|
|
40
|
+
status: "migration-required",
|
|
41
|
+
reasonCode,
|
|
42
|
+
replacement: {
|
|
43
|
+
module: "@kungfu-tech/buildchain/v4-publication-rehearsal",
|
|
44
|
+
capsuleContract: V4_PUBLICATION_REHEARSAL_CAPSULE_CONTRACT,
|
|
45
|
+
requiredInputs: REQUIRED_V4_INPUTS,
|
|
46
|
+
},
|
|
47
|
+
productionAuthority: false,
|
|
48
|
+
};
|
|
49
|
+
return { ...body, migrationRoot: releaseTailRoot(body) };
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export class PublicationRehearsalError extends Error {
|
|
53
|
+
constructor(
|
|
54
|
+
message,
|
|
55
|
+
{
|
|
56
|
+
code = "publication-rehearsal-v4-migration-required",
|
|
57
|
+
classification = "migration",
|
|
58
|
+
migration = null,
|
|
59
|
+
cause,
|
|
60
|
+
} = {},
|
|
61
|
+
) {
|
|
62
|
+
super(message, cause ? { cause } : undefined);
|
|
63
|
+
this.name = "PublicationRehearsalError";
|
|
64
|
+
this.rehearsalCode = code;
|
|
65
|
+
this.rehearsalClass = classification;
|
|
66
|
+
this.migration = migration;
|
|
67
|
+
this.bindingRoot = "";
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function hasV4CreationBindings(input) {
|
|
72
|
+
return Boolean(
|
|
73
|
+
input?.source?.repository &&
|
|
74
|
+
input?.source?.revision &&
|
|
75
|
+
input?.manifest?.path &&
|
|
76
|
+
input?.manifest?.root &&
|
|
77
|
+
input?.config?.path &&
|
|
78
|
+
input?.config?.root,
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function requireV4CreationBindings(input, surface) {
|
|
83
|
+
if (hasV4CreationBindings(input)) return;
|
|
84
|
+
const migration = migrationReceipt(surface, "v4-source-binding-required");
|
|
85
|
+
throw new PublicationRehearsalError(
|
|
86
|
+
"v3 rehearsal capsules cannot be upgraded implicitly; provide the v4 source, manifest, and config bindings",
|
|
87
|
+
{ migration },
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export function createPublicationRehearsalCapsule(input = {}) {
|
|
92
|
+
requireV4CreationBindings(
|
|
93
|
+
input,
|
|
94
|
+
"@kungfu-tech/buildchain/publication-rehearsal-runtime#createPublicationRehearsalCapsule",
|
|
95
|
+
);
|
|
96
|
+
return createV4PublicationRehearsalCapsule(input);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export function normalizePublicationRehearsalCapsule(input) {
|
|
100
|
+
return validateV4PublicationRehearsalCapsule(input);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export function verifyPublicationRehearsalCapsule({
|
|
104
|
+
capsule,
|
|
105
|
+
capsuleRoot,
|
|
106
|
+
candidateRoot = capsuleRoot,
|
|
107
|
+
} = {}) {
|
|
108
|
+
return verifyV4PublicationRehearsalCapsule({ capsule, candidateRoot });
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export function publicationRehearsalBindingRoot(capsule) {
|
|
112
|
+
return validateV4PublicationRehearsalCapsule(capsule).capsuleRoot;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export function publicationRehearsalDiagnostic(error, { capsule } = {}) {
|
|
116
|
+
const migration = error?.migration || null;
|
|
117
|
+
const body = {
|
|
118
|
+
contract: PUBLICATION_REHEARSAL_DIAGNOSTIC_CONTRACT,
|
|
119
|
+
status: "rejected",
|
|
120
|
+
errorClass: error?.rehearsalClass || (migration ? "migration" : "input"),
|
|
121
|
+
code:
|
|
122
|
+
error?.rehearsalCode ||
|
|
123
|
+
error?.code ||
|
|
124
|
+
"publication-rehearsal-runtime-error",
|
|
125
|
+
message: String(error?.message || "publication rehearsal failed"),
|
|
126
|
+
bindingRoot: capsule?.capsuleRoot || "",
|
|
127
|
+
migration,
|
|
128
|
+
productionAuthority: false,
|
|
129
|
+
};
|
|
130
|
+
return { ...body, diagnosticRoot: releaseTailRoot(body) };
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export async function executePublicationRehearsal({
|
|
134
|
+
capsule,
|
|
135
|
+
capsuleRoot,
|
|
136
|
+
candidateRoot = capsuleRoot,
|
|
137
|
+
mode = "simulate",
|
|
138
|
+
environment = {},
|
|
139
|
+
adapters = {},
|
|
140
|
+
authority = null,
|
|
141
|
+
transaction = null,
|
|
142
|
+
checkpoint,
|
|
143
|
+
} = {}) {
|
|
144
|
+
if (
|
|
145
|
+
!environment ||
|
|
146
|
+
typeof environment !== "object" ||
|
|
147
|
+
Array.isArray(environment) ||
|
|
148
|
+
Object.keys(environment).length > 0
|
|
149
|
+
) {
|
|
150
|
+
const migration = migrationReceipt(
|
|
151
|
+
"@kungfu-tech/buildchain/publication-rehearsal-runtime#executePublicationRehearsal",
|
|
152
|
+
"ambient-environment-forbidden",
|
|
153
|
+
);
|
|
154
|
+
throw new PublicationRehearsalError(
|
|
155
|
+
"v4 rehearsal accepts no ambient environment; bind every semantic input into the capsule",
|
|
156
|
+
{ code: "undeclared-environment", classification: "input", migration },
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
return executeV4PublicationRehearsal({
|
|
160
|
+
capsule,
|
|
161
|
+
candidateRoot,
|
|
162
|
+
mode,
|
|
163
|
+
adapters,
|
|
164
|
+
authority,
|
|
165
|
+
transaction,
|
|
166
|
+
checkpoint,
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export function resolvePublicationRehearsalFile(capsuleRoot, relativePath) {
|
|
171
|
+
if (!path.isAbsolute(capsuleRoot))
|
|
172
|
+
throw new PublicationRehearsalError(
|
|
173
|
+
"capsuleRoot must be an explicit absolute path",
|
|
174
|
+
{ code: "implicit-workspace-forbidden", classification: "input" },
|
|
175
|
+
);
|
|
176
|
+
const base = fs.realpathSync(capsuleRoot);
|
|
177
|
+
const resolved = path.resolve(base, String(relativePath || ""));
|
|
178
|
+
if (
|
|
179
|
+
!resolved.startsWith(`${base}${path.sep}`) ||
|
|
180
|
+
!fs.existsSync(resolved) ||
|
|
181
|
+
!fs.statSync(resolved).isFile() ||
|
|
182
|
+
fs.lstatSync(resolved).isSymbolicLink() ||
|
|
183
|
+
!fs.realpathSync(resolved).startsWith(`${base}${path.sep}`)
|
|
184
|
+
)
|
|
185
|
+
throw new PublicationRehearsalError(
|
|
186
|
+
"publication rehearsal file must be a regular file inside the explicit candidate root",
|
|
187
|
+
{ code: "capsule-path-ambiguous", classification: "input" },
|
|
188
|
+
);
|
|
189
|
+
return resolved;
|
|
190
|
+
}
|