From 5c014cc04cea0112c28355edfd6f47e68add06ec Mon Sep 17 00:00:00 2001 From: Jinjing <6427696+AmethystLiang@users.noreply.github.com> Date: Sat, 8 Aug 2026 14:21:33 -0700 Subject: [PATCH] fix(ci): stop hourly prune from deleting the just-published release Hourly prune sorted non-draft releases by createdAt, but nearly every orca-hourly release shares one createdAt from bulk import. At the retain cap, stable sort + reverse put the newest release past the window and immediately deleted it with --cleanup-tag. Sort by publishedAt (tagName as tie-break) and hard-skip the tag this run just published so prune cannot self-delete. --- .github/workflows/hourly-mac-build.yml | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/.github/workflows/hourly-mac-build.yml b/.github/workflows/hourly-mac-build.yml index b8ec40a0058..36e4937971f 100644 --- a/.github/workflows/hourly-mac-build.yml +++ b/.github/workflows/hourly-mac-build.yml @@ -384,20 +384,33 @@ jobs: shell: bash env: GH_TOKEN: ${{ steps.app_token_publish.outputs.token }} + # Protect the tag this run just shipped; at the retain cap, a bad sort + # can otherwise mark the newest release as stale and delete it. + TAG: ${{ steps.release.outputs.tag }} run: | set -euo pipefail # Why: --cleanup-tag so pruning does not leave orphan tags behind that # keep showing up in tag lists with no release or assets attached. # Drafts are excluded so retention counts shipped builds only; a stale # draft is handled by the failure path, not by the retention window. - stale="$(gh release list --repo "$HOURLY_REPO" --limit 200 --json tagName,createdAt,isDraft \ - --jq "map(select(.isDraft | not)) | sort_by(.createdAt) | reverse | .[${HOURLY_RETAIN_COUNT}:] | .[].tagName")" + # + # Sort by publishedAt (not createdAt). Many non-draft hourlies share one + # createdAt (bulk import / re-create), so createdAt ranking is unstable: + # reverse of a stable sort then drops the just-published tag past the + # retain window. publishedAt is real recency; tagName (...YYYYMMDDHHMM) + # is the deterministic tie-break. Never prune $TAG from this run. + stale="$(gh release list --repo "$HOURLY_REPO" --limit 200 --json tagName,publishedAt,isDraft \ + --jq "map(select(.isDraft | not)) | sort_by(.publishedAt // \"\", .tagName) | reverse | .[${HOURLY_RETAIN_COUNT}:] | .[].tagName")" if [[ -z "$stale" ]]; then echo "Nothing to prune; at or under $HOURLY_RETAIN_COUNT retained builds." exit 0 fi while read -r tag; do [[ -n "$tag" ]] || continue + if [[ -n "${TAG:-}" && "$tag" == "$TAG" ]]; then + echo "Skipping just-published $tag" + continue + fi echo "Pruning $tag" gh release delete "$tag" --repo "$HOURLY_REPO" --yes --cleanup-tag || \ echo "::warning::Could not prune $tag"