From e8e427b506d0138702f79428ef9007aedb4ee308 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 29 Sep 2026 13:36:18 +0000 Subject: [PATCH] pick-git-annex-build: sort runs ourselves, log the listing, allow pinning In eval-under run 36491628696 (push to master, 22:18 UTC) the picker took con/git-annex run 35319817132 (2026-09-18, no OsPath) as the newest schedule/workflow_dispatch master run with a package, although at 19:58 and 21:13 the same code picked 36399800528 (2026-09-28). The OsPath check in install-git-annex-daily.sh rejected the build, so every git-annex cell ended "incomplete". The loop logs each matching candidate, and only the 09-18 run was logged: the API either listed it above the newer runs or did not list them at all. We cannot tell which from that log. - One runs query per event (`&event=...`), so filtering is server-side and $GIT_ANNEX_BUILD_SCAN covers more history; the pages are merged and sorted by created_at here instead of trusting the API's (undocumented) order. Fixes the mis-ordered case. - Log, per event, how many runs were listed and the newest one, so a stale listing is distinguishable from a mis-ordered one next time. - ::warning:: (stderr, so the `$(...)` capture in install-git-annex-daily.sh stays clean) when the pick is older than GIT_ANNEX_BUILD_MAX_AGE days (default 3; the build is daily). Not fatal: the OsPath check already fails the cell. - test.yaml: workflow_dispatch input `git-annex-run`, passed to the picker as GIT_ANNEX_RUN_ID, to pin a known build (e.g. 36399800528) while the listing misbehaves. A pinned run is still checked for a package artifact. Tested: bin/ci/run-checks.sh (shellcheck 26 scripts, known-issues, unit tests); the picker against a stub gh for a mis-ordered listing (now picks 36399800528), an old-only listing (warns), a pinned run, and a pinned run without a package (fails). Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_0112v9r8Yrb9zytWDMmC9dZ2 --- .github/workflows/test.yaml | 8 +++++ bin/ci/pick-git-annex-build.sh | 63 +++++++++++++++++++++++++++------- 2 files changed, 58 insertions(+), 13 deletions(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index b4cd98b..71d15b1 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -28,6 +28,13 @@ on: - "evals/known-issues.yaml" - ".github/workflows/test.yaml" workflow_dispatch: + inputs: + git-annex-run: + description: >- + con/git-annex build-ubuntu.yaml run id to test + (empty: newest build with a package) + required: false + default: "" schedule: - cron: "17 5 * * 1" # Mon 05:17 UTC @@ -55,6 +62,7 @@ jobs: continue-on-error: true env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GIT_ANNEX_RUN_ID: ${{ inputs.git-annex-run }} run: bin/ci/pick-git-annex-build.sh test: diff --git a/bin/ci/pick-git-annex-build.sh b/bin/ci/pick-git-annex-build.sh index e24586a..a7fa53c 100755 --- a/bin/ci/pick-git-annex-build.sh +++ b/bin/ci/pick-git-annex-build.sh @@ -17,6 +17,19 @@ # Only master's scheduled/dispatched runs count: pull_request builds # carry unmerged patches. # +# Why sort here rather than trust the API's order: the runs endpoint +# does not document its ordering, and in eval-under run 36491628696 the +# first schedule/workflow_dispatch run it handed us was 35319817132 +# (2026-09-18) although 36399800528 (2026-09-28) had been first an hour +# earlier. So: one query per event (filtered server-side, so $scan +# covers more history), merged and sorted by created_at here, and the +# raw listing is summarised in the log so a stale answer can be told +# apart from a mis-ordered one. +# +# GIT_ANNEX_RUN_ID pins a run instead (test.yaml's workflow_dispatch +# input), e.g. to rerun the matrix on a known build while the listing +# misbehaves; it is still checked for a package artifact. +# # Run once per workflow (the `matrix` job) and hand the id to every # cell, so all cells of one run test the same build. @@ -34,7 +47,11 @@ env overrides: GIT_ANNEX_BUILD_BRANCH branch to take runs from (default: master) GIT_ANNEX_BUILD_EVENTS space-separated run events to accept (default: "schedule workflow_dispatch") - GIT_ANNEX_BUILD_SCAN how many recent runs to consider (default: 30) + GIT_ANNEX_BUILD_SCAN how many recent runs to fetch per event + (default: 30) + GIT_ANNEX_BUILD_MAX_AGE warn when the picked run is older than this + many days (default: 3; the build is daily) + GIT_ANNEX_RUN_ID pin this run instead of picking (empty: pick) GH_TOKEN required, for the GitHub REST API USAGE } @@ -48,16 +65,33 @@ repo="${1:-${GIT_ANNEX_BUILD_REPO:-con/git-annex}}" branch="${GIT_ANNEX_BUILD_BRANCH:-master}" events="${GIT_ANNEX_BUILD_EVENTS:-schedule workflow_dispatch}" scan="${GIT_ANNEX_BUILD_SCAN:-30}" +max_age="${GIT_ANNEX_BUILD_MAX_AGE:-3}" +pin="${GIT_ANNEX_RUN_ID:-}" : "${GH_TOKEN:?GH_TOKEN must be set (secrets.GITHUB_TOKEN in a workflow)}" -# `gh run list` has been observed returning stale (expired-artifact) -# runs on the runner's gh version; REST orders newest-first reliably. -runs="$(gh api \ - "repos/$repo/actions/workflows/build-ubuntu.yaml/runs?branch=$branch&per_page=$scan" \ - --jq '.workflow_runs[] | "\(.id) \(.event) \(.conclusion // .status) \(.created_at)"')" +# One line per run: "created_at id event state". ISO-8601 UTC sorts +# lexicographically, so `sort -r` puts the newest first. +fields='"\(.created_at) \(.id) \(.event) \(.conclusion // .status)"' +runs="" +if [ -n "$pin" ]; then + runs="$(gh api "repos/$repo/actions/runs/$pin" --jq "$fields" &2 + events="" +fi +for event in $events; do + page="$(gh api \ + "repos/$repo/actions/workflows/build-ubuntu.yaml/runs?branch=$branch&event=$event&per_page=$scan" \ + --jq ".workflow_runs[] | $fields" \ + &2 + runs+="$page"$'\n' +done +runs="$(sort -r -u <<< "$runs" | grep . || true)" if [ -z "$runs" ]; then - echo "E: no $repo build-ubuntu.yaml runs on $branch" >&2 + echo "E: no $repo build-ubuntu.yaml ${pin:-$events} runs on $branch" >&2 exit 1 fi @@ -66,11 +100,7 @@ fi mapfile -t candidates <<< "$runs" run_id="" for line in "${candidates[@]}"; do - read -r id event state created <<< "$line" - case " $events " in - *" $event "*) ;; - *) continue ;; - esac + read -r created id event state <<< "$line" n="$(gh api "repos/$repo/actions/runs/$id/artifacts" \ --jq '[.artifacts[] | select(.expired==false and (.name | startswith("git-annex-debianstandalone-packages_")))] | length' \ &2 + echo "E: none of the listed $branch runs (${pin:-$events}) has an unexpired debianstandalone artifact" >&2 exit 1 fi +# Not fatal: the build may genuinely have been failing for days. But +# a pick this old is what an incomplete/stale listing looks like too. +age_days=$(( ($(date -u +%s) - $(date -u -d "$created" +%s)) / 86400 )) +if [ -z "$pin" ] && [ "$age_days" -gt "$max_age" ]; then + echo "::warning::picked $repo run $run_id is $age_days days old ($created); newer builds failed or the API listing is stale" >&2 +fi + echo "$run_id" if [ -n "${GITHUB_OUTPUT:-}" ]; then echo "run_id=$run_id" >> "$GITHUB_OUTPUT"