diff --git a/.github/workflows/build_all.yml b/.github/workflows/build_all.yml index ae5231e784..10edec5aaa 100644 --- a/.github/workflows/build_all.yml +++ b/.github/workflows/build_all.yml @@ -313,6 +313,7 @@ jobs: echo "CCACHE_ENTRY=ccache-$leg-${{ github.run_id }}-${{ github.run_attempt }}" >> "$GITHUB_ENV" shell: bash - name: Restore compiler cache + id: ccache_restore uses: actions/cache/restore@v6 with: path: .flatpak-builder/ccache @@ -371,24 +372,31 @@ jobs: save-cache: false arch: ${{ matrix.variant.arch }} upload-artifact: false + # The build has just touched everything it can use, so an object untouched + # for a week is dead, usually orphaned by a flag change. - name: Compiler cache statistics if: always() run: | export CCACHE_DIR=$PWD/.flatpak-builder/ccache + ccache --evict-older-than 7d ccache -s -v || ccache -s shell: bash # Save the new entry first, then drop the older ones for this leg on this - # ref, so a failed save leaves the previous entry in place. + # ref, so a failed save leaves the previous entry in place. A cancelled or + # failed build saves too, since what it compiled is still valid; a restore + # that did not finish does not, since the directory may be a truncated copy. - name: Save compiler cache id: ccache_save - if: github.event_name != 'pull_request' + if: ${{ always() && steps.ccache_restore.outcome == 'success' && github.event_name != 'pull_request' }} uses: actions/cache/save@v6 with: path: .flatpak-builder/ccache key: ${{ env.CCACHE_ENTRY }} - name: Drop older compiler cache entries - if: ${{ steps.ccache_save.outcome == 'success' }} + if: ${{ always() && steps.ccache_save.outcome == 'success' }} # The container has no gh, so this is the list and delete over the REST API. + # Older means a lower run id, so two runs finishing close together keep + # the newer entry whichever of them cleans up last. continue-on-error: true env: GH_TOKEN: ${{ github.token }} @@ -396,7 +404,8 @@ jobs: api="$GITHUB_API_URL/repos/$GITHUB_REPOSITORY/actions/caches" curl -sSf -H "Authorization: Bearer $GH_TOKEN" \ "$api?ref=$GITHUB_REF&key=ccache-$CCACHE_LEG-&per_page=100" \ - | jq -r --arg keep "$CCACHE_ENTRY" '.actions_caches[] | select(.key != $keep) | .id' \ + | jq -r --arg prefix "ccache-$CCACHE_LEG-" --argjson run "$GITHUB_RUN_ID" \ + '.actions_caches[] | select((.key | ltrimstr($prefix) | split("-")[0] | tonumber?) < $run) | .id' \ | while read -r id; do curl -sSf -X DELETE -H "Authorization: Bearer $GH_TOKEN" "$api/$id" done diff --git a/.github/workflows/build_orca.yml b/.github/workflows/build_orca.yml index 4b53767d4f..95ec52a65d 100644 --- a/.github/workflows/build_orca.yml +++ b/.github/workflows/build_orca.yml @@ -106,6 +106,7 @@ jobs: job-summary: '' - name: Restore compiler cache + id: ccache_restore if: ${{ steps.ccache.outcome == 'success' }} uses: actions/cache/restore@v6 with: @@ -724,31 +725,40 @@ jobs: asset_content_type: application/octet-stream max_releases: 1 + # The build has just touched everything it can use, so an object + # untouched for a week is dead, usually orphaned by a flag change. - name: Compiler cache statistics if: ${{ always() && steps.ccache.outcome == 'success' }} shell: bash - run: ccache -s -v || ccache -s + run: | + ccache --evict-older-than 7d + ccache -s -v || ccache -s # Entries are immutable, so the new one is saved first and the older # ones for this leg on this ref are dropped afterwards: a failed save - # leaves the previous entry in place. + # leaves the previous entry in place. A cancelled or failed build saves + # too, since what it compiled is still valid; a restore that did not + # finish does not, since the directory may be a truncated copy. - name: Save compiler cache id: ccache_save - if: ${{ steps.ccache.outcome == 'success' && github.event_name != 'pull_request' }} + if: ${{ always() && steps.ccache_restore.outcome == 'success' && github.event_name != 'pull_request' }} uses: actions/cache/save@v6 with: path: ${{ github.workspace }}/.ccache key: ${{ env.CCACHE_ENTRY }} - name: Drop older compiler cache entries - if: ${{ steps.ccache_save.outcome == 'success' }} + if: ${{ always() && steps.ccache_save.outcome == 'success' }} # A read-only token (fork PRs) cannot delete; that only costs storage. + # Older means a lower run id, so two runs finishing close together keep + # the newer entry whichever of them cleans up last. continue-on-error: true shell: bash env: GH_TOKEN: ${{ github.token }} run: | gh cache list --ref "$GITHUB_REF" --key "ccache-$CCACHE_LEG-" --limit 100 --json id,key \ - | jq -r --arg keep "$CCACHE_ENTRY" '.[] | select(.key != $keep) | .id' \ + | jq -r --arg prefix "ccache-$CCACHE_LEG-" --argjson run "$GITHUB_RUN_ID" \ + '.[] | select((.key | ltrimstr($prefix) | split("-")[0] | tonumber?) < $run) | .id' \ | tr -d '\r' \ | while read -r id; do gh cache delete "$id"; done