diff --git a/.github/workflows/cleanup-actions-artifacts.yml b/.github/workflows/cleanup-actions-artifacts.yml new file mode 100644 index 0000000..6f73705 --- /dev/null +++ b/.github/workflows/cleanup-actions-artifacts.yml @@ -0,0 +1,78 @@ +name: cleanup-actions-artifacts + +# Manual reclaim of Actions artifact storage for this repository. +on: + workflow_dispatch: + inputs: + dry_run: + description: List artifacts only; do not delete + type: boolean + default: false + +permissions: + actions: write + contents: read + +jobs: + cleanup: + name: Delete stale Actions artifacts + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Remove old artifacts + env: + GH_TOKEN: ${{ github.token }} + DRY_RUN: ${{ inputs.dry_run }} + REPOSITORY: ${{ github.repository }} + run: | + python3 <<'PY' + import json, os, subprocess, sys + + repo = os.environ["REPOSITORY"] + dry_run = os.environ.get("DRY_RUN", "false").lower() == "true" + + def gh(*args): + return subprocess.check_output(["gh", "api", *args], text=True) + + def should_delete(name: str) -> bool: + if name.startswith("playwright-artifacts-"): + return True + if name.startswith("digest-"): + return True + if name == "desktop-e2e-screenshots": + return True + if name.endswith(".dockerbuild"): + return True + return False + + artifacts = [] + page = 1 + while True: + data = json.loads( + gh(f"repos/{repo}/actions/artifacts?per_page=100&page={page}") + ) + artifacts.extend(data.get("artifacts", [])) + if len(data.get("artifacts", [])) < 100: + break + page += 1 + + deleted = 0 + freed = 0 + for art in artifacts: + name = art["name"] + if not should_delete(name): + print(f"skip id={art['id']} name={name} size={art['size_in_bytes']}") + continue + if dry_run: + print(f"would delete id={art['id']} name={name} size={art['size_in_bytes']}") + continue + subprocess.check_call( + ["gh", "api", "--method", "DELETE", f"repos/{repo}/actions/artifacts/{art['id']}"] + ) + print(f"deleted id={art['id']} name={name} size={art['size_in_bytes']}") + deleted += 1 + freed += art["size_in_bytes"] + + print(f"deleted_count={deleted}") + print(f"freed_bytes={freed}") + PY