Skip to content

Commit fff2bc8

Browse files
committed
Refactor merge-ref retries per review; add merge_ref_retry_attempts
- Add try_fetch_merge_ref helper and loop over MERGE_REF_RETRY_ATTEMPTS - Default 3 attempts; validate numeric config and document in README/plugin.yml - Keep 2s then 5s backoff between attempts; non-merge-ref errors still fail fast Made-with: Cursor
1 parent 724370e commit fff2bc8

3 files changed

Lines changed: 59 additions & 45 deletions

File tree

README.md

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,10 @@ Use this option for pipeline upload jobs that don't need to preserve local chang
4242

4343
Enable verbose logging with bash execution tracing (`set -x`). This shows each command being executed and can help debug issues with ssh-keyscan, git operations, or other checkout problems. When enabled, you'll see detailed output including command arguments and any error messages from underlying tools.
4444

45+
#### `merge_ref_retry_attempts` (integer)
46+
47+
How many times to try fetching the GitHub pull-request merge ref (`refs/pull/<n>/merge`) when using merge-ref checkout (see `BUILDKITE_PULL_REQUEST_USING_MERGE_REFSPEC` below). Defaults to `3`. Between attempts the hook waits 2 seconds after the first failure and 5 seconds after later failures. Set to `0` to skip merge-ref fetch attempts and fall back immediately.
48+
4549
#### `post_checkout` (object)
4650

4751
Options that run after the sparse checkout completes, in the `post-checkout` hook.
@@ -54,13 +58,11 @@ Convert the shallow clone into a full-depth clone by running `git fetch --unshal
5458

5559
### BUILDKITE_PULL_REQUEST_USING_MERGE_REFSPEC
5660
When `BUILDKITE_PULL_REQUEST_USING_MERGE_REFSPEC=true`, the plugin will retry the
57-
GitHub merge ref checkout if it sees the known "missing merge ref" failure:
58-
59-
- retry after 2 seconds
60-
- retry after 5 seconds
61-
- if the merge ref is still unavailable, fall back to the normal non-merge-ref
62-
target (`BUILDKITE_BRANCH` when `BUILDKITE_COMMIT=HEAD`, otherwise
63-
`BUILDKITE_COMMIT`)
61+
GitHub merge ref checkout if it sees the known "missing merge ref" failure, up to
62+
`merge_ref_retry_attempts` times (default `3`). Between attempts it waits 2 seconds
63+
after the first failure and 5 seconds after subsequent failures. If the merge ref
64+
is still unavailable, it falls back to the normal non-merge-ref target
65+
(`BUILDKITE_BRANCH` when `BUILDKITE_COMMIT=HEAD`, otherwise `BUILDKITE_COMMIT`).
6466

6567
This retry logic only applies to the specific merge-ref-not-ready error. Other
6668
`git fetch` failures still fail immediately.

hooks/checkout

Lines changed: 47 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,13 @@ fi
3030
SKIP_SSH_KEYSCAN_OPTION="$(plugin_read_config SKIP_SSH_KEYSCAN "false")"
3131
CLEAN_CHECKOUT_OPTION="$(plugin_read_config CLEAN_CHECKOUT "false")"
3232

33+
MERGE_REF_RETRY_ATTEMPTS_RAW="$(plugin_read_config MERGE_REF_RETRY_ATTEMPTS "3")"
34+
if [[ "${MERGE_REF_RETRY_ATTEMPTS_RAW}" =~ ^[0-9]+$ ]]; then
35+
MERGE_REF_RETRY_ATTEMPTS="${MERGE_REF_RETRY_ATTEMPTS_RAW}"
36+
else
37+
MERGE_REF_RETRY_ATTEMPTS=3
38+
fi
39+
3340
if [[ "${CLEAN_CHECKOUT_OPTION}" = "true" ]]; then
3441
log_warning "clean_checkout is enabled - this will destroy any local changes and reset the repository state"
3542
fi
@@ -106,6 +113,25 @@ is_missing_merge_ref_error() {
106113
|| [[ "${FETCH_OUTPUT}" == *"not our ref ${merge_ref}"* ]]
107114
}
108115

116+
# Try to fetch merge ref. Returns 0 on success, 1 if merge ref is missing (retry),
117+
# otherwise logs and exits with git's status.
118+
try_fetch_merge_ref() {
119+
local merge_ref="$1"
120+
if FETCH_OUTPUT=$(git fetch "${FETCH_FLAGS[@]}" "${merge_ref}" 2>&1); then
121+
[[ -n "${FETCH_OUTPUT}" ]] && printf '%s\n' "${FETCH_OUTPUT}"
122+
return 0
123+
else
124+
local status=$?
125+
[[ -n "${FETCH_OUTPUT}" ]] && printf '%s\n' "${FETCH_OUTPUT}" >&2
126+
if is_missing_merge_ref_error; then
127+
return 1
128+
else
129+
log_error "Failed to fetch merge ref for PR #${BUILDKITE_PULL_REQUEST} from origin"
130+
exit "${status}"
131+
fi
132+
fi
133+
}
134+
109135
# Determine if we should use the pull request merge refspec
110136
USE_MERGE_REFSPEC="false"
111137
if [[ "${BUILDKITE_PULL_REQUEST_USING_MERGE_REFSPEC:-false}" = "true" ]] \
@@ -115,49 +141,32 @@ if [[ "${BUILDKITE_PULL_REQUEST_USING_MERGE_REFSPEC:-false}" = "true" ]] \
115141
MERGE_REF="refs/pull/${BUILDKITE_PULL_REQUEST}/merge"
116142

117143
log_info "Fetching merge ref for PR #${BUILDKITE_PULL_REQUEST} from origin"
118-
if FETCH_OUTPUT=$(git fetch "${FETCH_FLAGS[@]}" "${MERGE_REF}" 2>&1); then
119-
[[ -n "${FETCH_OUTPUT}" ]] && printf '%s\n' "${FETCH_OUTPUT}"
120-
else
121-
status=$?
122-
[[ -n "${FETCH_OUTPUT}" ]] && printf '%s\n' "${FETCH_OUTPUT}" >&2
123-
if ! is_missing_merge_ref_error; then
124-
log_error "Failed to fetch merge ref for PR #${BUILDKITE_PULL_REQUEST} from origin"
125-
exit "${status}"
126-
fi
127144

128-
log_warning "Merge ref ${MERGE_REF} was not available yet; retrying in 2s"
129-
sleep 2
130-
if FETCH_OUTPUT=$(git fetch "${FETCH_FLAGS[@]}" "${MERGE_REF}" 2>&1); then
131-
[[ -n "${FETCH_OUTPUT}" ]] && printf '%s\n' "${FETCH_OUTPUT}"
132-
else
133-
status=$?
134-
[[ -n "${FETCH_OUTPUT}" ]] && printf '%s\n' "${FETCH_OUTPUT}" >&2
135-
if ! is_missing_merge_ref_error; then
136-
log_error "Failed to fetch merge ref for PR #${BUILDKITE_PULL_REQUEST} from origin"
137-
exit "${status}"
138-
fi
145+
fetch_succeeded=false
146+
for ((attempt = 1; attempt <= MERGE_REF_RETRY_ATTEMPTS; attempt++)); do
147+
if try_fetch_merge_ref "${MERGE_REF}"; then
148+
fetch_succeeded=true
149+
break
150+
fi
139151

140-
log_warning "Merge ref ${MERGE_REF} was still not available; retrying in 5s"
141-
sleep 5
142-
if FETCH_OUTPUT=$(git fetch "${FETCH_FLAGS[@]}" "${MERGE_REF}" 2>&1); then
143-
[[ -n "${FETCH_OUTPUT}" ]] && printf '%s\n' "${FETCH_OUTPUT}"
152+
if [[ "${attempt}" -lt "${MERGE_REF_RETRY_ATTEMPTS}" ]]; then
153+
if [[ "${attempt}" -eq 1 ]]; then
154+
log_warning "Merge ref ${MERGE_REF} was not available yet; retrying in 2s"
155+
sleep 2
144156
else
145-
status=$?
146-
[[ -n "${FETCH_OUTPUT}" ]] && printf '%s\n' "${FETCH_OUTPUT}" >&2
147-
if ! is_missing_merge_ref_error; then
148-
log_error "Failed to fetch merge ref for PR #${BUILDKITE_PULL_REQUEST} from origin"
149-
exit "${status}"
150-
fi
151-
152-
if [[ "${BUILDKITE_COMMIT}" = "HEAD" ]]; then
153-
log_warning "Merge ref ${MERGE_REF} was still unavailable; falling back to branch ${BUILDKITE_BRANCH}"
154-
else
155-
log_warning "Merge ref ${MERGE_REF} was still unavailable; falling back to ${BUILDKITE_COMMIT}"
156-
fi
157-
158-
USE_MERGE_REFSPEC="false"
157+
log_warning "Merge ref ${MERGE_REF} was not available yet; retrying in 5s"
158+
sleep 5
159159
fi
160160
fi
161+
done
162+
163+
if [[ "${fetch_succeeded}" != "true" ]]; then
164+
if [[ "${BUILDKITE_COMMIT}" = "HEAD" ]]; then
165+
log_warning "Merge ref ${MERGE_REF} was still unavailable; falling back to branch ${BUILDKITE_BRANCH}"
166+
else
167+
log_warning "Merge ref ${MERGE_REF} was still unavailable; falling back to ${BUILDKITE_COMMIT}"
168+
fi
169+
USE_MERGE_REFSPEC="false"
161170
fi
162171
fi
163172

plugin.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ configuration:
1616
clean_checkout:
1717
type: boolean
1818
default: false
19+
merge_ref_retry_attempts:
20+
type: integer
21+
default: 3
1922
verbose:
2023
type: boolean
2124
default: false

0 commit comments

Comments
 (0)