diff --git a/.claude/settings.json b/.claude/settings.json index d8f7e469..b525aebb 100644 --- a/.claude/settings.json +++ b/.claude/settings.json @@ -9,6 +9,7 @@ "Bash(gh pr status:*)", "Bash(gh pr update-branch:*)", "Bash(gh pr view:*)", + "Bash(gh release list:*)", "Bash(gh search code:*)", "Bash(git diff:*)", "Bash(git fetch:*)", diff --git a/.github/workflows/delete-pr-build-on-close.yml b/.github/workflows/delete-pr-build-on-close.yml deleted file mode 100644 index 0834ad35..00000000 --- a/.github/workflows/delete-pr-build-on-close.yml +++ /dev/null @@ -1,84 +0,0 @@ -name: Delete pre-release when a branch is deleted -# This workflow action deletes pre-releases when a PR is merged or closed. -# -# The CircleCI configuration builds CLI binaries when a PR is opened. -# These are uploaded to the upstream project as GitHub (pre-)releases. -# -# The release tag matches one of the following patterns: -# - v1.2.3-example-branch-placeholder # Branches on upstream -# - v1.2.3-pull-12-head # Branches from forks -# -# A "pull_request_target" event is used to delete pre-releases upstream. -# -# See https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#running-your-pull_request-workflow-when-a-pull-request-merges -# See https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows#pull_request_target -on: - pull_request_target: # zizmor: ignore[dangerous-triggers] - types: - - closed - -jobs: - delete-pre-release: - name: Delete pre-release if exists - runs-on: ubuntu-latest - permissions: - contents: write - steps: - - name: Delete pre-release and tag named after branch - env: - GH_TOKEN: ${{ github.token }} - PR_BRANCH: ${{ github.event.pull_request.head.ref }} - PR_NUMBER: ${{ github.event.pull_request.number }} - PR_REPO: ${{ github.event.pull_request.head.repo.full_name }} - shell: bash - run: | - # Use either an upstream or fork PR branch - if [[ "$PR_REPO" != "slackapi/slack-cli" ]]; then - BRANCH="pull/$PR_NUMBER/head" - else - BRANCH="$PR_BRANCH" - fi - - # Escape tags to create a semantic version - REF=$(echo "${BRANCH}" | sed 's/\//-/g') - RELEASE_FOUND=1 - - # Delete tags matching the pull request branch from forks - if GH_DEBUG=1 gh release --repo="slackapi/slack-cli" delete "${REF}" -y --cleanup-tag; then - echo "Successfully deleted ${REF}" - RELEASE_FOUND=0 - else - echo "Did not find ${REF} tag, trying version prefixes..." - fi - - # Figure out tag name from branch name. This is coupled to the tag name generation that exists in .circleci/config.yml's `create-github-release` job. - RELEASES=$(gh release list --repo="slackapi/slack-cli" --order="desc" --json="tagName" --exclude-drafts --exclude-pre-releases --limit=24 --jq ".[] | .tagName") - for TAGS in $RELEASES; do - TAG_NAME="${TAGS}-${REF}" - echo "Identified pre-release tagname to 🔪: $TAG_NAME" - - # Delete the pre-release - if GH_DEBUG=1 gh release --repo="slackapi/slack-cli" delete "$TAG_NAME" -y --cleanup-tag; then - echo "Successfully deleted $TAG_NAME" - RELEASE_FOUND=0 - else - echo "Failed to find $TAG_NAME, trying next..." - fi - sleep 1 - - FEATURE_TAG_NAME="${TAGS}-${REF}-feature" - echo "Identified feature-release tagname as 🔪: $FEATURE_TAG_NAME" - - # Delete a feature-release - if GH_DEBUG=1 gh release --repo="slackapi/slack-cli" delete "$FEATURE_TAG_NAME" -y --cleanup-tag; then - echo "Successfully deleted $FEATURE_TAG_NAME" - RELEASE_FOUND=0 - else - echo "Failed to find $FEATURE_TAG_NAME, trying next..." - fi - sleep 1 - done - if [ "$RELEASE_FOUND" -ne 0 ]; then - echo "No matching pre-releases tag was found for the branch $TAG_NAME in recent versions" - fi - exit "$RELEASE_FOUND" diff --git a/.github/workflows/delete-stale-pre-releases.yml b/.github/workflows/delete-stale-pre-releases.yml new file mode 100644 index 00000000..b7f57d52 --- /dev/null +++ b/.github/workflows/delete-stale-pre-releases.yml @@ -0,0 +1,84 @@ +name: Delete stale pre-releases +# This workflow deletes pre-releases whose source branch no longer exists. +# +# The CircleCI configuration builds CLI binaries when a PR is opened. +# These are uploaded to the upstream project as GitHub (pre-)releases. +# +# The release tag matches one of the following patterns: +# - v1.2.3-example-branch-placeholder # Branches on upstream +# - v1.2.3-pull-12-head # Branches from forks +# +# Runs on push to main (catches merges) and on a daily schedule +# (catches closed-without-merge PRs and any other missed cleanups). +# +# Only pre-releases are considered. Production releases (v1.2.3) and +# the long-running dev-build pre-release are never touched. +on: + push: + branches: + - main + schedule: + - cron: "0 0 * * *" + workflow_dispatch: + +jobs: + delete-stale-pre-releases: + name: Delete stale pre-releases + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - name: Delete pre-releases for branches that no longer exist + env: + GH_TOKEN: ${{ github.token }} + shell: bash + run: | + REPO="slackapi/slack-cli" + DELETED=0 + + # Gather all pre-release tag names + PRE_RELEASES=$(gh release list --repo="$REPO" --json="tagName,isPrerelease" --limit=100 --jq '.[] | select(.isPrerelease) | .tagName') + + for TAG in $PRE_RELEASES; do + # Skip the long-running dev-build pre-release + if [ "$TAG" = "dev-build" ]; then + continue + fi + + echo "Checking pre-release: $TAG" + + # Extract branch name from the tag. Tags follow the pattern: + # v1.2.3-branch-name (standard PR builds) + # v1.2.3-branch-name-feature (feature builds) + # Strip the leading semver prefix to recover the branch name. + BRANCH=$(echo "$TAG" | sed 's/^v[0-9]*\.[0-9]*\.[0-9]*-//') + + # Feature builds have a "-feature" suffix that is not part of the branch name + BRANCH_WITHOUT_FEATURE=$(echo "$BRANCH" | sed 's/-feature$//') + + # Check if the branch still exists on the remote + BRANCH_EXISTS=false + for CANDIDATE in "$BRANCH" "$BRANCH_WITHOUT_FEATURE"; do + if gh api "repos/$REPO/branches/$CANDIDATE" --silent 2>/dev/null; then + BRANCH_EXISTS=true + break + fi + done + + if [ "$BRANCH_EXISTS" = "true" ]; then + echo " Branch still exists, skipping" + continue + fi + + # Branch is gone — delete the stale pre-release and its tag + echo " Branch no longer exists, deleting pre-release: $TAG" + if gh release --repo="$REPO" delete "$TAG" -y --cleanup-tag; then + echo " Successfully deleted $TAG" + DELETED=$((DELETED + 1)) + else + echo " Failed to delete $TAG" + fi + sleep 1 + done + + echo "Deleted $DELETED stale pre-release(s)"