@kungfu-tech/buildchain 4.1.3-alpha.1 → 4.1.3-alpha.10
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 +41 -75
- package/CONTRIBUTING.md +19 -26
- package/architecture/action-taxonomy.json +24 -0
- package/architecture/agent-change-map.md +266 -10
- package/architecture/capability-state-machine-manifest.json +1 -4
- package/architecture/ci-lane-change-budget.json +190 -807
- package/architecture/core-mechanisms.json +3 -6
- package/architecture/decisions/0005-minimal-consumer-contract.md +122 -0
- package/architecture/decisions/0006-business-attempt-journal.md +89 -0
- package/architecture/decisions/0007-hosted-pipeline-controller.md +106 -0
- package/architecture/decisions/0008-pipeline-product-publication.md +120 -0
- package/architecture/decisions/0009-pipeline-version-preparation.md +186 -0
- package/architecture/internal-capabilities.json +362 -7
- package/architecture/maintainability-debt.json +50 -34
- package/architecture/maintainability-policy.json +8 -8
- package/architecture/minimal-consumer-migration.json +5670 -0
- package/architecture/platform-stage-checkpoints.json +31 -7
- package/architecture/product-upstreams.json +20 -0
- package/architecture/release-topology.json +328 -74
- package/architecture/stage-capsule-qualification.json +3 -1
- package/architecture/universal-workflow-bootstrap.json +15 -1
- package/architecture/universal-workflow-capability-policy.json +4 -4
- package/architecture/universal-workflow-fault-campaign.json +3 -1
- package/architecture/workflow-taxonomy.json +77 -328
- package/dist/readers/business-attempt.cjs +809 -0
- package/dist/site/buildchain-contract.json +16 -68
- package/dist/site/buildchain-site.json +40 -25
- package/dist/site/capability-registry.json +4 -4
- package/dist/site/controller-registry.json +1 -47
- package/dist/site/kfd-claims.json +499 -494
- package/dist/site/kfd-upstream-aggregate.json +1 -1
- package/dist/site/manual-registry.json +3 -3
- package/dist/site/node-api-registry.json +54 -54
- package/dist/site/page-registry.json +33 -18
- package/dist/site/public-surface-audit.json +286 -349
- package/dist/site/publication-authority-registry.json +203 -523
- package/dist/site/publication-registry.json +4 -4
- package/dist/site/site-manifest.json +7 -7
- package/dist/site/workflow-registry.json +590 -940
- package/docs/code-organization.md +9 -6
- package/docs/node-api-reference.md +102 -102
- package/docs/release-governance.md +10 -12
- package/docs/runtime-entry.md +73 -5
- package/docs/runtime-train-validation.md +39 -11
- package/docs/stable-candidate-patrol.md +6 -5
- package/docs/workflow-catalog.md +9 -32
- package/package.json +4 -3
- package/packages/core/adoption/buildchain-kfd-claims.js +7 -7
- package/packages/core/build/standalone/build.js +7 -1
- package/packages/core/consumer/buildchain-config.js +2 -21
- package/packages/core/consumer/contract/entries.js +85 -0
- package/packages/core/consumer/contract/examples.js +96 -0
- package/packages/core/consumer/contract/identity.js +65 -0
- package/packages/core/consumer/contract/inspection.js +45 -0
- package/packages/core/consumer/contract/plan.js +149 -0
- package/packages/core/consumer/contract/products.js +119 -0
- package/packages/core/consumer/contract/reader.js +47 -0
- package/packages/core/consumer/contract/shape.js +46 -0
- package/packages/core/dev-delivery/candidate/admission.js +9 -5
- package/packages/core/dev-delivery/candidate/reservation.js +11 -0
- package/packages/core/dev-delivery/candidate/source-paths.js +12 -4
- package/packages/core/dev-delivery/commands/project-cut-merge-queue-admission.mjs +47 -20
- package/packages/core/dev-delivery/dev-delivery-provider-heartbeat.js +18 -15
- package/packages/core/dev-delivery/native/actions.js +9 -1
- package/packages/core/dev-delivery/native/heartbeat-action.js +16 -0
- package/packages/core/dev-delivery/native/heartbeat.js +6 -4
- package/packages/core/dev-delivery/native/process-entry.js +19 -0
- package/packages/core/dev-delivery/queue/landing-action.js +53 -0
- package/packages/core/dev-delivery/warrant/failure-settlement.js +8 -6
- package/packages/core/dev-delivery/warrant/service.js +7 -0
- package/packages/core/governance/buildchain-publication-authority.js +8 -23
- package/packages/core/governance/github-governance-authority.js +1 -0
- package/packages/core/observability/controller-evidence.js +0 -8
- package/packages/core/paper/operations/bootstrap.js +2 -0
- package/packages/core/paper/paper-npm-bootstrap.js +2 -2
- package/packages/core/providers/github/attempt-index.js +60 -0
- package/packages/core/providers/github/attempt-journal.js +154 -0
- package/packages/core/providers/github/discussions/materials.js +19 -6
- package/packages/core/providers/github/pipeline-checkout.js +49 -0
- package/packages/core/providers/github/pipeline-entry-runs.js +210 -0
- package/packages/core/providers/github/pipeline-events.js +131 -0
- package/packages/core/providers/github/pipeline-integration.js +95 -0
- package/packages/core/providers/github/pipeline-policy.js +240 -0
- package/packages/core/providers/github/pipeline-product-payload.js +50 -0
- package/packages/core/providers/github/pipeline-product-release.js +198 -0
- package/packages/core/providers/github/pipeline-publication-artifacts.js +153 -0
- package/packages/core/providers/github/pipeline-release-evidence.js +30 -0
- package/packages/core/providers/github/pipeline-run-entry.js +86 -0
- package/packages/core/providers/github/pipeline-runs.js +132 -0
- package/packages/core/providers/github/pipeline-source.js +195 -0
- package/packages/core/providers/github/pipeline-stable-baseline.js +86 -0
- package/packages/core/providers/github/pipeline-stable-source.js +372 -0
- package/packages/core/providers/github/pipeline-version-artifacts.js +91 -0
- package/packages/core/providers/github/pipeline-version-readback.js +117 -0
- package/packages/core/providers/github/pipeline-version.js +199 -0
- package/packages/core/providers/github/pipeline-worker.js +108 -0
- package/packages/core/providers/github/release-evidence-assets.js +97 -0
- package/packages/core/publication/candidate/registry-hydration.js +2 -2
- package/packages/core/publication/npm/pack-preview.js +2 -2
- package/packages/core/publication/npm/pack-result.js +31 -0
- package/packages/core/publication/npm/package.js +3 -6
- package/packages/core/publication/npm/pipeline-channel.js +36 -0
- package/packages/core/publication/npm/pipeline-provider.js +137 -0
- package/packages/core/publication/npm/registry.js +12 -9
- package/packages/core/publication/pipeline/actions.js +116 -0
- package/packages/core/publication/pipeline/apply.js +189 -0
- package/packages/core/publication/pipeline/build-segments.js +107 -0
- package/packages/core/publication/pipeline/capsules.js +92 -0
- package/packages/core/publication/pipeline/context.js +39 -0
- package/packages/core/publication/pipeline/development-anchor.js +91 -0
- package/packages/core/publication/pipeline/development-current.js +63 -0
- package/packages/core/publication/pipeline/development-pr.js +112 -0
- package/packages/core/publication/pipeline/development-proof.js +76 -0
- package/packages/core/publication/pipeline/development-transition.js +112 -0
- package/packages/core/publication/pipeline/distribution.js +130 -0
- package/packages/core/publication/pipeline/documents.js +192 -0
- package/packages/core/publication/pipeline/effects.js +192 -0
- package/packages/core/publication/pipeline/files.js +66 -0
- package/packages/core/publication/pipeline/imported-materials.js +32 -0
- package/packages/core/publication/pipeline/journal.js +122 -0
- package/packages/core/publication/pipeline/next-development.js +117 -0
- package/packages/core/publication/pipeline/pack.js +180 -0
- package/packages/core/publication/pipeline/package-policy.js +22 -0
- package/packages/core/publication/pipeline/plan.js +136 -0
- package/packages/core/publication/pipeline/prepare.js +139 -0
- package/packages/core/publication/pipeline/qualification.js +175 -0
- package/packages/core/publication/pipeline/qualify.js +104 -0
- package/packages/core/publication/pipeline/recovery-admission.js +122 -0
- package/packages/core/publication/pipeline/recovery-build-download.js +80 -0
- package/packages/core/publication/pipeline/recovery-build-plan.js +122 -0
- package/packages/core/publication/pipeline/recovery-build-readback.js +68 -0
- package/packages/core/publication/pipeline/recovery-capsules.js +54 -0
- package/packages/core/publication/pipeline/recovery-import.js +80 -0
- package/packages/core/publication/pipeline/recovery-inspection.js +218 -0
- package/packages/core/publication/pipeline/recovery-materials.js +104 -0
- package/packages/core/publication/pipeline/recovery-plan.js +46 -0
- package/packages/core/publication/pipeline/recovery-prepare.js +73 -0
- package/packages/core/publication/pipeline/recovery-qualification.js +128 -0
- package/packages/core/publication/pipeline/recovery-readback.js +56 -0
- package/packages/core/publication/pipeline/recovery-signing.js +58 -0
- package/packages/core/publication/pipeline/sealed-products.js +79 -0
- package/packages/core/publication/pipeline/settle.js +42 -0
- package/packages/core/publication/pipeline/signing.js +140 -0
- package/packages/core/publication/pipeline/source-plan.js +83 -0
- package/packages/core/publication/pipeline/stable-entry.js +219 -0
- package/packages/core/publication/pipeline/stable-products.js +317 -0
- package/packages/core/publication/pipeline/stable-wait.js +170 -0
- package/packages/core/publication/pipeline/stable.js +82 -0
- package/packages/core/publication/pipeline/version-actions.js +93 -0
- package/packages/core/publication/pipeline/version-build.js +130 -0
- package/packages/core/publication/pipeline/version-context.js +143 -0
- package/packages/core/publication/pipeline/version-material.js +36 -0
- package/packages/core/publication/pipeline/version-preparation.js +69 -0
- package/packages/core/publication/pipeline/version-regeneration.js +162 -0
- package/packages/core/publication/pipeline/version-source-guard.js +69 -0
- package/packages/core/publication/pipeline/version.js +81 -0
- package/packages/core/publication/pipeline/worker.js +72 -0
- package/packages/core/publication/publication-reproducibility.js +2 -2
- package/packages/core/release/next-development-transition.js +15 -15
- package/packages/core/release/promote-candidate/product-provider-adapters.js +4 -3
- package/packages/core/release/promote-ref/internal/npm-existing-evidence.js +2 -1
- package/packages/core/release/promote-ref/internal/publish-command.js +16 -5
- package/packages/core/release/stable-release-gate.js +11 -0
- package/packages/core/runtime/buildchain-domain.wasm +0 -0
- package/packages/core/runtime/domain-wasm-artifact.js +2 -2
- package/packages/core/runtime/entry/actions.js +27 -25
- package/packages/core/runtime/entry/attempt.js +25 -0
- package/packages/core/runtime/entry/selection.js +14 -0
- package/packages/core/runtime/entry/source.js +62 -0
- package/packages/core/workflow/attempt/identity.js +170 -0
- package/packages/core/workflow/attempt/journal.js +89 -0
- package/packages/core/workflow/attempt/materials.js +72 -0
- package/packages/core/workflow/attempt/reader-entry.js +12 -0
- package/packages/core/workflow/attempt/reader.js +149 -0
- package/packages/core/workflow/attempt/records.js +153 -0
- package/packages/core/workflow/attempt/store.js +75 -0
- package/packages/core/workflow/commands/pipeline-build.mjs +23 -0
- package/packages/core/workflow/pipeline/action-output.js +11 -0
- package/packages/core/workflow/pipeline/actions.js +52 -0
- package/packages/core/workflow/pipeline/build-control.js +140 -0
- package/packages/core/workflow/pipeline/build-evidence.js +210 -0
- package/packages/core/workflow/pipeline/build-qualification.js +145 -0
- package/packages/core/workflow/pipeline/build-result.js +78 -0
- package/packages/core/workflow/pipeline/build.js +142 -0
- package/packages/core/workflow/pipeline/cancellation.js +169 -0
- package/packages/core/workflow/pipeline/channel-control.js +157 -0
- package/packages/core/workflow/pipeline/channel-source.js +53 -0
- package/packages/core/workflow/pipeline/controller.js +141 -0
- package/packages/core/workflow/pipeline/delivery-control.js +249 -0
- package/packages/core/workflow/pipeline/delivery-observation.js +124 -0
- package/packages/core/workflow/pipeline/delivery-request.js +198 -0
- package/packages/core/workflow/pipeline/events.js +84 -0
- package/packages/core/workflow/pipeline/fence.js +42 -0
- package/packages/core/workflow/pipeline/group-control.js +59 -0
- package/packages/core/workflow/pipeline/guard-build.js +74 -0
- package/packages/core/workflow/pipeline/guard.js +158 -0
- package/packages/core/workflow/pipeline/host.js +144 -0
- package/packages/core/workflow/pipeline/integration-status.js +113 -0
- package/packages/core/workflow/pipeline/materials.js +58 -0
- package/packages/core/workflow/pipeline/notifications.js +36 -0
- package/packages/core/workflow/pipeline/parent-notification.js +27 -0
- package/packages/core/workflow/pipeline/platforms.js +21 -0
- package/packages/core/workflow/pipeline/progress.js +108 -0
- package/packages/core/workflow/pipeline/projection.js +20 -0
- package/packages/core/workflow/pipeline/reconcile.js +194 -0
- package/packages/core/workflow/pipeline/record-action.js +27 -0
- package/packages/core/workflow/pipeline/recorder-status.js +69 -0
- package/packages/core/workflow/pipeline/recovery-action.js +30 -0
- package/packages/core/workflow/pipeline/recovery-admission.js +122 -0
- package/packages/core/workflow/pipeline/recovery-build-control.js +95 -0
- package/packages/core/workflow/pipeline/recovery-build-evidence.js +71 -0
- package/packages/core/workflow/pipeline/recovery-build.js +121 -0
- package/packages/core/workflow/pipeline/recovery-controller.js +91 -0
- package/packages/core/workflow/pipeline/recovery-integration.js +79 -0
- package/packages/core/workflow/pipeline/recovery-merge-proof.js +90 -0
- package/packages/core/workflow/pipeline/recovery-ownership-settlement.js +150 -0
- package/packages/core/workflow/pipeline/recovery-ownership.js +91 -0
- package/packages/core/workflow/pipeline/recovery-plan.js +126 -0
- package/packages/core/workflow/pipeline/recovery-runtime.js +68 -0
- package/packages/core/workflow/pipeline/recovery-session.js +129 -0
- package/packages/core/workflow/pipeline/recovery-transition.js +135 -0
- package/packages/core/workflow/pipeline/recovery-unrecorded-build.js +74 -0
- package/packages/core/workflow/pipeline/runtime-source.js +111 -0
- package/packages/core/workflow/pipeline/selection.js +111 -0
- package/packages/core/workflow/pipeline/session.js +72 -0
- package/packages/core/workflow/pipeline/settlement.js +137 -0
- package/packages/core/workflow/pipeline/wake.js +38 -0
- package/packages/core/workflow/pipeline/web-status.js +108 -0
- package/packages/core/workflow/workflow-taxonomy.mjs +67 -47
- package/scripts/build-release-discussion-reader.mjs +11 -7
- package/scripts/check-inventory.mjs +7 -70
- package/scripts/check-pipeline-publication-topology.mjs +50 -0
- package/scripts/check-public-dogfood-contract.mjs +38 -51
- package/scripts/check-release-topology.mjs +15 -8
- package/scripts/check-self-consumer-contract.mjs +133 -0
- package/scripts/check-universal-workflow-bootstrap.mjs +4 -6
- package/scripts/generate-minimal-consumer-contract.mjs +155 -0
- package/scripts/generate-site-bundle.mjs +1 -1
- package/scripts/inventory/binary.mjs +9 -19
- package/scripts/inventory/promotion.mjs +2 -13
- package/scripts/prepare-product-toolchain.mjs +10 -0
- package/scripts/verify-golden-path.mjs +5 -1
- package/scripts/verify-product-platform.mjs +65 -0
- package/scripts/verify-standalone-product.mjs +42 -0
- package/templates/minimal-consumer/binary/.buildchain/buildchain.toml +45 -0
- package/templates/minimal-consumer/binary/.github/workflows/buildchain-recover.yml +30 -0
- package/templates/minimal-consumer/binary/.github/workflows/buildchain.yml +28 -0
- package/templates/minimal-consumer/binary/package.json +6 -0
- package/templates/minimal-consumer/binary/src/build.mjs +5 -0
- package/templates/minimal-consumer/binary/src/hello.c +2 -0
- package/templates/minimal-consumer/binary/src/verify.mjs +3 -0
- package/templates/minimal-consumer/npm/.buildchain/buildchain.toml +46 -0
- package/templates/minimal-consumer/npm/.github/workflows/buildchain-recover.yml +30 -0
- package/templates/minimal-consumer/npm/.github/workflows/buildchain.yml +28 -0
- package/templates/minimal-consumer/npm/package.json +6 -0
- package/templates/minimal-consumer/npm/src/build.mjs +5 -0
- package/templates/minimal-consumer/npm/src/verify.mjs +3 -0
- package/templates/minimal-consumer/paper/.buildchain/buildchain.toml +45 -0
- package/templates/minimal-consumer/paper/.github/workflows/buildchain-recover.yml +30 -0
- package/templates/minimal-consumer/paper/.github/workflows/buildchain.yml +28 -0
- package/templates/minimal-consumer/paper/package.json +6 -0
- package/templates/minimal-consumer/paper/src/build.mjs +12 -0
- package/templates/minimal-consumer/paper/src/verify.mjs +3 -0
|
@@ -61,19 +61,32 @@ function git(
|
|
|
61
61
|
{
|
|
62
62
|
env = {},
|
|
63
63
|
input = undefined,
|
|
64
|
+
inputFile = undefined,
|
|
65
|
+
outputFile = undefined,
|
|
64
66
|
encoding = "utf8",
|
|
65
67
|
reason = "git-failed",
|
|
66
68
|
} = {},
|
|
67
69
|
) {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
70
|
+
let result, inputDescriptor, outputDescriptor;
|
|
71
|
+
try {
|
|
72
|
+
if (inputFile) inputDescriptor = fs.openSync(inputFile, "r");
|
|
73
|
+
if (outputFile) outputDescriptor = fs.openSync(outputFile, "w");
|
|
74
|
+
result = spawnSync("git", args, {
|
|
75
|
+
cwd,
|
|
76
|
+
env: { ...process.env, ...env },
|
|
77
|
+
input,
|
|
78
|
+
encoding,
|
|
79
|
+
stdio: [inputDescriptor ?? "pipe", outputDescriptor ?? "pipe", "pipe"],
|
|
80
|
+
maxBuffer: 64 * 1024 * 1024,
|
|
81
|
+
});
|
|
82
|
+
} finally {
|
|
83
|
+
for (const descriptor of [inputDescriptor, outputDescriptor])
|
|
84
|
+
if (descriptor !== undefined) fs.closeSync(descriptor);
|
|
85
|
+
}
|
|
75
86
|
if (result.status !== 0) {
|
|
76
|
-
const detail = String(
|
|
87
|
+
const detail = String(
|
|
88
|
+
result.stderr || result.error?.message || result.stdout || "",
|
|
89
|
+
).trim();
|
|
77
90
|
throw new ProjectCutAdmissionError(
|
|
78
91
|
reason,
|
|
79
92
|
`git ${args.join(" ")} failed${detail ? `: ${detail}` : ""}`,
|
|
@@ -135,7 +148,7 @@ function linearSourceCommits(cwd, fork, head) {
|
|
|
135
148
|
|
|
136
149
|
function patchId(cwd, patch) {
|
|
137
150
|
const output = git(cwd, ["patch-id", "--stable"], {
|
|
138
|
-
|
|
151
|
+
inputFile: patch,
|
|
139
152
|
encoding: "utf8",
|
|
140
153
|
reason: "patch-id-failed",
|
|
141
154
|
});
|
|
@@ -184,30 +197,41 @@ function replayProjectCut(cwd, base, head) {
|
|
|
184
197
|
env: indexEnvironment,
|
|
185
198
|
reason: "base-tree-unreadable",
|
|
186
199
|
});
|
|
187
|
-
|
|
200
|
+
// Generated bundles can exceed the metadata-output bound. Git streams the
|
|
201
|
+
// exact patches through private files; patch identity and replay stay intact.
|
|
202
|
+
const sourcePatch = path.join(temporary, "source.patch");
|
|
203
|
+
git(
|
|
188
204
|
cwd,
|
|
189
205
|
["diff", "--binary", "--full-index", "--no-ext-diff", fork, head],
|
|
190
206
|
{
|
|
191
|
-
|
|
207
|
+
outputFile: sourcePatch,
|
|
192
208
|
reason: "source-patch-unreadable",
|
|
193
209
|
},
|
|
194
210
|
);
|
|
195
|
-
const emptySourcePatch = sourcePatch.
|
|
211
|
+
const emptySourcePatch = fs.statSync(sourcePatch).size === 0;
|
|
196
212
|
if (emptySourcePatch && commits.length !== 1)
|
|
197
|
-
throw new ProjectCutAdmissionError(
|
|
198
|
-
|
|
199
|
-
|
|
213
|
+
throw new ProjectCutAdmissionError(
|
|
214
|
+
"empty-source-composition",
|
|
215
|
+
"an empty source composition must be exactly one linear commit",
|
|
216
|
+
);
|
|
217
|
+
if (!emptySourcePatch)
|
|
218
|
+
git(cwd, ["apply", "--cached", "--3way", "--whitespace=nowarn", "-"], {
|
|
219
|
+
env: indexEnvironment,
|
|
220
|
+
inputFile: sourcePatch,
|
|
221
|
+
encoding: "utf8",
|
|
222
|
+
reason: "project-cut-conflict",
|
|
200
223
|
});
|
|
201
224
|
const candidateTreeOid = git(cwd, ["write-tree"], {
|
|
202
225
|
env: indexEnvironment,
|
|
203
226
|
reason: "candidate-tree-unreadable",
|
|
204
227
|
});
|
|
205
|
-
const replayPatch =
|
|
228
|
+
const replayPatch = path.join(temporary, "replay.patch");
|
|
229
|
+
git(
|
|
206
230
|
cwd,
|
|
207
231
|
["diff", "--cached", "--binary", "--full-index", "--no-ext-diff", base],
|
|
208
232
|
{
|
|
209
233
|
env: indexEnvironment,
|
|
210
|
-
|
|
234
|
+
outputFile: replayPatch,
|
|
211
235
|
reason: "replay-patch-unreadable",
|
|
212
236
|
},
|
|
213
237
|
);
|
|
@@ -226,9 +250,12 @@ function replayProjectCut(cwd, base, head) {
|
|
|
226
250
|
reason: "replay-paths-unreadable",
|
|
227
251
|
},
|
|
228
252
|
);
|
|
229
|
-
const compositionDrifted = emptySourcePatch
|
|
230
|
-
|
|
231
|
-
|
|
253
|
+
const compositionDrifted = emptySourcePatch
|
|
254
|
+
? fs.statSync(replayPatch).size !== 0 ||
|
|
255
|
+
sourceNames !== "" ||
|
|
256
|
+
replayNames !== ""
|
|
257
|
+
: patchId(cwd, sourcePatch) !== patchId(cwd, replayPatch) ||
|
|
258
|
+
sourceNames !== replayNames;
|
|
232
259
|
if (compositionDrifted) {
|
|
233
260
|
throw new ProjectCutAdmissionError(
|
|
234
261
|
"composition-drift",
|
|
@@ -207,29 +207,32 @@ export async function runDevDeliveryProviderHeartbeat(
|
|
|
207
207
|
const startedAt = timestamp(now(), "provider heartbeat startedAt");
|
|
208
208
|
let previousStateRoot = initial.stateRoot;
|
|
209
209
|
const heartbeats = [];
|
|
210
|
+
let untilHeartbeat = 0;
|
|
210
211
|
let jobs;
|
|
211
212
|
try {
|
|
212
213
|
while (true) {
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
214
|
+
if (untilHeartbeat === 0) {
|
|
215
|
+
const result = await heartbeat({
|
|
216
|
+
expectedOldStateRoot: previousStateRoot,
|
|
217
|
+
fencingToken: initial.fencingToken,
|
|
218
|
+
leaseGeneration: initial.generation,
|
|
219
|
+
leaseSeconds: duration,
|
|
220
|
+
});
|
|
221
|
+
const entry = heartbeatEntry(result, previousStateRoot);
|
|
222
|
+
heartbeats.push(entry);
|
|
223
|
+
previousStateRoot = entry.nextStateRoot;
|
|
224
|
+
untilHeartbeat = cadence * 1000;
|
|
225
|
+
}
|
|
222
226
|
jobs = providerJobs(
|
|
223
227
|
await readJobs(),
|
|
224
228
|
{ nativeJobName, sealJobName },
|
|
225
229
|
{ allowMissingSeal: true },
|
|
226
230
|
);
|
|
227
|
-
if (
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
await wait(cadence * 1000);
|
|
231
|
+
if (jobs && terminal(jobs.native) && terminal(jobs.seal)) break;
|
|
232
|
+
// Terminal detection must not wait for the next durable lease renewal.
|
|
233
|
+
const interval = Math.min(untilHeartbeat, 5000);
|
|
234
|
+
await wait(interval);
|
|
235
|
+
untilHeartbeat -= interval;
|
|
233
236
|
}
|
|
234
237
|
} catch (error) {
|
|
235
238
|
await onHeartbeatLoss({
|
|
@@ -5,6 +5,8 @@ import {
|
|
|
5
5
|
qualifyTransferredNative,
|
|
6
6
|
} from "./transactions.js";
|
|
7
7
|
import { admitProviderFinalizer } from "./finalizer-admission.js";
|
|
8
|
+
import { guardPipelineAdmission } from "../../workflow/pipeline/guard.js";
|
|
9
|
+
import { replaceNativeActionProcess } from "./process-entry.js";
|
|
8
10
|
function request(core, env) {
|
|
9
11
|
const candidate = JSON.parse(
|
|
10
12
|
core.getInput("request-json", { required: true }),
|
|
@@ -22,6 +24,7 @@ function outputs(core, result) {
|
|
|
22
24
|
core.setOutput(name, value);
|
|
23
25
|
}
|
|
24
26
|
export async function executeDeliveryNativeAction(core, env) {
|
|
27
|
+
replaceNativeActionProcess();
|
|
25
28
|
const context = deliveryActionContext(core, env);
|
|
26
29
|
const input = request(core, env);
|
|
27
30
|
outputs(
|
|
@@ -41,11 +44,16 @@ export async function executeDeliveryNativeAction(core, env) {
|
|
|
41
44
|
}
|
|
42
45
|
export async function qualifyTransferredNativeAction(core, env) {
|
|
43
46
|
const context = deliveryActionContext(core, env);
|
|
47
|
+
const input = request(core, env);
|
|
48
|
+
await guardPipelineAdmission(input.request, {
|
|
49
|
+
repository: env.GITHUB_REPOSITORY,
|
|
50
|
+
token: core.getInput("token", { required: true }),
|
|
51
|
+
});
|
|
44
52
|
outputs(
|
|
45
53
|
core,
|
|
46
54
|
await qualifyTransferredNative({
|
|
47
55
|
...context,
|
|
48
|
-
candidate:
|
|
56
|
+
candidate: input.candidate,
|
|
49
57
|
sourceProofRoot: core.getInput("source-proof-root", { required: true }),
|
|
50
58
|
token: core.getInput("token", { required: true }),
|
|
51
59
|
apiUrl: env.GITHUB_API_URL || "https://api.github.com",
|
|
@@ -2,6 +2,7 @@ import fs from "node:fs";
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { deliveryActionContext } from "./action-context.js";
|
|
4
4
|
import { heartbeatDeliveryAttempt } from "./heartbeat.js";
|
|
5
|
+
import { guardPipelineDelivery } from "../../workflow/pipeline/guard.js";
|
|
5
6
|
export async function heartbeatDeliveryAttemptAction(core, env) {
|
|
6
7
|
const { workspace, run } = deliveryActionContext(core, env);
|
|
7
8
|
const admission = JSON.parse(
|
|
@@ -20,6 +21,21 @@ export async function heartbeatDeliveryAttemptAction(core, env) {
|
|
|
20
21
|
apiUrl: env.GITHUB_API_URL || "https://api.github.com",
|
|
21
22
|
leaseSeconds: Number(core.getInput("lease-seconds")),
|
|
22
23
|
heartbeatSeconds: Number(core.getInput("heartbeat-seconds")),
|
|
24
|
+
beforeHeartbeat: () => {
|
|
25
|
+
const warrant = admission.observation?.activeWarrant || admission.warrant;
|
|
26
|
+
return guardPipelineDelivery(
|
|
27
|
+
{
|
|
28
|
+
"pipeline-attempt": core.getInput("pipeline-attempt"),
|
|
29
|
+
"target-branch": core.getInput("branch", { required: true }),
|
|
30
|
+
"expected-pr-number": warrant?.pullRequestNumber,
|
|
31
|
+
"expected-head-sha": warrant?.sourceHead,
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
repository: env.GITHUB_REPOSITORY,
|
|
35
|
+
token: core.getInput("token", { required: true }),
|
|
36
|
+
},
|
|
37
|
+
);
|
|
38
|
+
},
|
|
23
39
|
});
|
|
24
40
|
fs.writeFileSync(
|
|
25
41
|
path.join(workspace, ".buildchain/provider-heartbeat-receipt.json"),
|
|
@@ -68,13 +68,14 @@ export async function heartbeatDeliveryAttempt(options) {
|
|
|
68
68
|
heartbeatSeconds: options.heartbeatSeconds,
|
|
69
69
|
},
|
|
70
70
|
{
|
|
71
|
-
heartbeat: ({
|
|
71
|
+
heartbeat: async ({
|
|
72
72
|
expectedOldStateRoot,
|
|
73
73
|
fencingToken,
|
|
74
74
|
leaseGeneration,
|
|
75
75
|
leaseSeconds,
|
|
76
|
-
}) =>
|
|
77
|
-
|
|
76
|
+
}) => {
|
|
77
|
+
await options.beforeHeartbeat?.();
|
|
78
|
+
return runDevDeliveryCommand({
|
|
78
79
|
command: "heartbeat",
|
|
79
80
|
repository: options.repository,
|
|
80
81
|
branch: options.branch,
|
|
@@ -85,7 +86,8 @@ export async function heartbeatDeliveryAttempt(options) {
|
|
|
85
86
|
execute: true,
|
|
86
87
|
token: options.token,
|
|
87
88
|
apiUrl: options.apiUrl,
|
|
88
|
-
})
|
|
89
|
+
});
|
|
90
|
+
},
|
|
89
91
|
readJobs: () =>
|
|
90
92
|
githubJson(
|
|
91
93
|
options.apiUrl,
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// Node Actions receive artifact/OIDC authority even without token inputs.
|
|
2
|
+
// Replace the process before candidate work so the original environment is
|
|
3
|
+
// absent from /proc, memory and ancestry. A scrubbed child would retain it.
|
|
4
|
+
export function replaceNativeActionProcess(host = process) {
|
|
5
|
+
const injected = ["ACTIONS_RUNTIME_TOKEN", "ACTIONS_ID_TOKEN_REQUEST_TOKEN"];
|
|
6
|
+
if (!injected.some((name) => host.env[name])) return;
|
|
7
|
+
if (host.platform !== "linux" || typeof host.execve !== "function") {
|
|
8
|
+
throw new Error("credentialless native action requires Linux execve");
|
|
9
|
+
}
|
|
10
|
+
const environment = { ...host.env };
|
|
11
|
+
for (const name of injected) delete environment[name];
|
|
12
|
+
// Other credential-like variables remain subject to the ancestry guard.
|
|
13
|
+
host.execve(
|
|
14
|
+
host.execPath,
|
|
15
|
+
[host.execPath, ...host.execArgv, ...host.argv.slice(1)],
|
|
16
|
+
environment,
|
|
17
|
+
);
|
|
18
|
+
throw new Error("credentialless native process replacement returned");
|
|
19
|
+
}
|
|
@@ -3,9 +3,31 @@ import { deliveryActionContext } from "../native/action-context.js";
|
|
|
3
3
|
import { admissionPolicyRequest } from "../admission/request.js";
|
|
4
4
|
import { runAdmissionTransaction } from "../admission/transaction.js";
|
|
5
5
|
import { enforceLanding } from "./completion.js";
|
|
6
|
+
import { guardPipelineAdmission } from "../../workflow/pipeline/guard.js";
|
|
7
|
+
import { githubJsonClient } from "../../providers/github/json-client.js";
|
|
8
|
+
import { publishPipelineBuildStatus } from "../../workflow/pipeline/guard-build.js";
|
|
9
|
+
import { publishIntegratedPipelineStatus } from "../../workflow/pipeline/integration-status.js";
|
|
10
|
+
import { githubPipelineIntegration } from "../../providers/github/pipeline-integration.js";
|
|
11
|
+
import { githubPipelineSource } from "../../providers/github/pipeline-source.js";
|
|
12
|
+
import { githubPipelineRuns } from "../../providers/github/pipeline-runs.js";
|
|
13
|
+
import { GitHubClient } from "../admission/github-client.js";
|
|
6
14
|
export async function admitLandingAction(core, env) {
|
|
7
15
|
const context = deliveryActionContext(core, env);
|
|
8
16
|
const request = JSON.parse(core.getInput("request-json", { required: true }));
|
|
17
|
+
const admitted = await guardPipelineAdmission(request, {
|
|
18
|
+
repository: env.GITHUB_REPOSITORY,
|
|
19
|
+
token: core.getInput("token", { required: true }),
|
|
20
|
+
publishBuildStatus: (build) =>
|
|
21
|
+
publishPipelineBuildStatus(
|
|
22
|
+
request,
|
|
23
|
+
build,
|
|
24
|
+
githubJsonClient({
|
|
25
|
+
token: core.getInput("status-token", { required: true }),
|
|
26
|
+
userAgent: "buildchain-qualified-status",
|
|
27
|
+
}),
|
|
28
|
+
env.GITHUB_REPOSITORY,
|
|
29
|
+
),
|
|
30
|
+
});
|
|
9
31
|
const result = await runAdmissionTransaction(
|
|
10
32
|
{
|
|
11
33
|
...admissionPolicyRequest(request, {
|
|
@@ -40,6 +62,37 @@ export async function admitLandingAction(core, env) {
|
|
|
40
62
|
await core.summary.addRaw(result.summary).write();
|
|
41
63
|
if (!result.ok)
|
|
42
64
|
throw new Error("Targeted PR was not admitted at its expected head");
|
|
65
|
+
if (admitted) {
|
|
66
|
+
const repository = env.GITHUB_REPOSITORY;
|
|
67
|
+
const token = core.getInput("token", { required: true });
|
|
68
|
+
const read = githubJsonClient({
|
|
69
|
+
token,
|
|
70
|
+
userAgent: "buildchain-integration-status",
|
|
71
|
+
});
|
|
72
|
+
const [owner, repo] = repository.split("/");
|
|
73
|
+
const queue = new GitHubClient({ repository: { owner, repo }, token });
|
|
74
|
+
await publishIntegratedPipelineStatus(
|
|
75
|
+
{
|
|
76
|
+
...admitted.history.at(-1),
|
|
77
|
+
intent: admitted.intent,
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
repository,
|
|
81
|
+
request: read,
|
|
82
|
+
queue: (branch) => queue.getMergeQueueState(branch),
|
|
83
|
+
integration: githubPipelineIntegration(
|
|
84
|
+
read,
|
|
85
|
+
repository,
|
|
86
|
+
githubPipelineSource(read, repository),
|
|
87
|
+
githubPipelineRuns(read, repository),
|
|
88
|
+
),
|
|
89
|
+
status: githubJsonClient({
|
|
90
|
+
token: core.getInput("status-token", { required: true }),
|
|
91
|
+
userAgent: "buildchain-qualified-integration-status",
|
|
92
|
+
}),
|
|
93
|
+
},
|
|
94
|
+
);
|
|
95
|
+
}
|
|
43
96
|
}
|
|
44
97
|
export function completeDeliveryAction(core) {
|
|
45
98
|
enforceLanding({
|
|
@@ -11,13 +11,15 @@ export function verifyFailureSettlement(result, settlement, stateRoot) {
|
|
|
11
11
|
result.receipt.evidenceRoot === settlement.evidenceRoot,
|
|
12
12
|
"Failure settlement receipt does not bind its exact evidence and prior state",
|
|
13
13
|
);
|
|
14
|
-
const candidate = result.
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
14
|
+
const candidate = result.terminalCandidate;
|
|
15
|
+
requireValue(
|
|
16
|
+
candidate?.candidateId === result.receipt.candidateId &&
|
|
17
|
+
candidate?.pullRequestNumber === settlement.pullRequestNumber &&
|
|
18
|
+
candidate?.sourceHead === settlement.sourceHead &&
|
|
19
|
+
candidate?.status === "terminal-failure" &&
|
|
20
|
+
candidate?.terminal?.evidenceRoot === settlement.evidenceRoot,
|
|
21
|
+
"Failure settlement terminal candidate is missing",
|
|
19
22
|
);
|
|
20
|
-
requireValue(candidate, "Failure settlement terminal candidate is missing");
|
|
21
23
|
for (const key of [
|
|
22
24
|
"transferRoot",
|
|
23
25
|
"finalizerBoundaryRoot",
|
|
@@ -104,6 +104,13 @@ export async function runDevDeliveryCommand(optionsInput = {}, clientInput) {
|
|
|
104
104
|
concurrencyRecovery,
|
|
105
105
|
receipt: persisted.changed.receipt,
|
|
106
106
|
receiptRoot: persisted.changed.receiptRoot,
|
|
107
|
+
terminalCandidate:
|
|
108
|
+
options.command === "settle"
|
|
109
|
+
? persisted.changed.queue.candidates.find(
|
|
110
|
+
(candidate) =>
|
|
111
|
+
candidate.candidateId === persisted.changed.receipt.candidateId,
|
|
112
|
+
) || null
|
|
113
|
+
: null,
|
|
107
114
|
warrant:
|
|
108
115
|
persisted.changed.warrant ||
|
|
109
116
|
persisted.changed.queue.activeWarrant ||
|
|
@@ -4,6 +4,8 @@ import path from "node:path";
|
|
|
4
4
|
import { createPublicationAuthorityRegistry } from "../publication/publication-authority.js";
|
|
5
5
|
|
|
6
6
|
const DESCRIPTORS = Object.freeze([
|
|
7
|
+
[".github/workflows/buildchain.yml", "product-publication", true, ["npm-publish", "github-release", "channel-ref"], "trusted-publishing", "none", "fixed", "fixed", ".github/workflows/.release-pipeline-products.yml"],
|
|
8
|
+
[".github/workflows/buildchain-recover.yml", "product-publication", true, ["npm-publish", "github-release", "channel-ref"], "trusted-publishing", "none", "fixed", "fixed", ".github/workflows/.release-pipeline-products.yml"],
|
|
7
9
|
[".github/workflows/public-release-oci-compose-preview.yml", "product-publication", true, ["oci-compose-preview"], "caller-secrets", "none", "fixed", "caller-bound"],
|
|
8
10
|
[".github/workflows/.build-demo-adapter.yml", "non-publication-oidc"],
|
|
9
11
|
[".github/workflows/public-build-demo.yml", "governance-write"],
|
|
@@ -16,36 +18,23 @@ const DESCRIPTORS = Object.freeze([
|
|
|
16
18
|
[".github/workflows/public-release-web.yml", "product-publication", true, ["web-production"], "oidc", "consumer-defined", "caller-bound", "caller-bound"],
|
|
17
19
|
[".github/workflows/.build-wheel-verify-compat.yml", "retired-deny"],
|
|
18
20
|
[".github/workflows/.release-binary-assets.yml", "product-publication", true, ["github-release"], "github-token", "buildchain-release-assets"],
|
|
19
|
-
[".github/workflows/self-build-binary-distribution.yml", "evidence-publication"],
|
|
20
|
-
[".github/workflows/self-release-binary-assets.yml", "governance-write"],
|
|
21
21
|
[".github/workflows/public-release-signing-authority.yml", "evidence-publication"],
|
|
22
|
-
[".github/workflows/self-build-demo-dogfood.yml", "governance-write"],
|
|
23
|
-
[".github/workflows/self-build-fixture.yml", "non-publication-oidc"],
|
|
24
22
|
[".github/workflows/build.yml", "non-publication-oidc"],
|
|
25
23
|
[".github/workflows/public-ops-bootstrap.yml", "product-publication", true, ["universal-candidate-execution"], "caller-secrets", "none", "fixed", "caller-bound"],
|
|
26
|
-
[".github/workflows/self-build-alpha-dogfood.yml", "non-publication-oidc"],
|
|
27
|
-
[".github/workflows/self-build-stable-dogfood.yml", "non-publication-oidc"],
|
|
28
|
-
[".github/workflows/self-ops-dev-delivery.yml", "governance-write"],
|
|
29
|
-
[".github/workflows/self-ops-patrol-daily.yml", "governance-write"],
|
|
30
|
-
[".github/workflows/self-ops-patrol-monthly.yml", "governance-write"],
|
|
31
|
-
[".github/workflows/self-ops-patrol-weekly.yml", "governance-write"],
|
|
32
24
|
[".github/workflows/public-ops-patrol.yml", "governance-write"],
|
|
33
|
-
[".github/workflows/self-ops-promotion-recovery.yml", "governance-write"],
|
|
34
|
-
[".github/workflows/self-release-promote.yml", "governance-write"],
|
|
35
|
-
[".github/workflows/self-ops-stable-candidate-patrol.yml", "governance-write"],
|
|
36
25
|
[".github/workflows/public-ops-dev-qualification-patrol.yml", "governance-write"],
|
|
37
26
|
[".github/workflows/public-ops-alpha-candidate-patrol.yml", "governance-write"],
|
|
38
27
|
[".github/workflows/public-ops-warrant-close.yml", "governance-write"],
|
|
39
28
|
[".github/workflows/public-ops-warrant-cancel.yml", "governance-write"],
|
|
40
|
-
[".github/workflows/self-ops-merge-queue.yml", "governance-write"],
|
|
41
29
|
[".github/workflows/public-ops-dev-auto-merge.yml", "governance-write"],
|
|
42
|
-
[".github/workflows/
|
|
43
|
-
[".github/workflows/
|
|
44
|
-
[".github/workflows
|
|
30
|
+
[".github/workflows/public-ops-pipeline.yml", "product-publication", true, ["npm-publish", "github-release", "channel-ref"], "trusted-publishing", "none", "fixed", "fixed", ".github/workflows/.release-pipeline-products.yml"],
|
|
31
|
+
[".github/workflows/public-ops-recover.yml", "product-publication", true, ["npm-publish", "github-release", "channel-ref"], "trusted-publishing", "none", "fixed", "fixed", ".github/workflows/.release-pipeline-products.yml"],
|
|
32
|
+
[".github/workflows/.ops-pipeline-execute.yml", "product-publication", true, ["npm-publish", "github-release", "channel-ref"], "trusted-publishing", "none", "fixed", "fixed", ".github/workflows/.release-pipeline-products.yml"],
|
|
33
|
+
[".github/workflows/.release-pipeline-products.yml", "product-publication", true, ["npm-publish", "github-release", "channel-ref"], "trusted-publishing", "none", "fixed", "fixed"],
|
|
34
|
+
[".github/workflows/.release-pipeline-version.yml", "governance-write"],
|
|
35
|
+
[".github/workflows/.ops-pipeline-delivery.yml", "governance-write"],
|
|
45
36
|
[".github/workflows/public-ops-housekeeping.yml", "governance-write"],
|
|
46
|
-
[".github/workflows/self-ops-governance-audit.yml", "governance-write"],
|
|
47
37
|
[".github/workflows/public-release-artifact-attestation.yml", "evidence-publication", true, ["github-artifact-attestation"], "oidc", "consumer-defined", "caller-bound", "caller-bound"],
|
|
48
|
-
[".github/workflows/self-release-npm-dry-run.yml", "dry-run-only"],
|
|
49
38
|
[".github/workflows/public-release-paper.yml", "product-publication", true, ["npm-publish", "github-release"], "trusted-publishing", "none", "fixed", "caller-bound"],
|
|
50
39
|
[".github/workflows/public-ops-patrol-daily.yml", "governance-write"],
|
|
51
40
|
[".github/workflows/public-ops-patrol-monthly.yml", "governance-write"],
|
|
@@ -53,13 +42,9 @@ const DESCRIPTORS = Object.freeze([
|
|
|
53
42
|
[".github/workflows/public-ops-patrol-weekly.yml", "governance-write"],
|
|
54
43
|
[".github/workflows/public-build-publication.yml", "evidence-publication"],
|
|
55
44
|
[".github/workflows/public-release-promote.yml", "product-publication", true, ["npm-publish", "github-release", "channel-ref"], "trusted-publishing", "none", "fixed", "caller-bound"],
|
|
56
|
-
[".github/workflows/self-release-line-open.yml", "governance-write"],
|
|
57
45
|
[".github/workflows/public-release-propagation.yml", "governance-write"],
|
|
58
46
|
[".github/workflows/public-release-tail.yml", "product-publication", true, ["artifact.publish", "signed-channel.commit", "release.activate", "released-evidence.synthesize"], "caller-secrets", "caller-bound", "caller-bound", "fixed"],
|
|
59
|
-
[".github/workflows/self-build-release-verify-compat.yml", "governance-write"],
|
|
60
47
|
[".github/workflows/public-ops-stable-candidate-patrol.yml", "governance-write"],
|
|
61
|
-
[".github/workflows/self-ops-bootstrap-dogfood.yml", "product-publication", true, ["universal-candidate-execution"], "caller-secrets", "none", "fixed", "caller-bound"],
|
|
62
|
-
[".github/workflows/self-release-tail-dogfood.yml", "product-publication", true, ["artifact.publish", "signed-channel.commit", "release.activate", "released-evidence.synthesize"], "caller-secrets", "none", "fixed", "fixed"],
|
|
63
48
|
]);
|
|
64
49
|
|
|
65
50
|
export function buildchainPublicationAuthorityDescriptors({ root = process.cwd() } = {}) {
|
|
@@ -1257,6 +1257,7 @@ export function createGithubRulesetGovernanceRolloutPlan({
|
|
|
1257
1257
|
dismiss_stale_reviews_on_push: true,
|
|
1258
1258
|
require_code_owner_review: true,
|
|
1259
1259
|
require_last_push_approval: true,
|
|
1260
|
+
require_extra_approval_for_unattributed_changes: true,
|
|
1260
1261
|
required_approving_review_count: requiredApprovals,
|
|
1261
1262
|
required_review_thread_resolution: true,
|
|
1262
1263
|
},
|
|
@@ -124,14 +124,6 @@ const CONTROLLER_SPECS = [
|
|
|
124
124
|
],
|
|
125
125
|
evidence: ["propagation-plan", "propagation-lock", "controller-receipt"],
|
|
126
126
|
},
|
|
127
|
-
{
|
|
128
|
-
id: "binary-distribution",
|
|
129
|
-
workflowId: "binary-distribution",
|
|
130
|
-
version: 1,
|
|
131
|
-
capabilities: ["standalone-binary-build", "release-evidence-bundle", "artifact-admission"],
|
|
132
|
-
stages: ["resolve-runtime", "build", "verify", "bundle", "aggregate"],
|
|
133
|
-
evidence: ["release-evidence-bundle", "release-passport", "controller-receipt"],
|
|
134
|
-
},
|
|
135
127
|
];
|
|
136
128
|
|
|
137
129
|
function stableJson(value) {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { readNpmPackResult } from "../../publication/npm/pack-result.js";
|
|
1
2
|
import path from "node:path";
|
|
2
3
|
import fs from "node:fs";
|
|
3
4
|
import { jsonText, extractUrls, safeParseJson, toPosix } from "./files.js";
|
|
@@ -66,6 +67,7 @@ export function executePaperNpmBootstrap(options = {}) {
|
|
|
66
67
|
path,
|
|
67
68
|
resolvePaperRepository,
|
|
68
69
|
safeParseJson,
|
|
70
|
+
readNpmPackResult,
|
|
69
71
|
toPosix,
|
|
70
72
|
validatePaperProvisioningAuthority,
|
|
71
73
|
writePaperReceipt,
|
|
@@ -20,6 +20,7 @@ function executePaperNpmBootstrap(options = {}, runtime) {
|
|
|
20
20
|
path,
|
|
21
21
|
resolvePaperRepository,
|
|
22
22
|
safeParseJson,
|
|
23
|
+
readNpmPackResult,
|
|
23
24
|
toPosix,
|
|
24
25
|
validatePaperProvisioningAuthority,
|
|
25
26
|
writePaperReceipt,
|
|
@@ -208,8 +209,7 @@ function executePaperNpmBootstrap(options = {}, runtime) {
|
|
|
208
209
|
pack: {
|
|
209
210
|
status: pack.ok ? "pass" : "fail",
|
|
210
211
|
entryCount: (() => {
|
|
211
|
-
const
|
|
212
|
-
const value = Array.isArray(parsed) ? parsed[0] : parsed;
|
|
212
|
+
const value = pack.ok ? readNpmPackResult(pack.stdout) : null;
|
|
213
213
|
return Array.isArray(value?.files) ? value.files.length : 0;
|
|
214
214
|
})(),
|
|
215
215
|
},
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// Immutable lookup pointers are derived from admitted journal commits. The
|
|
2
|
+
// canonical intent journal must contain the selected root before a pointer can
|
|
3
|
+
// resolve; an orphan commit or pointer cannot create attempt authority.
|
|
4
|
+
export function githubAttemptIndex(request, journal, repository) {
|
|
5
|
+
if (!/^[\w.-]+\/[\w.-]+$/u.test(repository || ""))
|
|
6
|
+
throw new Error("Invalid attempt lookup repository");
|
|
7
|
+
const base = `/repos/${repository}`;
|
|
8
|
+
function ref(attempt) {
|
|
9
|
+
if (!/^attempt-[0-9a-f]{64}$/u.test(attempt || ""))
|
|
10
|
+
throw new Error("Invalid business attempt selector");
|
|
11
|
+
return `buildchain/attempt-index/${attempt}`;
|
|
12
|
+
}
|
|
13
|
+
function root(snapshot, attempt) {
|
|
14
|
+
const roots = snapshot.records.filter(
|
|
15
|
+
(record) => record.attempt === attempt && record.node === "attempt",
|
|
16
|
+
);
|
|
17
|
+
if (roots.length !== 1)
|
|
18
|
+
throw new Error("Lookup does not identify one admitted attempt root");
|
|
19
|
+
return roots[0];
|
|
20
|
+
}
|
|
21
|
+
async function resolve(attempt) {
|
|
22
|
+
const pointer = await request(`${base}/git/ref/heads/${ref(attempt)}`, {
|
|
23
|
+
allow404: true,
|
|
24
|
+
});
|
|
25
|
+
if (!pointer) throw new Error("Business attempt lookup is not retained");
|
|
26
|
+
const indexed = await journal.readCommit(repository, pointer.object?.sha);
|
|
27
|
+
const selected = root(indexed.snapshot, attempt);
|
|
28
|
+
const current = await journal.read(indexed.snapshot.intent);
|
|
29
|
+
if (!current || root(current.snapshot, attempt).id !== selected.id)
|
|
30
|
+
throw new Error(
|
|
31
|
+
"Lookup attempt is absent from the authoritative journal",
|
|
32
|
+
);
|
|
33
|
+
return { ...current, selectedAttempt: attempt };
|
|
34
|
+
}
|
|
35
|
+
async function retain(intent, attempt) {
|
|
36
|
+
if (intent.repository !== repository)
|
|
37
|
+
throw new Error("Attempt lookup repository drift");
|
|
38
|
+
const current = await journal.read(intent);
|
|
39
|
+
if (!current) throw new Error("Cannot index an unadmitted attempt");
|
|
40
|
+
const expected = root(current.snapshot, attempt);
|
|
41
|
+
let failure;
|
|
42
|
+
try {
|
|
43
|
+
await request(`${base}/git/refs`, {
|
|
44
|
+
method: "POST",
|
|
45
|
+
body: { ref: `refs/heads/${ref(attempt)}`, sha: current.commit },
|
|
46
|
+
});
|
|
47
|
+
} catch (error) {
|
|
48
|
+
failure = error;
|
|
49
|
+
}
|
|
50
|
+
try {
|
|
51
|
+
const result = await resolve(attempt);
|
|
52
|
+
if (root(result.snapshot, attempt).id !== expected.id)
|
|
53
|
+
throw new Error("Conflicting business attempt lookup");
|
|
54
|
+
return result;
|
|
55
|
+
} catch (error) {
|
|
56
|
+
throw failure || error;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return { retain, resolve };
|
|
60
|
+
}
|