R2 CI cache cleanup #
The CI cache lives in the Cloudflare R2 bucket pod-ci-cache under two prefixes,
icons/ and rust-cache/. Its size is kept bounded by two independent mechanisms,
because this Cloudflare account rejects Worker Cron Triggers (see
web/telemetry/wrangler.toml), so a Worker cron is never an option here.
1. R2 object lifecycle rule (the backstop) #
lifecycle.json is the S3 lifecycle configuration that expires every rust-cache/
object 7 days after it is written and every icons/ object 30 days after it is
written. This is the hard size backstop: even if the scheduled prune below never
runs, neither prefix can grow without bound.
It is applied out of band (like the bucket creation and the S3 token minting), because rclone cannot set bucket lifecycle. Apply it with any S3 client pointed at the R2 endpoint, for example the AWS CLI:
aws s3api put-bucket-lifecycle-configuration \
--bucket pod-ci-cache \
--lifecycle-configuration "file://$(git rev-parse --show-toplevel)/.github/r2/lifecycle.json" \
--endpoint-url "https://${R2_ACCOUNT_ID}.r2.cloudflarestorage.com"
Or set the same rule from the Cloudflare dashboard under
R2 > pod-ci-cache > Settings > Object lifecycle rules: prefix rust-cache/,
"Delete uploaded objects", 7 days after upload; and prefix icons/, "Delete
uploaded objects", 30 days after upload.
Re-run the command (or re-check the dashboard) whenever lifecycle.json changes;
the rule replaces the bucket's lifecycle configuration wholesale.
2. Scheduled prune job (the trimmer) #
.github/workflows/prune-r2-cache.yml runs scripts/ci/r2-prune daily. It trims
faster than the lifecycle rules by keeping only the newest rust-cache/ object per
group and only the newest 2 icons/<buildNumber>.tar.zst archives (current build
plus one rollback). Its grouping heuristic is unvalidated (see the warning in the
script). Each delete is logged and verified: a wrong-but-successful delete never
breaks CI, but a delete that leaves its object present fails the job instead of
passing silently.