diff --git a/.github/actions/go-cache/action.yml b/.github/actions/go-cache/action.yml index 9c7aacd7762..7cd1fd3e34d 100644 --- a/.github/actions/go-cache/action.yml +++ b/.github/actions/go-cache/action.yml @@ -2,12 +2,18 @@ name: go-caches description: Restore the go module, build, and golangci-lint caches. Save only on the cache-seeder workflow. # Only the cache-seeder workflow saves; rename requires updating cache-seeder.yml. -# The lint job restores but does not save the gobuild cache, so only one writer -# (the gobuild job) populates it and there is no contention on the cache key. +# The lint job restores but does not save the gobuild and gomod caches, so every key +# has a single writer and there is no contention on it. # Seeder restores by exact key only (no restore-keys) so each go.sum seeds a clean # cache and size stays bounded; do not add restore-keys here. PR runs keep them. +# The go version is part of the gobuild and golint keys because a toolchain bump +# invalidates every entry in them, so a cross-version fallback would restore +# gigabytes that no build can use. The module cache is version-independent. inputs: + go-version: + description: Go version the build caches belong to + required: true lint-cache: description: Restore (and save in cache-seeder) ~/.cache/golangci-lint default: "false" @@ -15,12 +21,12 @@ inputs: runs: using: composite steps: - - if: ${{ github.workflow == 'cache-seeder' }} + - if: ${{ github.workflow == 'cache-seeder' && inputs.lint-cache != 'true' }} uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 with: path: ~/go/pkg/mod key: gomod-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('go.sum') }} - - if: ${{ github.workflow != 'cache-seeder' }} + - if: ${{ github.workflow != 'cache-seeder' || inputs.lint-cache == 'true' }} uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 with: path: ~/go/pkg/mod @@ -30,21 +36,21 @@ runs: uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 with: path: ~/.cache/go-build - key: gobuild-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('go.sum') }} + key: gobuild-${{ runner.os }}-${{ runner.arch }}-go${{ inputs.go-version }}-${{ hashFiles('go.sum') }} - if: ${{ github.workflow != 'cache-seeder' || inputs.lint-cache == 'true' }} uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 with: path: ~/.cache/go-build - key: gobuild-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('go.sum') }} - restore-keys: gobuild-${{ runner.os }}-${{ runner.arch }} + key: gobuild-${{ runner.os }}-${{ runner.arch }}-go${{ inputs.go-version }}-${{ hashFiles('go.sum') }} + restore-keys: gobuild-${{ runner.os }}-${{ runner.arch }}-go${{ inputs.go-version }}- - if: ${{ inputs.lint-cache == 'true' && github.workflow == 'cache-seeder' }} uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 with: path: ~/.cache/golangci-lint - key: golint-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('go.sum', '.golangci.yml') }} + key: golint-${{ runner.os }}-${{ runner.arch }}-go${{ inputs.go-version }}-${{ hashFiles('go.sum', '.golangci.yml') }} - if: ${{ inputs.lint-cache == 'true' && github.workflow != 'cache-seeder' }} uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 with: path: ~/.cache/golangci-lint - key: golint-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('go.sum', '.golangci.yml') }} - restore-keys: golint-${{ runner.os }}-${{ runner.arch }} + key: golint-${{ runner.os }}-${{ runner.arch }}-go${{ inputs.go-version }}-${{ hashFiles('go.sum', '.golangci.yml') }} + restore-keys: golint-${{ runner.os }}-${{ runner.arch }}-go${{ inputs.go-version }}- diff --git a/.github/actions/go-setup/action.yml b/.github/actions/go-setup/action.yml index 2e576f12908..108f3408b08 100644 --- a/.github/actions/go-setup/action.yml +++ b/.github/actions/go-setup/action.yml @@ -14,6 +14,7 @@ runs: steps: - uses: ./.github/actions/free-disk-space - uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7 + id: setup-go with: go-version-file: go.mod check-latest: true @@ -21,4 +22,5 @@ runs: - if: ${{ inputs.cache == 'true' }} uses: ./.github/actions/go-cache with: + go-version: ${{ steps.setup-go.outputs.go-version }} lint-cache: ${{ inputs.lint-cache }} diff --git a/.github/workflows/cache-prune.yml b/.github/workflows/cache-prune.yml new file mode 100644 index 00000000000..847603e7fd1 --- /dev/null +++ b/.github/workflows/cache-prune.yml @@ -0,0 +1,38 @@ +name: cache-prune + +# Keeps the repository's total cache size below a fixed limit, so that GitHub never +# rejects a save for being over the allowance. + +on: + schedule: + - cron: "37 2 * * *" # every day at 02:37 UTC + workflow_dispatch: + workflow_call: + +permissions: + actions: write # to delete caches + +concurrency: + group: cache-prune + +jobs: + prune: + runs-on: ubuntu-latest + if: github.repository == 'go-gitea/gitea' + env: + GH_TOKEN: ${{ github.token }} + GH_REPO: ${{ github.repository }} + steps: + # Deletes least recently used first, the order GitHub itself evicts in, which takes + # superseded generations first as those stop being restored once a newer one exists. + - name: delete caches over the size limit + run: | + caches=$(gh cache list --limit 1000 --sort last_accessed_at --order asc --json id,key,sizeInBytes) + size=$(jq '[.[].sizeInBytes] | add // 0' <<< "$caches") + echo "cache usage: $((size / 1000000)) MB" + jq -r '.[] | "\(.id) \(.sizeInBytes) \(.key)"' <<< "$caches" | + while [ "$size" -gt 6500000000 ] && read -r id bytes key; do + echo "deleting $key" + gh cache delete "$id" + size=$((size - bytes)) + done diff --git a/.github/workflows/cache-seeder.yml b/.github/workflows/cache-seeder.yml index e233f39b8ac..ef73ce574fc 100644 --- a/.github/workflows/cache-seeder.yml +++ b/.github/workflows/cache-seeder.yml @@ -8,10 +8,12 @@ name: cache-seeder on: + workflow_dispatch: push: branches: - main paths: + - "go.mod" # a toolchain bump invalidates the build caches - "go.sum" - ".golangci.yml" - ".github/actions/go-cache/action.yml" @@ -70,3 +72,10 @@ jobs: - run: make ${{ matrix.target }} env: TAGS: ${{ matrix.tags }} + + # reclaims the caches this run superseded, so the next save still fits in the allowance + prune: + needs: [gobuild, lint] + permissions: + actions: write + uses: ./.github/workflows/cache-prune.yml diff --git a/.github/workflows/cron-licenses.yml b/.github/workflows/cron-licenses.yml index 5a2f1adc8e2..9a6fa72be72 100644 --- a/.github/workflows/cron-licenses.yml +++ b/.github/workflows/cron-licenses.yml @@ -17,6 +17,7 @@ jobs: with: go-version-file: go.mod check-latest: true + cache: false - run: make generate-gitignore timeout-minutes: 40 - name: push translations to repo diff --git a/.github/workflows/release-nightly.yml b/.github/workflows/release-nightly.yml index bf298d66fef..fcaf22dc5ae 100644 --- a/.github/workflows/release-nightly.yml +++ b/.github/workflows/release-nightly.yml @@ -23,6 +23,7 @@ jobs: with: go-version-file: go.mod check-latest: true + cache: false - uses: ./.github/actions/node-setup - run: make deps-frontend deps-backend # xgo build diff --git a/.github/workflows/release-tag-rc.yml b/.github/workflows/release-tag-rc.yml index 1f25a52f3a2..e337eb3ec6a 100644 --- a/.github/workflows/release-tag-rc.yml +++ b/.github/workflows/release-tag-rc.yml @@ -24,6 +24,7 @@ jobs: with: go-version-file: go.mod check-latest: true + cache: false - uses: ./.github/actions/node-setup - run: make deps-frontend deps-backend # xgo build diff --git a/.github/workflows/release-tag-version.yml b/.github/workflows/release-tag-version.yml index cfa2a6b07b7..a12f61c30b4 100644 --- a/.github/workflows/release-tag-version.yml +++ b/.github/workflows/release-tag-version.yml @@ -27,6 +27,7 @@ jobs: with: go-version-file: go.mod check-latest: true + cache: false - uses: ./.github/actions/node-setup - run: make deps-frontend deps-backend # xgo build