diff --git a/.github/workflows/hourly-mac-build.yml b/.github/workflows/hourly-mac-build.yml index 36e4937971f..7d4ecc44131 100644 --- a/.github/workflows/hourly-mac-build.yml +++ b/.github/workflows/hourly-mac-build.yml @@ -380,7 +380,10 @@ jobs: echo "::warning::Could not discard draft $TAG; remove it manually." - name: Prune old hourly releases - if: steps.freshness.outputs.should_build == 'true' + # Only after a live publish: $TAG is then a non-draft we must not delete, + # and failed runs should not reshuffle retention around a draft that the + # failure path is about to discard. + if: steps.publish_live.outcome == 'success' shell: bash env: GH_TOKEN: ${{ steps.app_token_publish.outputs.token }} @@ -398,17 +401,29 @@ jobs: # 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. + # is the deterministic tie-break. + # + # Why force $TAG to the front before slicing: a hard retain-window seat + # for this run's release. Dropping $TAG from the list *before* the slice + # would permanently keep retain+1 releases; skipping it only in the + # delete loop would under-prune when the sort is still wrong. Partition + # keeps relative order of every other tag. + jq_filter='map(select(.isDraft | not)) | sort_by(.publishedAt // "", .tagName) | reverse' + if [[ -n "${TAG:-}" ]]; then + jq_filter+=" | (map(select(.tagName == \"${TAG//\"/\\\"}\")) + map(select(.tagName != \"${TAG//\"/\\\"}\")))" + fi + jq_filter+=" | .[${HOURLY_RETAIN_COUNT}:] | .[].tagName" 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")" + --jq "$jq_filter")" 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 + # Belt-and-suspenders: partition above should already exclude $TAG. if [[ -n "${TAG:-}" && "$tag" == "$TAG" ]]; then - echo "Skipping just-published $tag" + echo "::warning::Prune list still included just-published $tag after protect; skipping delete." continue fi echo "Pruning $tag"