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"