@kungfu-tech/buildchain 3.0.9-alpha.8 → 4.0.0-alpha.0
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 +39 -0
- package/README.md +17 -13
- package/actions/promote-buildchain-ref/README.md +0 -10
- package/actions/release-tail/README.md +6 -10
- package/architecture/internal-capabilities.json +502 -0
- package/architecture/maintainability-baseline.json +59 -0
- package/architecture/maintainability-policy.json +1384 -0
- package/architecture/release-tail-contract-inventory.json +587 -0
- package/architecture/v3-core-mechanism-inventory.json +1043 -0
- package/architecture/v3-core-mechanism-inventory.md +67 -0
- package/architecture/v4-adopter-delivery-parity.json +47 -0
- package/architecture/v4-architecture-constitution.md +210 -0
- package/architecture/v4-bootstrap-authority.json +56 -0
- package/architecture/v4-canonical-contract-fixtures.json +105 -0
- package/architecture/v4-capability-state-machine-manifest.json +480 -0
- package/architecture/v4-capability-state-machine-manifest.schema.json +202 -0
- package/architecture/v4-delivery-warrant-shadow-bootstrap-plan.json +830 -0
- package/architecture/v4-delivery-warrant-shadow-bootstrap-plan.md +223 -0
- package/architecture/v4-delivery-warrant-shadow-bootstrap-plan.schema.json +318 -0
- package/architecture/v4-delivery-warrant-shadow-fixtures.json +227 -0
- package/architecture/v4-delivery-warrant-shadow-fixtures.schema.json +67 -0
- package/architecture/v4-exception-ledger.json +5 -0
- package/architecture/v4-exception-ledger.schema.json +56 -0
- package/architecture/v4-partial-mutation-recovery-qualification.json +65 -0
- package/architecture/v4-platform-stage-checkpoints.json +97 -0
- package/architecture/v4-provider-operation-journal-contract.json +113 -0
- package/architecture/v4-release-activation-shadow-domain.json +61 -0
- package/architecture/v4-release-train-parity.json +68 -0
- package/architecture/v4-rust-libnode-bridge-evaluation.json +107 -0
- package/architecture/v4-rust-libnode-bridge-spike.md +85 -0
- package/architecture/v4-stable-publication-fence.json +66 -0
- package/architecture/v4-stage-capsule-contract.json +90 -0
- package/architecture/v4-stage-capsule-qualification.json +99 -0
- package/architecture/v4-stage-capsule-resume-planner.json +52 -0
- package/architecture/v4-stage-capsule-store-contract.json +72 -0
- package/bin/buildchain.mjs +15 -6
- package/contracts/auditable-demo-scenario-v1.schema.json +1 -1
- package/contracts/buildchain-v2-residuals-v1.json +2 -2
- package/contracts/fixtures/v4-delivery-warrant-trace-v1/golden.json +158 -0
- package/contracts/fixtures/v4-delivery-warrant-trace-v1/property.json +380 -0
- package/contracts/fixtures/v4-delivery-warrant-trace-v1/replay.json +163 -0
- package/contracts/fixtures/v4-partial-mutation-recovery-v1/shared.json +155 -0
- package/contracts/fixtures/v4-provider-operation-journal-v1/shared.json +457 -0
- package/contracts/fixtures/v4-provider-readback-idempotency-v1/shared.json +682 -0
- package/contracts/fixtures/v4-release-activation-shadow-v1/shared.json +53 -0
- package/contracts/fixtures/v4-stable-publication-fence-v1/shared.json +56 -0
- package/contracts/fixtures/v4-stage-capsule-resume-v1/late-platform-failure.json +176 -0
- package/contracts/fixtures/v4-stage-capsule-store-v1/shared.json +132 -0
- package/contracts/fixtures/v4-stage-capsule-v1/shared.json +111 -0
- package/contracts/v4-canonical-contracts-v1.schema.json +182 -0
- package/contracts/v4-delivery-warrant-semantic-diff-report-v1.schema.json +284 -0
- package/contracts/v4-delivery-warrant-trace-v1.schema.json +215 -0
- package/contracts/v4-host-contract-v1.schema.json +135 -0
- package/contracts/v4-partial-mutation-recovery-v1.schema.json +168 -0
- package/contracts/v4-provider-operation-journal-v1.schema.json +381 -0
- package/contracts/v4-release-activation-shadow-v1.schema.json +186 -0
- package/contracts/v4-stable-publication-fence-v1.schema.json +125 -0
- package/contracts/v4-stage-capsule-resume-v1.schema.json +225 -0
- package/contracts/v4-stage-capsule-store-v1.schema.json +154 -0
- package/contracts/v4-stage-capsule-v1.schema.json +178 -0
- package/crates/buildchain-v4-bridge/Cargo.lock +213 -0
- package/crates/buildchain-v4-bridge/Cargo.toml +12 -0
- package/crates/buildchain-v4-bridge/src/main.rs +495 -0
- package/dist/site/agent-index.json +0 -3
- package/dist/site/artifact-schemas.json +0 -6
- package/dist/site/buildchain-contract.json +103 -397
- package/dist/site/buildchain-site.json +302 -270
- package/dist/site/capability-registry.json +9 -11
- package/dist/site/cli-registry.json +8 -30
- package/dist/site/controller-registry.json +48 -8
- package/dist/site/kfd-claims.json +129 -142
- package/dist/site/kfd-upstream-aggregate.json +9 -9
- package/dist/site/manual-registry.json +18 -49
- package/dist/site/node-api-registry.json +1882 -4118
- package/dist/site/page-registry.json +278 -230
- package/dist/site/public-surface-audit.json +87 -185
- package/dist/site/publication-authority-registry.json +11 -1
- package/dist/site/publication-registry.json +4 -4
- package/dist/site/release-provenance.json +3 -7
- package/dist/site/schemas/kfd-support-projection-v1.schema.json +106 -0
- package/dist/site/site-manifest.json +22 -38
- package/dist/site/workflow-registry.json +80 -35
- package/docs/MAP.md +8 -4
- package/docs/auditable-demo.md +2 -2
- package/docs/aws-us-elastic-runner-burst-plane.md +7 -59
- package/docs/cli-reference.md +15 -92
- package/docs/cli.md +14 -14
- package/docs/dev-alpha-candidate-patrol.md +3 -11
- package/docs/dev-delivery-warrant.md +28 -100
- package/docs/engineering-housekeeper.md +12 -4
- package/docs/getting-started.md +1 -1
- package/docs/install.md +5 -5
- package/docs/kfd-support.md +2 -7
- package/docs/lifecycle-protocol.md +2 -4
- package/docs/node-api-reference.md +414 -523
- package/docs/release-flow.md +31 -31
- package/docs/release-governance.md +16 -28
- package/docs/release-passport.md +2 -15
- package/docs/release-tail-contract.md +13 -6
- package/docs/release-tail-provider-plane.md +13 -19
- package/docs/release-train.md +4 -4
- package/docs/reusable-build-surface.md +14 -27
- package/docs/site-bundle-contract.md +1 -5
- package/docs/stable-candidate-patrol.md +7 -7
- package/docs/v4-canonical-contracts.md +92 -0
- package/docs/v4-delivery-warrant-read-candidate.md +73 -0
- package/docs/v4-delivery-warrant-semantic-diff.md +75 -0
- package/docs/v4-production-release.md +63 -0
- package/docs/v4-stage-capsule.md +193 -0
- package/docs/versioning.md +3 -3
- package/package.json +19 -17
- package/packages/core/adopter-delivery-gate.js +576 -0
- package/packages/core/adopter-delivery-json.js +66 -0
- package/packages/core/artifact-signing.js +0 -61
- package/packages/core/artifact-verification-envelope.js +5 -5
- package/packages/core/build-facts.js +12 -4
- package/packages/core/buildchain-agent-manuals.js +0 -2
- package/packages/core/buildchain-config.js +4 -69
- package/packages/core/buildchain-contract.js +232 -46
- package/packages/core/buildchain-kfd-claims.js +1 -1
- package/packages/core/channel-candidate.js +0 -8
- package/packages/core/channel-promotion-baseline.js +0 -52
- package/packages/core/controller-evidence.js +3 -4
- package/packages/core/dev-alpha-candidate-selection.js +2 -10
- package/packages/core/dev-delivery-warrant-cancellation.js +0 -1
- package/packages/core/dev-delivery-warrant-settlement.js +1 -5
- package/packages/core/dev-delivery-warrant.js +12 -52
- package/packages/core/diagnostics.js +3 -8
- package/packages/core/github-governance-authority.js +3 -1
- package/packages/core/kfd-adopter-category-driver.js +167 -0
- package/packages/core/kfd-adopter-manifest.js +47 -46
- package/packages/core/kfd-gate.js +15 -45
- package/packages/core/paper-agent-entry.js +7 -16
- package/packages/core/paper-fleet.js +2 -2
- package/packages/core/paper-repository.js +2 -3
- package/packages/core/paper-scaffold-content.js +2 -23
- package/packages/core/paper.js +11 -43
- package/packages/core/publication-reproducibility.js +4 -4
- package/packages/core/release-line-bootstrap.js +21 -8
- package/packages/core/release-passport-contract.js +5 -5
- package/packages/core/release-passport.js +20 -131
- package/packages/core/self-dogfood-version.js +48 -35
- package/packages/core/spawn-command.js +232 -8
- package/packages/core/v4-adopter-delivery-parity.js +219 -0
- package/packages/core/v4-canonical-contracts.js +284 -0
- package/packages/core/v4-delivery-warrant-fixture-runner.js +371 -0
- package/packages/core/v4-delivery-warrant-read-candidate.js +279 -0
- package/packages/core/v4-delivery-warrant-semantic-diff-gate.js +352 -0
- package/packages/core/v4-delivery-warrant-shadow-adapter.js +447 -0
- package/packages/core/v4-partial-mutation-recovery-qualification.js +395 -0
- package/packages/core/v4-platform-stage-checkpoints.js +471 -0
- package/packages/core/v4-provider-operation-journal.js +531 -0
- package/packages/core/v4-provider-readback-idempotency.js +394 -0
- package/packages/core/v4-release-activation-shadow.js +519 -0
- package/packages/core/v4-stable-publication-fence.js +357 -0
- package/packages/core/v4-stage-capsule-local-store.js +426 -0
- package/packages/core/v4-stage-capsule-qualification-campaign.js +512 -0
- package/packages/core/v4-stage-capsule-qualification.js +456 -0
- package/packages/core/v4-stage-capsule-resume-planner.js +397 -0
- package/packages/core/v4-stage-capsule-store.js +401 -0
- package/packages/core/v4-stage-capsule.js +319 -0
- package/scripts/assemble-self-publication-admission.mjs +1 -1
- package/scripts/audit-publication-control-plane.mjs +5 -5
- package/scripts/auditable-demo-bundle-verification.mjs +3 -2
- package/scripts/auditable-demo-platform.mjs +2 -2
- package/scripts/auditable-demo-renditions.mjs +1 -1
- package/scripts/auditable-demo.mjs +2 -2
- package/scripts/aws-macos-jit-controller-core.mjs +13 -346
- package/scripts/aws-macos-jit-controller-runtime.mjs +2 -210
- package/scripts/aws-macos-jit-controller.mjs +37 -275
- package/scripts/aws-macos-jit-core.mjs +3 -41
- package/scripts/aws-macos-jit.mjs +0 -12
- package/scripts/aws-windows-jit-campaign.mjs +2 -2
- package/scripts/aws-windows-jit-controller-core.mjs +1 -1
- package/scripts/aws-windows-jit-controller.mjs +3 -3
- package/scripts/build-contract-core.mjs +3 -8
- package/scripts/build-standalone-binary.mjs +3 -14
- package/scripts/buildchain-channel-router.mjs +4 -0
- package/scripts/buildchain-cli-help.mjs +7 -13
- package/scripts/buildchain-contract-lock.mjs +0 -6
- package/scripts/check-action-bundles.mjs +3 -4
- package/scripts/check-internal-architecture.mjs +7 -6
- package/scripts/check-inventory.mjs +30 -25
- package/scripts/check-v4-public-dogfood-contract.mjs +270 -0
- package/scripts/create-release-bundle.mjs +8 -4
- package/scripts/dev-alpha-candidate-patrol.mjs +1 -22
- package/scripts/dev-delivery-proof.mjs +2 -54
- package/scripts/dev-delivery-warrant.mjs +59 -77
- package/scripts/dev-pr-auto-merge.mjs +4 -30
- package/scripts/dev-pr-delivery-warrant.mjs +0 -55
- package/scripts/dispatch-artifact-signing-authority.mjs +7 -4
- package/scripts/generate-buildchain-kfd-witnesses.mjs +94 -88
- package/scripts/generate-channel-build-workflow.mjs +54 -24
- package/scripts/generate-channel-promotion-workflow.mjs +15 -6
- package/scripts/generate-site-bundle.mjs +5 -21
- package/scripts/git-fetch-process-tree.mjs +34 -1
- package/scripts/init-repo.mjs +64 -159
- package/scripts/inspect-artifact-signing-requests.mjs +0 -6
- package/scripts/locked-source-checkout.mjs +3 -4
- package/scripts/maintainability-metrics.mjs +8 -2
- package/scripts/npm-publish-dry-run.mjs +2 -2
- package/scripts/npm-publish-transaction.mjs +2 -2
- package/scripts/publication-commit-evidence.mjs +23 -69
- package/scripts/release-candidate-resolver.mjs +11 -17
- package/scripts/release-tail.mjs +3 -49
- package/scripts/resume-from-candidate-run.mjs +9 -123
- package/scripts/run-lifecycle-core.mjs +12 -13
- package/scripts/seal-artifact-signing-requests.mjs +17 -21
- package/scripts/site-capability-metadata.mjs +2 -7
- package/scripts/stable-candidate-qualification.mjs +10 -10
- package/scripts/v4-architecture.mjs +12 -2
- package/scripts/v4-bridge-bootstrap.mjs +141 -0
- package/scripts/v4-bridge-evidence.mjs +141 -0
- package/scripts/v4-host-adapter.mjs +182 -0
- package/scripts/v4-platform-stage-checkpoint-rehearsal.mjs +207 -0
- package/scripts/v4-stage-capsule-qualification.mjs +528 -0
- package/scripts/v4-stage-capsule-resume-rehearsal.mjs +57 -0
- package/scripts/v4-warrant-shadow-plan.mjs +601 -0
- package/scripts/verify-golden-path.mjs +5 -5
- package/scripts/web-surface-cloudfront-rewrite.mjs +15 -15
- package/scripts/web-surface-core.mjs +2 -8
- package/scripts/workflow-call-contract.mjs +5 -184
- package/contracts/fixtures/kfd-adopter-release-v1/kfd-4-perspective.json +0 -16
- package/contracts/fixtures/kfd-adopter-release-v1/kfd-4-replay.json +0 -57
- package/contracts/fixtures/next-development-transition-v1/anchored-manual-waiting.json +0 -47
- package/contracts/fixtures/next-development-transition-v1/semver-auto-planned.json +0 -47
- package/contracts/fixtures/next-development-transition-v1/version-model-cases.json +0 -40
- package/contracts/next-development-request-v1.schema.json +0 -66
- package/contracts/next-development-transition-v1.schema.json +0 -292
- package/contracts/publication-rehearsal-capsule-v1.schema.json +0 -173
- package/dist/site/schemas/publication-rehearsal-capsule-v1.schema.json +0 -269
- package/dist/site/schemas/release-tail-capabilities-v1.schema.json +0 -353
- package/dist/site/schemas/release-tail-provider-bindings-v1.schema.json +0 -94
- package/docs/next-development-transition.md +0 -119
- package/docs/publication-rehearsal.md +0 -94
- package/packages/core/buildchain-compatibility-proof.js +0 -631
- package/packages/core/dev-delivery-native-proof.js +0 -333
- package/packages/core/dev-delivery-warrant-shadow.js +0 -502
- package/packages/core/next-development-candidate-reservation.js +0 -186
- package/packages/core/next-development-controller.js +0 -726
- package/packages/core/next-development-projection.js +0 -288
- package/packages/core/next-development-transition.js +0 -738
- package/packages/core/publication-rehearsal-projection.js +0 -173
- package/packages/core/publication-rehearsal-runtime.js +0 -921
- package/scripts/aws-macos-jit-instance-rehydrate.mjs +0 -260
- package/scripts/aws-macos-jit-source-rebind.mjs +0 -724
- package/scripts/dev-delivery-native-run.mjs +0 -187
- package/scripts/dev-delivery-two-phase.mjs +0 -598
- package/scripts/generate-next-development-guidance.mjs +0 -49
- package/scripts/materialize-self-release-candidate-version.mjs +0 -137
- package/scripts/next-development-self-dogfood-harness.mjs +0 -409
- package/scripts/next-development-self-dogfood.mjs +0 -532
- package/scripts/next-development-transition.mjs +0 -47
- package/scripts/release-candidate-tail-reseal.mjs +0 -426
- package/templates/native-dev-delivery.yml +0 -82
|
@@ -25,42 +25,61 @@ function parseAlphaRef(ref) {
|
|
|
25
25
|
return Number(match[1]);
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
function isExactSha(value) {
|
|
29
|
+
return /^[0-9a-f]{40}$/u.test(String(value || ""));
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function hasQualifiedSelfDogfoodBootstrapAuthority({
|
|
29
33
|
packageVersion,
|
|
30
34
|
alphaRef,
|
|
31
|
-
|
|
35
|
+
authority,
|
|
32
36
|
} = {}) {
|
|
33
37
|
const version = parsePackageVersion(packageVersion);
|
|
34
38
|
const acceptedMajor = parseAlphaRef(alphaRef);
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
workflowMajor: acceptedMajor,
|
|
39
|
-
bootstrap: false,
|
|
40
|
-
};
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
const stableBootstrap =
|
|
44
|
-
version.minor === 0 && version.patch === 0 && version.alpha === undefined;
|
|
45
|
-
const nextAlphaBootstrap =
|
|
46
|
-
version.minor === 0 && version.patch === 1 && version.alpha !== undefined;
|
|
47
|
-
if (
|
|
48
|
-
majorBootstrap === true &&
|
|
39
|
+
const releaseLine = authority?.releaseLine;
|
|
40
|
+
const qualification = authority?.qualification;
|
|
41
|
+
return (
|
|
49
42
|
acceptedMajor + 1 === version.major &&
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
43
|
+
authority?.contract ===
|
|
44
|
+
`kungfu-buildchain-v${version.major}-bootstrap-authority` &&
|
|
45
|
+
releaseLine?.candidateBranch ===
|
|
46
|
+
`dev/v${version.major}/v${version.major}.0` &&
|
|
47
|
+
String(releaseLine?.sourceBranch || "").startsWith(
|
|
48
|
+
`dev/v${acceptedMajor}/v${acceptedMajor}.`,
|
|
49
|
+
) &&
|
|
50
|
+
releaseLine?.status === `qualified-protected-v${version.major}-bootstrap` &&
|
|
51
|
+
isExactSha(releaseLine?.sourceCommit) &&
|
|
52
|
+
isExactSha(releaseLine?.bootstrapCommit) &&
|
|
53
|
+
qualification?.contract ===
|
|
54
|
+
`kungfu-buildchain-v${version.major}-n-minus-one-qualification` &&
|
|
55
|
+
qualification?.authorityRevision === releaseLine.sourceCommit &&
|
|
56
|
+
qualification?.candidateRevision === releaseLine.bootstrapCommit &&
|
|
57
|
+
qualification?.candidateSelfQualified === false &&
|
|
58
|
+
qualification?.activeExceptions === 0 &&
|
|
59
|
+
/^sha256:[0-9a-f]{64}$/u.test(
|
|
60
|
+
String(qualification?.qualificationRoot || ""),
|
|
61
|
+
)
|
|
61
62
|
);
|
|
62
63
|
}
|
|
63
64
|
|
|
65
|
+
export function resolveSelfDogfoodMajor({
|
|
66
|
+
packageVersion,
|
|
67
|
+
alphaRef,
|
|
68
|
+
} = {}) {
|
|
69
|
+
const version = parsePackageVersion(packageVersion);
|
|
70
|
+
const acceptedMajor = parseAlphaRef(alphaRef);
|
|
71
|
+
if (acceptedMajor !== version.major) {
|
|
72
|
+
throw new Error(
|
|
73
|
+
"Buildchain self-dogfood alpha lock must target the current major alpha ref",
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
return {
|
|
77
|
+
packageMajor: version.major,
|
|
78
|
+
workflowMajor: acceptedMajor,
|
|
79
|
+
bootstrap: false,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
64
83
|
export function contractForSelfDogfoodEvaluation({
|
|
65
84
|
currentContract,
|
|
66
85
|
majorResolution,
|
|
@@ -68,18 +87,12 @@ export function contractForSelfDogfoodEvaluation({
|
|
|
68
87
|
if (!currentContract || typeof currentContract !== "object") {
|
|
69
88
|
throw new Error("Buildchain self-dogfood requires a current contract");
|
|
70
89
|
}
|
|
71
|
-
|
|
72
|
-
return currentContract;
|
|
73
|
-
}
|
|
74
|
-
return {
|
|
75
|
-
...currentContract,
|
|
76
|
-
majorLine: `v${majorResolution.workflowMajor}`,
|
|
77
|
-
};
|
|
90
|
+
return currentContract;
|
|
78
91
|
}
|
|
79
92
|
|
|
80
93
|
export function canAdmitSelfDogfoodLockEvaluation({
|
|
81
94
|
evaluation,
|
|
82
95
|
majorResolution,
|
|
83
96
|
} = {}) {
|
|
84
|
-
return evaluation?.compatible === true
|
|
97
|
+
return evaluation?.compatible === true;
|
|
85
98
|
}
|
|
@@ -1,16 +1,66 @@
|
|
|
1
|
-
import { spawnSync } from "node:child_process";
|
|
1
|
+
import { execFileSync, execSync, spawn, spawnSync } from "node:child_process";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
2
5
|
|
|
3
6
|
const WINDOWS_CMD_SHIMS = new Set(["corepack", "npm", "npx", "pnpm", "yarn"]);
|
|
7
|
+
const CMD_META_CHARACTERS = /([()\][%!^`"<>&|;, *?])/g;
|
|
8
|
+
const INTERNAL_PROCESS_TREE_COMMAND = "--buildchain-internal-process-tree-command";
|
|
9
|
+
const TIMEOUT_EXIT_CODE = 124;
|
|
10
|
+
const modulePath = fileURLToPath(import.meta.url);
|
|
4
11
|
|
|
5
|
-
|
|
6
|
-
|
|
12
|
+
function runningStandaloneBundle() {
|
|
13
|
+
return process.argv[1] && process.argv[2] !== INTERNAL_PROCESS_TREE_COMMAND
|
|
14
|
+
&& import.meta.url === pathToFileURL(process.argv[1]).href;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function pathCommandShim(command, env) {
|
|
18
|
+
for (const directory of String(env.PATH || "").split(";")) {
|
|
19
|
+
const candidate = path.join(directory, `${command}.cmd`);
|
|
20
|
+
if (directory && fs.existsSync(candidate)) return true;
|
|
21
|
+
}
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function resolveSpawnCommand(command, platform = process.platform, env = process.env) {
|
|
26
|
+
if (platform !== "win32") {
|
|
7
27
|
return command;
|
|
8
28
|
}
|
|
9
|
-
return
|
|
29
|
+
return pathCommandShim(command, env) || WINDOWS_CMD_SHIMS.has(command)
|
|
30
|
+
? `${command}.cmd`
|
|
31
|
+
: command;
|
|
10
32
|
}
|
|
11
33
|
|
|
12
|
-
export function usesShellForSpawnCommand(command, platform = process.platform) {
|
|
13
|
-
return platform === "win32" &&
|
|
34
|
+
export function usesShellForSpawnCommand(command, platform = process.platform, env = process.env) {
|
|
35
|
+
return platform === "win32" && /\.cmd$/i.test(resolveSpawnCommand(command, platform, env));
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function cmdEscapeCommand(value) {
|
|
39
|
+
return String(value).replace(CMD_META_CHARACTERS, "^$1");
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function cmdEscapeArgument(value, doubleEscapeMetaCharacters = false) {
|
|
43
|
+
let text = String(value);
|
|
44
|
+
text = text.replace(/(?=(\\+?)?)\1"/g, '$1$1\\"');
|
|
45
|
+
text = text.replace(/(?=(\\+?)?)\1$/g, "$1$1");
|
|
46
|
+
let escaped = `"${text}"`.replace(CMD_META_CHARACTERS, "^$1");
|
|
47
|
+
if (doubleEscapeMetaCharacters) {
|
|
48
|
+
escaped = escaped.replace(CMD_META_CHARACTERS, "^$1");
|
|
49
|
+
}
|
|
50
|
+
return escaped;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function windowsBatchInvocation(command, args, env = process.env) {
|
|
54
|
+
const shellCommand = [
|
|
55
|
+
cmdEscapeCommand(command),
|
|
56
|
+
...args.map((argument) =>
|
|
57
|
+
cmdEscapeArgument(argument, /\.cmd$/i.test(command)),
|
|
58
|
+
),
|
|
59
|
+
].join(" ");
|
|
60
|
+
return {
|
|
61
|
+
command: env.ComSpec || env.comspec || process.env.ComSpec || process.env.comspec || "cmd.exe",
|
|
62
|
+
args: ["/d", "/s", "/c", `"${shellCommand}"`],
|
|
63
|
+
};
|
|
14
64
|
}
|
|
15
65
|
|
|
16
66
|
export function spawnSyncCommand(
|
|
@@ -19,8 +69,182 @@ export function spawnSyncCommand(
|
|
|
19
69
|
options = {},
|
|
20
70
|
{ platform = process.platform, spawn = spawnSync } = {},
|
|
21
71
|
) {
|
|
22
|
-
|
|
72
|
+
const env = options.env || process.env;
|
|
73
|
+
const resolvedCommand = resolveSpawnCommand(command, platform, env);
|
|
74
|
+
if (
|
|
75
|
+
platform === "win32" &&
|
|
76
|
+
/\.cmd$/i.test(resolvedCommand) &&
|
|
77
|
+
options.shell !== false
|
|
78
|
+
) {
|
|
79
|
+
const invocation = windowsBatchInvocation(resolvedCommand, args, env);
|
|
80
|
+
return spawn(invocation.command, invocation.args, {
|
|
81
|
+
...options,
|
|
82
|
+
shell: false,
|
|
83
|
+
windowsVerbatimArguments: true,
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
return spawn(resolvedCommand, args, {
|
|
23
87
|
...options,
|
|
24
|
-
shell: options.shell ??
|
|
88
|
+
shell: options.shell ?? false,
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async function terminateProcessTree(child, graceMs) {
|
|
93
|
+
if (!child?.pid) return;
|
|
94
|
+
if (process.platform === "win32") {
|
|
95
|
+
await new Promise((resolve) => {
|
|
96
|
+
const killer = spawn("taskkill", ["/pid", String(child.pid), "/t", "/f"], {
|
|
97
|
+
stdio: "ignore",
|
|
98
|
+
windowsHide: true,
|
|
99
|
+
});
|
|
100
|
+
const fallback = setTimeout(() => {
|
|
101
|
+
try {
|
|
102
|
+
child.kill("SIGKILL");
|
|
103
|
+
} catch {
|
|
104
|
+
// The process tree may already be gone.
|
|
105
|
+
}
|
|
106
|
+
resolve();
|
|
107
|
+
}, graceMs);
|
|
108
|
+
killer.once("error", () => {
|
|
109
|
+
clearTimeout(fallback);
|
|
110
|
+
try {
|
|
111
|
+
child.kill("SIGKILL");
|
|
112
|
+
} catch {
|
|
113
|
+
// The process tree may already be gone.
|
|
114
|
+
}
|
|
115
|
+
resolve();
|
|
116
|
+
});
|
|
117
|
+
killer.once("close", () => {
|
|
118
|
+
clearTimeout(fallback);
|
|
119
|
+
resolve();
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
try {
|
|
125
|
+
process.kill(-child.pid, "SIGTERM");
|
|
126
|
+
} catch {
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
await new Promise((resolve) => setTimeout(resolve, graceMs));
|
|
130
|
+
try {
|
|
131
|
+
process.kill(-child.pid, "SIGKILL");
|
|
132
|
+
} catch {
|
|
133
|
+
// The process group exited during the grace period.
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
async function runInternalProcessTreeCommand() {
|
|
138
|
+
const payload = JSON.parse(fs.readFileSync(0, "utf8"));
|
|
139
|
+
const child = spawn(payload.command, payload.args, {
|
|
140
|
+
cwd: payload.cwd,
|
|
141
|
+
env: process.env,
|
|
142
|
+
detached: process.platform !== "win32",
|
|
143
|
+
stdio: ["ignore", "inherit", "inherit"],
|
|
144
|
+
windowsHide: true,
|
|
145
|
+
windowsVerbatimArguments: payload.windowsVerbatimArguments === true,
|
|
146
|
+
});
|
|
147
|
+
let timedOut = false;
|
|
148
|
+
let spawnError;
|
|
149
|
+
const closed = new Promise((resolve) => {
|
|
150
|
+
child.once("error", (error) => {
|
|
151
|
+
spawnError = error;
|
|
152
|
+
resolve({ code: 1, signal: "" });
|
|
153
|
+
});
|
|
154
|
+
child.once("close", (code, signal) => resolve({ code, signal }));
|
|
155
|
+
});
|
|
156
|
+
const timer = Number.isFinite(payload.timeoutMs) && payload.timeoutMs > 0
|
|
157
|
+
? setTimeout(() => {
|
|
158
|
+
timedOut = true;
|
|
159
|
+
void terminateProcessTree(child, payload.graceMs);
|
|
160
|
+
}, payload.timeoutMs)
|
|
161
|
+
: undefined;
|
|
162
|
+
const result = await closed;
|
|
163
|
+
if (timer) clearTimeout(timer);
|
|
164
|
+
process.exitCode = timedOut
|
|
165
|
+
? TIMEOUT_EXIT_CODE
|
|
166
|
+
: spawnError || result.signal
|
|
167
|
+
? 1
|
|
168
|
+
: (result.code ?? 1);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function shellCommandInvocation(command, shell) {
|
|
172
|
+
if (typeof shell === "string" && shell.trim()) {
|
|
173
|
+
return { command: shell, args: ["-c", command] };
|
|
174
|
+
}
|
|
175
|
+
if (process.platform === "win32") {
|
|
176
|
+
return {
|
|
177
|
+
command: process.env.ComSpec || process.env.comspec || "cmd.exe",
|
|
178
|
+
args: ["/d", "/s", "/c", `"${command}"`],
|
|
179
|
+
windowsVerbatimArguments: true,
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
return { command: process.env.SHELL || "/bin/sh", args: ["-c", command] };
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function standaloneProcessCommand(command, args, options) {
|
|
186
|
+
const result = spawnSync(command, args, { ...options, windowsHide: true });
|
|
187
|
+
if (result.error) throw result.error;
|
|
188
|
+
if (result.status !== 0 || result.signal) {
|
|
189
|
+
const error = new Error(`process tree command failed with status ${result.status ?? ""}`);
|
|
190
|
+
error.status = result.status ?? 1;
|
|
191
|
+
error.signal = result.signal || "";
|
|
192
|
+
throw error;
|
|
193
|
+
}
|
|
194
|
+
return result.stdout;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
export function runProcessTreeCommandSync(command, args, {
|
|
198
|
+
cwd = process.cwd(),
|
|
199
|
+
env = process.env,
|
|
200
|
+
stdio = "inherit",
|
|
201
|
+
timeout,
|
|
202
|
+
graceMs = 2000,
|
|
203
|
+
windowsVerbatimArguments = false,
|
|
204
|
+
} = {}) {
|
|
205
|
+
if (runningStandaloneBundle()) {
|
|
206
|
+
return standaloneProcessCommand(command, args, { cwd, env, stdio, timeout });
|
|
207
|
+
}
|
|
208
|
+
const commandStdio = Array.isArray(stdio)
|
|
209
|
+
? ["pipe", stdio[1] || "inherit", stdio[2] || "inherit"]
|
|
210
|
+
: ["pipe", stdio, stdio];
|
|
211
|
+
try {
|
|
212
|
+
return execFileSync(process.execPath, [modulePath, INTERNAL_PROCESS_TREE_COMMAND], {
|
|
213
|
+
cwd,
|
|
214
|
+
env,
|
|
215
|
+
stdio: commandStdio,
|
|
216
|
+
input: JSON.stringify({
|
|
217
|
+
command,
|
|
218
|
+
args,
|
|
219
|
+
cwd,
|
|
220
|
+
timeoutMs: timeout,
|
|
221
|
+
graceMs,
|
|
222
|
+
windowsVerbatimArguments,
|
|
223
|
+
}),
|
|
224
|
+
windowsHide: true,
|
|
225
|
+
});
|
|
226
|
+
} catch (error) {
|
|
227
|
+
if (error?.status === TIMEOUT_EXIT_CODE) error.code = "ETIMEDOUT";
|
|
228
|
+
throw error;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
export function runShellCommandSync(command, options = {}) {
|
|
233
|
+
if (runningStandaloneBundle()) return execSync(command, options);
|
|
234
|
+
const invocation = shellCommandInvocation(command, options.shell);
|
|
235
|
+
return runProcessTreeCommandSync(invocation.command, invocation.args, {
|
|
236
|
+
...options,
|
|
237
|
+
windowsVerbatimArguments: invocation.windowsVerbatimArguments,
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
if (
|
|
242
|
+
process.argv[1]
|
|
243
|
+
&& process.argv[2] === INTERNAL_PROCESS_TREE_COMMAND
|
|
244
|
+
&& import.meta.url === pathToFileURL(process.argv[1]).href
|
|
245
|
+
) {
|
|
246
|
+
void runInternalProcessTreeCommand().catch((error) => {
|
|
247
|
+
console.error(`buildchain: process tree command failed: ${error.message}`);
|
|
248
|
+
process.exitCode = 1;
|
|
25
249
|
});
|
|
26
250
|
}
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
|
2
|
+
import crypto from "node:crypto";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
adopterDeliveryGateDigest,
|
|
8
|
+
createAdopterDeliveryGate,
|
|
9
|
+
} from "./adopter-delivery-gate.js";
|
|
10
|
+
|
|
11
|
+
export const V4_ADOPTER_DELIVERY_PARITY_INPUT_CONTRACT =
|
|
12
|
+
"kungfu-buildchain-v4-adopter-delivery-parity-input";
|
|
13
|
+
export const V4_ADOPTER_DELIVERY_PARITY_PROJECTION_CONTRACT =
|
|
14
|
+
"buildchain-v4-adopter-delivery-parity-projection/v1";
|
|
15
|
+
|
|
16
|
+
const HOST_REQUEST_CONTRACT = "kungfu-buildchain-v4-host-request";
|
|
17
|
+
const HOST_RESPONSE_CONTRACT = "kungfu-buildchain-v4-host-response";
|
|
18
|
+
const DRIVER_INTERFACE = "kungfu-buildchain-adopter-protocol-driver/v1";
|
|
19
|
+
const PROFILE_INTERFACE = "kungfu-buildchain-adopter-artifact-profile/v1";
|
|
20
|
+
const REQUIRED_CAPABILITIES = Object.freeze([
|
|
21
|
+
"canonical-input-v1",
|
|
22
|
+
"adopter-delivery-parity-projection-v1",
|
|
23
|
+
"diagnostics-v1",
|
|
24
|
+
"effects-disabled-v1",
|
|
25
|
+
"structured-result-v1",
|
|
26
|
+
]);
|
|
27
|
+
const MAX_RESPONSE_BYTES = 8 * 1024 * 1024;
|
|
28
|
+
const root = path.resolve(
|
|
29
|
+
path.dirname(fileURLToPath(import.meta.url)),
|
|
30
|
+
"../..",
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
export const V4_ADOPTER_DELIVERY_PARITY_SOURCE = Object.freeze({
|
|
34
|
+
v3Commit: "c3f58d76391c1c6ceddfc900a68e91c7ab82a575",
|
|
35
|
+
vectorSuiteRoot:
|
|
36
|
+
"sha256:cf329805d928a9883cbafbdfdf21ef66c6a0889ed8dfe14356b4e0d25d6738f9",
|
|
37
|
+
kfdPackage: "@kungfu-tech/kfd@1.0.0-alpha.62",
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
function clone(value) {
|
|
41
|
+
return structuredClone(value);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function exactKey(definition) {
|
|
45
|
+
return `${definition.id}@${definition.version}`;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function capture(operation) {
|
|
49
|
+
try {
|
|
50
|
+
return { state: "returned", result: clone(operation()) };
|
|
51
|
+
} catch {
|
|
52
|
+
return { state: "threw", result: null };
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function definition(definition, execution, { profile = false } = {}) {
|
|
57
|
+
if (!definition) return null;
|
|
58
|
+
return {
|
|
59
|
+
interface: definition.interface,
|
|
60
|
+
id: definition.id,
|
|
61
|
+
version: definition.version,
|
|
62
|
+
...(profile ? { kinds: [...definition.kinds] } : {}),
|
|
63
|
+
execution,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function createV4AdopterDeliveryParityInput({
|
|
68
|
+
vectorId,
|
|
69
|
+
request,
|
|
70
|
+
context = {},
|
|
71
|
+
drivers = [],
|
|
72
|
+
artifactProfiles = [],
|
|
73
|
+
sourceAuthority = V4_ADOPTER_DELIVERY_PARITY_SOURCE,
|
|
74
|
+
} = {}) {
|
|
75
|
+
if (typeof vectorId !== "string" || vectorId.length === 0) {
|
|
76
|
+
throw new TypeError("vectorId must be a non-empty string");
|
|
77
|
+
}
|
|
78
|
+
const gate = createAdopterDeliveryGate({ drivers, artifactProfiles });
|
|
79
|
+
const expectedResult = gate.evaluate(clone(request), clone(context));
|
|
80
|
+
const driver = drivers.find(
|
|
81
|
+
(entry) =>
|
|
82
|
+
exactKey(entry) === `${request.protocol.id}@${request.protocol.version}`,
|
|
83
|
+
);
|
|
84
|
+
const artifactProfile = artifactProfiles.find(
|
|
85
|
+
(entry) =>
|
|
86
|
+
exactKey(entry) ===
|
|
87
|
+
`${request.artifactProfile.id}@${request.artifactProfile.version}`,
|
|
88
|
+
);
|
|
89
|
+
let profileExecution = null;
|
|
90
|
+
if (artifactProfile?.kinds.includes(request.artifact.kind)) {
|
|
91
|
+
profileExecution = capture(() =>
|
|
92
|
+
artifactProfile.verify(clone(request.artifact)),
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
let driverExecution = null;
|
|
96
|
+
if (
|
|
97
|
+
driver &&
|
|
98
|
+
profileExecution?.state === "returned" &&
|
|
99
|
+
profileExecution.result?.valid === true
|
|
100
|
+
) {
|
|
101
|
+
driverExecution = capture(() =>
|
|
102
|
+
driver.verify({ request: clone(request), context: clone(context) }),
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
return {
|
|
106
|
+
schemaVersion: 1,
|
|
107
|
+
contract: V4_ADOPTER_DELIVERY_PARITY_INPUT_CONTRACT,
|
|
108
|
+
vectorId,
|
|
109
|
+
sourceAuthority: clone(sourceAuthority),
|
|
110
|
+
request: clone(request),
|
|
111
|
+
driver: definition(driver, driverExecution),
|
|
112
|
+
artifactProfile: definition(artifactProfile, profileExecution, {
|
|
113
|
+
profile: true,
|
|
114
|
+
}),
|
|
115
|
+
expectedResult,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function hostRequest(input, timeoutMs) {
|
|
120
|
+
return {
|
|
121
|
+
schemaVersion: 1,
|
|
122
|
+
contract: HOST_REQUEST_CONTRACT,
|
|
123
|
+
protocolVersion: "1.0",
|
|
124
|
+
requestId: crypto.randomUUID(),
|
|
125
|
+
command: { id: "adopter-delivery.parity-project", arguments: [] },
|
|
126
|
+
input: {
|
|
127
|
+
encoding: "base64",
|
|
128
|
+
bytes: Buffer.from(JSON.stringify(input), "utf8").toString("base64"),
|
|
129
|
+
},
|
|
130
|
+
requiredCapabilities: [...REQUIRED_CAPABILITIES],
|
|
131
|
+
timeoutMs,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export function runV4AdopterDeliveryParity(
|
|
136
|
+
input,
|
|
137
|
+
{
|
|
138
|
+
command = process.platform === "win32" ? "cargo.exe" : "cargo",
|
|
139
|
+
arguments: commandArguments = [
|
|
140
|
+
"run",
|
|
141
|
+
"--locked",
|
|
142
|
+
"--quiet",
|
|
143
|
+
"--manifest-path",
|
|
144
|
+
"crates/buildchain-v4-contracts/Cargo.toml",
|
|
145
|
+
"--",
|
|
146
|
+
"host",
|
|
147
|
+
],
|
|
148
|
+
cwd = root,
|
|
149
|
+
timeoutMs = 30_000,
|
|
150
|
+
} = {},
|
|
151
|
+
) {
|
|
152
|
+
if (!Number.isInteger(timeoutMs) || timeoutMs < 1 || timeoutMs > 30_000) {
|
|
153
|
+
throw new RangeError("timeoutMs must be between 1 and 30000");
|
|
154
|
+
}
|
|
155
|
+
const request = hostRequest(input, timeoutMs);
|
|
156
|
+
const completed = spawnSync(command, commandArguments, {
|
|
157
|
+
cwd,
|
|
158
|
+
input: JSON.stringify(request),
|
|
159
|
+
encoding: "utf8",
|
|
160
|
+
timeout: timeoutMs,
|
|
161
|
+
maxBuffer: MAX_RESPONSE_BYTES,
|
|
162
|
+
env: process.env,
|
|
163
|
+
});
|
|
164
|
+
if (completed.error) {
|
|
165
|
+
throw new Error(
|
|
166
|
+
`v4 adopter delivery parity host failed: ${completed.error.message}`,
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
if (completed.status !== 0) {
|
|
170
|
+
throw new Error(
|
|
171
|
+
`v4 adopter delivery parity host exited ${completed.status}: ${completed.stderr.trim()}`,
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
let response;
|
|
175
|
+
try {
|
|
176
|
+
response = JSON.parse(completed.stdout);
|
|
177
|
+
} catch {
|
|
178
|
+
throw new Error("v4 adopter delivery parity host returned invalid JSON");
|
|
179
|
+
}
|
|
180
|
+
if (
|
|
181
|
+
response.schemaVersion !== 1 ||
|
|
182
|
+
response.contract !== HOST_RESPONSE_CONTRACT ||
|
|
183
|
+
response.protocolVersion !== "1.0" ||
|
|
184
|
+
response.requestId !== request.requestId ||
|
|
185
|
+
response.command?.id !== request.command.id ||
|
|
186
|
+
response.status !== "ok"
|
|
187
|
+
) {
|
|
188
|
+
const code = response.diagnostics?.[0]?.code || "invalid-response";
|
|
189
|
+
throw new Error(`v4 adopter delivery parity failed closed: ${code}`);
|
|
190
|
+
}
|
|
191
|
+
const projection = response.structuredResult;
|
|
192
|
+
if (
|
|
193
|
+
projection?.schema !== V4_ADOPTER_DELIVERY_PARITY_PROJECTION_CONTRACT ||
|
|
194
|
+
projection.effectMode !== "disabled" ||
|
|
195
|
+
projection.vectorId !== input.vectorId ||
|
|
196
|
+
!/^sha256:[0-9a-f]{64}$/u.test(projection.projectionRoot || "")
|
|
197
|
+
) {
|
|
198
|
+
throw new Error("v4 adopter delivery parity projection is invalid");
|
|
199
|
+
}
|
|
200
|
+
return projection;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
export function assertV4AdopterDeliveryParity(input, options = {}) {
|
|
204
|
+
const projection = runV4AdopterDeliveryParity(input, options);
|
|
205
|
+
if (
|
|
206
|
+
adopterDeliveryGateDigest(projection.result) !==
|
|
207
|
+
adopterDeliveryGateDigest(input.expectedResult)
|
|
208
|
+
) {
|
|
209
|
+
throw new Error(
|
|
210
|
+
"v4 adopter delivery parity result differs from v3 authority",
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
return projection;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
export const V4_ADOPTER_DELIVERY_PARITY_INTERFACES = Object.freeze({
|
|
217
|
+
driver: DRIVER_INTERFACE,
|
|
218
|
+
artifactProfile: PROFILE_INTERFACE,
|
|
219
|
+
});
|