1
0
Fork 0
mirror of https://github.com/actions/checkout.git synced 2026-03-22 15:32:13 +01:00

Add retry logic to checkout and submoduleUpdate for partial clone resilience

When using partial clones (filter=blob:none, which is automatically set
for sparse checkouts), `git checkout` lazily fetches missing blobs from
the promisor remote. If the remote is temporarily unavailable, this
network call fails and surfaces as a hard error with no retry.

The `fetch`, `getDefaultBranch`, and `lfsFetch` methods already use
retryHelper, but `checkout` and `submoduleUpdate` did not, despite both
performing network operations:

- `checkout`: fetches blobs on-demand from promisor remotes during
  partial clone checkouts
- `submoduleUpdate`: clones/fetches submodule repositories

This was observed in production when GitHub's git service had a brief
outage, causing the checkout step to fail with:

  fatal: unable to access '...': Failed to connect to github.com port
  443 after 135272 ms: Couldn't connect to server
  fatal: could not fetch <sha> from promisor remote

Wrapping both methods with the existing retryHelper (3 attempts with
10-20s jittered backoff) makes these operations resilient to transient
network failures, consistent with how fetch already behaves.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Jason Barnett 2026-03-20 16:52:56 +00:00
parent 0c366fd6a8
commit b6a00b350f
2 changed files with 24 additions and 4 deletions

View file

@ -228,7 +228,14 @@ class GitCommandManager {
args.push(ref)
}
await this.execGit(args)
// Retry checkout because it can trigger network I/O when using partial
// clones (filter=blob:none). In that mode git lazily fetches missing
// blobs from the promisor remote during checkout, so a transient network
// failure would otherwise surface as a hard error here.
const that = this
await retryHelper.execute(async () => {
await that.execGit(args)
})
}
async checkoutDetach(): Promise<void> {
@ -457,7 +464,10 @@ class GitCommandManager {
args.push('--recursive')
}
await this.execGit(args)
const that = this
await retryHelper.execute(async () => {
await that.execGit(args)
})
}
async submoduleStatus(): Promise<boolean> {