@kungfu-tech/buildchain 3.0.5 → 3.0.6-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/README.md +31 -0
- package/actions/macos-credential-island/README.md +8 -0
- package/bin/buildchain.mjs +1 -1
- package/contracts/auditable-demo-media-profiles-v1.json +61 -0
- package/contracts/auditable-demo-scenario-v1.schema.json +164 -0
- package/contracts/evidence/auditable-demo-responsive-web-delivery-v1.json +3 -3
- package/contracts/evidence/auditable-demo-web-delivery-v1.json +3 -3
- package/dist/site/buildchain-contract.json +107 -26
- package/dist/site/buildchain-site.json +121 -49
- package/dist/site/capability-registry.json +3 -3
- package/dist/site/cli-registry.json +9 -3
- package/dist/site/controller-registry.json +27 -3
- package/dist/site/kfd-claims.json +101 -15
- package/dist/site/kfd-upstream-aggregate.json +1 -1
- package/dist/site/manual-registry.json +10 -9
- package/dist/site/node-api-registry.json +540 -31
- package/dist/site/page-registry.json +105 -29
- package/dist/site/public-surface-audit.json +184 -11
- package/dist/site/publication-authority-registry.json +62 -1
- package/dist/site/publication-registry.json +4 -4
- package/dist/site/site-manifest.json +13 -13
- package/dist/site/workflow-registry.json +118 -4
- package/docs/MAP.md +4 -1
- package/docs/auditable-demo.md +90 -7
- package/docs/aws-us-elastic-runner-burst-plane.md +151 -16
- package/docs/cli-reference.md +35 -2
- package/docs/dev-alpha-candidate-patrol.md +14 -5
- package/docs/dev-qualification-patrol.md +108 -0
- package/docs/node-api-reference.md +123 -72
- package/docs/publish-transaction.md +7 -5
- package/docs/release-flow.md +4 -0
- package/docs/release-governance.md +39 -0
- package/docs/release-propagation.md +93 -0
- package/docs/reusable-build-surface.md +53 -12
- package/docs/stable-candidate-patrol.md +5 -2
- package/package.json +1 -1
- package/packages/core/buildchain-contract.js +36 -1
- package/packages/core/buildchain-publication-authority.js +3 -0
- package/packages/core/public-surface-audit.js +4 -35
- package/packages/core/release-propagation-agent-entry.js +185 -0
- package/packages/core/release-propagation-pickup.js +387 -0
- package/packages/core/release-propagation-push.js +293 -0
- package/packages/core/release-propagation-release.js +61 -18
- package/packages/core/release-propagation-stage-evidence.js +36 -0
- package/packages/core/release-propagation-work.js +8 -4
- package/packages/core/release-propagation.js +27 -2
- package/packages/core/workflow-call-contract.js +308 -0
- package/packages/core/workflow-yaml-contract.js +272 -0
- package/scripts/artifact-signing-controller-core.mjs +525 -0
- package/scripts/artifact-signing-controller.mjs +302 -0
- package/scripts/artifact-signing-delegation.mjs +51 -3
- package/scripts/auditable-demo-platform.mjs +564 -0
- package/scripts/auditable-demo-renditions.mjs +7 -2
- package/scripts/auditable-demo-transport-smoke.mjs +176 -0
- package/scripts/auditable-demo.mjs +27 -21
- package/scripts/aws-macos-jit-controller-core.mjs +389 -0
- package/scripts/aws-macos-jit-controller-runtime.mjs +82 -0
- package/scripts/aws-macos-jit-controller.mjs +558 -0
- package/scripts/aws-macos-jit-job-controller.mjs +401 -0
- package/scripts/aws-windows-jit-campaign-core.mjs +296 -0
- package/scripts/aws-windows-jit-campaign.mjs +202 -0
- package/scripts/aws-windows-jit-controller-core.mjs +20 -2
- package/scripts/aws-windows-jit-controller.mjs +184 -109
- package/scripts/aws-windows-jit-core.mjs +21 -3
- package/scripts/aws-windows-jit.mjs +2 -0
- package/scripts/build-standalone-binary.mjs +6 -0
- package/scripts/buildchain-cli-help.mjs +2 -1
- package/scripts/capture-package-release-propagation.mjs +24 -1
- package/scripts/check-inventory.mjs +6 -0
- package/scripts/compiler-cache-evidence.mjs +90 -7
- package/scripts/dev-alpha-candidate-patrol.mjs +80 -5
- package/scripts/dev-pr-auto-merge.mjs +4 -4
- package/scripts/dev-qualification-patrol.mjs +594 -0
- package/scripts/dispatch-artifact-signing-authority.mjs +312 -61
- package/scripts/finalize-native-artifact-signing-result.mjs +37 -15
- package/scripts/generate-site-bundle.mjs +5 -0
- package/scripts/inspect-artifact-signing-requests.mjs +12 -0
- package/scripts/locked-source-checkout.mjs +17 -3
- package/scripts/release-propagation.mjs +150 -2
- package/scripts/run-lifecycle-core.mjs +25 -0
- package/scripts/site-capability-metadata.mjs +1 -1
- package/scripts/stable-candidate-patrol.mjs +30 -4
- package/scripts/workflow-call-contract.mjs +133 -0
|
@@ -12,37 +12,110 @@ function required(value, label) {
|
|
|
12
12
|
|
|
13
13
|
function repository(value, label) {
|
|
14
14
|
const normalized = required(value, label);
|
|
15
|
-
if (!/^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/u.test(normalized))
|
|
15
|
+
if (!/^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/u.test(normalized))
|
|
16
|
+
throw new Error(`${label} must be owner/repository`);
|
|
16
17
|
return normalized;
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
const TRANSIENT_GITHUB_STATUSES = new Set([408, 429, 500, 502, 503, 504]);
|
|
20
|
-
const DEFAULT_ARTIFACT_SIGNING_AUTHORITY_REF =
|
|
21
|
+
const DEFAULT_ARTIFACT_SIGNING_AUTHORITY_REF =
|
|
22
|
+
"authority/v3/v3.0/artifact-signing";
|
|
23
|
+
const AUTHORITY_WORKFLOW = "artifact-signing-authority.yml";
|
|
21
24
|
|
|
22
25
|
export function resolveAuthorityDispatchRef(value) {
|
|
23
26
|
const ref = required(value, "authority ref");
|
|
24
|
-
return /^[0-9a-f]{40}$/u.test(ref)
|
|
27
|
+
return /^[0-9a-f]{40}$/u.test(ref)
|
|
28
|
+
? DEFAULT_ARTIFACT_SIGNING_AUTHORITY_REF
|
|
29
|
+
: ref;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export async function resolveArtifactSigningAuthorityRuntime({
|
|
33
|
+
authorityRepository,
|
|
34
|
+
authorityRef,
|
|
35
|
+
token,
|
|
36
|
+
requestImpl = githubRequest,
|
|
37
|
+
}) {
|
|
38
|
+
const authorityRepo = repository(authorityRepository, "authority repository");
|
|
39
|
+
const ref = resolveAuthorityDispatchRef(authorityRef);
|
|
40
|
+
const encodedRef = ref.split("/").map(encodeURIComponent).join("/");
|
|
41
|
+
const response = await requestImpl(
|
|
42
|
+
`/repos/${authorityRepo}/git/ref/heads/${encodedRef}`,
|
|
43
|
+
{ token: required(token, "Buildchain authority dispatch token") },
|
|
44
|
+
);
|
|
45
|
+
if (response?.object?.type !== "commit") {
|
|
46
|
+
throw new Error(
|
|
47
|
+
"Buildchain signing authority ref does not resolve to a commit",
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
const sha = String(response.object.sha || "");
|
|
51
|
+
if (!/^[0-9a-f]{40}$/u.test(sha)) {
|
|
52
|
+
throw new Error("Buildchain signing authority runtime SHA must be exact");
|
|
53
|
+
}
|
|
54
|
+
return { ref, sha };
|
|
25
55
|
}
|
|
26
56
|
|
|
27
57
|
function retryDelayMs(attempt) {
|
|
28
|
-
return Math.min(1_000 *
|
|
58
|
+
return Math.min(1_000 * 2 ** (attempt - 1), 10_000);
|
|
29
59
|
}
|
|
30
60
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
61
|
+
function authorityError(message, outputs = {}) {
|
|
62
|
+
const error = new Error(message);
|
|
63
|
+
error.authorityOutputs = outputs;
|
|
64
|
+
return error;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function validateArtifactSigningAuthorityRun(
|
|
68
|
+
run,
|
|
69
|
+
{ authorityRepository, authorityRuntimeSha, expectedTitle },
|
|
70
|
+
) {
|
|
71
|
+
if (!run || typeof run !== "object") {
|
|
72
|
+
throw new Error("Buildchain signing authority run is missing");
|
|
73
|
+
}
|
|
74
|
+
if (String(run.display_title || "") !== expectedTitle) {
|
|
75
|
+
throw new Error("Buildchain signing authority correlation mismatch");
|
|
76
|
+
}
|
|
77
|
+
if (String(run.event || "") !== "workflow_dispatch") {
|
|
78
|
+
throw new Error("Buildchain signing authority event mismatch");
|
|
79
|
+
}
|
|
80
|
+
if (String(run.head_sha || "") !== authorityRuntimeSha) {
|
|
81
|
+
throw new Error("Buildchain signing authority runtime SHA mismatch");
|
|
82
|
+
}
|
|
83
|
+
if (
|
|
84
|
+
run.repository?.full_name &&
|
|
85
|
+
String(run.repository.full_name) !== authorityRepository
|
|
86
|
+
) {
|
|
87
|
+
throw new Error("Buildchain signing authority repository mismatch");
|
|
88
|
+
}
|
|
89
|
+
if (
|
|
90
|
+
run.path &&
|
|
91
|
+
!String(run.path).startsWith(`.github/workflows/${AUTHORITY_WORKFLOW}@`)
|
|
92
|
+
) {
|
|
93
|
+
throw new Error("Buildchain signing authority workflow path mismatch");
|
|
94
|
+
}
|
|
95
|
+
return run;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export async function githubRequest(
|
|
99
|
+
url,
|
|
100
|
+
{
|
|
101
|
+
token,
|
|
102
|
+
method = "GET",
|
|
103
|
+
body,
|
|
104
|
+
fetchImpl = fetch,
|
|
105
|
+
delayImpl = delay,
|
|
106
|
+
maxAttempts = 5,
|
|
107
|
+
warnImpl = console.warn,
|
|
108
|
+
} = {},
|
|
109
|
+
) {
|
|
40
110
|
const methodName = String(method).toUpperCase();
|
|
41
111
|
const retrySafe = methodName === "GET";
|
|
42
112
|
const requestedAttempts = Number(maxAttempts);
|
|
43
|
-
const attemptLimit =
|
|
44
|
-
|
|
45
|
-
|
|
113
|
+
const attemptLimit =
|
|
114
|
+
retrySafe &&
|
|
115
|
+
Number.isSafeInteger(requestedAttempts) &&
|
|
116
|
+
requestedAttempts > 0
|
|
117
|
+
? requestedAttempts
|
|
118
|
+
: 1;
|
|
46
119
|
for (let attempt = 1; attempt <= attemptLimit; attempt += 1) {
|
|
47
120
|
let response;
|
|
48
121
|
try {
|
|
@@ -59,7 +132,9 @@ export async function githubRequest(url, {
|
|
|
59
132
|
});
|
|
60
133
|
} catch (error) {
|
|
61
134
|
if (retrySafe && attempt < attemptLimit) {
|
|
62
|
-
warnImpl(
|
|
135
|
+
warnImpl(
|
|
136
|
+
`Buildchain signing authority: GitHub API GET transport failure; retry ${attempt + 1}/${attemptLimit}`,
|
|
137
|
+
);
|
|
63
138
|
await delayImpl(retryDelayMs(attempt));
|
|
64
139
|
continue;
|
|
65
140
|
}
|
|
@@ -67,19 +142,29 @@ export async function githubRequest(url, {
|
|
|
67
142
|
}
|
|
68
143
|
if (!response.ok) {
|
|
69
144
|
const text = await response.text();
|
|
70
|
-
if (
|
|
71
|
-
|
|
145
|
+
if (
|
|
146
|
+
retrySafe &&
|
|
147
|
+
TRANSIENT_GITHUB_STATUSES.has(response.status) &&
|
|
148
|
+
attempt < attemptLimit
|
|
149
|
+
) {
|
|
150
|
+
warnImpl(
|
|
151
|
+
`Buildchain signing authority: GitHub API GET returned ${response.status}; retry ${attempt + 1}/${attemptLimit}`,
|
|
152
|
+
);
|
|
72
153
|
await delayImpl(retryDelayMs(attempt));
|
|
73
154
|
continue;
|
|
74
155
|
}
|
|
75
|
-
throw new Error(
|
|
156
|
+
throw new Error(
|
|
157
|
+
`GitHub API ${methodName} ${url} failed (${response.status}): ${text.slice(0, 500)}`,
|
|
158
|
+
);
|
|
76
159
|
}
|
|
77
160
|
if (response.status === 204) return {};
|
|
78
161
|
try {
|
|
79
162
|
return await response.json();
|
|
80
163
|
} catch (error) {
|
|
81
164
|
if (retrySafe && attempt < attemptLimit) {
|
|
82
|
-
warnImpl(
|
|
165
|
+
warnImpl(
|
|
166
|
+
`Buildchain signing authority: GitHub API GET response failed to decode; retry ${attempt + 1}/${attemptLimit}`,
|
|
167
|
+
);
|
|
83
168
|
await delayImpl(retryDelayMs(attempt));
|
|
84
169
|
continue;
|
|
85
170
|
}
|
|
@@ -89,70 +174,236 @@ export async function githubRequest(url, {
|
|
|
89
174
|
throw new Error("GitHub API retry loop exhausted");
|
|
90
175
|
}
|
|
91
176
|
|
|
177
|
+
async function pollArtifactSigningAuthorityRun({
|
|
178
|
+
authorityRepository,
|
|
179
|
+
token,
|
|
180
|
+
authorityRuntimeSha,
|
|
181
|
+
expectedTitle,
|
|
182
|
+
startedAtMs,
|
|
183
|
+
deadline,
|
|
184
|
+
baseOutputs,
|
|
185
|
+
requestImpl,
|
|
186
|
+
delayImpl,
|
|
187
|
+
nowImpl,
|
|
188
|
+
}) {
|
|
189
|
+
let run;
|
|
190
|
+
while (nowImpl() < deadline) {
|
|
191
|
+
const response = await requestImpl(
|
|
192
|
+
`/repos/${authorityRepository}/actions/workflows/${AUTHORITY_WORKFLOW}/runs?event=workflow_dispatch&per_page=50`,
|
|
193
|
+
{ token },
|
|
194
|
+
);
|
|
195
|
+
const matches = (response.workflow_runs || []).filter(
|
|
196
|
+
(entry) =>
|
|
197
|
+
new Date(entry.created_at).getTime() >= startedAtMs - 30_000 &&
|
|
198
|
+
String(entry.display_title || "") === expectedTitle,
|
|
199
|
+
);
|
|
200
|
+
if (matches.length > 1) {
|
|
201
|
+
throw authorityError(
|
|
202
|
+
"multiple Buildchain signing authority runs share one correlation",
|
|
203
|
+
{ ...baseOutputs, "authority-status": "failed" },
|
|
204
|
+
);
|
|
205
|
+
}
|
|
206
|
+
if (matches.length === 1) {
|
|
207
|
+
try {
|
|
208
|
+
run = validateArtifactSigningAuthorityRun(matches[0], {
|
|
209
|
+
authorityRepository,
|
|
210
|
+
authorityRuntimeSha,
|
|
211
|
+
expectedTitle,
|
|
212
|
+
});
|
|
213
|
+
} catch (error) {
|
|
214
|
+
throw authorityError(error.message, {
|
|
215
|
+
...baseOutputs,
|
|
216
|
+
"authority-run-id": String(matches[0].id || ""),
|
|
217
|
+
"authority-run-url": String(matches[0].html_url || ""),
|
|
218
|
+
"authority-status": "failed",
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
if (run?.status === "completed") return run;
|
|
223
|
+
await delayImpl(10_000);
|
|
224
|
+
}
|
|
225
|
+
throw authorityError("timed out waiting for Buildchain signing authority", {
|
|
226
|
+
...baseOutputs,
|
|
227
|
+
"authority-run-id": String(run?.id || ""),
|
|
228
|
+
"authority-run-url": String(run?.html_url || ""),
|
|
229
|
+
"authority-status": "timed-out",
|
|
230
|
+
"authority-conclusion": String(run?.conclusion || "timed-out"),
|
|
231
|
+
"controller-completed-at": new Date(nowImpl()).toISOString(),
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
function successfulAuthorityResult({
|
|
236
|
+
run,
|
|
237
|
+
baseOutputs,
|
|
238
|
+
resultArtifact,
|
|
239
|
+
correlationId,
|
|
240
|
+
controllerStartedAt,
|
|
241
|
+
completedAt,
|
|
242
|
+
}) {
|
|
243
|
+
if (run.conclusion !== "success") {
|
|
244
|
+
const status = run.conclusion === "cancelled" ? "cancelled" : "failed";
|
|
245
|
+
throw authorityError(
|
|
246
|
+
`Buildchain signing authority failed: ${run.html_url}`,
|
|
247
|
+
{
|
|
248
|
+
...baseOutputs,
|
|
249
|
+
"authority-run-id": String(run.id),
|
|
250
|
+
"authority-run-url": String(run.html_url || ""),
|
|
251
|
+
"authority-status": status,
|
|
252
|
+
"authority-conclusion": String(run.conclusion || status),
|
|
253
|
+
"controller-completed-at": completedAt,
|
|
254
|
+
},
|
|
255
|
+
);
|
|
256
|
+
}
|
|
257
|
+
return {
|
|
258
|
+
outputs: {
|
|
259
|
+
...baseOutputs,
|
|
260
|
+
"authority-run-id": String(run.id),
|
|
261
|
+
"authority-run-url": String(run.html_url || ""),
|
|
262
|
+
"authority-status": "succeeded",
|
|
263
|
+
"authority-conclusion": "success",
|
|
264
|
+
"controller-completed-at": completedAt,
|
|
265
|
+
},
|
|
266
|
+
result: {
|
|
267
|
+
runId: run.id,
|
|
268
|
+
runUrl: run.html_url,
|
|
269
|
+
authorityRuntimeSha: baseOutputs["authority-runtime-sha"],
|
|
270
|
+
resultArtifact,
|
|
271
|
+
correlationId,
|
|
272
|
+
status: "succeeded",
|
|
273
|
+
startedAt: controllerStartedAt,
|
|
274
|
+
completedAt,
|
|
275
|
+
},
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
|
|
92
279
|
export async function dispatchArtifactSigningAuthority({
|
|
93
280
|
token = process.env.BUILDCHAIN_AUTHORITY_DISPATCH_TOKEN,
|
|
94
|
-
authorityRepository = process.env.BUILDCHAIN_AUTHORITY_REPOSITORY ||
|
|
281
|
+
authorityRepository = process.env.BUILDCHAIN_AUTHORITY_REPOSITORY ||
|
|
282
|
+
"kungfu-systems/buildchain",
|
|
95
283
|
authorityRef = process.env.BUILDCHAIN_AUTHORITY_REF,
|
|
96
284
|
sourceRepository = process.env.GITHUB_REPOSITORY,
|
|
97
285
|
sourceRunId = process.env.GITHUB_RUN_ID,
|
|
98
286
|
sourceRunAttempt = process.env.GITHUB_RUN_ATTEMPT || "1",
|
|
99
287
|
requestArtifact = process.env.BUILDCHAIN_SIGNING_REQUEST_ARTIFACT,
|
|
288
|
+
requestRoot = process.env.BUILDCHAIN_SIGNING_REQUEST_ROOT_DIGEST,
|
|
100
289
|
runtimeSha = process.env.BUILDCHAIN_RUNTIME_SHA,
|
|
101
290
|
resultArtifact = process.env.BUILDCHAIN_SIGNING_RESULT_ARTIFACT,
|
|
291
|
+
correlationId = process.env.BUILDCHAIN_AUTHORITY_CORRELATION_ID || "",
|
|
102
292
|
timeoutSeconds = process.env.BUILDCHAIN_SIGNING_TIMEOUT_SECONDS || "7200",
|
|
293
|
+
requestImpl = githubRequest,
|
|
294
|
+
delayImpl = delay,
|
|
295
|
+
nowImpl = () => Date.now(),
|
|
103
296
|
} = {}) {
|
|
104
297
|
const authToken = required(token, "Buildchain authority dispatch token");
|
|
105
298
|
const authorityRepo = repository(authorityRepository, "authority repository");
|
|
106
299
|
const sourceRepo = repository(sourceRepository, "source repository");
|
|
107
300
|
const ref = resolveAuthorityDispatchRef(authorityRef);
|
|
108
301
|
const runtime = required(runtimeSha, "Buildchain runtime SHA");
|
|
109
|
-
if (!/^[0-9a-f]{40}$/u.test(runtime))
|
|
110
|
-
|
|
302
|
+
if (!/^[0-9a-f]{40}$/u.test(runtime))
|
|
303
|
+
throw new Error("Buildchain runtime SHA must be exact");
|
|
304
|
+
const runId = required(sourceRunId, "source run ID");
|
|
305
|
+
const runAttempt = required(sourceRunAttempt, "source run attempt");
|
|
306
|
+
if (!/^[1-9][0-9]*$/u.test(runAttempt)) {
|
|
307
|
+
throw new Error("source run attempt must be a positive integer");
|
|
308
|
+
}
|
|
309
|
+
const requestName = required(requestArtifact, "request artifact");
|
|
310
|
+
const requestRootDigest = required(requestRoot, "request root");
|
|
311
|
+
if (!/^sha256:[0-9a-f]{64}$/u.test(requestRootDigest)) {
|
|
312
|
+
throw new Error("request root must be a canonical sha256 root");
|
|
313
|
+
}
|
|
314
|
+
const correlation = required(
|
|
315
|
+
correlationId ||
|
|
316
|
+
`${runId}-${runAttempt}-${runtime.slice(0, 12)}-${requestName.replace(/[^A-Za-z0-9._-]+/gu, "-")}`,
|
|
317
|
+
"correlation ID",
|
|
318
|
+
);
|
|
111
319
|
const resultName = required(resultArtifact, "result artifact");
|
|
112
|
-
const
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
320
|
+
const timeout = Number(timeoutSeconds);
|
|
321
|
+
if (!Number.isFinite(timeout) || timeout <= 0) {
|
|
322
|
+
throw new Error("Buildchain signing timeout must be positive");
|
|
323
|
+
}
|
|
324
|
+
const controllerStartedAtMs = nowImpl();
|
|
325
|
+
const controllerStartedAt = new Date(controllerStartedAtMs).toISOString();
|
|
326
|
+
const baseOutputs = {
|
|
327
|
+
"result-artifact": resultName,
|
|
328
|
+
"correlation-id": correlation,
|
|
329
|
+
"controller-started-at": controllerStartedAt,
|
|
330
|
+
};
|
|
331
|
+
try {
|
|
332
|
+
const authorityRuntime = await resolveArtifactSigningAuthorityRuntime({
|
|
333
|
+
authorityRepository: authorityRepo,
|
|
334
|
+
authorityRef: ref,
|
|
335
|
+
token: authToken,
|
|
336
|
+
requestImpl,
|
|
337
|
+
});
|
|
338
|
+
baseOutputs["authority-runtime-sha"] = authorityRuntime.sha;
|
|
339
|
+
await requestImpl(
|
|
340
|
+
`/repos/${authorityRepo}/actions/workflows/${AUTHORITY_WORKFLOW}/dispatches`,
|
|
341
|
+
{
|
|
342
|
+
token: authToken,
|
|
343
|
+
method: "POST",
|
|
344
|
+
body: {
|
|
345
|
+
ref: authorityRuntime.ref,
|
|
346
|
+
inputs: {
|
|
347
|
+
"source-repository": sourceRepo,
|
|
348
|
+
"source-run-id": runId,
|
|
349
|
+
"source-run-attempt": runAttempt,
|
|
350
|
+
"request-artifact-pattern": requestName,
|
|
351
|
+
"expected-request-root": requestRootDigest,
|
|
352
|
+
"result-artifact-name": resultName,
|
|
353
|
+
"correlation-id": correlation,
|
|
354
|
+
"expected-runtime-sha": runtime,
|
|
355
|
+
},
|
|
356
|
+
},
|
|
126
357
|
},
|
|
127
|
-
},
|
|
128
|
-
});
|
|
129
|
-
const deadline = Date.now() + Number(timeoutSeconds) * 1000;
|
|
130
|
-
let run;
|
|
131
|
-
while (Date.now() < deadline) {
|
|
132
|
-
const response = await githubRequest(`/repos/${authorityRepo}/actions/workflows/${workflow}/runs?event=workflow_dispatch&per_page=50`, { token: authToken });
|
|
133
|
-
run = (response.workflow_runs || []).find((entry) =>
|
|
134
|
-
new Date(entry.created_at).getTime() >= startedAt &&
|
|
135
|
-
String(entry.display_title || "").includes(correlationId),
|
|
136
358
|
);
|
|
137
|
-
|
|
138
|
-
await
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
359
|
+
const expectedTitle = `Sign ${sourceRepo} run ${runId} (${correlation})`;
|
|
360
|
+
const run = await pollArtifactSigningAuthorityRun({
|
|
361
|
+
authorityRepository: authorityRepo,
|
|
362
|
+
token: authToken,
|
|
363
|
+
authorityRuntimeSha: authorityRuntime.sha,
|
|
364
|
+
expectedTitle,
|
|
365
|
+
startedAtMs: controllerStartedAtMs,
|
|
366
|
+
deadline: controllerStartedAtMs + timeout * 1000,
|
|
367
|
+
baseOutputs,
|
|
368
|
+
requestImpl,
|
|
369
|
+
delayImpl,
|
|
370
|
+
nowImpl,
|
|
371
|
+
});
|
|
372
|
+
const completedAt = new Date(nowImpl()).toISOString();
|
|
373
|
+
const completed = successfulAuthorityResult({
|
|
374
|
+
run,
|
|
375
|
+
baseOutputs,
|
|
376
|
+
resultArtifact: resultName,
|
|
377
|
+
correlationId: correlation,
|
|
378
|
+
controllerStartedAt,
|
|
379
|
+
completedAt,
|
|
380
|
+
});
|
|
381
|
+
writeGitHubOutputs(completed.outputs);
|
|
382
|
+
return completed.result;
|
|
383
|
+
} catch (error) {
|
|
384
|
+
if (!error.authorityOutputs) {
|
|
385
|
+
error.authorityOutputs = {
|
|
386
|
+
...baseOutputs,
|
|
387
|
+
"authority-status": "failed",
|
|
388
|
+
"authority-conclusion": "controller-error",
|
|
389
|
+
"controller-completed-at": new Date(nowImpl()).toISOString(),
|
|
390
|
+
};
|
|
391
|
+
}
|
|
392
|
+
throw error;
|
|
393
|
+
}
|
|
149
394
|
}
|
|
150
395
|
|
|
151
|
-
if (
|
|
396
|
+
if (
|
|
397
|
+
process.argv[1] &&
|
|
398
|
+
import.meta.url === pathToFileURL(process.argv[1]).href
|
|
399
|
+
) {
|
|
152
400
|
try {
|
|
153
401
|
await dispatchArtifactSigningAuthority();
|
|
154
402
|
} catch (error) {
|
|
155
|
-
|
|
403
|
+
if (error?.authorityOutputs) writeGitHubOutputs(error.authorityOutputs);
|
|
404
|
+
console.error(
|
|
405
|
+
`::error::${String(error?.message || error).replace(/\r?\n/gu, "%0A")}`,
|
|
406
|
+
);
|
|
156
407
|
process.exitCode = 1;
|
|
157
408
|
}
|
|
158
409
|
}
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
artifactSigningEvidenceDigest,
|
|
13
13
|
createArtifactSigningResult,
|
|
14
14
|
} from "../packages/core/artifact-signing-result.js";
|
|
15
|
+
import { acceptedMacosCredentialEvidence } from "../actions/macos-credential-island/dmg-assembly.js";
|
|
15
16
|
import { writeGitHubOutputs } from "./build-contract-core.mjs";
|
|
16
17
|
|
|
17
18
|
function required(value, label) {
|
|
@@ -32,15 +33,39 @@ function resolveBelow(root, relative, label) {
|
|
|
32
33
|
return target;
|
|
33
34
|
}
|
|
34
35
|
|
|
36
|
+
function expectedCredentialExecution(
|
|
37
|
+
appBundleResult,
|
|
38
|
+
expectedRunId,
|
|
39
|
+
expectedRunAttempt,
|
|
40
|
+
) {
|
|
41
|
+
if (!appBundleResult) return null;
|
|
42
|
+
return {
|
|
43
|
+
runId: required(expectedRunId, "credential execution run id"),
|
|
44
|
+
runAttempt: required(
|
|
45
|
+
expectedRunAttempt,
|
|
46
|
+
"credential execution run attempt",
|
|
47
|
+
),
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function acceptedProviderEvidence(evidence, request, expectedExecution) {
|
|
52
|
+
return expectedExecution
|
|
53
|
+
? acceptedMacosCredentialEvidence(evidence, request, expectedExecution)
|
|
54
|
+
: evidence.status === "passed" &&
|
|
55
|
+
evidence.provider === request.signature.provider;
|
|
56
|
+
}
|
|
57
|
+
|
|
35
58
|
export function finalizeNativeArtifactSigningResult({
|
|
36
59
|
requestRoot = process.env.BUILDCHAIN_SIGNING_REQUEST_ROOT,
|
|
37
60
|
requestPath = process.env.BUILDCHAIN_SIGNING_REQUEST_PATH,
|
|
38
61
|
signedPayload = process.env.BUILDCHAIN_SIGNED_PAYLOAD,
|
|
39
62
|
evidencePath = process.env.BUILDCHAIN_SIGNING_EVIDENCE,
|
|
40
|
-
credentialArtifactRoot =
|
|
41
|
-
|
|
63
|
+
credentialArtifactRoot = process.env
|
|
64
|
+
.BUILDCHAIN_SIGNING_CREDENTIAL_ARTIFACT_ROOT,
|
|
42
65
|
outputRoot = process.env.BUILDCHAIN_SIGNING_RESULT_ROOT,
|
|
43
66
|
checks = process.env.BUILDCHAIN_SIGNING_VERIFICATION_CHECKS,
|
|
67
|
+
expectedRunId = process.env.GITHUB_RUN_ID,
|
|
68
|
+
expectedRunAttempt = process.env.GITHUB_RUN_ATTEMPT,
|
|
44
69
|
} = {}) {
|
|
45
70
|
const requests = path.resolve(required(requestRoot, "signing request root"));
|
|
46
71
|
const request = JSON.parse(
|
|
@@ -77,19 +102,16 @@ export function finalizeNativeArtifactSigningResult({
|
|
|
77
102
|
fs.copyFileSync(evidenceSource, evidenceOutput, fs.constants.COPYFILE_EXCL);
|
|
78
103
|
const evidenceDocument = JSON.parse(fs.readFileSync(evidenceOutput, "utf8"));
|
|
79
104
|
const appBundleResult = request.artifact.kind === "app-bundle";
|
|
80
|
-
const
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
evidenceDocument.notarization?.diskImage?.status === "Accepted"
|
|
91
|
-
: evidenceDocument.status === "passed" &&
|
|
92
|
-
evidenceDocument.provider === request.signature.provider;
|
|
105
|
+
const expectedExecution = expectedCredentialExecution(
|
|
106
|
+
appBundleResult,
|
|
107
|
+
expectedRunId,
|
|
108
|
+
expectedRunAttempt,
|
|
109
|
+
);
|
|
110
|
+
const providerEvidencePassed = acceptedProviderEvidence(
|
|
111
|
+
evidenceDocument,
|
|
112
|
+
request,
|
|
113
|
+
expectedExecution,
|
|
114
|
+
);
|
|
93
115
|
if (!providerEvidencePassed) {
|
|
94
116
|
throw new Error(
|
|
95
117
|
"provider evidence does not prove the requested native signature",
|
|
@@ -364,6 +364,7 @@ const manualMetaById = new Map(Object.entries({
|
|
|
364
364
|
"release-activation-transaction": { capabilityGroup: "release-passport-trust", audience: ["release-operator", "agent"], maturity: "preview", order: 125 },
|
|
365
365
|
"release-candidate": { capabilityGroup: "reusable-build", audience: ["release-operator", "consumer"], maturity: "stable", order: 130 },
|
|
366
366
|
"stable-candidate-patrol": { capabilityGroup: "governance-versioning", audience: ["release-operator", "consumer"], maturity: "preview", order: 135 },
|
|
367
|
+
"dev-qualification-patrol": { capabilityGroup: "governance-versioning", audience: ["release-operator", "consumer", "agent"], maturity: "preview", order: 136 },
|
|
367
368
|
"dev-alpha-candidate-patrol": { capabilityGroup: "governance-versioning", audience: ["release-operator", "consumer", "agent"], maturity: "preview", order: 137 },
|
|
368
369
|
"observed-evidence-patrol": { capabilityGroup: "governance-versioning", audience: ["release-operator", "consumer", "agent"], maturity: "preview", order: 140 },
|
|
369
370
|
"reusable-build-surface": { capabilityGroup: "reusable-build", audience: ["consumer", "release-operator"], maturity: "stable", order: 200 },
|
|
@@ -724,6 +725,7 @@ function buildSiteBundle() {
|
|
|
724
725
|
"docs/reusable-build-surface.md",
|
|
725
726
|
"docs/release-candidate.md",
|
|
726
727
|
"docs/stable-candidate-patrol.md",
|
|
728
|
+
"docs/dev-qualification-patrol.md",
|
|
727
729
|
"docs/dev-alpha-candidate-patrol.md",
|
|
728
730
|
"docs/observed-evidence-patrol.md",
|
|
729
731
|
"docs/release-governance.md",
|
|
@@ -767,6 +769,7 @@ function buildSiteBundle() {
|
|
|
767
769
|
["build", "channel-build-router"],
|
|
768
770
|
[".build", "reusable-build"],
|
|
769
771
|
[".auditable-demo", "auditable-demo"],
|
|
772
|
+
[".declarative-auditable-demo", "declarative-auditable-demo"],
|
|
770
773
|
["web-surface", "site-app-deployment"],
|
|
771
774
|
["buildchain-ref-promotion", "release-governance"],
|
|
772
775
|
["release-line-bootstrap", "release-governance"],
|
|
@@ -783,6 +786,7 @@ function buildSiteBundle() {
|
|
|
783
786
|
["buildchain-patrol-weekly", "repository-patrol"],
|
|
784
787
|
["buildchain-patrol-monthly", "repository-patrol"],
|
|
785
788
|
["stable-candidate-patrol", "repository-patrol"],
|
|
789
|
+
["dev-qualification-patrol", "repository-patrol"],
|
|
786
790
|
["dev-alpha-candidate-patrol", "repository-patrol"],
|
|
787
791
|
["buildchain-stable-candidate-patrol", "repository-patrol"],
|
|
788
792
|
["buildchain-stable-candidate-qualification", "repository-patrol"],
|
|
@@ -793,6 +797,7 @@ function buildSiteBundle() {
|
|
|
793
797
|
]);
|
|
794
798
|
const statusById = new Map([
|
|
795
799
|
[".auditable-demo", "preview"],
|
|
800
|
+
[".declarative-auditable-demo", "preview"],
|
|
796
801
|
["release-propagation", "preview"],
|
|
797
802
|
["candidate-lab", "repository-internal"],
|
|
798
803
|
["build-surface-fixture", "repository-internal"],
|
|
@@ -4,6 +4,7 @@ import path from "node:path";
|
|
|
4
4
|
import { pathToFileURL } from "node:url";
|
|
5
5
|
|
|
6
6
|
import { validateArtifactSigningRequest } from "../packages/core/artifact-signing.js";
|
|
7
|
+
import { artifactSigningRequestRoot } from "./artifact-signing-controller-core.mjs";
|
|
7
8
|
import { writeGitHubOutputs } from "./build-contract-core.mjs";
|
|
8
9
|
|
|
9
10
|
function required(value, label) {
|
|
@@ -35,12 +36,14 @@ export function inspectArtifactSigningRequests({
|
|
|
35
36
|
inputRoot = process.env.BUILDCHAIN_SIGNING_REQUEST_ROOT,
|
|
36
37
|
expectedRepository = process.env.BUILDCHAIN_SIGNING_SOURCE_REPOSITORY,
|
|
37
38
|
expectedRuntimeSha = process.env.BUILDCHAIN_RUNTIME_SHA,
|
|
39
|
+
expectedRequestRoot = process.env.BUILDCHAIN_SIGNING_EXPECTED_REQUEST_ROOT,
|
|
38
40
|
} = {}) {
|
|
39
41
|
const root = path.resolve(required(inputRoot, "signing request root"));
|
|
40
42
|
const indexes = walk(root, "index.json");
|
|
41
43
|
if (indexes.length === 0)
|
|
42
44
|
throw new Error("no artifact signing request indexes found");
|
|
43
45
|
const seen = new Set();
|
|
46
|
+
const observedRoots = [];
|
|
44
47
|
const matrices = {
|
|
45
48
|
detached: [],
|
|
46
49
|
macos: [],
|
|
@@ -52,6 +55,7 @@ export function inspectArtifactSigningRequests({
|
|
|
52
55
|
index.contract !== "kungfu-buildchain-artifact-signing-request-index/v1"
|
|
53
56
|
)
|
|
54
57
|
continue;
|
|
58
|
+
observedRoots.push(artifactSigningRequestRoot(index));
|
|
55
59
|
for (const entry of index.requests || []) {
|
|
56
60
|
const requestPath = path.resolve(path.dirname(indexPath), entry.path);
|
|
57
61
|
const relative = path.relative(root, requestPath);
|
|
@@ -113,6 +117,14 @@ export function inspectArtifactSigningRequests({
|
|
|
113
117
|
);
|
|
114
118
|
}
|
|
115
119
|
}
|
|
120
|
+
if (expectedRequestRoot) {
|
|
121
|
+
if (observedRoots.length !== 1) {
|
|
122
|
+
throw new Error("signing authority expected exactly one request root");
|
|
123
|
+
}
|
|
124
|
+
if (observedRoots[0] !== expectedRequestRoot) {
|
|
125
|
+
throw new Error("signing authority request root mismatch");
|
|
126
|
+
}
|
|
127
|
+
}
|
|
116
128
|
for (const entries of Object.values(matrices))
|
|
117
129
|
entries.sort((a, b) => a.request.localeCompare(b.request));
|
|
118
130
|
writeGitHubOutputs({
|
|
@@ -46,6 +46,14 @@ function normalizeFallback(value = "github") {
|
|
|
46
46
|
return fallback;
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
+
function normalizeHistoryMode(value = "shallow") {
|
|
50
|
+
const mode = String(value || "shallow").trim().toLowerCase() || "shallow";
|
|
51
|
+
if (!["shallow", "full"].includes(mode)) {
|
|
52
|
+
throw new Error(`checkout-history-mode must be shallow or full; got ${value}`);
|
|
53
|
+
}
|
|
54
|
+
return mode;
|
|
55
|
+
}
|
|
56
|
+
|
|
49
57
|
function splitRepository(repository) {
|
|
50
58
|
const match = String(repository || "").trim().match(/^([^/\s]+)\/([^/\s]+)$/);
|
|
51
59
|
if (!match) {
|
|
@@ -237,19 +245,25 @@ export function fetchSourceCommit({
|
|
|
237
245
|
sourceTreeSha = "",
|
|
238
246
|
timeoutMs,
|
|
239
247
|
env = {},
|
|
248
|
+
historyMode = readEnv("BUILDCHAIN_CHECKOUT_HISTORY_MODE", "shallow"),
|
|
240
249
|
allowFullFetchRetry = false,
|
|
241
250
|
runGit = git,
|
|
242
251
|
containsCommit = hasCommit,
|
|
243
252
|
}) {
|
|
253
|
+
const normalizedHistoryMode = normalizeHistoryMode(historyMode);
|
|
244
254
|
const fetchEnv = isolatedGitFetchEnv(env, targetPath);
|
|
245
255
|
const fetch = (refspec) => {
|
|
246
256
|
const options = { cwd: targetPath, timeoutMs, env: fetchEnv };
|
|
257
|
+
const fetchArgs = ["fetch", "--no-tags"];
|
|
258
|
+
if (normalizedHistoryMode === "shallow") fetchArgs.push("--depth=1");
|
|
259
|
+
fetchArgs.push(remoteName, refspec);
|
|
247
260
|
try {
|
|
248
|
-
runGit(
|
|
249
|
-
return
|
|
261
|
+
runGit(fetchArgs, options);
|
|
262
|
+
return normalizedHistoryMode;
|
|
250
263
|
} catch (error) {
|
|
251
264
|
if (
|
|
252
|
-
|
|
265
|
+
normalizedHistoryMode !== "shallow"
|
|
266
|
+
|| !allowFullFetchRetry
|
|
253
267
|
|| !/dumb http transport does not support shallow capabilities/i.test(
|
|
254
268
|
String(error?.message || error || ""),
|
|
255
269
|
)
|