Repository navigation
ci: delete a PR's caches when it closes - #206
Conversation
Caches saved by a PR run are scoped to refs/pull/<N>/merge and can't be read by any other ref, so after the PR closes they only take quota. The repo sits at ~10.1 GB of the 10 GB limit; ~4.3 GB of it belongs to twelve closed PRs. Over the limit GitHub evicts least-recently-used entries, which can include master's ccache that PRs fall back to (#202). On pull_request_target closed, delete every cache for that PR's merge ref. workflow_dispatch sweeps all closed PRs that still own caches (the current backlog); open PRs are kept. The job never checks out PR code.
|
Measured the current state of the quota this workflow is about, so the effect can be checked after merge (2026-10-30 06:2x UTC,
Two notes on the numbers in the commit message: the "~4.3 GB in twelve closed PRs" figure has since shrunk to 2.31 GiB in eight — LRU already evicted some of them, which is exactly the churn this workflow stops. And the single largest holder is not a closed PR at all: #205 alone owns 2.84 GiB in 77 entries (per- I also verified the exact call the workflow makes, unencoded slashes included: One correctness gap worth closing before merge: an API failure currently reads as "nothing to delete". Both branches swallow it: # manual sweep
for n in $(gh api --paginate "repos/$repo/actions/caches?per_page=100" --jq …); do— if that call fails (rate limit, token scope), the list is empty, the loop body never runs, and the job prints Nothing else looked off: Merge order matters for the payoff: landing this before #205/#203 closes means their 3.5 GiB is reclaimed automatically at merge time instead of waiting for LRU. |
|
Measured the current state before this merges (2026-10-30, 06:24Z; 514 cache entries, 10.38 GiB total — still pinned at the cap): Reclaimable backlog is exactly PRs #196–#202 — all seven are already merged/closed: 2.01 GiB in 122 entries (#196–#201 ~253 MiB each, #202 535 MiB). That is what a Eviction is already active, not theoretical: between two measurements 4 minutes apart master lost 26 entries (3.94 -> 3.69 GiB) while 17 new entries appeared from in-flight PR builds. Older entries survive only while they keep being restored (oldest master entry dates from 15.10). It has not hurt builds yet — after #202, ccache hit rates from |
Review on #206: a failed "gh api" call read as "nothing to delete". With a rate limit or a token without actions:write the cache list came back empty, the loop never ran, and the job printed "Done, 0 failure(s)" in green, which would keep happening for as long as the token stayed broken. - listing caches, a PR's caches, or a PR's state now fails the job (::error:: + exit 1) instead of being taken as an empty result; - a delete that returns HTTP 404 is "already evicted" and still fine; any other delete error is counted and fails the job at the end; - the summary line reports deleted vs failed.
|
Thanks, both points taken. Fixed in
I checked it against the real API with
Agreed on merge order: landing this before #203/#205 lets their caches go at close time. I've updated the numbers in the description to your measurement. One correction on the #202 figure, since it matters for #208: the "brand-new PR 2195/2589 = 84.8%" is almost exactly master's production build (2196/2589). ccache keeps its counters inside the cache directory, so |
On1x
left a comment
There was a problem hiding this comment.
Re-reviewed the final head 50a02024. The failure handling now matches what I asked: a failed cache list or PR-state read fails the job, a 404 on delete counts as already evicted, any other delete error fails at the end. The workflow never checks out or runs PR code (pull_request_target is used only for the token), so the write permission is limited to the cache API. The 2.01 GiB backlog measurement in the thread gives us a before/after check after merge.
Caches saved by a PR run (ccache, BuildKit) are scoped to
refs/pull/<N>/merge: no other branch or PR can restore them, so once the PR closes they only take quota.Why now: the repo sits at the 10 GB cache limit (10.38 GiB on Oct 30 06:24 UTC, per On1x's measurement below). 2.01 GiB in 122 entries belongs to seven already-closed PRs (#196–#202); when I opened this it was ~4.3 GB in twelve, and LRU has evicted the rest since. Over the limit GitHub evicts least-recently-used entries, and that can include master's ccache, which every PR now falls back to after #202. Losing it would put PR builds back to cold.
What it does (
.github/workflows/cache-cleanup-closed-pr.yml):pull_request_target: closed: deletes every cache for that PR's merge ref (merged or just closed).workflow_dispatch: a manual sweep of every PR ref that still owns caches; only PRs whose state isclosedare deleted, open ones are kept. Run it once after merge to clear the current backlog.pull_request_targetis used so the token can delete caches for PRs from forks too. It is safe here: the job never checks out or runs PR code, it only calls the cache API with the PR number from the event payload. Permissions areactions: writeandpull-requests: read.Checked: I ran the step's script against this repo through the real API with the
DELETEcall stubbed out:Failure handling (second commit, after review): any failed list call fails the job; a delete that returns 404 counts as already evicted; any other delete error fails the job at the end.
Nothing was deleted.
bash -nand the YAML parse are clean.Doesn't touch master caches or
cache-reset.yml.