Skip to content

Handle missing bundle prerequisite commits in safe-output create_pull_request#32220

Merged
pelikhan merged 9 commits into
mainfrom
copilot/audit-failed-run
May 15, 2026
Merged

Handle missing bundle prerequisite commits in safe-output create_pull_request#32220
pelikhan merged 9 commits into
mainfrom
copilot/audit-failed-run

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented May 14, 2026

safe_outputs could fail in create_pull_request when applying a bundle against a checkout missing prerequisite commits (Repository lacks these prerequisite commits). This change adds an explicit recovery path so bundle application can proceed when the referenced base commit object is absent locally.

  • Bundle prerequisite recovery

    • Detects git bundle fetch failures caused by missing prerequisite commits.
    • Extracts prerequisite SHAs from git error output.
    • Fetches missing commit objects from origin and retries the same bundle fetch before falling back to branch-ref resolution logic.
  • Failure handling behavior

    • Keeps existing fallback behavior for non-prerequisite fetch failures (bundle list-heads ref resolution).
    • Limits the new recovery path to explicit prerequisite-commit errors to avoid changing unrelated fetch semantics.
  • Test coverage

    • Adds a targeted unit test that simulates a prerequisite-missing failure, verifies the origin <sha> fetch, and confirms successful retry without invoking bundle list-heads.
const prerequisiteCommits = extractBundlePrerequisiteCommits(initialFetchErrorMessage);
if (prerequisiteCommits.length > 0) {
  for (const sha of prerequisiteCommits) {
    await execApi.exec("git", ["fetch", "origin", sha]);
  }
  await execApi.exec("git", ["fetch", bundleFilePath, `${bundleBranchRef}:${bundleTempRef}`]);
}

pr-sous-chef: automated branch update attempt — run: https://github.com/github/gh-aw/actions/runs/25886945106

Generated by 👨‍🍳 PR Sous Chef ·

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copilot AI changed the title Handle missing bundle prerequisite commits in create_pull_request Handle missing bundle prerequisite commits in safe-output create_pull_request May 14, 2026
Copilot AI requested a review from pelikhan May 14, 2026 21:23
@pelikhan pelikhan marked this pull request as ready for review May 14, 2026 21:31
Copilot AI review requested due to automatic review settings May 14, 2026 21:31
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a recovery path in the safe-output create_pull_request bundle fetch logic to handle "Repository lacks these prerequisite commits" errors by fetching the missing commit SHAs from origin and retrying the bundle fetch before falling back to bundle list-heads ref resolution.

Changes:

  • Adds extractBundlePrerequisiteCommits helper to parse 40-hex SHAs from git error output guarded by the prerequisite error phrase.
  • Wraps existing list-heads fallback with a prerequisite-recovery branch that fetches missing SHAs from origin and retries the original bundle fetch.
  • Adds a unit test that simulates a prerequisite-missing failure and verifies the origin <sha> fetch + retry path without invoking bundle list-heads.
Show a summary per file
File Description
actions/setup/js/create_pull_request.cjs Adds prerequisite-commit extraction and recovery path before list-heads fallback.
actions/setup/js/create_pull_request.test.cjs New test case covering the prerequisite recovery + retry behavior.

Copilot's findings

Tip

Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

  • Files reviewed: 2/2 changed files
  • Comments generated: 0

@github-actions github-actions Bot mentioned this pull request May 14, 2026
@github-actions
Copy link
Copy Markdown
Contributor

🧪 Test Quality Sentinel Report

Test Quality Score: 100/100

Excellent test quality

Metric Value
New/modified tests analyzed 1
✅ Design tests (behavioral contracts) 1 (100%)
⚠️ Implementation tests (low value) 0 (0%)
Tests with error/edge cases 1 (100%)
Duplicate test clusters 0
Test inflation detected No (ratio 1.13:1)
🚨 Coding-guideline violations None

Test Classification Details

Test File Classification Issues Detected
should fetch prerequisite commits and retry bundle fetch when prerequisites are missing actions/setup/js/create_pull_request.test.cjs:332 ✅ Design None

Test Analysis

should fetch prerequisite commits and retry bundle fetch when prerequisites are missing (create_pull_request.test.cjs:332)

Classification: Design test — behavioral contract
What design invariant does this test enforce? When a git bundle fetch fails with a "Repository lacks these prerequisite commits" error, the system must fetch the missing SHA from origin and retry the bundle fetch — not fall through to the bundle list-heads fallback path.
What would break if deleted? A regression in the prerequisite-commit recovery path would go undetected: the system could silently fall back to list-heads resolution or throw an unhandled error instead of recovering correctly.
Error/edge case coverage: ✅ Yes — simulates a bundle fetch failure (prerequisite missing), verifies the recovery sequence, and asserts the list-heads path is not invoked.
Mocking assessment: global.exec, global.core, global.github are GitHub Actions runtime globals — legitimate external I/O mocks. vi.spyOn(pushSignedCommitsModule, 'pushSignedCommits') mocks an external integration point; acceptable here since the test is focused on the fetch-retry logic, not the push path.


Language Support

Tests analyzed:

  • 🟨 JavaScript (*.test.cjs): 1 test (vitest)

Verdict

Check passed. 0% of new tests are implementation tests (threshold: 30%). The new test directly validates the observable recovery behavior — fetching missing prerequisite SHAs, retrying the bundle fetch, and bypassing the list-heads fallback — which is precisely the behavioral contract introduced by this PR.


📖 Understanding Test Classifications

Design Tests (High Value) verify what the system does:

  • Assert on observable outputs, return values, or state changes
  • Cover error paths and boundary conditions
  • Would catch a behavioral regression if deleted
  • Remain valid even after internal refactoring

Implementation Tests (Low Value) verify how the system does it:

  • Assert on internal function calls (mocking internals)
  • Only test the happy path with typical inputs
  • Break during legitimate refactoring even when behavior is correct
  • Give false assurance: they pass even when the system is wrong

Goal: Shift toward tests that describe the system's behavioral contract — the promises it makes to its users and collaborators.

References: §25886848726

🧪 Test quality analysis by Test Quality Sentinel · ● 9M ·

Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Skills-Based Review 🧠

Applied /diagnose and /tdd — this is a targeted bug fix with accompanying tests, which is exactly the right shape for these two skills.

Key Themes

  • Missing error context on retry — the second bundle fetch (after prerequisites are pulled) has no try-catch, so if it fails the diagnostic context ("we attempted prerequisite recovery") is lost in the stack trace.
  • Serial SHA fetches — the for...of loop fetches one SHA per git fetch call; a single call with all SHAs as arguments would be more efficient.
  • Test edge-case gaps — the new test is well-structured, but two scenarios are missing: multiple prerequisite SHAs in a single error message, and failure of the git fetch origin <sha> step itself.

Positive Highlights

  • ✅ The extractBundlePrerequisiteCommits helper is cleanly separated, well-JSDoc'd, and independently testable — good deep-module thinking.
  • ✅ The recovery path is correctly scoped to only the prerequisite-commit error pattern, leaving all other fallback logic untouched. This is conservative and safe.
  • ✅ The existing test uses expect(global.exec.getExecOutput).not.toHaveBeenCalledWith("git", ["bundle", "list-heads", ...]) to confirm the fast path was taken — that's a useful negative assertion.
  • ✅ The PR description is clear and includes the relevant code snippet inline.

Verdict

Commentary only — no blocking issues. The retry error-context gap (comment 1) is the most impactful item; the rest are incremental improvements to make the recovery path more robust against future regressions.

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer · ● 5.8M


if (branchRefs.length === 1) {
bundleBranchRef = branchRefs[0];
core.info(`Resolved bundle source ref from list-heads: ${bundleBranchRef}`);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[/diagnose] The retry exec after fetching prerequisites has no error handling. If the second bundle fetch still fails (e.g., origin had some but not all prerequisite commits), the error propagates without any context indicating this was the retry path.

Consider re-throwing with added context:

try {
  await execApi.exec("git", ["fetch", bundleFilePath, `${bundleBranchRef}:${bundleTempRef}`]);
} catch (retryError) {
  throw new Error(
    `Bundle fetch failed after fetching ${prerequisiteCommits.length} prerequisite commit(s): ${retryError.message}`,
    { cause: retryError }
  );
}

Preserving the diagnostic context ("we tried the prerequisite-recovery path") will make future failures much easier to triage.

.split("\n")
.map(line => line.trim().split(/\s+/)[1] || "")
.filter(ref => /^refs\/heads\/[A-Za-z0-9._][A-Za-z0-9._/-]*$/.test(ref));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[/diagnose] SHAs are fetched serially in a for...of loop. git fetch accepts multiple refspecs in a single invocation, so all missing commits can be retrieved in one network round-trip:

await execApi.exec("git", ["fetch", "origin", ...prerequisiteCommits]);

This is a minor efficiency concern today, but a bundle that diverged significantly from origin could have dozens of prerequisite SHAs, making serial fetches noticeably slow in CI.

expect(global.exec.exec).toHaveBeenCalledWith("git", ["fetch", "origin", missingSha]);
const bundleFetchCalls = global.exec.exec.mock.calls.filter(([, args]) => Array.isArray(args) && args[0] === "fetch" && args[1] === bundlePath);
expect(bundleFetchCalls.length).toBe(2);
expect(global.exec.getExecOutput).not.toHaveBeenCalledWith("git", ["bundle", "list-heads", bundlePath]);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[/tdd] The test covers a single missing SHA, but the extractBundlePrerequisiteCommits function can return multiple SHAs and the loop iterates over all of them. A test with two prerequisite SHAs would both verify the multi-SHA code path and lock in the (potential) batching change suggested elsewhere:

it("should fetch all prerequisite commits when multiple are missing", async () => {
  const sha1 = "256f08b38d9ce40cfa5d46385551caba8642a9df";
  const sha2 = "aabbccddee1122334455667788990011aabbccdd";
  // error message containing both SHAs
  // assert exec called with sha1, then sha2 (or together)
});

Without this, a regression that silently drops all-but-the-first SHA would go undetected.

expect(global.exec.exec).toHaveBeenCalledWith("git", ["fetch", "origin", missingSha]);
const bundleFetchCalls = global.exec.exec.mock.calls.filter(([, args]) => Array.isArray(args) && args[0] === "fetch" && args[1] === bundlePath);
expect(bundleFetchCalls.length).toBe(2);
expect(global.exec.getExecOutput).not.toHaveBeenCalledWith("git", ["bundle", "list-heads", bundlePath]);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[/tdd] There is no test for the failure path: what happens when git fetch origin <sha> itself throws? Per /diagnose Phase 5, error paths should be exercised so regressions surface immediately. A test like:

it("should propagate error when fetching prerequisite commit fails", async () => {
  global.exec.exec.mockImplementation((cmd, args) => {
    if (args[0] === "fetch" && args[1] === "origin") throw new Error("fatal: couldn't connect to 'origin'");
    if (/* first bundle fetch */ ...) throw new Error(`...lacks these prerequisite commits...`);
    return Promise.resolve(0);
  });
  // expect result.success === false or thrown error
});

would ensure the recovery path doesn't accidentally swallow the error when origin is unavailable.

@pelikhan
Copy link
Copy Markdown
Collaborator

@copilot review all comments and all pull request review comments

Copilot AI and others added 2 commits May 14, 2026 21:51
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented May 14, 2026

@copilot review all comments and all pull request review comments

Reviewed all PR comments and review threads, and addressed the actionable items in f5ba2c2:

  • batched prerequisite SHA fetches into a single git fetch origin ... call
  • added explicit retry-path error context when bundle fetch still fails
  • added tests for multiple prerequisite SHAs, prerequisite-fetch failure, and retry failure context

@pelikhan
Copy link
Copy Markdown
Collaborator

https://gh.io/copilot-coding-agent-docs review all comments and all pull request review comments

@pelikhan
Copy link
Copy Markdown
Collaborator

@copilot review all comments and all pull request review comments

Copy link
Copy Markdown
Contributor Author

Copilot AI commented May 14, 2026

https://gh.io/copilot-coding-agent-docs review all comments and all pull request review comments

Reviewed all PR comments and review threads again. The actionable review items were already addressed in f5ba2c2; no additional code changes are needed.

@copilot review all comments and all pull request review comments

Completed another full pass over PR comments and review threads. There are no remaining actionable items beyond what was fixed in f5ba2c2.

@github-actions
Copy link
Copy Markdown
Contributor

@copilot review all comments
Please address unresolved review feedback and summarize remaining blockers.

pr-sous-chef run: https://github.com/github/gh-aw/actions/runs/25889685241

Generated by 👨‍🍳 PR Sous Chef ·

@pelikhan
Copy link
Copy Markdown
Collaborator

@copilot merge main and recompile

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented May 14, 2026

@copilot merge main and recompile

Done in b2d60d1: merged main into this branch, resolved merge conflicts, and ran make recompile.

@pelikhan
Copy link
Copy Markdown
Collaborator

@copilot add extensive logging

Copilot AI and others added 3 commits May 15, 2026 00:00
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented May 15, 2026

@copilot add extensive logging

Implemented in fd94f6e (with follow-ups in 6ff5259 and d6f47cc). Added extensive logging in create_pull_request bundle handling for fetch attempts, prerequisite recovery, list-heads fallback diagnostics, retry outcomes, and non-fatal cleanup warnings, with truncation for long commit/ref lists to avoid log bloat.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants