@kungfu-tech/buildchain 4.0.1-alpha.2 → 4.0.1-alpha.21
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/actions/release-tail/README.md +8 -0
- package/architecture/ci-lane-change-budget.json +187 -0
- package/architecture/decisions/0002-next-development-transition.md +101 -0
- package/architecture/decisions/0003-two-phase-delivery-warrant.md +152 -0
- package/architecture/internal-capabilities.json +211 -8
- package/architecture/maintainability-policy.json +113 -28
- package/architecture/release-tail-contract-inventory.json +5 -2
- package/architecture/v3-core-mechanism-inventory.json +91 -0
- package/architecture/v4-adopter-delivery-parity.json +32 -5
- package/architecture/v4-capability-state-machine-manifest.json +73 -0
- package/architecture/v4-compatibility-facts-parity.json +77 -0
- package/architecture/v4-delivery-authority-parity.json +250 -0
- package/architecture/v4-delivery-warrant-shadow-fixtures.json +41 -9
- package/architecture/v4-floating-consumer-policy.json +6 -0
- package/architecture/v4-next-development-parity.json +192 -0
- package/architecture/v4-publication-rehearsal-parity.json +84 -0
- package/architecture/v4-release-train-parity.json +1 -0
- package/architecture/v4-tail-reseal-parity.json +150 -0
- package/bin/buildchain.mjs +32 -3
- package/bin/internal/adopter-delivery-cli.mjs +70 -0
- package/bin/internal/command-registry.mjs +3 -0
- package/bin/internal/compatibility-facts-cli.mjs +101 -0
- package/contracts/dev-delivery-authority-v2.schema.json +662 -0
- package/contracts/fixtures/next-development-transition-v1/anchored-manual-waiting.json +47 -0
- package/contracts/fixtures/next-development-transition-v1/semver-auto-planned.json +47 -0
- package/contracts/fixtures/next-development-transition-v1/version-model-cases.json +40 -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-compatibility-facts-v1/shared.json +1129 -0
- package/contracts/fixtures/v4-floating-consumer-policy-v1/cases.json +7 -1
- 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 +229 -0
- package/contracts/next-development-request-v1.schema.json +66 -0
- package/contracts/next-development-transition-v1.schema.json +292 -0
- package/contracts/v4-adopter-delivery-v1.schema.json +81 -0
- package/contracts/v4-compatibility-facts-v1.schema.json +230 -0
- package/contracts/v4-floating-consumer-policy-receipt-v1.schema.json +4 -2
- package/contracts/v4-publication-rehearsal-capsule-v1.schema.json +233 -0
- package/contracts/v4-tail-reseal-v1.schema.json +276 -0
- package/dist/site/agent-index.json +5 -0
- package/dist/site/artifact-schemas.json +10 -0
- package/dist/site/buildchain-contract.json +1475 -27
- package/dist/site/buildchain-site.json +627 -31
- package/dist/site/capability-registry.json +17 -14
- package/dist/site/cli-registry.json +205 -3
- package/dist/site/kfd-claims.json +490 -16
- package/dist/site/kfd-upstream-aggregate.json +1 -1
- package/dist/site/manual-registry.json +52 -6
- package/dist/site/node-api-registry.json +14736 -8851
- package/dist/site/page-registry.json +593 -21
- package/dist/site/public-surface-audit.json +837 -48
- package/dist/site/publication-authority-registry.json +44 -8
- package/dist/site/publication-registry.json +4 -4
- package/dist/site/release-provenance.json +24 -0
- package/dist/site/schemas/dev-delivery-authority-v2.schema.json +662 -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-compatibility-facts-v1.schema.json +230 -0
- package/dist/site/schemas/v4-publication-rehearsal-capsule-v1.schema.json +233 -0
- package/dist/site/site-manifest.json +34 -10
- package/dist/site/workflow-registry.json +160 -10
- package/docs/MAP.md +8 -2
- package/docs/cli-reference.md +404 -13
- package/docs/cli.md +8 -0
- package/docs/dev-alpha-candidate-patrol.md +8 -0
- package/docs/dev-delivery-qualification-landing-adr.md +251 -0
- package/docs/dev-delivery-warrant.md +351 -38
- package/docs/next-development-transition.md +118 -0
- package/docs/node-api-reference.md +646 -293
- package/docs/release-tail-provider-plane.md +30 -0
- package/docs/v4-adopter-delivery.md +133 -0
- package/docs/v4-compatibility-facts.md +91 -0
- package/docs/v4-publication-rehearsal.md +130 -0
- package/docs/v4-tail-reseal.md +73 -0
- package/docs/versioning.md +4 -3
- package/package.json +34 -6
- package/packages/core/adopter-delivery-passport.js +259 -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 +3 -0
- package/packages/core/buildchain-compatibility-authority.js +336 -0
- package/packages/core/buildchain-compatibility-fact.js +394 -0
- package/packages/core/buildchain-compatibility-facts.json +217 -0
- package/packages/core/buildchain-compatibility-proof.js +566 -0
- package/packages/core/buildchain-config.js +84 -0
- package/packages/core/buildchain-contract.js +96 -143
- 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 +1 -0
- package/packages/core/channel-candidate.js +8 -0
- package/packages/core/channel-promotion-baseline.js +55 -0
- package/packages/core/ci-lane-change-budget.js +247 -0
- package/packages/core/dev-alpha-candidate-selection.js +10 -2
- package/packages/core/dev-delivery-authority-candidate.js +270 -0
- package/packages/core/dev-delivery-authority-evidence.js +146 -0
- package/packages/core/dev-delivery-authority-landing.js +461 -0
- package/packages/core/dev-delivery-authority-observation.js +48 -0
- package/packages/core/dev-delivery-authority-qualification.js +591 -0
- package/packages/core/dev-delivery-authority-settlement.js +213 -0
- package/packages/core/dev-delivery-authority-state.js +583 -0
- package/packages/core/dev-delivery-candidate-identity.js +13 -0
- package/packages/core/dev-delivery-contract-surface.js +76 -0
- package/packages/core/dev-delivery-execution-failure.js +133 -0
- package/packages/core/dev-delivery-execution-transfer.js +592 -0
- package/packages/core/dev-delivery-landing-admission-core.js +119 -0
- package/packages/core/dev-delivery-landing-readback.js +598 -0
- package/packages/core/dev-delivery-landing-terminal-evidence.js +271 -0
- package/packages/core/dev-delivery-landing-testing-port.js +6 -0
- package/packages/core/dev-delivery-native-execution.js +110 -0
- package/packages/core/dev-delivery-native-proof.js +562 -0
- package/packages/core/dev-delivery-process-boundary.js +563 -0
- package/packages/core/dev-delivery-provider-attempt.js +127 -0
- package/packages/core/dev-delivery-provider-heartbeat.js +382 -0
- package/packages/core/dev-delivery-warrant-cancellation.js +1 -0
- package/packages/core/dev-delivery-warrant-qualification.js +145 -0
- package/packages/core/dev-delivery-warrant-settlement.js +237 -36
- package/packages/core/dev-delivery-warrant-state.js +587 -0
- package/packages/core/dev-delivery-warrant.js +328 -370
- package/packages/core/github-governance-authority.js +1 -0
- package/packages/core/index.js +5 -0
- package/packages/core/kungfu-temporal-fact.js +557 -0
- package/packages/core/legacy-kfd-adopter-driver.js +167 -0
- package/packages/core/next-development-candidate-reservation.js +186 -0
- package/packages/core/next-development-controller.js +728 -0
- package/packages/core/next-development-projection.js +287 -0
- package/packages/core/next-development-transition.js +738 -0
- package/packages/core/paper-agent-entry.js +11 -5
- package/packages/core/paper.js +16 -5
- package/packages/core/published-delivery-authority.js +266 -0
- package/packages/core/release-candidate-recovery.js +70 -15
- package/packages/core/release-candidate.js +2 -2
- package/packages/core/release-passport.js +36 -9
- package/packages/core/v4-adopter-delivery-parity.js +3 -3
- package/packages/core/v4-adopter-delivery.js +359 -0
- package/packages/core/v4-canonical-contracts.js +5 -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 +143 -20
- package/packages/core/v4-floating-consumer-release-passport.js +32 -5
- 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/v4-tail-reseal-contract.js +26 -0
- package/packages/core/v4-tail-reseal-github.js +178 -0
- package/packages/core/v4-tail-reseal-receipt.js +225 -0
- package/packages/core/v4-tail-reseal.js +594 -0
- package/packages/core/workflow-yaml-contract.js +24 -2
- package/scripts/buildchain-cli-help.mjs +41 -2
- package/scripts/check-ci-lane-change-budget.mjs +100 -0
- package/scripts/check-inventory.mjs +14 -3
- package/scripts/check-v4-floating-consumer-policy-contract.mjs +23 -4
- package/scripts/dev-alpha-candidate-patrol.mjs +22 -1
- package/scripts/dev-delivery-authority-command-adapters.mjs +206 -0
- package/scripts/dev-delivery-authority-provider.mjs +28 -0
- package/scripts/dev-delivery-authority.mjs +490 -0
- package/scripts/dev-delivery-native-run.mjs +177 -0
- package/scripts/dev-delivery-process-boundary.mjs +260 -0
- package/scripts/dev-delivery-proof.mjs +67 -2
- package/scripts/dev-delivery-provider-heartbeat.mjs +215 -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-two-phase-resume.mjs +345 -0
- package/scripts/dev-delivery-two-phase.mjs +573 -0
- package/scripts/dev-delivery-warrant-options.mjs +266 -0
- package/scripts/dev-delivery-warrant-store.mjs +231 -0
- package/scripts/dev-delivery-warrant.mjs +232 -194
- package/scripts/dev-pr-auto-merge.mjs +1 -1
- package/scripts/dev-pr-delivery-warrant.mjs +11 -0
- package/scripts/dev-qualification-patrol.mjs +9 -9
- package/scripts/dispatch-artifact-signing-authority.mjs +1 -1
- package/scripts/generate-channel-promotion-workflow.mjs +2 -2
- package/scripts/generate-next-development-guidance.mjs +49 -0
- package/scripts/generate-release-candidate-passport.mjs +152 -0
- package/scripts/generate-site-bundle.mjs +69 -4
- package/scripts/init-repo.mjs +22 -4
- package/scripts/next-development-self-dogfood-harness.mjs +460 -0
- package/scripts/next-development-self-dogfood.mjs +597 -0
- package/scripts/next-development-transition.mjs +47 -0
- package/scripts/npm-publish-transaction.mjs +2 -2
- package/scripts/promotion-channel-router.mjs +4 -4
- package/scripts/release-candidate-anchor-provenance.mjs +99 -0
- package/scripts/release-tail.mjs +51 -3
- package/scripts/release-train-self-dogfood.mjs +94 -92
- package/scripts/resume-from-candidate-run.mjs +557 -30
- package/scripts/site-capability-metadata.mjs +41 -0
- package/scripts/stable-candidate-qualification.mjs +9 -10
- package/scripts/v4-consumer-policy.mjs +5 -5
- package/scripts/v4-publication-rehearsal-fixture.mjs +144 -0
- package/scripts/v4-tail-reseal-macos-rehearsal.mjs +134 -0
- package/scripts/v4-tail-reseal.mjs +416 -0
- package/templates/native-dev-delivery.yml +143 -0
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
|
|
3
|
+
export function resolveAnchorRecoveryRequest({ passport, buildSummary, transactionId = "" } = {}) {
|
|
4
|
+
if (Number(passport?.pullRequest?.number || 0)) return undefined;
|
|
5
|
+
if (passport?.target?.channel !== "anchor" || passport?.target?.ref !== "publish-gate/anchor") {
|
|
6
|
+
throw new Error("Release Candidate Passport has no PR identity");
|
|
7
|
+
}
|
|
8
|
+
let releaseManifest;
|
|
9
|
+
try {
|
|
10
|
+
releaseManifest = JSON.parse(buildSummary?.publishSource?.releaseManifest || "");
|
|
11
|
+
} catch {
|
|
12
|
+
throw new Error("anchored candidate release manifest is invalid");
|
|
13
|
+
}
|
|
14
|
+
const request = releaseManifest?.anchorRequest;
|
|
15
|
+
if (
|
|
16
|
+
releaseManifest?.sourceRef !== "publish-gate/anchor" ||
|
|
17
|
+
releaseManifest?.sourceSha !== passport.source?.headSha ||
|
|
18
|
+
request?.schema !== 1 ||
|
|
19
|
+
request?.contract !== "kungfu-buildchain-explicit-publish-anchor-request/v1" ||
|
|
20
|
+
!request.assignmentId ||
|
|
21
|
+
!/^native:[0-9a-f-]+$/u.test(String(request.attemptId || "")) ||
|
|
22
|
+
request.transactionId !== transactionId ||
|
|
23
|
+
request.source?.sha !== passport.source?.headSha ||
|
|
24
|
+
request.source?.tree !== passport.source?.treeHash ||
|
|
25
|
+
!/^\d+$/u.test(String(request.supersededCandidate?.workflowRunId || "")) ||
|
|
26
|
+
!/^[0-9a-f]{40}$/u.test(String(request.supersededCandidate?.sha || "")) ||
|
|
27
|
+
!/^sha256:[0-9a-f]{64}$/u.test(String(request.supersededCandidate?.root || "")) ||
|
|
28
|
+
request.runtime?.sha !== passport.buildchain?.sha
|
|
29
|
+
) {
|
|
30
|
+
throw new Error("anchored candidate request does not bind the sealed recovery identity");
|
|
31
|
+
}
|
|
32
|
+
return request;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function normalizeRun(run, repository) {
|
|
36
|
+
return {
|
|
37
|
+
id: String(run.id), repository,
|
|
38
|
+
headRepository: run.head_repository?.full_name || "",
|
|
39
|
+
status: run.status, conclusion: run.conclusion,
|
|
40
|
+
event: run.event, path: run.path, name: run.name,
|
|
41
|
+
headSha: run.head_sha || "", headBranch: run.head_branch || "",
|
|
42
|
+
pullRequestNumbers: (run.pull_requests || []).map((entry) => Number(entry.number)),
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function normalizeCandidateRun(run, repository) {
|
|
47
|
+
return normalizeRun(run, repository);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function normalizeAnchorProvenance(anchorProvenance, repository) {
|
|
51
|
+
if (!anchorProvenance) return undefined;
|
|
52
|
+
return {
|
|
53
|
+
request: anchorProvenance.request,
|
|
54
|
+
run: normalizeRun(anchorProvenance.run, repository),
|
|
55
|
+
workflow: {
|
|
56
|
+
path: anchorProvenance.workflow.path,
|
|
57
|
+
name: anchorProvenance.workflow.name,
|
|
58
|
+
state: anchorProvenance.workflow.state,
|
|
59
|
+
},
|
|
60
|
+
passport: anchorProvenance.passport,
|
|
61
|
+
buildSummary: anchorProvenance.buildSummary,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export async function recoverCandidateProvenance({
|
|
66
|
+
passport, buildSummary, transactionId, repoInfo, artifactName,
|
|
67
|
+
expectedWorkflowFile, expectedWorkflowName, channel, apiUrl, token,
|
|
68
|
+
fetchImpl, archiveDir, bundleRoot, githubJson, selectReleaseCandidateArtifacts,
|
|
69
|
+
downloadArtifact, readOnlyJson,
|
|
70
|
+
}) {
|
|
71
|
+
const request = resolveAnchorRecoveryRequest({ passport, buildSummary, transactionId });
|
|
72
|
+
if (!request) {
|
|
73
|
+
return { anchorProvenance: undefined, provenancePassport: passport };
|
|
74
|
+
}
|
|
75
|
+
const runId = String(request.supersededCandidate.workflowRunId);
|
|
76
|
+
const run = await githubJson({ apiUrl, token, fetchImpl, path: `/repos/${repoInfo.owner}/${repoInfo.repo}/actions/runs/${runId}` });
|
|
77
|
+
const workflow = await githubJson({ apiUrl, token, fetchImpl, path: `/repos/${repoInfo.owner}/${repoInfo.repo}/actions/workflows/${run.workflow_id}` });
|
|
78
|
+
const artifactResponse = await githubJson({ apiUrl, token, fetchImpl, path: `/repos/${repoInfo.owner}/${repoInfo.repo}/actions/runs/${runId}/artifacts?per_page=100` });
|
|
79
|
+
const artifacts = Array.isArray(artifactResponse.artifacts) ? artifactResponse.artifacts : [];
|
|
80
|
+
if (Number(artifactResponse.total_count || artifacts.length) !== artifacts.length) throw new Error("superseded candidate run has more than 100 artifacts; complete pagination is required before recovery");
|
|
81
|
+
const selected = selectReleaseCandidateArtifacts({ artifacts, artifactName });
|
|
82
|
+
const provenanceRoot = path.join(bundleRoot, "anchored-provenance");
|
|
83
|
+
const passportDownload = await downloadArtifact({ artifact: selected.passport, repoInfo, apiUrl, token, archiveDir, bundleRoot: provenanceRoot, fetchImpl });
|
|
84
|
+
const summaryDownload = await downloadArtifact({ artifact: selected.summary, repoInfo, apiUrl, token, archiveDir, bundleRoot: provenanceRoot, fetchImpl });
|
|
85
|
+
const recoveredPassport = readOnlyJson(passportDownload.files.filter((file) => path.basename(file.path) === "release-candidate-passport.json"), "superseded release-candidate-passport.json");
|
|
86
|
+
const recoveredBuildSummary = readOnlyJson(summaryDownload.files.filter((file) => path.basename(file.path) === "build-summary.json"), "superseded build-summary.json");
|
|
87
|
+
if (
|
|
88
|
+
String(run.id) !== runId ||
|
|
89
|
+
recoveredPassport.workflow?.runId !== runId ||
|
|
90
|
+
recoveredPassport.workflow?.name !== expectedWorkflowName ||
|
|
91
|
+
String(workflow.path || run.path || "").split("@")[0].replace(/^\.github\/workflows\//u, "") !== expectedWorkflowFile.replace(/^\.github\/workflows\//u, "") ||
|
|
92
|
+
recoveredPassport.target?.channel !== channel ||
|
|
93
|
+
recoveredPassport.source?.headSha !== request.supersededCandidate.sha ||
|
|
94
|
+
recoveredPassport.source?.treeHash !== request.source.tree ||
|
|
95
|
+
`sha256:${recoveredPassport.candidateHash}` !== request.supersededCandidate.root
|
|
96
|
+
) throw new Error("superseded candidate provenance does not match the anchored request");
|
|
97
|
+
const anchorProvenance = { request, run, workflow, passport: recoveredPassport, buildSummary: recoveredBuildSummary };
|
|
98
|
+
return { anchorProvenance, provenancePassport: recoveredPassport };
|
|
99
|
+
}
|
package/scripts/release-tail.mjs
CHANGED
|
@@ -11,6 +11,10 @@ import {
|
|
|
11
11
|
writeReleaseTailTransaction,
|
|
12
12
|
} from "../packages/core/release-tail-provider-plane.js";
|
|
13
13
|
import { diagnoseLegacyReleaseTailHooks } from "../packages/core/release-tail-compatibility.js";
|
|
14
|
+
import {
|
|
15
|
+
V4_PUBLICATION_REHEARSAL_EVIDENCE_CONTRACT,
|
|
16
|
+
executeV4PublicationRehearsal,
|
|
17
|
+
} from "../packages/core/v4-publication-rehearsal.js";
|
|
14
18
|
|
|
15
19
|
function flag(args, name, fallback = "") {
|
|
16
20
|
const index = args.indexOf(`--${name}`);
|
|
@@ -34,8 +38,52 @@ function output(value, filePath = "") {
|
|
|
34
38
|
process.stdout.write(`${JSON.stringify(value, null, 2)}\n`);
|
|
35
39
|
}
|
|
36
40
|
|
|
37
|
-
export function runReleaseTailCli(args = process.argv.slice(2)) {
|
|
41
|
+
export async function runReleaseTailCli(args = process.argv.slice(2)) {
|
|
38
42
|
const [mode = "status", ...options] = args;
|
|
43
|
+
if (mode === "rehearse") {
|
|
44
|
+
const capsulePath = flag(options, "capsule");
|
|
45
|
+
const candidateRoot = flag(options, "candidate-root");
|
|
46
|
+
const statePath = flag(options, "state");
|
|
47
|
+
const evidencePath = flag(options, "evidence");
|
|
48
|
+
for (const [name, value] of Object.entries({
|
|
49
|
+
"--capsule": capsulePath,
|
|
50
|
+
"--candidate-root": candidateRoot,
|
|
51
|
+
"--state": statePath,
|
|
52
|
+
"--evidence": evidencePath,
|
|
53
|
+
}))
|
|
54
|
+
if (!value || !path.isAbsolute(value))
|
|
55
|
+
throw new Error(`${name} must be an explicit absolute path`);
|
|
56
|
+
const rehearsalMode = flag(options, "mode", "simulate");
|
|
57
|
+
if (!new Set(["simulate", "replay"]).has(rehearsalMode))
|
|
58
|
+
throw new Error(
|
|
59
|
+
"local rehearsal --mode must be simulate or replay; authorized provider rehearsal uses the reusable workflow",
|
|
60
|
+
);
|
|
61
|
+
try {
|
|
62
|
+
const result = await executeV4PublicationRehearsal({
|
|
63
|
+
capsule: readJson(capsulePath, "--capsule"),
|
|
64
|
+
candidateRoot,
|
|
65
|
+
mode: rehearsalMode,
|
|
66
|
+
checkpoint: (transaction) =>
|
|
67
|
+
writeReleaseTailTransaction(statePath, transaction),
|
|
68
|
+
});
|
|
69
|
+
writeReleaseTailTransaction(statePath, result.transaction);
|
|
70
|
+
output(result.evidence, evidencePath);
|
|
71
|
+
return result;
|
|
72
|
+
} catch (error) {
|
|
73
|
+
output(
|
|
74
|
+
{
|
|
75
|
+
schema: V4_PUBLICATION_REHEARSAL_EVIDENCE_CONTRACT,
|
|
76
|
+
status: "rejected",
|
|
77
|
+
code: error.code || "publication-rehearsal-runtime-error",
|
|
78
|
+
message: error.message,
|
|
79
|
+
productionAuthority: false,
|
|
80
|
+
releasePassport: null,
|
|
81
|
+
},
|
|
82
|
+
evidencePath,
|
|
83
|
+
);
|
|
84
|
+
throw error;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
39
87
|
if (mode === "plan") {
|
|
40
88
|
const plan = compileReleaseTailDeclaration(
|
|
41
89
|
readJson(flag(options, "declaration"), "--declaration"),
|
|
@@ -95,7 +143,7 @@ export function runReleaseTailCli(args = process.argv.slice(2)) {
|
|
|
95
143
|
return report;
|
|
96
144
|
}
|
|
97
145
|
throw new Error(
|
|
98
|
-
"usage: buildchain release-tail <plan|init|status|verify|compat> [--declaration <json-or-path>] [--state <path>] [--hooks-json <json-or-path>] [--output <path>]",
|
|
146
|
+
"usage: buildchain release-tail <plan|init|status|verify|compat|rehearse> [--declaration <json-or-path>] [--capsule <absolute-path>] [--candidate-root <absolute-path>] [--mode <simulate|replay>] [--state <absolute-path>] [--evidence <absolute-path>] [--hooks-json <json-or-path>] [--output <path>]",
|
|
99
147
|
);
|
|
100
148
|
}
|
|
101
149
|
|
|
@@ -105,7 +153,7 @@ if (
|
|
|
105
153
|
import.meta.url === pathToFileURL(process.argv[1]).href
|
|
106
154
|
) {
|
|
107
155
|
try {
|
|
108
|
-
runReleaseTailCli();
|
|
156
|
+
await runReleaseTailCli();
|
|
109
157
|
} catch (error) {
|
|
110
158
|
console.error(`release-tail: ${error.message}`);
|
|
111
159
|
process.exitCode = 1;
|
|
@@ -6,7 +6,11 @@ import fs from "node:fs";
|
|
|
6
6
|
import {
|
|
7
7
|
closeDevDeliveryWarrant,
|
|
8
8
|
createDevDeliveryQueue,
|
|
9
|
+
createNativeCommandContract,
|
|
10
|
+
createNativeProofReuseDecision,
|
|
11
|
+
createNativeQualificationProof,
|
|
9
12
|
createReleaseBlockerPriorityClaim,
|
|
13
|
+
qualifyDevDeliveryWarrant,
|
|
10
14
|
selectDevDeliveryWarrant,
|
|
11
15
|
submitDevDeliveryCandidate,
|
|
12
16
|
} from "../packages/core/dev-delivery-warrant.js";
|
|
@@ -81,9 +85,12 @@ function branch(value, label) {
|
|
|
81
85
|
return normalized;
|
|
82
86
|
}
|
|
83
87
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
88
|
+
const at = (recordedAt, seconds) => new Date(Date.parse(recordedAt) + seconds * 1000).toISOString();
|
|
89
|
+
|
|
90
|
+
const normalizeFields = (input, fields, normalizer, prefix = "") =>
|
|
91
|
+
Object.fromEntries(fields.map((field) => [field, normalizer(input?.[field], `${prefix}${field}`)]));
|
|
92
|
+
|
|
93
|
+
const requireEvidence = (condition, message) => { if (!condition) throw new Error(message); };
|
|
87
94
|
|
|
88
95
|
function normalizeDelivery(input, expected) {
|
|
89
96
|
const delivery = {
|
|
@@ -92,59 +99,44 @@ function normalizeDelivery(input, expected) {
|
|
|
92
99
|
"delivery.pullRequestNumber",
|
|
93
100
|
),
|
|
94
101
|
baseBranch: branch(input?.baseBranch, "delivery.baseBranch"),
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
artifactRoot: root(input?.artifactRoot, "delivery.artifactRoot"),
|
|
108
|
-
installedProductRoot: root(
|
|
109
|
-
input?.installedProductRoot,
|
|
110
|
-
"delivery.installedProductRoot",
|
|
102
|
+
...normalizeFields(
|
|
103
|
+
input,
|
|
104
|
+
["headSha", "headTreeSha", "reviewHeadSha", "mergeSha", "mergeTreeSha", "ciHeadSha"],
|
|
105
|
+
sha,
|
|
106
|
+
"delivery.",
|
|
107
|
+
),
|
|
108
|
+
...normalizeFields(input, ["state", "reviewState", "ciConclusion"], text, "delivery."),
|
|
109
|
+
...normalizeFields(
|
|
110
|
+
input,
|
|
111
|
+
["reviewRoot", "ciRoot", "mergeRoot", "artifactRoot", "installedProductRoot"],
|
|
112
|
+
root,
|
|
113
|
+
"delivery.",
|
|
111
114
|
),
|
|
112
115
|
};
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
delivery
|
|
122
|
-
)
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
delivery.
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
delivery.mergeTreeSha !== delivery.headTreeSha
|
|
138
|
-
) {
|
|
139
|
-
throw new Error(
|
|
140
|
-
"protected delivery merge tree does not equal the repaired candidate tree",
|
|
141
|
-
);
|
|
142
|
-
}
|
|
143
|
-
if (delivery.ciHeadSha !== delivery.mergeSha) {
|
|
144
|
-
throw new Error(
|
|
145
|
-
"protected delivery CI does not bind the merge-group result",
|
|
146
|
-
);
|
|
147
|
-
}
|
|
116
|
+
requireEvidence(
|
|
117
|
+
delivery.baseBranch === expected.sourceBranch,
|
|
118
|
+
"protected delivery base does not match the Release Cut source branch",
|
|
119
|
+
);
|
|
120
|
+
requireEvidence(
|
|
121
|
+
delivery.state === "MERGED" &&
|
|
122
|
+
delivery.reviewState === "APPROVED" &&
|
|
123
|
+
delivery.ciConclusion === "success",
|
|
124
|
+
"protected delivery is not merged, approved, and CI-successful",
|
|
125
|
+
);
|
|
126
|
+
requireEvidence(
|
|
127
|
+
delivery.headSha === expected.devLandingSha &&
|
|
128
|
+
delivery.reviewHeadSha === delivery.headSha,
|
|
129
|
+
"protected delivery review does not bind the exact repaired head",
|
|
130
|
+
);
|
|
131
|
+
requireEvidence(
|
|
132
|
+
delivery.headTreeSha === expected.cutCandidateTreeSha &&
|
|
133
|
+
delivery.mergeTreeSha === delivery.headTreeSha,
|
|
134
|
+
"protected delivery merge tree does not equal the repaired candidate tree",
|
|
135
|
+
);
|
|
136
|
+
requireEvidence(
|
|
137
|
+
delivery.ciHeadSha === delivery.mergeSha,
|
|
138
|
+
"protected delivery CI does not bind the merge-group result",
|
|
139
|
+
);
|
|
148
140
|
return delivery;
|
|
149
141
|
}
|
|
150
142
|
|
|
@@ -153,50 +145,32 @@ function normalizeInput(input = {}) {
|
|
|
153
145
|
repository: repository(input.repository),
|
|
154
146
|
sourceBranch: branch(input.sourceBranch, "sourceBranch"),
|
|
155
147
|
targetBranch: branch(input.targetBranch, "targetBranch"),
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
candidateSha: sha(input.candidateSha, "candidateSha"),
|
|
161
|
-
candidateTreeSha: sha(input.candidateTreeSha, "candidateTreeSha"),
|
|
162
|
-
alphaBaseSha: sha(input.alphaBaseSha, "alphaBaseSha"),
|
|
163
|
-
buildchainRuntimeSha: sha(
|
|
164
|
-
input.buildchainRuntimeSha,
|
|
165
|
-
"buildchainRuntimeSha",
|
|
166
|
-
),
|
|
167
|
-
observedDevSha: sha(input.observedDevSha, "observedDevSha"),
|
|
168
|
-
blockerRoot: root(input.blockerRoot, "blockerRoot"),
|
|
169
|
-
patchRoot: root(input.patchRoot, "patchRoot"),
|
|
170
|
-
cutCandidateSha: sha(input.cutCandidateSha, "cutCandidateSha"),
|
|
171
|
-
cutCandidateTreeSha: sha(input.cutCandidateTreeSha, "cutCandidateTreeSha"),
|
|
172
|
-
cutLandingEvidenceRoot: root(
|
|
173
|
-
input.cutLandingEvidenceRoot,
|
|
174
|
-
"cutLandingEvidenceRoot",
|
|
148
|
+
...normalizeFields(
|
|
149
|
+
input,
|
|
150
|
+
["assignmentRoot", "initiativeRoot", "dependencyProofRoot", "environmentRoot", "blockerRoot", "patchRoot", "cutLandingEvidenceRoot", "devConflictEvidenceRoot", "devLandingEvidenceRoot"],
|
|
151
|
+
root,
|
|
175
152
|
),
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
"devConflictEvidenceRoot",
|
|
181
|
-
),
|
|
182
|
-
devLandingEvidenceRoot: root(
|
|
183
|
-
input.devLandingEvidenceRoot,
|
|
184
|
-
"devLandingEvidenceRoot",
|
|
153
|
+
...normalizeFields(
|
|
154
|
+
input,
|
|
155
|
+
["originDevSha", "candidateSha", "candidateTreeSha", "alphaBaseSha", "buildchainRuntimeSha", "observedDevSha", "cutCandidateSha", "cutCandidateTreeSha", "devBaseSha", "devLandingSha"],
|
|
156
|
+
sha,
|
|
185
157
|
),
|
|
158
|
+
nativeCommand: text(input.nativeCommand, "nativeCommand"),
|
|
159
|
+
nativeExecutionReceipt: structuredClone(input.nativeExecutionReceipt || {}),
|
|
186
160
|
ordinaryPullRequestNumber: positiveInteger(
|
|
187
161
|
input.ordinaryPullRequestNumber,
|
|
188
162
|
"ordinaryPullRequestNumber",
|
|
189
163
|
),
|
|
190
164
|
recordedAt: timestamp(input.recordedAt, "recordedAt"),
|
|
191
165
|
};
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
166
|
+
requireEvidence(
|
|
167
|
+
normalized.observedDevSha !== normalized.originDevSha,
|
|
168
|
+
"self-dogfood requires dev to advance beyond the frozen origin",
|
|
169
|
+
);
|
|
170
|
+
requireEvidence(
|
|
171
|
+
normalized.cutCandidateSha !== normalized.candidateSha,
|
|
172
|
+
"self-dogfood requires a successor repair candidate",
|
|
173
|
+
);
|
|
200
174
|
normalized.delivery = normalizeDelivery(input.delivery, normalized);
|
|
201
175
|
return normalized;
|
|
202
176
|
}
|
|
@@ -214,6 +188,10 @@ function deliveryCandidate(input, deliveryClass = "release") {
|
|
|
214
188
|
closureRoot: input.closureRoot,
|
|
215
189
|
dependencyRoot: input.dependencyRoot,
|
|
216
190
|
toolchainRoot: input.toolchainRoot,
|
|
191
|
+
...(input.environmentRoot ? { environmentRoot: input.environmentRoot } : {}),
|
|
192
|
+
...(input.nativeCommand
|
|
193
|
+
? { nativeCommandContract: createNativeCommandContract(input.nativeCommand) }
|
|
194
|
+
: {}),
|
|
217
195
|
deliveryClass,
|
|
218
196
|
priority: "ordinary",
|
|
219
197
|
...(input.releaseBlockerPriority
|
|
@@ -366,7 +344,7 @@ function runQueueCampaign(value, repaired, priorityClaim) {
|
|
|
366
344
|
dependencyRoot: value.dependencyProofRoot,
|
|
367
345
|
toolchainRoot: value.dependencyProofRoot,
|
|
368
346
|
},
|
|
369
|
-
"native-
|
|
347
|
+
"non-native-fast",
|
|
370
348
|
);
|
|
371
349
|
queue = submitDevDeliveryCandidate(queue, ordinary, {
|
|
372
350
|
now: at(value.recordedAt, 6),
|
|
@@ -388,6 +366,8 @@ function runQueueCampaign(value, repaired, priorityClaim) {
|
|
|
388
366
|
closureRoot: value.delivery.mergeRoot,
|
|
389
367
|
dependencyRoot: value.dependencyProofRoot,
|
|
390
368
|
toolchainRoot: value.delivery.installedProductRoot,
|
|
369
|
+
environmentRoot: value.environmentRoot,
|
|
370
|
+
nativeCommand: value.nativeCommand,
|
|
391
371
|
releaseBlockerPriority: priorityClaim,
|
|
392
372
|
});
|
|
393
373
|
const submitted = submitDevDeliveryCandidate(queue, blocker, {
|
|
@@ -426,7 +406,29 @@ function runQueueCampaign(value, repaired, priorityClaim) {
|
|
|
426
406
|
"eligible release blocker did not receive bounded priority",
|
|
427
407
|
);
|
|
428
408
|
}
|
|
429
|
-
const
|
|
409
|
+
const nativeCommandRoot = createNativeCommandContract(value.nativeCommand).commandRoot;
|
|
410
|
+
const proof = createNativeQualificationProof({
|
|
411
|
+
...selected.warrant,
|
|
412
|
+
repository: value.repository,
|
|
413
|
+
protectedBase: value.sourceBranch,
|
|
414
|
+
qualifiedBase: value.devBaseSha,
|
|
415
|
+
nativeCommandRoot,
|
|
416
|
+
nativeExecutionReceipt: value.nativeExecutionReceipt,
|
|
417
|
+
affectedPaths: [], shardEvidenceRoots: [],
|
|
418
|
+
qualifiedAt: at(value.recordedAt, 12),
|
|
419
|
+
});
|
|
420
|
+
const current = {
|
|
421
|
+
...selected.warrant,
|
|
422
|
+
nativeCommandRoot,
|
|
423
|
+
currentBase: value.devBaseSha,
|
|
424
|
+
graphKnown: true, attributionComplete: true,
|
|
425
|
+
changedPaths: [], renames: [],
|
|
426
|
+
};
|
|
427
|
+
const reuseDecision = createNativeProofReuseDecision({ proof, current });
|
|
428
|
+
const qualified = qualifyDevDeliveryWarrant(selected.queue, selected.warrant, {
|
|
429
|
+
nativeProof: proof, reuseDecision, current, now: at(value.recordedAt, 12),
|
|
430
|
+
});
|
|
431
|
+
const closed = closeDevDeliveryWarrant(qualified.queue, qualified.warrant, {
|
|
430
432
|
outcome: "merged",
|
|
431
433
|
evidenceRoot: value.delivery.mergeRoot,
|
|
432
434
|
now: at(value.recordedAt, 13),
|