Skip to content

Commit b8b2ffe

Browse files
committed
ci: resolve branch run by walking HEAD history
Rewrites branch mode in ci/tools/lookup-run-id to walk the branch's commit history from HEAD and return the first commit whose workflow has a successful run passing any required artifact check. The one Actions-API filter used in the hot path is ?head_sha=<SHA>, which is not among the server-side filters community/discussions/24626 flags as returning stale slices (branch=, status=, created=). The cross-check / union / wall-clock-freshness logic from #2976 and #2999 is no longer needed. Also removes ci/tools/tests/test_lookup_run_id.py; CI tooling that runs on every PR is exercised by CI itself. Refs #2975.
1 parent ab9e20f commit b8b2ffe

2 files changed

Lines changed: 113 additions & 333 deletions

File tree

‎ci/tools/lookup-run-id‎

Lines changed: 113 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -97,31 +97,48 @@ for cmd in jq gh; do
9797
done
9898

9999
# ── Mode: branch ──
100+
#
101+
# Walk the branch's commit history from HEAD and return the first commit
102+
# whose workflow has a successful run matching any required artifact
103+
# patterns. The one Actions-API filter used is ?head_sha=<SHA>, which is
104+
# not on the list of filters flagged as returning stale/cached slices in
105+
# https://github.com/orgs/community/discussions/24626.
100106
if [[ "${MODE}" == "branch" ]]; then
101107
echo "Looking up latest successful '${WORKFLOW_NAME}' run on branch: ${REF}" >&2
102108

103-
RUN_DATA=$(gh run list \
104-
--repo "${REPOSITORY}" \
105-
--branch "${REF}" \
106-
--workflow "${WORKFLOW_NAME}" \
107-
--json databaseId,workflowName,status,conclusion,headSha,headBranch,createdAt,url \
108-
--limit 100)
109-
110-
CANDIDATE_RUNS=$(echo "${RUN_DATA}" | jq -r \
111-
--arg branch "${REF}" '
112-
map(select(
113-
.headBranch == $branch
114-
and .conclusion == "success"
115-
))
116-
| sort_by(.createdAt, .databaseId)
117-
| reverse
118-
| .[]
119-
| [.databaseId, .headSha, .createdAt]
120-
| @tsv
121-
')
122-
123-
if [[ -z "${CANDIDATE_RUNS}" ]]; then
124-
echo "Error: No successful '${WORKFLOW_NAME}' run found on branch '${REF}'" >&2
109+
# Resolve workflow selector to a numeric workflow ID.
110+
WORKFLOW_ID=""
111+
if [[ "${WORKFLOW_NAME}" =~ ^[0-9]+$ || "${WORKFLOW_NAME}" =~ \.(yml|yaml)$ ]]; then
112+
if ! WORKFLOW_ID=$(gh api "repos/${REPOSITORY}/actions/workflows/${WORKFLOW_NAME}" \
113+
--jq '.id' 2>/dev/null); then
114+
WORKFLOW_ID=""
115+
fi
116+
fi
117+
if [[ -z "${WORKFLOW_ID}" ]]; then
118+
WORKFLOW_ID=$(gh api --paginate \
119+
"repos/${REPOSITORY}/actions/workflows?per_page=100" \
120+
| jq -sr --arg name "${WORKFLOW_NAME}" '
121+
[.[] | .workflows[]]
122+
| map(select(
123+
.name == $name
124+
or .path == $name
125+
or ((.path // "") | split("/") | last) == $name
126+
))
127+
| .[0].id // empty
128+
')
129+
fi
130+
if [[ -z "${WORKFLOW_ID}" ]]; then
131+
echo "Error: Could not resolve workflow '${WORKFLOW_NAME}' in '${REPOSITORY}'" >&2
132+
exit 1
133+
fi
134+
135+
# Resolve branch HEAD SHA.
136+
if ! BRANCH_HEAD_SHA=$(gh api "repos/${REPOSITORY}/branches/${REF}" \
137+
--jq '.commit.sha' 2>/dev/null); then
138+
BRANCH_HEAD_SHA=""
139+
fi
140+
if [[ -z "${BRANCH_HEAD_SHA}" ]]; then
141+
echo "Error: Could not resolve branch '${REF}' in '${REPOSITORY}'" >&2
125142
exit 1
126143
fi
127144

@@ -130,59 +147,96 @@ if [[ "${MODE}" == "branch" ]]; then
130147
printf ' - %s\n' "${ARTIFACT_PATTERNS[@]}" >&2
131148
fi
132149

150+
# Walk branch commits newest-first; accept the first commit with a
151+
# successful run that passes the artifact check.
152+
COMMIT_SCAN_LIMIT="${LOOKUP_RUN_ID_COMMIT_SCAN_LIMIT:-200}"
133153
RUN_ID=""
134154
HEAD_SHA=""
135-
while IFS= read -r candidate; do
136-
IFS=$'\t' read -r candidate_id candidate_sha candidate_created_at <<< "${candidate}"
137-
138-
missing_patterns=()
139-
if [[ ${#ARTIFACT_PATTERNS[@]} -gt 0 ]]; then
140-
if ! ARTIFACT_NAMES=$(gh api --paginate \
141-
"repos/${REPOSITORY}/actions/runs/${candidate_id}/artifacts?per_page=100" \
142-
--jq '.artifacts[] | select(.expired == false) | .name'); then
143-
echo "Error: Failed to list artifacts for run ${candidate_id}" >&2
155+
commits_scanned=0
156+
page=1
157+
158+
while [[ ${commits_scanned} -lt ${COMMIT_SCAN_LIMIT} ]]; do
159+
if ! COMMIT_PAGE=$(gh api --method GET \
160+
"repos/${REPOSITORY}/commits" \
161+
-f "sha=${BRANCH_HEAD_SHA}" \
162+
-F "per_page=100" \
163+
-F "page=${page}" \
164+
--jq '.[].sha' 2>/dev/null); then
165+
echo "Error: Failed to list commits for branch '${REF}' (page ${page})" >&2
166+
exit 1
167+
fi
168+
[[ -n "${COMMIT_PAGE}" ]] || break
169+
170+
while IFS= read -r commit_sha; do
171+
[[ -n "${commit_sha}" ]] || continue
172+
[[ ${commits_scanned} -lt ${COMMIT_SCAN_LIMIT} ]] || break
173+
commits_scanned=$((commits_scanned + 1))
174+
175+
if ! SHA_RUNS=$(gh api --method GET \
176+
"repos/${REPOSITORY}/actions/workflows/${WORKFLOW_ID}/runs" \
177+
-f "head_sha=${commit_sha}" \
178+
-F "per_page=100" \
179+
--jq '[.workflow_runs[] | select(.conclusion == "success") | {id, head_sha, created_at}] | sort_by(.created_at) | reverse'); then
180+
echo "Error: Failed to list runs for commit ${commit_sha}" >&2
144181
exit 1
145182
fi
146-
147-
for pattern in "${ARTIFACT_PATTERNS[@]}"; do
148-
pattern_matched=0
149-
while IFS= read -r artifact_name; do
150-
# The caller supplies a shell glob, so the RHS must remain unquoted.
151-
# shellcheck disable=SC2053
152-
if [[ -n "${artifact_name}" && "${artifact_name}" == ${pattern} ]]; then
153-
pattern_matched=1
154-
break
183+
[[ "${SHA_RUNS}" != "[]" ]] || continue
184+
185+
count=$(echo "${SHA_RUNS}" | jq 'length')
186+
for ((i=0; i<count; i++)); do
187+
candidate_id=$(echo "${SHA_RUNS}" | jq -r ".[${i}].id")
188+
candidate_sha=$(echo "${SHA_RUNS}" | jq -r ".[${i}].head_sha")
189+
candidate_created_at=$(echo "${SHA_RUNS}" | jq -r ".[${i}].created_at")
190+
191+
missing_patterns=()
192+
if [[ ${#ARTIFACT_PATTERNS[@]} -gt 0 ]]; then
193+
if ! ARTIFACT_NAMES=$(gh api --paginate \
194+
"repos/${REPOSITORY}/actions/runs/${candidate_id}/artifacts?per_page=100" \
195+
--jq '.artifacts[] | select(.expired == false) | .name'); then
196+
echo "Error: Failed to list artifacts for run ${candidate_id}" >&2
197+
exit 1
155198
fi
156-
done <<< "${ARTIFACT_NAMES}"
157-
if [[ ${pattern_matched} == 0 ]]; then
158-
missing_patterns+=("${pattern}")
199+
200+
for pattern in "${ARTIFACT_PATTERNS[@]}"; do
201+
pattern_matched=0
202+
while IFS= read -r artifact_name; do
203+
# The caller supplies a shell glob; RHS must stay unquoted.
204+
# shellcheck disable=SC2053
205+
if [[ -n "${artifact_name}" && "${artifact_name}" == ${pattern} ]]; then
206+
pattern_matched=1
207+
break
208+
fi
209+
done <<< "${ARTIFACT_NAMES}"
210+
if [[ ${pattern_matched} == 0 ]]; then
211+
missing_patterns+=("${pattern}")
212+
fi
213+
done
159214
fi
160-
done
161-
fi
162215

163-
if [[ ${#missing_patterns[@]} -gt 0 ]]; then
164-
echo "Skipping run ${candidate_id} (${candidate_created_at}); missing unexpired artifact(s): ${missing_patterns[*]}" >&2
165-
continue
166-
fi
216+
if [[ ${#missing_patterns[@]} -gt 0 ]]; then
217+
echo "Skipping run ${candidate_id} (sha ${candidate_sha:0:12}, ${candidate_created_at}); missing unexpired artifact(s): ${missing_patterns[*]}" >&2
218+
continue
219+
fi
220+
221+
RUN_ID="${candidate_id}"
222+
HEAD_SHA="${candidate_sha}"
223+
break 3
224+
done
225+
done <<< "${COMMIT_PAGE}"
167226

168-
RUN_ID="${candidate_id}"
169-
HEAD_SHA="${candidate_sha}"
170-
break
171-
done <<< "${CANDIDATE_RUNS}"
227+
page=$((page + 1))
228+
done
172229

173230
if [[ -z "${RUN_ID}" ]]; then
174-
echo "Error: No successful '${WORKFLOW_NAME}' run on branch '${REF}' has all required artifacts" >&2
231+
echo "Error: No successful '${WORKFLOW_NAME}' run with required artifacts found in the ${commits_scanned} most-recent commits of branch '${REF}'" >&2
232+
echo "Branch HEAD: ${BRANCH_HEAD_SHA}" >&2
175233
exit 1
176234
fi
177235

178-
echo "Found run ID: ${RUN_ID}" >&2
236+
echo "Found run ID: ${RUN_ID} (sha ${HEAD_SHA:0:12})" >&2
179237
echo "${RUN_ID}"
180238

181239
if [[ "${HEAD_SHA_FLAG}" == 1 ]]; then
182-
if [[ -z "${HEAD_SHA}" ]]; then
183-
echo "Error: Run ${RUN_ID} has no head SHA" >&2
184-
exit 1
185-
fi
186240
echo "Head SHA: ${HEAD_SHA}" >&2
187241
echo "${HEAD_SHA}"
188242
fi

0 commit comments

Comments
 (0)