Clean up #148024
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| on: | |
| workflow_dispatch: | |
| inputs: | |
| delete_draft_releases: | |
| type: boolean | |
| description: Delete all draft releases on GitHub. This will interfere with any active publishing workflow. | |
| required: false | |
| default: false | |
| delete_temp_branches: | |
| type: boolean | |
| description: Delete temporary branches created for publishing. This will interfere with any active publishing workflow. | |
| required: false | |
| default: false | |
| schedule: | |
| - cron: "*/5 * * * *" | |
| - cron: "0 0 * * *" | |
| pull_request_target: | |
| types: | |
| - closed | |
| name: Clean up | |
| jobs: | |
| bot_branches: | |
| name: Delete temporary bot/* branches | |
| # Manual dispatch deletes all bot/* branches; the daily schedule deletes only stale ones. | |
| if: >- | |
| (github.event_name == 'workflow_dispatch' && inputs.delete_temp_branches) || | |
| github.event.schedule == '0 0 * * *' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - run: | | |
| retry() { for n in 1 2 3; do "$@" && return 0; [ $n -lt 3 ] && sleep $((15*n*n)); done; return 1; } | |
| # On the schedule, only delete branches older than the cutoff so an active publish is not disrupted. | |
| cutoff="" | |
| if [ "${{ github.event_name }}" == "schedule" ]; then | |
| cutoff=$(date -u -d '1 day ago' +%s) | |
| fi | |
| branches=`retry gh api --paginate \ | |
| -H "Accept: application/vnd.github+json" \ | |
| -H "X-GitHub-Api-Version: 2022-11-28" \ | |
| /repos/${{ github.repository }}/branches \ | |
| --jq '.[] | select(.name | test("^bot/[0-9]+")) | "\(.name) \(.commit.sha)"'` | |
| echo "$branches" | while read -r branch sha; do | |
| [ -n "$branch" ] || continue | |
| if [ -n "$cutoff" ]; then | |
| commit_date=`retry gh api \ | |
| -H "Accept: application/vnd.github+json" \ | |
| -H "X-GitHub-Api-Version: 2022-11-28" \ | |
| /repos/${{ github.repository }}/commits/$sha \ | |
| --jq '.commit.committer.date'` | |
| commit_ts=$(date -u -d "$commit_date" +%s) | |
| if [ "$commit_ts" -ge "$cutoff" ]; then | |
| echo "Skipping $branch (tip commit updated within the last day)." | |
| continue | |
| fi | |
| fi | |
| echo "Deleting $branch." | |
| retry gh api \ | |
| --method DELETE \ | |
| -H "Accept: application/vnd.github+json" \ | |
| -H "X-GitHub-Api-Version: 2022-11-28" \ | |
| /repos/${{ github.repository }}/git/refs/heads/$branch | |
| done | |
| env: | |
| GH_TOKEN: ${{ secrets.REPO_BOT_ACCESS_TOKEN || github.token }} | |
| draft_releases: | |
| name: Delete draft release | |
| if: github.event_name == 'workflow_dispatch' && inputs.delete_draft_releases | |
| runs-on: ubuntu-latest | |
| steps: | |
| - run: | | |
| gh release list -L 1000 -R ${{ github.repository }} > rels.txt | |
| while read rel _; do | |
| isDraft=`gh release view $rel -R ${{ github.repository }} --json isDraft --jq '.isDraft'` | |
| isPrerelease=`gh release view $rel -R ${{ github.repository }} --json isPrerelease --jq '.isPrerelease'` | |
| if $isDraft && $isPrerelease; then | |
| echo "Deleting release $rel." | |
| gh release delete $rel -R ${{ github.repository }} -y | |
| else | |
| echo "Skipping release $rel." | |
| fi | |
| done < rels.txt | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| ghcr_images: | |
| name: Clean up GHCR images | |
| runs-on: ubuntu-latest | |
| strategy: | |
| matrix: | |
| image_name: [cuda-quantum, cuda-quantum-assets, cuda-quantum-dev, cuda-quantum-devdeps, open-mpi, cuda-quantum-macos-devdeps, cuda-quantum-macos-devdeps-ci, cuda-quantum-macos-ccache, cuda-quantum-linux-ccache] | |
| fail-fast: false | |
| steps: | |
| # We need to keep a good number of untagged manifests, | |
| # since each tagged image contains/depends on untagged components | |
| - name: Delete untagged images | |
| uses: actions/delete-package-versions@v5 | |
| with: | |
| package-name: ${{ matrix.image_name }} | |
| package-type: 'container' | |
| min-versions-to-keep: 400 | |
| delete-only-untagged-versions: 'true' | |
| - name: Log in to the container registry | |
| uses: docker/login-action@v4 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ github.token }} | |
| - name: Find matching signatures files | |
| id: sig_files | |
| run: | | |
| retry() { for n in 1 2 3; do "$@" && return 0; [ $n -lt 3 ] && sleep $((15*n*n)); done; return 1; } | |
| retry gh api -X GET --paginate -H "Accept: application/vnd.github+json" -H "X-GitHub-Api-Version: 2022-11-28" \ | |
| /orgs/${{ github.repository_owner }}/packages/container/${{ matrix.image_name }}/versions >> packages.json | |
| sig_tags=`cat packages.json | jq -r ".[] | select(.metadata.package_type==\"container\").metadata.container.tags[]" | (egrep -o '^sha256-\S+\.sig$' || true)` | |
| nr_sigs=100 # limit how much we delete at a time to avoid exceeding the service rate limit | |
| for sig in $sig_tags; do | |
| if [ $nr_sigs -lt 1 ]; then continue; fi | |
| matching_image=`cat packages.json | jq -r ".[] | select(.name==\"sha256:${sig:7:-4}\").id"` | |
| if [ -z "$matching_image" ]; then | |
| echo "Marking signature $sig for deletion." | |
| delete+=", $sig" | |
| nr_sigs=$(($nr_sigs-1)) | |
| fi | |
| done | |
| echo "tags_to_remove=${delete:2}" >> $GITHUB_OUTPUT | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| - name: Look up version numbers | |
| id: packages | |
| run: | | |
| for tag in ${{ steps.sig_files.outputs.tags_to_remove }}; do | |
| echo "Finding version id for tag ${tag%,}." | |
| version_id=`cat packages.json | jq ".[] | select(.metadata.package_type==\"container\" and (.metadata.container.tags[] | contains(\"${tag%,}\")))" | jq '.id'` | |
| if [ -n "$version_id" ]; then | |
| echo "Marking version $version_id for deletion." | |
| delete+=", $version_id" | |
| fi | |
| done | |
| echo "[${delete:2}]" | |
| echo "versions_to_remove=${delete:2}" >> $GITHUB_OUTPUT | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| - name: Delete matching signatures | |
| if: steps.packages.outputs.versions_to_remove | |
| uses: actions/delete-package-versions@v5 | |
| with: | |
| package-name: ${{ matrix.image_name }} | |
| package-type: 'container' | |
| package-version-ids: '${{ steps.packages.outputs.versions_to_remove }}' | |
| # We use environments to deploy to a public registry after PRs are merged. | |
| # Since we use the same workflows during CI, a default environment that defines | |
| # the necessary variables is used instead. Unfortunately, this automatically | |
| # also creates an (unwanted) deployment, which we delete with this job. | |
| # The ghcr-ci environment similarly produces unwanted deployment entries | |
| # from the dev_environment workflow during CI runs on pull requests. | |
| # See also https://github.com/actions/runner/issues/2120 | |
| deployments: | |
| name: Deployments | |
| runs-on: ubuntu-latest | |
| permissions: | |
| deployments: write | |
| steps: | |
| - uses: actions/github-script@v9 | |
| with: | |
| script: | | |
| for (const environment of ['default', 'ghcr-ci']) { | |
| const deployments = await github.rest.repos.listDeployments({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| environment: environment | |
| }); | |
| await Promise.all( | |
| deployments.data.map(async (deployment) => { | |
| await github.rest.repos.createDeploymentStatus({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| deployment_id: deployment.id, | |
| state: 'inactive' | |
| }); | |
| return github.rest.repos.deleteDeployment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| deployment_id: deployment.id | |
| }); | |
| }) | |
| ); | |
| } | |
| pr_cleanup: | |
| name: Clean up documentation previews | |
| if: github.event_name == 'pull_request_target' || github.event.schedule == '0 0 * * *' | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: write | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v6 | |
| with: | |
| ref: ${{ vars.preview_branch }} | |
| fetch-depth: 0 | |
| - name: Delete preview folder for closed PR | |
| if: github.event_name == 'pull_request_target' | |
| run: | | |
| git config --global user.name "cuda-quantum-bot" | |
| git config --global user.email "cuda-quantum-bot@users.noreply.github.com" | |
| git rm -r "pr-${{ github.event.pull_request.number }}" --ignore-unmatch | |
| git commit --allow-empty -m "Cleaning up docs preview for PR #${{ github.event.pull_request.number }}." | |
| git config pull.rebase true | |
| git pull --no-edit && git push | |
| - name: Delete preview folder for stale PRs (open > 5 days) | |
| if: github.event_name == 'schedule' | |
| env: | |
| GH_TOKEN: ${{ secrets.REPO_BOT_ACCESS_TOKEN || github.token }} | |
| run: | | |
| set -euo pipefail | |
| git config --global user.name "cuda-quantum-bot" | |
| git config --global user.email "cuda-quantum-bot@users.noreply.github.com" | |
| git config pull.rebase true | |
| # Keep preview branch up-to-date | |
| git pull --no-edit | |
| # 5 days ago timestamp | |
| cutoff=$(date -u -d '5 days ago' +%s) | |
| # Go over existing pr-* folders and remove any whose last | |
| # git commit is older than the cutoff date. | |
| for folder in pr-*/; do | |
| [ -d "$folder" ] || continue | |
| last_commit=$(git log --format="%at" -1 -- "$folder") | |
| if [ -z "$last_commit" ] || [ "$last_commit" -lt "$cutoff" ]; then | |
| echo "Removing stale preview folder $folder (last updated: ${last_commit:-never})." | |
| git rm -r "$folder" --ignore-unmatch || true | |
| fi | |
| done | |
| # If nothing matches, skip commit/push | |
| if git diff --cached --quiet; then | |
| echo "No preview folders were removed (nothing to commit)." | |
| exit 0 | |
| fi | |
| git commit -m "Cleaning up stale docs previews (not updated in > 5 days)." | |
| git push | |
| pr_ccache_cleanup: | |
| name: Clean up PR ccache artifacts | |
| if: github.event_name == 'pull_request_target' | |
| runs-on: ubuntu-latest | |
| permissions: | |
| packages: write | |
| steps: | |
| - run: | | |
| retry() { for n in 1 2 3; do "$@" && return 0; [ $n -lt 3 ] && sleep $((15*n*n)); done; return 1; } | |
| pr_number=${{ github.event.pull_request.number }} | |
| repo_owner=${{ github.repository_owner }} | |
| for package in cuda-quantum-macos-ccache cuda-quantum-linux-ccache; do | |
| echo "Cleaning up $package for PR #${pr_number}..." | |
| versions=`retry gh api --paginate \ | |
| -H "Accept: application/vnd.github+json" \ | |
| -H "X-GitHub-Api-Version: 2022-11-28" \ | |
| /orgs/${repo_owner}/packages/container/${package}/versions 2>/dev/null || echo "[]"` | |
| echo "$versions" | jq -r ".[] | select(.metadata.container.tags[] | test(\"pr-${pr_number}$\")).id" | while read version_id; do | |
| if [ -n "$version_id" ]; then | |
| echo "Deleting version $version_id from $package" | |
| retry gh api --method DELETE \ | |
| -H "Accept: application/vnd.github+json" \ | |
| -H "X-GitHub-Api-Version: 2022-11-28" \ | |
| /orgs/${repo_owner}/packages/container/${package}/versions/${version_id} 2>/dev/null || true | |
| fi | |
| done | |
| done | |
| env: | |
| GH_TOKEN: ${{ github.token }} |