diff --git a/.github/actions/prepare-bazel-ci/action.yml b/.github/actions/prepare-bazel-ci/action.yml index 598c6e4c16..78f5aeb9a3 100644 --- a/.github/actions/prepare-bazel-ci/action.yml +++ b/.github/actions/prepare-bazel-ci/action.yml @@ -4,6 +4,9 @@ inputs: target: description: Target triple used for setup and cache namespacing. required: true + cache-scope: + description: Logical namespace used to keep concurrent Bazel jobs from reserving the same repository cache key. + required: true install-test-prereqs: description: Install Node.js and DotSlash for Bazel-backed test jobs. required: false @@ -12,6 +15,12 @@ outputs: repository-cache-path: description: Filesystem path used for the Bazel repository cache. value: ${{ steps.setup_bazel.outputs.repository-cache-path }} + repository-cache-key: + description: Primary actions/cache key for the Bazel repository cache. + value: ${{ steps.cache_bazel_repository_key.outputs.repository-cache-key }} + repository-cache-hit: + description: Whether the Bazel repository cache restore found an exact key match. + value: ${{ steps.cache_bazel_repository_restore.outputs.cache-hit }} runs: using: composite @@ -23,6 +32,17 @@ runs: target: ${{ inputs.target }} install-test-prereqs: ${{ inputs.install-test-prereqs }} + - name: Compute bazel repository cache key + id: cache_bazel_repository_key + shell: bash + env: + CACHE_SCOPE: ${{ inputs.cache-scope }} + TARGET: ${{ inputs.target }} + CACHE_HASH: ${{ hashFiles('MODULE.bazel', 'codex-rs/Cargo.lock', 'codex-rs/Cargo.toml') }} + run: | + echo "repository-cache-key=bazel-cache-${CACHE_SCOPE}-${TARGET}-${CACHE_HASH}" >> "${GITHUB_OUTPUT}" + echo "repository-cache-restore-key=bazel-cache-${CACHE_SCOPE}-${TARGET}-" >> "${GITHUB_OUTPUT}" + # Restore the Bazel repository cache explicitly so external dependencies # do not need to be re-downloaded on every CI run. Keep restore failures # non-fatal so transient cache-service errors degrade to a cold build @@ -33,9 +53,9 @@ runs: uses: actions/cache/restore@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5 with: path: ${{ steps.setup_bazel.outputs.repository-cache-path }} - key: bazel-cache-${{ inputs.target }}-${{ hashFiles('MODULE.bazel', 'codex-rs/Cargo.lock', 'codex-rs/Cargo.toml') }} + key: ${{ steps.cache_bazel_repository_key.outputs.repository-cache-key }} restore-keys: | - bazel-cache-${{ inputs.target }} + ${{ steps.cache_bazel_repository_key.outputs.repository-cache-restore-key }} - name: Set up Bazel execution logs shell: bash diff --git a/.github/workflows/bazel.yml b/.github/workflows/bazel.yml index 842a3ce12f..f4501440c9 100644 --- a/.github/workflows/bazel.yml +++ b/.github/workflows/bazel.yml @@ -63,6 +63,7 @@ jobs: uses: ./.github/actions/prepare-bazel-ci with: target: ${{ matrix.target }} + cache-scope: bazel-${{ github.job }} install-test-prereqs: "true" - name: Check MODULE.bazel.lock is up to date if: matrix.os == 'ubuntu-24.04' && matrix.target == 'x86_64-unknown-linux-gnu' @@ -113,15 +114,15 @@ jobs: path: ${{ runner.temp }}/bazel-execution-logs if-no-files-found: ignore - # Save the Bazel repository cache after every non-cancelled run. Keep the + # Save the job-scoped Bazel repository cache after cache misses. Keep the # upload non-fatal so cache service issues never fail the job itself. - name: Save bazel repository cache - if: always() && !cancelled() + if: always() && !cancelled() && steps.prepare_bazel.outputs.repository-cache-hit != 'true' continue-on-error: true uses: actions/cache/save@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5 with: path: ${{ steps.prepare_bazel.outputs.repository-cache-path }} - key: bazel-cache-${{ matrix.target }}-${{ hashFiles('MODULE.bazel', 'codex-rs/Cargo.lock', 'codex-rs/Cargo.toml') }} + key: ${{ steps.prepare_bazel.outputs.repository-cache-key }} clippy: timeout-minutes: 30 @@ -150,6 +151,7 @@ jobs: uses: ./.github/actions/prepare-bazel-ci with: target: ${{ matrix.target }} + cache-scope: bazel-${{ github.job }} - name: bazel build --config=clippy lint targets env: @@ -196,15 +198,15 @@ jobs: path: ${{ runner.temp }}/bazel-execution-logs if-no-files-found: ignore - # Save the Bazel repository cache after every non-cancelled run. Keep the + # Save the job-scoped Bazel repository cache after cache misses. Keep the # upload non-fatal so cache service issues never fail the job itself. - name: Save bazel repository cache - if: always() && !cancelled() + if: always() && !cancelled() && steps.prepare_bazel.outputs.repository-cache-hit != 'true' continue-on-error: true uses: actions/cache/save@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5 with: path: ${{ steps.prepare_bazel.outputs.repository-cache-path }} - key: bazel-cache-${{ matrix.target }}-${{ hashFiles('MODULE.bazel', 'codex-rs/Cargo.lock', 'codex-rs/Cargo.toml') }} + key: ${{ steps.prepare_bazel.outputs.repository-cache-key }} verify-release-build: timeout-minutes: 30 @@ -229,6 +231,7 @@ jobs: uses: ./.github/actions/prepare-bazel-ci with: target: ${{ matrix.target }} + cache-scope: bazel-${{ github.job }} - name: bazel build verify-release-build targets env: @@ -278,12 +281,12 @@ jobs: path: ${{ runner.temp }}/bazel-execution-logs if-no-files-found: ignore - # Save the Bazel repository cache after every non-cancelled run. Keep the + # Save the job-scoped Bazel repository cache after cache misses. Keep the # upload non-fatal so cache service issues never fail the job itself. - name: Save bazel repository cache - if: always() && !cancelled() + if: always() && !cancelled() && steps.prepare_bazel.outputs.repository-cache-hit != 'true' continue-on-error: true uses: actions/cache/save@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5 with: path: ${{ steps.prepare_bazel.outputs.repository-cache-path }} - key: bazel-cache-${{ matrix.target }}-${{ hashFiles('MODULE.bazel', 'codex-rs/Cargo.lock', 'codex-rs/Cargo.toml') }} + key: ${{ steps.prepare_bazel.outputs.repository-cache-key }}