diff --git a/.github/workflows/cleanup_caches.yml b/.github/workflows/cleanup_caches.yml index 1b324ddf5a4dc..48b0985f13abb 100644 --- a/.github/workflows/cleanup_caches.yml +++ b/.github/workflows/cleanup_caches.yml @@ -6,7 +6,7 @@ on: types: [completed] concurrency: - group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + group: ${{ github.workflow }} cancel-in-progress: true permissions: @@ -17,32 +17,50 @@ jobs: runs-on: ubuntu-latest steps: - run: | - gh cache list -L 100 --json id,key,ref -S last_accessed_at -O desc --jq ' - map(select(.key | startswith("x86_64-w64-mingw32-") or - startswith("i686-w64-mingw32-") or - startswith("aarch64-pc-windows-msvc-") or - startswith("x86_64-pc-windows-msvc-"))) | - group_by(.ref) | - map({ - ref: .[0].ref, - caches: map({ - key: .key, - prefix: (.key | capture("^(?[\\w_-]+-)\\d+$").prefix) - }) | group_by(.prefix) | map({keys: map(.key)}) - }) | - .[] - ' | - while read -r group; do - pr=$(echo "$group" | jq -r '.ref | capture("refs/pull/(?[0-9]+)/merge").num') - if [[ -n "$pr" ]] && [ "$(gh pr view $pr --json state --jq '.state')" != "OPEN" ]; then - keys=$(echo "$group" | jq -c '.caches | map(.keys) | .[]') - else - keys=$(echo "$group" | jq -c '.caches | map(.keys[1:]) | .[]') - fi - for key in $(echo "$keys" | jq -r '.[]'); do - gh cache delete "$key" - done + set -o pipefail + gh cache list -L 1000 --json id,key,ref -S last_accessed_at -O desc > caches.json + + # Resolve the state of every pull request that owns a cache. + for pr in $(jq -r '.[].ref | select(test("^refs/pull/[0-9]+/merge$")) | + split("/")[2]' caches.json | sort -u); do + jq -n --arg pr "$pr" --arg state "$(gh pr view $pr --json state --jq .state)" \ + '{($pr): $state}' + done | jq -s 'add // {}' > prs.json + + jq -r --slurpfile prs prs.json --arg default "$DEFAULT_REF" ' + $prs[0] as $state | + + # Group caches by kind, dropping the timestamp or content hash + # that every save appends to the key. + def family: if startswith("msys2-") then "msys2" + elif test("-[0-9]+$") then "ccache" + else "other" end; + def group: if family == "msys2" then sub("-(files|state):.*$"; "") + else sub("-[0-9]+$"; "") end; + + [ .[] | . + { family: (.key | family), + group: (.key | group), + pr: (.ref | if test("^refs/pull/[0-9]+/merge$") + then split("/")[2] else null end) } ] + | map(select(.family != "other")) as $all + + # Caches of merged or closed pull requests, and msys2 caches + # outside the default branch, which every ref restores from. + | ( $all | map(select( + (.pr != null and $state[.pr] != "OPEN") or + (.family == "msys2" and .ref != $default) + )) ) as $dead + + # Of the rest only the newest cache of each kind per ref is used. + | ( ($all - $dead) | group_by([.ref, .group]) | map(.[1:]) | add // [] ) as $superseded + + | ($dead + $superseded)[] | "\(.id)\t\(.ref)\t\(.key)" + ' caches.json | + while IFS=$'\t' read -r id ref key; do + echo "deleting $ref $key" + gh cache delete "$id" done env: GH_REPO: ${{ github.repository }} GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + DEFAULT_REF: refs/heads/${{ github.event.repository.default_branch }}