diff --git a/.github/workflows/cleanup-wip-releases.yml b/.github/workflows/cleanup-wip-releases.yml index 29fb219..a5fe36d 100644 --- a/.github/workflows/cleanup-wip-releases.yml +++ b/.github/workflows/cleanup-wip-releases.yml @@ -15,6 +15,11 @@ on: required: false type: boolean default: false + min-age-days: + description: Grace period (days) before a non-tip release is eligible for deletion + required: false + type: number + default: 14 secrets: github-token: description: grants access to list/delete releases and read branch refs @@ -34,30 +39,42 @@ jobs: set -euo pipefail PREFIX="${{ inputs.tag-prefix }}" DRY_RUN="${{ inputs.dry-run }}" + MIN_AGE_DAYS="${{ inputs.min-age-days }}" LIVE_SHAS=$(git ls-remote --heads origin | awk '{print $1}') + NOW_EPOCH=$(date -u +%s) # Hard safety check: never touch a release that isn't marked pre-release, no # matter what its tag looks like. This is a second, independent gate on top of # the tag-prefix filter, not a substitute for it. JQ_FILTER='.[] | select(.isPrerelease == true)' - JQ_FILTER="${JQ_FILTER} | select(.tagName | startswith(\"${PREFIX}\")) | .tagName" - gh release list --limit 1000 --json tagName,isPrerelease -q "${JQ_FILTER}" | - while IFS= read -r tag; do + JQ_FILTER="${JQ_FILTER} | select(.tagName | startswith(\"${PREFIX}\"))" + JQ_FILTER="${JQ_FILTER} | [.tagName, .publishedAt] | @tsv" + gh release list --limit 1000 --json tagName,isPrerelease,publishedAt -q "${JQ_FILTER}" | + while IFS=$'\t' read -r tag published_at; do sha="${tag#"${PREFIX}"}" if echo "${LIVE_SHAS}" | grep -q "^${sha}"; then echo "Keeping ${tag} (${sha} is still a branch tip)" - else - echo "Deleting ${tag} (${sha} is no longer any branch's tip)" - if [[ "${DRY_RUN}" != "true" ]]; then - # Re-check immediately before deleting - never delete anything that isn't - # (still) marked pre-release, even if the listing above was somehow stale. - is_prerelease=$(gh release view "${tag}" --json isPrerelease -q .isPrerelease) - if [[ "${is_prerelease}" != "true" ]]; then - echo "REFUSING to delete ${tag}: not marked pre-release" >&2 - continue - fi - gh release delete "${tag}" --yes --cleanup-tag + continue + fi + + # Not a tip != unused: another repo may still be pinned to this tarball. + # Grace period gives time to notice and repin before deleting it. + age_days=$(( (NOW_EPOCH - $(date -u -d "${published_at}" +%s)) / 86400 )) + if (( age_days < MIN_AGE_DAYS )); then + echo "Keeping ${tag} (not a tip, ${age_days}d old, grace is ${MIN_AGE_DAYS}d)" + continue + fi + + echo "Deleting ${tag} (${sha} is no longer any branch's tip, ${age_days}d old)" + if [[ "${DRY_RUN}" != "true" ]]; then + # Re-check immediately before deleting - never delete anything that isn't + # (still) marked pre-release, even if the listing above was somehow stale. + is_prerelease=$(gh release view "${tag}" --json isPrerelease -q .isPrerelease) + if [[ "${is_prerelease}" != "true" ]]; then + echo "REFUSING to delete ${tag}: not marked pre-release" >&2 + continue fi + gh release delete "${tag}" --yes --cleanup-tag fi done