Fix Bazel Xcode version caching in CI (#6309)

* Fix Bazel Xcode version caching causing CI build failures

When Xcode updates on self-hosted runners, Bazel's cached
local_config_xcode still references the old version, causing
"xcode-locator <old_version> failed" errors.

Add sync_bazel_xcode.sh which detects Xcode version changes
and runs `bazel sync --configure` to refresh the config without
nuking the build cache. Added to all 8 CI workflows that use Bazel.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Use rm of cached repo instead of bazel sync --configure

bazel sync --configure requires WORKSPACE mode and fails with
bzlmod. Instead, delete the stale local_config_xcode from the
output base directly — the next bazel build regenerates it.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Use bazel clean --expunge instead of targeted repo deletion

Deleting just local_config_xcode is insufficient — the stale Xcode
version is also embedded in cached actions and other toolchain repos.
The only reliable fix is a full expunge. Since Xcode updates are
infrequent, the one-time rebuild cost is acceptable.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Use action_env to bake Xcode version into cache keys

Instead of expunging the cache (which doesn't help with the remote
cache anyway), write the Xcode build version to .bazelrc.xcode via
--action_env. This makes the version part of every action's cache
key, so stale entries in both local and remote caches are naturally
ignored after an Xcode update — no deletion needed anywhere.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Add expunge back alongside action_env for belt-and-suspenders fix

action_env alone isn't enough — local_config_xcode is a cached
repository rule that bakes in the old Xcode version before any
actions run. Need expunge to clear the local toolchain config,
plus action_env to prevent stale remote cache hits afterward.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Add Xcode sync to mac_build build-and-sign job

The sync step was only in the deploy job, but build-and-sign calls
bazel via build_protos.sh before that. This runs on the unity-mac
runner which also has the stale Xcode cache.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-27 12:43:14 -08:00
committed by GitHub
co-authored by Claude Opus 4.6
parent 38c3fbd6bd
commit 97bf29d4a2
11 changed files with 73 additions and 1 deletions
+4 -1
View File
@@ -37,7 +37,10 @@ common:macos --host_linkopt=-Wl,-no_warn_duplicate_libraries
# See: https://github.com/grpc/grpc/issues/37619
common:macos --features=-module_maps
# Fix Xcode version caching issue - avoids need for `bazel clean --expunge` after Xcode updates
# Bake the Xcode build version into action cache keys so that stale
# local/remote cache entries are naturally ignored after Xcode updates.
# Generated by scripts/sync_bazel_xcode.sh (run in CI before every build).
try-import %workspace%/.bazelrc.xcode
common:macos --repo_env=DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer
common --java_language_version=25
+3
View File
@@ -35,6 +35,9 @@ jobs:
with:
lfs: false
- name: Sync Bazel Xcode config
run: ./scripts/sync_bazel_xcode.sh
- name: Build Auth Server Docker image
id: build-auth
run: |
+4
View File
@@ -44,6 +44,8 @@ jobs:
uses: actions/checkout@v4
with:
lfs: false
- name: Sync Bazel Xcode config
run: ./scripts/sync_bazel_xcode.sh
- name: Check BUILD.bazel dependencies
run: ./scripts/check_build_deps.sh --strict
- name: Set up Node.js
@@ -66,6 +68,8 @@ jobs:
uses: actions/checkout@v4
with:
lfs: false
- name: Sync Bazel Xcode config
run: ./scripts/sync_bazel_xcode.sh
- name: Run tests
run: bazel test --build_event_json_file=test.json //src/test/... //src/main/go/...
- name: Collect failed test logs
+4
View File
@@ -74,6 +74,10 @@ jobs:
with:
lfs: false
- name: Sync Bazel Xcode config
if: steps.check-latest.outputs.skip != 'true'
run: ./scripts/sync_bazel_xcode.sh
- name: Fetch LFS files needed for admin server
if: steps.check-latest.outputs.skip != 'true'
run: git lfs pull --include="src/main/go/net/eagle0/admin_server/static/tiles/*"
+2
View File
@@ -29,5 +29,7 @@ jobs:
uses: actions/checkout@v4
with:
lfs: false
- name: Sync Bazel Xcode config
run: ./scripts/sync_bazel_xcode.sh
- name: Build Eagle server
run: bazel build //src/main/scala/net/eagle0/eagle:eagle_server
+6
View File
@@ -115,6 +115,9 @@ jobs:
- name: Ensure Unity version installed
run: ./ci/github_actions/ensure_unity_installed.sh mac
- name: Sync Bazel Xcode config
run: ./scripts/sync_bazel_xcode.sh
- name: Build Mac Unity
run: ./ci/github_actions/build_unity_mac.sh "${{ env.EAGLE0_BUILD_DIR }}/eagle0MAC"
@@ -320,6 +323,9 @@ jobs:
ditto -x -k eagle0.app.zip .
rm eagle0.app.zip
- name: Sync Bazel Xcode config
run: ./scripts/sync_bazel_xcode.sh
- name: Deploy Mac Build
id: deploy-mac
env:
@@ -38,6 +38,9 @@ jobs:
with:
lfs: false
- name: Sync Bazel Xcode config
run: ./scripts/sync_bazel_xcode.sh
- name: Build Shardok ARM64 binary (cross-compile for Linux ARM64)
run: |
set -ex
+2
View File
@@ -30,5 +30,7 @@ jobs:
uses: actions/checkout@v4
with:
lfs: false
- name: Sync Bazel Xcode config
run: ./scripts/sync_bazel_xcode.sh
- name: Build Shardok server
run: bazel build -c opt //src/main/cpp/net/eagle0/shardok:shardok-server
+2
View File
@@ -99,6 +99,8 @@ jobs:
run: |
git lfs install
git lfs pull
- name: Sync Bazel Xcode config
run: ./scripts/sync_bazel_xcode.sh
- name: Ensure Unity version installed
run: ./ci/github_actions/ensure_unity_installed.sh windows
- name: Build Windows unity
+1
View File
@@ -23,6 +23,7 @@ bazel-bin
bazel-eagle0*
bazel-out
bazel-testlogs
.bazelrc.xcode
.ijwb
.clwb
buildWin.sh
+42
View File
@@ -0,0 +1,42 @@
#!/usr/bin/env bash
# Keeps Bazel in sync with the installed Xcode version. Two mechanisms:
#
# 1. Writes .bazelrc.xcode with --action_env=XCODE_BUILD_VERSION=<version>
# so the version is part of every action's cache key. This prevents stale
# remote cache entries from being reused after an Xcode update.
#
# 2. Runs `bazel clean --expunge` when the version actually changes, because
# local_config_xcode (a cached repository rule) bakes in the old version
# and can only be refreshed by clearing the output base.
#
# .bazelrc imports the generated file via: try-import %workspace%/.bazelrc.xcode
set -euo pipefail
XCODE_BUILD_VERSION=$(xcodebuild -version 2>/dev/null | awk '/Build version/ {print $3}')
if [ -z "$XCODE_BUILD_VERSION" ]; then
echo "Warning: Could not detect Xcode build version, skipping"
exit 0
fi
BAZELRC_XCODE=".bazelrc.xcode"
EXPECTED="common --action_env=XCODE_BUILD_VERSION=${XCODE_BUILD_VERSION}"
# If the file already has the right version, nothing to do
if [ -f "$BAZELRC_XCODE" ] && [ "$(cat "$BAZELRC_XCODE")" = "$EXPECTED" ]; then
exit 0
fi
# Xcode version changed (or first run) — expunge local cache to clear
# stale local_config_xcode, then write the new version for remote cache keys
OLD_VERSION="unknown"
if [ -f "$BAZELRC_XCODE" ]; then
OLD_VERSION=$(sed -n 's/.*XCODE_BUILD_VERSION=//p' "$BAZELRC_XCODE")
fi
echo "Xcode build version changed: ${OLD_VERSION} -> ${XCODE_BUILD_VERSION}"
echo "Running bazel clean --expunge to clear stale toolchain config..."
bazel clean --expunge 2>/dev/null || true
echo "$EXPECTED" > "$BAZELRC_XCODE"
echo "Updated ${BAZELRC_XCODE} — next build will do a full rebuild with Xcode ${XCODE_BUILD_VERSION}"