Skip to content

homebrew: publish and verify installable Kandelo bottles #424

homebrew: publish and verify installable Kandelo bottles

homebrew: publish and verify installable Kandelo bottles #424

Workflow file for this run

name: Staging cleanup
# Deletes pr-<NNN>-staging pre-releases when the corresponding PR closes
# and deletes same-repo branches for merged `ready-to-ship` PRs. The daily
# sweep classifies every merge-candidate release from current GitHub state.
# Only authoritative ready candidates and recent terminal-rejection evidence
# survive after merge; API uncertainty always retains evidence.
#
# See docs/plans/2026-04-29-pr-package-builds-design.md §4.3.
on:
pull_request:
types: [closed]
schedule:
- cron: '0 8 * * *' # daily at 08:00 UTC
workflow_dispatch: # manual sweep
jobs:
cleanup-on-close:
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
permissions:
contents: write
env:
GH_TOKEN: ${{ github.token }}
steps:
- name: Delete pr-${{ github.event.pull_request.number }}-staging
run: |
TAG="pr-${{ github.event.pull_request.number }}-staging"
if gh release view "$TAG" --repo ${{ github.repository }} >/dev/null 2>&1; then
gh release delete "$TAG" --repo ${{ github.repository }} --yes --cleanup-tag
echo "Deleted $TAG"
else
echo "$TAG does not exist; nothing to clean up"
fi
- name: Delete merged ready-to-ship PR branch
if: |
github.event.pull_request.merged == true &&
github.event.pull_request.head.repo.full_name == github.repository &&
contains(github.event.pull_request.labels.*.name, 'ready-to-ship')
env:
DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
HEAD_REF: ${{ github.event.pull_request.head.ref }}
PR_HEAD_SHA: ${{ github.event.pull_request.head.sha }}
PR_NUMBER: ${{ github.event.pull_request.number }}
run: |
if [ -z "$HEAD_REF" ]; then
echo "::warning::PR #$PR_NUMBER has no head ref; skipping branch cleanup"
exit 0
fi
if [ -z "$PR_HEAD_SHA" ]; then
echo "::warning::PR #$PR_NUMBER has no head SHA; skipping branch cleanup"
exit 0
fi
if [ "$HEAD_REF" = "$DEFAULT_BRANCH" ]; then
echo "::warning::Refusing to delete default branch $HEAD_REF for PR #$PR_NUMBER"
exit 0
fi
if ! git check-ref-format --branch "$HEAD_REF" >/dev/null 2>&1; then
echo "::warning::Refusing to delete invalid branch ref $HEAD_REF for PR #$PR_NUMBER"
exit 0
fi
git init --quiet "$RUNNER_TEMP/branch-cleanup"
cd "$RUNNER_TEMP/branch-cleanup"
REMOTE="https://x-access-token:${GH_TOKEN}@github.com/${{ github.repository }}.git"
remote_sha=$(git ls-remote "$REMOTE" "refs/heads/$HEAD_REF" | awk '{print $1}')
if [ -n "$remote_sha" ]; then
if [ "$remote_sha" != "$PR_HEAD_SHA" ]; then
echo "::warning::Refusing to delete branch $HEAD_REF for PR #$PR_NUMBER because it now points at $remote_sha, not merged head $PR_HEAD_SHA"
exit 0
fi
if git push "$REMOTE" ":refs/heads/$HEAD_REF"; then
echo "Deleted merged PR #$PR_NUMBER branch $HEAD_REF"
else
echo "::warning::Failed to delete merged PR #$PR_NUMBER branch $HEAD_REF"
fi
else
echo "Merged PR #$PR_NUMBER branch $HEAD_REF is already absent"
fi
sweep:
if: |
github.event_name == 'schedule' ||
(github.event_name == 'workflow_dispatch' &&
github.ref_name == github.event.repository.default_branch)
runs-on: ubuntu-latest
permissions:
actions: read
contents: write
pull-requests: read
statuses: read
env:
GH_TOKEN: ${{ github.token }}
steps:
- uses: actions/checkout@v7.0.0
with:
ref: ${{ github.event.repository.default_branch }}
sparse-checkout: |
.github/scripts/cleanup-merge-candidates.sh
.github/scripts/github-api-get.sh
.github/scripts/latest-merge-gate-status.sh
.github/scripts/state-lock.sh
sparse-checkout-cone-mode: false
- name: Sweep orphan pr-*-staging tags
run: |
# Up to 200 most recent pre-releases; pr-<N>-staging is the
# only naming pattern we own, so we filter on it directly.
gh release list --repo ${{ github.repository }} --limit 200 \
--json tagName,isPrerelease \
-q '.[] | select(.isPrerelease and (.tagName | startswith("pr-")) and (.tagName | endswith("-staging"))) | .tagName' \
| while read -r TAG; do
# Extract PR number from pr-<N>-staging.
PR=${TAG#pr-}
PR=${PR%-staging}
if [ -z "$PR" ] || ! [[ "$PR" =~ ^[0-9]+$ ]]; then
echo "WARN: cannot parse PR number from $TAG, skipping"
continue
fi
STATE=$(gh pr view "$PR" --repo ${{ github.repository }} --json state -q .state 2>/dev/null || echo "MISSING")
if [ "$STATE" != "OPEN" ]; then
echo "Deleting orphan $TAG (PR state: $STATE)"
gh release delete "$TAG" --repo ${{ github.repository }} --yes --cleanup-tag
else
echo "Keeping $TAG (PR is OPEN)"
fi
done
- name: Sweep completed merge-candidate releases
run: |
bash .github/scripts/cleanup-merge-candidates.sh \
--max-pages 50 \
--per-page 100 \
--max-asset-pages 50 \
--asset-per-page 100 \
--rejected-retention-days 14
- name: Sweep merged ready-to-ship PR branches
env:
DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
run: |
git init --quiet "$RUNNER_TEMP/branch-cleanup"
cd "$RUNNER_TEMP/branch-cleanup"
REMOTE="https://x-access-token:${GH_TOKEN}@github.com/${{ github.repository }}.git"
delete_branch() {
local pr_number="$1"
local head_ref="$2"
local expected_sha="$3"
if [ -z "$head_ref" ]; then
echo "::warning::PR #$pr_number has no head ref; skipping branch cleanup"
return 0
fi
if [ -z "$expected_sha" ]; then
echo "::warning::PR #$pr_number has no head SHA; skipping branch cleanup"
return 0
fi
if [ "$head_ref" = "$DEFAULT_BRANCH" ]; then
echo "::warning::Refusing to delete default branch $head_ref for PR #$pr_number"
return 0
fi
if ! git check-ref-format --branch "$head_ref" >/dev/null 2>&1; then
echo "::warning::Refusing to delete invalid branch ref $head_ref for PR #$pr_number"
return 0
fi
remote_sha=$(git ls-remote "$REMOTE" "refs/heads/$head_ref" | awk '{print $1}')
if [ -n "$remote_sha" ]; then
if [ "$remote_sha" != "$expected_sha" ]; then
echo "::warning::Refusing to delete branch $head_ref for PR #$pr_number because it now points at $remote_sha, not merged head $expected_sha"
return 0
fi
if git push "$REMOTE" ":refs/heads/$head_ref"; then
echo "Deleted merged PR #$pr_number branch $head_ref"
else
echo "::warning::Failed to delete merged PR #$pr_number branch $head_ref"
fi
else
echo "Merged PR #$pr_number branch $head_ref is already absent"
fi
}
gh pr list \
--repo ${{ github.repository }} \
--state merged \
--base "$DEFAULT_BRANCH" \
--label ready-to-ship \
--limit 200 \
--json number,headRefName,headRefOid,isCrossRepository \
-q '.[] | select(.isCrossRepository == false) | [.number, .headRefName, .headRefOid] | @tsv' \
| while IFS=$'\t' read -r pr_number head_ref head_sha; do
delete_branch "$pr_number" "$head_ref" "$head_sha"
done