@kungfu-tech/buildchain 2.11.14-alpha.3 → 2.11.14-alpha.5
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/dist/site/buildchain-contract.json +4 -4
- package/dist/site/buildchain-site.json +10 -10
- package/dist/site/kfd-claims.json +4 -3
- package/dist/site/kfd-upstream-aggregate.json +1 -1
- package/dist/site/manual-registry.json +2 -2
- package/dist/site/page-registry.json +4 -4
- package/dist/site/public-surface-audit.json +4 -3
- package/dist/site/publication-registry.json +4 -4
- package/dist/site/site-manifest.json +6 -6
- package/dist/site/workflow-registry.json +2 -1
- package/docs/release-governance.md +7 -0
- package/docs/reusable-build-surface.md +11 -4
- package/package.json +1 -1
- package/packages/core/stable-release-gate.js +2 -0
- package/scripts/locked-source-checkout.mjs +56 -22
- package/scripts/stable-release-gate.mjs +23 -4
|
@@ -182,35 +182,64 @@ function checkoutFetchedCommit(targetPath, sha, timeoutMs) {
|
|
|
182
182
|
git(["checkout", "--force", "--detach", sha], { cwd: targetPath, timeoutMs });
|
|
183
183
|
}
|
|
184
184
|
|
|
185
|
-
function
|
|
185
|
+
function retryableGitFetchError(error) {
|
|
186
|
+
const code = String(error?.code || "").toUpperCase();
|
|
187
|
+
if (["ETIMEDOUT", "ECONNRESET", "ECONNREFUSED", "EAI_AGAIN", "ENETUNREACH", "EPIPE"].includes(code)) return true;
|
|
188
|
+
return /timed?\s*out|timeout|connection (?:reset|refused)|remote end hung up|early eof|rpc failed|http (?:429|5\d\d)|temporary failure|network is unreachable/i.test(String(error?.message || error || ""));
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
export function fetchSourceCommit({
|
|
192
|
+
targetPath,
|
|
193
|
+
remoteName,
|
|
194
|
+
remoteUrl,
|
|
195
|
+
sha,
|
|
196
|
+
fetchRef,
|
|
197
|
+
timeoutMs,
|
|
198
|
+
env = {},
|
|
199
|
+
runGit = git,
|
|
200
|
+
containsCommit = hasCommit,
|
|
201
|
+
}) {
|
|
186
202
|
try {
|
|
187
|
-
|
|
203
|
+
runGit(["remote", "remove", remoteName], { cwd: targetPath, timeoutMs, stdio: "ignore" });
|
|
188
204
|
} catch {
|
|
189
205
|
// The remote is optional; a fresh checkout target will not have it yet.
|
|
190
206
|
}
|
|
191
207
|
try {
|
|
192
|
-
|
|
208
|
+
runGit(["remote", "add", remoteName, remoteUrl], { cwd: targetPath, timeoutMs });
|
|
193
209
|
} catch {
|
|
194
|
-
|
|
210
|
+
runGit(["remote", "set-url", remoteName, remoteUrl], { cwd: targetPath, timeoutMs });
|
|
195
211
|
}
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
212
|
+
|
|
213
|
+
if (fetchRef) {
|
|
214
|
+
try {
|
|
215
|
+
runGit(["fetch", "--no-tags", "--depth=1", remoteName, `+${fetchRef}:refs/buildchain/source-ref`], {
|
|
216
|
+
cwd: targetPath,
|
|
217
|
+
timeoutMs,
|
|
218
|
+
env,
|
|
219
|
+
stdio: "ignore",
|
|
220
|
+
});
|
|
221
|
+
if (containsCommit(targetPath, sha, timeoutMs)) {
|
|
222
|
+
return { selector: "ref" };
|
|
223
|
+
}
|
|
224
|
+
} catch (error) {
|
|
225
|
+
// A retryable transport failure belongs to the bounded outer retry. Do
|
|
226
|
+
// not immediately spend the same timeout again on an unadvertised SHA.
|
|
227
|
+
if (retryableGitFetchError(error)) {
|
|
228
|
+
throw error;
|
|
229
|
+
}
|
|
203
230
|
}
|
|
204
231
|
}
|
|
205
|
-
|
|
232
|
+
|
|
233
|
+
runGit(["fetch", "--no-tags", "--depth=1", remoteName, `+${sha}:refs/buildchain/source`], {
|
|
206
234
|
cwd: targetPath,
|
|
207
235
|
timeoutMs,
|
|
208
236
|
env,
|
|
209
237
|
stdio: "ignore",
|
|
210
238
|
});
|
|
211
|
-
if (!
|
|
212
|
-
throw new Error(`fetched ${fetchRef}, but ${sha} is not available`);
|
|
239
|
+
if (!containsCommit(targetPath, sha, timeoutMs)) {
|
|
240
|
+
throw new Error(`fetched ${fetchRef || sha}, but ${sha} is not available`);
|
|
213
241
|
}
|
|
242
|
+
return { selector: "sha" };
|
|
214
243
|
}
|
|
215
244
|
|
|
216
245
|
export function runBoundedFetch({ attempts = 1, fetch, onAttempt = () => {}, onRetry = () => {}, shouldRetry = () => true }) {
|
|
@@ -232,12 +261,6 @@ export function runBoundedFetch({ attempts = 1, fetch, onAttempt = () => {}, onR
|
|
|
232
261
|
throw lastError;
|
|
233
262
|
}
|
|
234
263
|
|
|
235
|
-
function retryableGitFetchError(error) {
|
|
236
|
-
const code = String(error?.code || "").toUpperCase();
|
|
237
|
-
if (["ETIMEDOUT", "ECONNRESET", "ECONNREFUSED", "EAI_AGAIN", "ENETUNREACH", "EPIPE"].includes(code)) return true;
|
|
238
|
-
return /timed?\s*out|timeout|connection (?:reset|refused)|remote end hung up|early eof|rpc failed|http (?:429|5\d\d)|temporary failure|network is unreachable/i.test(String(error?.message || error || ""));
|
|
239
|
-
}
|
|
240
|
-
|
|
241
264
|
function githubRemoteUrl({ repository, serverUrl = "https://github.com" }) {
|
|
242
265
|
const base = String(serverUrl || "https://github.com").replace(/\/+$/, "");
|
|
243
266
|
return `${base}/${repository}.git`;
|
|
@@ -277,6 +300,7 @@ export function lockedSourceCheckout({
|
|
|
277
300
|
referenceRepositoryTemplate = readEnv("BUILDCHAIN_CHECKOUT_CACHE_REFERENCE_REPOSITORY_TEMPLATE"),
|
|
278
301
|
fallback = readEnv("BUILDCHAIN_CHECKOUT_CACHE_FALLBACK", "github"),
|
|
279
302
|
timeoutSeconds = Number(readEnv("BUILDCHAIN_CHECKOUT_CACHE_TIMEOUT_SECONDS", "60") || 60),
|
|
303
|
+
githubTimeoutSeconds = Number(readEnv("BUILDCHAIN_CHECKOUT_CACHE_GITHUB_TIMEOUT_SECONDS", "600") || 600),
|
|
280
304
|
fetchAttempts = Number(readEnv("BUILDCHAIN_CHECKOUT_CACHE_FETCH_ATTEMPTS", "3") || 3),
|
|
281
305
|
diagnosticsPath = readEnv("BUILDCHAIN_SOURCE_CHECKOUT_DIAGNOSTICS_PATH", ".buildchain/diagnostics/source-checkout.json"),
|
|
282
306
|
githubToken = readEnv("GITHUB_TOKEN"),
|
|
@@ -291,6 +315,7 @@ export function lockedSourceCheckout({
|
|
|
291
315
|
const treeSha = sourceTreeSha ? assertSha(sourceTreeSha, "sourceTreeSha") : "";
|
|
292
316
|
const repoParts = splitRepository(repository);
|
|
293
317
|
const timeoutMs = Math.max(1, Number(timeoutSeconds || 60)) * 1000;
|
|
318
|
+
const githubTimeoutMs = Math.max(1, Number(githubTimeoutSeconds || 600)) * 1000;
|
|
294
319
|
const normalizedFetchAttempts = Math.max(1, Math.floor(Number(fetchAttempts) || 3));
|
|
295
320
|
const targetPath = ensureCheckoutTarget(checkoutPath, workspace);
|
|
296
321
|
git(["init"], { cwd: targetPath, timeoutMs });
|
|
@@ -312,6 +337,7 @@ export function lockedSourceCheckout({
|
|
|
312
337
|
mode: normalizedMode,
|
|
313
338
|
fallback: normalizedFallback,
|
|
314
339
|
timeoutSeconds: Math.max(1, Number(timeoutSeconds || 60)),
|
|
340
|
+
githubTimeoutSeconds: Math.max(1, Number(githubTimeoutSeconds || 600)),
|
|
315
341
|
fetchAttempts: normalizedFetchAttempts,
|
|
316
342
|
mirror: sanitizeIdentity(renderedMirrorUrl),
|
|
317
343
|
referenceRepository: sanitizeIdentity(renderedReferenceRepository),
|
|
@@ -348,7 +374,7 @@ export function lockedSourceCheckout({
|
|
|
348
374
|
checkoutFetchedCommit(targetPath, sha, timeoutMs);
|
|
349
375
|
} else if (renderedMirrorUrl) {
|
|
350
376
|
evidence.cache.transport = "mirror-url";
|
|
351
|
-
|
|
377
|
+
fetchSourceCommit({
|
|
352
378
|
targetPath,
|
|
353
379
|
remoteName: "buildchain-cache",
|
|
354
380
|
remoteUrl: renderedMirrorUrl,
|
|
@@ -384,7 +410,15 @@ export function lockedSourceCheckout({
|
|
|
384
410
|
try {
|
|
385
411
|
const fetchResult = runBoundedFetch({
|
|
386
412
|
attempts: normalizedFetchAttempts,
|
|
387
|
-
fetch: () =>
|
|
413
|
+
fetch: () => fetchSourceCommit({
|
|
414
|
+
targetPath,
|
|
415
|
+
remoteName: "origin",
|
|
416
|
+
remoteUrl,
|
|
417
|
+
sha,
|
|
418
|
+
fetchRef,
|
|
419
|
+
timeoutMs: githubTimeoutMs,
|
|
420
|
+
env: githubAuthEnv(githubToken),
|
|
421
|
+
}),
|
|
388
422
|
onAttempt: ({ attempt }) => { evidence.cache.githubFetchAttempts = attempt; },
|
|
389
423
|
onRetry: ({ attempt, limit, error }) => console.log(`buildchain: GitHub source fetch failed, retry ${attempt + 1}/${limit}: ${error.message}`),
|
|
390
424
|
shouldRetry: retryableGitFetchError,
|
|
@@ -147,20 +147,37 @@ async function resolveCanaryEvidence({
|
|
|
147
147
|
const status = statuses.find((entry) => entry.context === canary.context);
|
|
148
148
|
const targetRun = parseRunUrl(status?.target_url);
|
|
149
149
|
let targetRunEvidence;
|
|
150
|
+
let targetWorkflowEvidence;
|
|
150
151
|
if (targetRun && (!canary.repository || targetRun.repository === canary.repository)) {
|
|
151
152
|
targetRunEvidence = await api(
|
|
152
153
|
`/repos/${targetRun.owner}/${targetRun.repo}/actions/runs/${targetRun.runId}`,
|
|
153
154
|
);
|
|
155
|
+
if (targetRunEvidence?.workflow_id) {
|
|
156
|
+
targetWorkflowEvidence = await api(
|
|
157
|
+
`/repos/${targetRun.owner}/${targetRun.repo}/actions/workflows/${encodeURIComponent(targetRunEvidence.workflow_id)}`,
|
|
158
|
+
);
|
|
159
|
+
}
|
|
154
160
|
}
|
|
161
|
+
const workflowName = targetWorkflowEvidence?.name || targetRunEvidence?.name || "";
|
|
162
|
+
const workflowPath = targetWorkflowEvidence?.path || targetRunEvidence?.path || "";
|
|
155
163
|
const workflowMatches = !canary.workflow ||
|
|
156
|
-
canary.workflow ===
|
|
157
|
-
canary.workflow ===
|
|
164
|
+
canary.workflow === workflowName ||
|
|
165
|
+
canary.workflow === workflowPath.split("/").pop();
|
|
158
166
|
const repositoryMatches = !canary.repository || targetRun?.repository === canary.repository;
|
|
159
|
-
const
|
|
167
|
+
const inputRuntimeRef = String(
|
|
160
168
|
targetRunEvidence?.inputs?.buildchain_ref ||
|
|
161
169
|
targetRunEvidence?.inputs?.buildchainRef ||
|
|
162
170
|
"",
|
|
163
171
|
).trim();
|
|
172
|
+
const runDisplayName = String(
|
|
173
|
+
targetRunEvidence?.display_title || targetRunEvidence?.name || "",
|
|
174
|
+
).trim();
|
|
175
|
+
const runNamePrefix = `${workflowName || canary.workflow || ""} / `;
|
|
176
|
+
const runNameRuntimeRef = runNamePrefix.trim() && runDisplayName.startsWith(runNamePrefix)
|
|
177
|
+
? runDisplayName.slice(runNamePrefix.length).trim()
|
|
178
|
+
: "";
|
|
179
|
+
const runtimeRef = inputRuntimeRef || runNameRuntimeRef;
|
|
180
|
+
const runtimeRefSource = inputRuntimeRef ? "workflow-input" : runNameRuntimeRef ? "run-name" : "missing";
|
|
164
181
|
const runtimeRefMatches = runtimeRef === candidateTag || runtimeRef === candidateSha;
|
|
165
182
|
const evidenceMatches =
|
|
166
183
|
status?.state === "success" &&
|
|
@@ -179,9 +196,11 @@ async function resolveCanaryEvidence({
|
|
|
179
196
|
completedAt: targetRunEvidence?.updated_at || status?.updated_at || "",
|
|
180
197
|
evidenceUrl: status?.target_url || "",
|
|
181
198
|
repository: targetRun?.repository || canary.repository,
|
|
182
|
-
workflow:
|
|
199
|
+
workflow: workflowName || workflowPath,
|
|
183
200
|
attestor: status?.creator?.login || "",
|
|
184
201
|
runtimeRef,
|
|
202
|
+
runtimeRefSource,
|
|
203
|
+
workflowId: targetRunEvidence?.workflow_id || "",
|
|
185
204
|
});
|
|
186
205
|
}
|
|
187
206
|
return evidence;
|