@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.
@@ -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 fetchSha({ targetPath, remoteName, remoteUrl, sha, fetchRef, timeoutMs, env = {} }) {
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
- git(["remote", "remove", remoteName], { cwd: targetPath, timeoutMs, stdio: "ignore" });
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
- git(["remote", "add", remoteName, remoteUrl], { cwd: targetPath, timeoutMs });
208
+ runGit(["remote", "add", remoteName, remoteUrl], { cwd: targetPath, timeoutMs });
193
209
  } catch {
194
- git(["remote", "set-url", remoteName, remoteUrl], { cwd: targetPath, timeoutMs });
210
+ runGit(["remote", "set-url", remoteName, remoteUrl], { cwd: targetPath, timeoutMs });
195
211
  }
196
- const fetchArgs = ["fetch", "--no-tags", "--depth=1", remoteName, `+${sha}:refs/buildchain/source`];
197
- try {
198
- git(fetchArgs, { cwd: targetPath, timeoutMs, env, stdio: "ignore" });
199
- return;
200
- } catch (error) {
201
- if (!fetchRef) {
202
- throw error;
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
- git(["fetch", "--no-tags", "--depth=1", remoteName, `+${fetchRef}:refs/buildchain/source-ref`], {
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 (!hasCommit(targetPath, sha, timeoutMs)) {
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
- fetchSha({
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: () => fetchSha({ targetPath, remoteName: "origin", remoteUrl, sha, fetchRef, timeoutMs, env: githubAuthEnv(githubToken) }),
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 === targetRunEvidence?.name ||
157
- canary.workflow === targetRunEvidence?.path?.split("/").pop();
164
+ canary.workflow === workflowName ||
165
+ canary.workflow === workflowPath.split("/").pop();
158
166
  const repositoryMatches = !canary.repository || targetRun?.repository === canary.repository;
159
- const runtimeRef = String(
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: targetRunEvidence?.name || targetRunEvidence?.path || "",
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;