From ec881def9c21131e8ba1fb28f289a1a07b21e03f Mon Sep 17 00:00:00 2001 From: Leo Li Date: Sun, 1 Nov 2026 03:10:06 +0000 Subject: [PATCH 1/6] ci(nightly): share the managed fleet compiler-cache integration Agent-Session: https://cmux.com/deeplink/agent?id=aid-de7c2a203b0e789233a0 --- .github/warm-sources.json | 2 +- .github/workflows/nightly.yml | 6 ++ scripts/ci/compile-app-host-test-product.sh | 45 +-------- scripts/ci/fleet-compilation-cache.sh | 46 +++++++++ scripts/ci/nightly_build_state.py | 2 +- tests/test_ci_fleet_compilation_cache.py | 104 ++++++++++++++++++++ tests/test_ci_fleet_compilation_cache.sh | 5 +- 7 files changed, 165 insertions(+), 45 deletions(-) create mode 100755 scripts/ci/fleet-compilation-cache.sh create mode 100644 tests/test_ci_fleet_compilation_cache.py diff --git a/.github/warm-sources.json b/.github/warm-sources.json index 94e8b71c8c27..d61c5b500b18 100644 --- a/.github/warm-sources.json +++ b/.github/warm-sources.json @@ -161,7 +161,7 @@ "nightly-next-canary.yml:canary": {"class": "mac-sign", "warm": [], "exempt": "verification only: checks the feed's signed DMG with Gatekeeper and the notary tickets, compiles nothing"}, "nightly-next-launch-smoke.yml:smoke": {"class": "nightly-release-compile", "warm": [], "exempt": "launches the feed's published DMG from a fresh home; compiles nothing"}, "nightly-next-train.yml:build": {"class": "nightly-release-compile", "warm": ["deriveddata-reuse"]}, - "nightly.yml:build-nightly-app": {"class": "nightly-release-compile", "warm": ["actions-cache", "product-reuse"]}, + "nightly.yml:build-nightly-app": {"class": "nightly-release-compile", "warm": ["actions-cache", "product-reuse", "ssd-cas"]}, "nightly.yml:build-nightly-ghostty-cli-helper": {"class": "nightly-release-compile", "warm": ["actions-cache"]}, "nightly.yml:build-nightly-inputs": {"class": "webview", "warm": ["actions-cache"]}, "nightly.yml:build-nightly-remote-daemon": {"class": "linux-build", "warm": [], "exempt": "cold: a build script from a clean tree; no warm source wired yet"}, diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 8f153ce87999..9beca570eca6 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -2498,6 +2498,11 @@ jobs: # Bundle cmux-tui bundles none (deferred): "Bundle the fast cmux-tui client" # or build-sign-notarize-nightly's "Bundle the cmux-tui client" installs # this tree's client (install-cmux-tui-client.sh). + # Reuse the managed fleet CAS; unavailable nodes keep the local cache. + fleet_cache_settings=() + while IFS= read -r setting; do + fleet_cache_settings+=("$setting") + done < <(CMUX_CI_DISABLE_COMPILATION_CACHE="${{ needs.decide.outputs.cold_cache == 'true' && '1' || '' }}" bash scripts/ci/fleet-compilation-cache.sh) python3 scripts/ci/run-with-helper-prebuild.py \ --derived-data build-universal --configuration Release --archs "$archs" \ --log "$RUNNER_TEMP/helper-prebuild.log" -- \ @@ -2511,6 +2516,7 @@ jobs: ONLY_ACTIVE_ARCH="$only_active" \ COMPILATION_CACHE_ENABLE_CACHING=YES \ COMPILATION_CACHE_LIMIT_SIZE=3221225472 \ + "${fleet_cache_settings[@]}" \ COMPILER_INDEX_STORE_ENABLE=NO \ CODE_SIGNING_ALLOWED=NO ASSETCATALOG_COMPILER_APPICON_NAME="$CHANNEL_ICON_NAME" \ "${notary_test_flags[@]}" diff --git a/scripts/ci/compile-app-host-test-product.sh b/scripts/ci/compile-app-host-test-product.sh index 6c50a67076ad..18490014eea4 100755 --- a/scripts/ci/compile-app-host-test-product.sh +++ b/scripts/ci/compile-app-host-test-product.sh @@ -223,15 +223,6 @@ build() { # shellcheck disable=SC2016 # Xcode expands $(TARGET_NAME), not the shell local -a cache_setting=( 'COMPILATION_CACHE_ENABLE_CACHING=$(CMUX_CI_COMPILATION_CACHE_$(TARGET_NAME):default=YES)' - # Keep cache keys stable when a PR runner's checkout and DerivedData live - # under a different absolute root than the fleet CAS writer. - SWIFT_ENABLE_PREFIX_MAPPING=YES - CLANG_ENABLE_PREFIX_MAPPING=YES - SWIFT_ENABLE_PROJECT_PREFIX_MAPPING=YES - CLANG_ENABLE_PROJECT_PREFIX_MAPPING=YES - # Xcode 26.6 emits one bounded remark per cache query. The build metrics - # receipt turns those remarks into cacheable-task and hit counters. - COMPILATION_CACHE_ENABLE_DIAGNOSTIC_REMARKS=YES ) # Xcode's explicit-module dependency graph can survive a clean DerivedData # rebuild when a reused CAS entry is incomplete. Admission recovery sets @@ -255,40 +246,10 @@ build() { if [ "${CMUX_CI_DISABLE_COMPILATION_CACHE:-}" = 1 ]; then cache_setting+=(COMPILATION_CACHE_ENABLE_CACHING=NO) fi - # Owned minis carry the fleet-cas node installed by glaeda. Its settings - # select the node's fixed CAS and Unix socket; Blacksmith and unprovisioned - # runners simply keep the local CAS path above. The helper is probed once - # per build and is advisory, so a down node never refuses a CI job. local -a fleet_cache_setting=() - local fleet_cas_root="${CMUX_FLEET_CAS_ROOT:-/Users/Shared/cmux-build-fleet/xcode}" - local fleet_cas_settings="${CMUX_FLEET_CAS_SETTINGS:-$fleet_cas_root/bin/fleet-cas-settings.sh}" - local fleet_cas_socket="${CMUX_FLEET_CAS_SOCKET:-$fleet_cas_root/fleet-cas.sock}" - if [ -x "$fleet_cas_settings" ] && [ -S "$fleet_cas_socket" ]; then - local fleet_settings='' - fleet_settings="$("$fleet_cas_settings" "$fleet_cas_socket" 2>/dev/null | head -n 8)" || fleet_settings='' - local fleet_plugin_ok=0 - local fleet_remote_ok=0 - while IFS= read -r setting; do - case "$setting" in - COMPILATION_CACHE_ENABLE_PLUGIN=YES) fleet_plugin_ok=1 ;; - COMPILATION_CACHE_REMOTE_SERVICE_PATH=/*) fleet_remote_ok=1 ;; - COMPILATION_CACHE_CAS_PATH=/*) ;; - esac - done <<< "$fleet_settings" - if [ "$fleet_plugin_ok" -eq 1 ] && [ "$fleet_remote_ok" -eq 1 ]; then - fleet_cache_setting+=("COMPILATION_CACHE_CAS_PATH=$fleet_cas_root/cas") - while IFS= read -r setting; do - case "$setting" in - COMPILATION_CACHE_ENABLE_PLUGIN=YES|COMPILATION_CACHE_REMOTE_SERVICE_PATH=/*) - fleet_cache_setting+=("$setting") - ;; - esac - done <<< "$fleet_settings" - fi - fi - if [ "${CMUX_CI_DISABLE_COMPILATION_CACHE:-}" = 1 ]; then - fleet_cache_setting=() - fi + while IFS= read -r setting; do + fleet_cache_setting+=("$setting") + done < <(bash "$SCRIPT_DIR/fleet-compilation-cache.sh") # xcodebuild runs under the resolve's fixed environment (see resolve()), but # the app's script phases still need the caller's: PATH for cargo, rustup, # go and zig (the diff sidecar, wireguard-go, bundled diff --git a/scripts/ci/fleet-compilation-cache.sh b/scripts/ci/fleet-compilation-cache.sh new file mode 100755 index 000000000000..6c98ed9936ca --- /dev/null +++ b/scripts/ci/fleet-compilation-cache.sh @@ -0,0 +1,46 @@ +#!/usr/bin/env bash +# Print one allowlisted Xcode setting per line. Shared by app-host admission +# and nightly Release builds; an absent or unhealthy managed CAS stays local. +set -euo pipefail + +fleet_cache_setting=( + # Keep cache keys stable when a PR runner's checkout and DerivedData live + # under a different absolute root than the fleet CAS writer. + SWIFT_ENABLE_PREFIX_MAPPING=YES + CLANG_ENABLE_PREFIX_MAPPING=YES + SWIFT_ENABLE_PROJECT_PREFIX_MAPPING=YES + CLANG_ENABLE_PROJECT_PREFIX_MAPPING=YES + # Xcode 26.6 emits one bounded remark per cache query. The build metrics + # receipt turns those remarks into cacheable-task and hit counters. + COMPILATION_CACHE_ENABLE_DIAGNOSTIC_REMARKS=YES +) +# Keep mapping and diagnostics when caching is disabled, as admission did. +if [ "${CMUX_CI_DISABLE_COMPILATION_CACHE:-}" != 1 ]; then + fleet_cas_root="${CMUX_FLEET_CAS_ROOT:-/Users/Shared/cmux-build-fleet/xcode}" + fleet_cas_settings="${CMUX_FLEET_CAS_SETTINGS:-$fleet_cas_root/bin/fleet-cas-settings.sh}" + fleet_cas_socket="${CMUX_FLEET_CAS_SOCKET:-$fleet_cas_root/fleet-cas.sock}" + if [ -x "$fleet_cas_settings" ] && [ -S "$fleet_cas_socket" ]; then + fleet_settings='' + fleet_settings="$("$fleet_cas_settings" "$fleet_cas_socket" 2>/dev/null | head -n 8)" || fleet_settings='' + fleet_plugin_ok=0 + fleet_remote_ok=0 + while IFS= read -r setting; do + case "$setting" in + COMPILATION_CACHE_ENABLE_PLUGIN=YES) fleet_plugin_ok=1 ;; + COMPILATION_CACHE_REMOTE_SERVICE_PATH=/*) fleet_remote_ok=1 ;; + COMPILATION_CACHE_CAS_PATH=/*) ;; + esac + done <<< "$fleet_settings" + if [ "$fleet_plugin_ok" -eq 1 ] && [ "$fleet_remote_ok" -eq 1 ]; then + fleet_cache_setting+=("COMPILATION_CACHE_CAS_PATH=$fleet_cas_root/cas") + while IFS= read -r setting; do + case "$setting" in + COMPILATION_CACHE_ENABLE_PLUGIN=YES|COMPILATION_CACHE_REMOTE_SERVICE_PATH=/*) + fleet_cache_setting+=("$setting") + ;; + esac + done <<< "$fleet_settings" + fi + fi +fi +printf '%s\n' "${fleet_cache_setting[@]}" diff --git a/scripts/ci/nightly_build_state.py b/scripts/ci/nightly_build_state.py index 316d739ac83c..b4e4eb3003f4 100644 --- a/scripts/ci/nightly_build_state.py +++ b/scripts/ci/nightly_build_state.py @@ -60,7 +60,7 @@ INCREMENTAL_STATE = 'incremental.json' KEY_PATHS = re.compile(r'(^|/)(Package\.resolved|Cargo\.lock|build\.zig\.zon|rust-toolchain(\.toml)?|\.xcode-version' r'|cef-manifest\.json|ensure-cef\.sh|ensure-remote-browser-host\.sh|download-prebuilt-ghosttykit\.sh' - r'|nightly-swift-defines\.sh|run-xcodebuild-with-diagnostics\.sh)$') + r'|nightly-swift-defines\.sh|run-xcodebuild-with-diagnostics\.sh|fleet-compilation-cache\.sh)$') # The build's own settings: the Release build step's text (its xcodebuild settings and how it forms # OTHER_SWIFT_FLAGS) and the decide outputs it reads (BUILD_SETTINGS in the environment of the # prepare step). A change to either is a clean build: objects compiled under other -D flags, diff --git a/tests/test_ci_fleet_compilation_cache.py b/tests/test_ci_fleet_compilation_cache.py new file mode 100644 index 000000000000..1f1069d70cb6 --- /dev/null +++ b/tests/test_ci_fleet_compilation_cache.py @@ -0,0 +1,104 @@ +#!/usr/bin/env python3 +"""Exercise managed CAS settings and the nightly's emitted Xcode command.""" +import json +import os +from pathlib import Path +import shutil +import socket +import subprocess +import tempfile +import unittest + +ROOT = Path(__file__).resolve().parents[1] +SETTINGS = ROOT / "scripts/ci/fleet-compilation-cache.sh" + + +class FleetCacheTests(unittest.TestCase): + def setUp(self): + self.scratch = tempfile.TemporaryDirectory(prefix="fleet-cas-") + self.addCleanup(self.scratch.cleanup) + self.root = Path(self.scratch.name) + self.cache = self.root / "cache with spaces" + self.cache.mkdir() + self.sock = socket.socket(socket.AF_UNIX) + self.addCleanup(self.sock.close) + self.sock.bind(str(self.cache / "fleet-cas.sock")) + self.helper = self.cache / "settings.sh" + self.env = {**os.environ, "CMUX_FLEET_CAS_ROOT": str(self.cache), + "CMUX_FLEET_CAS_SETTINGS": str(self.helper), + "CMUX_FLEET_CAS_SOCKET": str(self.cache / "fleet-cas.sock"), + "CMUX_CI_DISABLE_COMPILATION_CACHE": ""} + self.helper.write_text('#!/bin/bash\nprintf "%s\\n" COMPILATION_CACHE_ENABLE_PLUGIN=YES ' + '"COMPILATION_CACHE_REMOTE_SERVICE_PATH=$1" ' + 'COMPILATION_CACHE_CAS_PATH=/untrusted ' + 'OTHER_SWIFT_FLAGS=-unsafe\n') + self.helper.chmod(0o755) + + def settings(self): + return subprocess.check_output(["bash", str(SETTINGS)], env=self.env, text=True).splitlines() + + def assert_local(self): + settings = self.settings() + self.assertIn("SWIFT_ENABLE_PREFIX_MAPPING=YES", settings) + self.assertIn("CLANG_ENABLE_PROJECT_PREFIX_MAPPING=YES", settings) + self.assertFalse(any("REMOTE_SERVICE" in s or "CAS_PATH=" in s or "ENABLE_PLUGIN" in s for s in settings)) + + def test_healthy_node_uses_fixed_cas_and_filters_helper_settings(self): + settings = self.settings() + self.assertIn(f"COMPILATION_CACHE_CAS_PATH={self.cache}/cas", settings) + self.assertIn(f"COMPILATION_CACHE_REMOTE_SERVICE_PATH={self.cache}/fleet-cas.sock", settings) + self.assertNotIn("COMPILATION_CACHE_CAS_PATH=/untrusted", settings) + self.assertNotIn("OTHER_SWIFT_FLAGS=-unsafe", settings) + + def test_missing_socket_keeps_local_cache(self): + self.env["CMUX_FLEET_CAS_SOCKET"] = str(self.root / "absent.sock") + self.assert_local() + + def test_failed_helper_keeps_local_cache(self): + self.helper.write_text("#!/bin/bash\nexit 1\n") + self.assert_local() + + def test_partial_response_keeps_local_cache(self): + self.helper.write_text("#!/bin/bash\necho COMPILATION_CACHE_ENABLE_PLUGIN=YES\n") + self.assert_local() + + def test_disabled_cache_never_probes_node(self): + self.env["CMUX_CI_DISABLE_COMPILATION_CACHE"] = "1" + marker = self.root / "probed" + self.helper.write_text(f"#!/bin/bash\ntouch '{marker}'\n") + self.assert_local() + self.assertFalse(marker.exists()) + + def test_nightly_xcode_command_receives_shared_settings(self): + # Execute the workflow's actual compile command through tiny pass-through + # wrappers. No Xcode or compiler runs; the last executable records argv. + workflow = (ROOT / ".github/workflows/nightly.yml").read_text() + step = workflow.split(" - name: Build nightly app (Release)\n", 1)[1].split(" - name:", 1)[0] + command = step[step.index(" fleet_cache_settings=()") :] + command = "\n".join(line[10:] for line in command.splitlines() if line.startswith(" ")) + command = command.replace("${{ needs.decide.outputs.cold_cache == 'true' && '1' || '' }}", "") + scripts = self.root / "scripts/ci" + scripts.mkdir(parents=True) + shutil.copyfile(SETTINGS, scripts / SETTINGS.name) + (scripts / "run-with-helper-prebuild.py").write_text( + "import os,sys\na=sys.argv[sys.argv.index('--')+1:]\nos.execvp(a[0],a)\n") + wrapper = scripts / "run-xcodebuild-with-diagnostics.sh" + wrapper.write_text('#!/bin/bash\nshift\nexec "$@"\n') + wrapper.chmod(0o755) + xcode = self.root / "xcodebuild" + xcode.write_text('#!/usr/bin/env python3\nimport json,sys\nprint(json.dumps(sys.argv[1:]))\n') + xcode.chmod(0o755) + env = {**self.env, "PATH": str(self.root) + os.pathsep + self.env["PATH"], + "RUNNER_TEMP": str(self.root), "CHANNEL_ICON_NAME": "AppIcon-Nightly"} + result = subprocess.run(["bash", "-euc", 'archs=arm64; only_active=YES; notary_test_flags=(build)\n' + command], + cwd=self.root, env=env, text=True, capture_output=True, check=True) + args = json.loads(result.stdout) + self.assertIn(f"COMPILATION_CACHE_CAS_PATH={self.cache}/cas", args) + self.assertIn("COMPILATION_CACHE_ENABLE_PLUGIN=YES", args) + self.assertIn("SWIFT_ENABLE_PROJECT_PREFIX_MAPPING=YES", args) + self.assertIn("CODE_SIGNING_ALLOWED=NO", args) + self.assertEqual(args[-1], "build") + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/test_ci_fleet_compilation_cache.sh b/tests/test_ci_fleet_compilation_cache.sh index 2b675976f19d..32625560117a 100755 --- a/tests/test_ci_fleet_compilation_cache.sh +++ b/tests/test_ci_fleet_compilation_cache.sh @@ -3,7 +3,8 @@ set -euo pipefail ROOT_DIR="$(cd "$(dirname "$0")/.." && pwd)" -SCRIPT="$ROOT_DIR/scripts/ci/compile-app-host-test-product.sh" +SCRIPT="$ROOT_DIR/scripts/ci/fleet-compilation-cache.sh" +COMPILE="$ROOT_DIR/scripts/ci/compile-app-host-test-product.sh" WORKFLOW="$ROOT_DIR/.github/workflows/ci-macos.yml" fail() { echo "FAIL: $*" >&2; exit 1; } @@ -28,4 +29,6 @@ grep -Fq 'fleet_cache_setting+=("COMPILATION_CACHE_CAS_PATH=$fleet_cas_root/cas" grep -Fq 'glaeda-compile-telemetry.json' "$WORKFLOW" || fail 'compile admission must publish the host telemetry sidecar' +grep -Fq 'bash "$SCRIPT_DIR/fleet-compilation-cache.sh"' "$COMPILE" || fail 'admission must use the shared settings' +python3 "$ROOT_DIR/tests/test_ci_fleet_compilation_cache.py" echo 'PASS: fleet compiler-cache settings and host telemetry are wired' From 79210d20ce22df9890e6a653ba0c48560cf5c9cf Mon Sep 17 00:00:00 2001 From: Leo Li Date: Sun, 1 Nov 2026 03:11:01 +0000 Subject: [PATCH 2/6] ci(cache): include shared settings in product and incremental identities Agent-Session: https://cmux.com/deeplink/agent?id=aid-de7c2a203b0e789233a0 --- scripts/ci/product_input_identity.py | 1 + tests/test_nightly_build_state.py | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/scripts/ci/product_input_identity.py b/scripts/ci/product_input_identity.py index 81fcc12cb07d..6e6d71d19ed3 100644 --- a/scripts/ci/product_input_identity.py +++ b/scripts/ci/product_input_identity.py @@ -61,6 +61,7 @@ def scheme_action(scheme: str) -> str: PRODUCT_CI_INPUTS = frozenset({ "scripts/ci/app_host_test_products.py", "scripts/ci/compile-app-host-test-product.sh", + "scripts/ci/fleet-compilation-cache.sh", "scripts/ci/canonical-build-root.sh", # canonical-build-root.sh copies the source tree the product compiles from with it. "scripts/ci/apfs_clone.py", diff --git a/tests/test_nightly_build_state.py b/tests/test_nightly_build_state.py index ff6bbeffdd3b..5edc66d9cd4f 100644 --- a/tests/test_nightly_build_state.py +++ b/tests/test_nightly_build_state.py @@ -146,6 +146,12 @@ def test_an_unfinished_build_makes_the_next_one_clean(self): self.build(finish=False) self.assertEqual(self.build()[:2], ('mode=clean', False)) + def test_a_fleet_cache_settings_change_invalidates_incremental_state(self): + self.build() + self.commit({'scripts/ci/fleet-compilation-cache.sh': 'echo SWIFT_ENABLE_PREFIX_MAPPING=YES\n'}) + self.assertEqual(self.build()[0], 'mode=clean') + self.assertEqual(self.build()[0], 'mode=incremental') + def test_a_toolchain_lockfile_or_submodule_change_makes_it_clean(self): self.build() self.assertEqual(self.build(toolchain='Xcode 26.7')[:2], ('mode=clean', False)) From 24b81a7227c73af0e1dda8b34eaed330b8a6d00c Mon Sep 17 00:00:00 2001 From: Leo Li Date: Sun, 1 Nov 2026 03:12:50 +0000 Subject: [PATCH 3/6] test(ci): keep fixture Unix sockets within the path limit Agent-Session: https://cmux.com/deeplink/agent?id=aid-de7c2a203b0e789233a0 --- tests/test_ci_fleet_compilation_cache.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_ci_fleet_compilation_cache.py b/tests/test_ci_fleet_compilation_cache.py index 1f1069d70cb6..11dfe751ffad 100644 --- a/tests/test_ci_fleet_compilation_cache.py +++ b/tests/test_ci_fleet_compilation_cache.py @@ -15,7 +15,8 @@ class FleetCacheTests(unittest.TestCase): def setUp(self): - self.scratch = tempfile.TemporaryDirectory(prefix="fleet-cas-") + # Runner TMPDIR may exceed sockaddr_un's ~100-byte pathname limit. + self.scratch = tempfile.TemporaryDirectory(prefix="fleet-cas-", dir="/tmp") self.addCleanup(self.scratch.cleanup) self.root = Path(self.scratch.name) self.cache = self.root / "cache with spaces" From 7bdeb6f5c93d6bc9715f3c24236c06856c019f04 Mon Sep 17 00:00:00 2001 From: Leo Li Date: Sun, 1 Nov 2026 03:16:03 +0000 Subject: [PATCH 4/6] test(ci): register cache fixtures and classify cold-cache control Agent-Session: https://cmux.com/deeplink/agent?id=aid-de7c2a203b0e789233a0 --- tests/test-execution.toml | 4 ++++ tests/test_nightly_release_product_reuse.py | 4 +++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/tests/test-execution.toml b/tests/test-execution.toml index 3fd8cd656297..9a22b60c8e9d 100644 --- a/tests/test-execution.toml +++ b/tests/test-execution.toml @@ -10,6 +10,10 @@ # wall-clock bounds a busy machine could miss. version = 1 +[[test]] +path = "tests/test_ci_fleet_compilation_cache.py" +lane = "linux-guard" + [[test]] path = "tests/test_web_ota_publication_contract.py" lane = "linux-guard" diff --git a/tests/test_nightly_release_product_reuse.py b/tests/test_nightly_release_product_reuse.py index 39743bc082bb..db67851232b5 100644 --- a/tests/test_nightly_release_product_reuse.py +++ b/tests/test_nightly_release_product_reuse.py @@ -344,7 +344,9 @@ def test_skipped_steps_read_no_unkeyed_decide_output(self): self.assertIn(f"needs.decide.outputs.{excluded} != 'true'", condition) self.assertIn("github.event_name == 'push'", condition) self.assertIn("github.ref == 'refs/heads/nightly-next'", condition) - allowed = set(fingerprint.NIGHTLY_PRODUCT_FLAGS) | {"fast_build", "build_only", "should_publish"} + # cold_cache only chooses local versus shared compiler-cache transport; + # both produce the same compiled inputs, so it does not key the product. + allowed = set(fingerprint.NIGHTLY_PRODUCT_FLAGS) | {"fast_build", "build_only", "should_publish", "cold_cache"} skipped = [step for step in self.steps() if "steps.nightly-product.outputs.hit != 'true'" in step] self.assertGreaterEqual(len(skipped), 15) for step in skipped: From beb297ffb5f5a77c7c7f539476c3c9f85df42c84 Mon Sep 17 00:00:00 2001 From: Leo Li Date: Sun, 1 Nov 2026 03:16:31 +0000 Subject: [PATCH 5/6] test(ci): run cache fixture directly in the existing guard step Agent-Session: https://cmux.com/deeplink/agent?id=aid-de7c2a203b0e789233a0 --- .github/workflows/ci-guards.yml | 4 +++- tests/test_ci_fleet_compilation_cache.sh | 1 - 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci-guards.yml b/.github/workflows/ci-guards.yml index 04cdb75b551b..a87027aa78cc 100644 --- a/.github/workflows/ci-guards.yml +++ b/.github/workflows/ci-guards.yml @@ -272,7 +272,9 @@ jobs: - name: Validate fleet compiler-cache wiring if: ${{ !cancelled() && matrix.group == 'ci' }} - run: bash tests/test_ci_fleet_compilation_cache.sh + run: | + bash tests/test_ci_fleet_compilation_cache.sh + python3 tests/test_ci_fleet_compilation_cache.py - name: Validate feature flag linter scope if: ${{ !cancelled() && matrix.group == 'ci' }} diff --git a/tests/test_ci_fleet_compilation_cache.sh b/tests/test_ci_fleet_compilation_cache.sh index 32625560117a..4f046b5c9741 100755 --- a/tests/test_ci_fleet_compilation_cache.sh +++ b/tests/test_ci_fleet_compilation_cache.sh @@ -30,5 +30,4 @@ grep -Fq 'fleet_cache_setting+=("COMPILATION_CACHE_CAS_PATH=$fleet_cas_root/cas" grep -Fq 'glaeda-compile-telemetry.json' "$WORKFLOW" || fail 'compile admission must publish the host telemetry sidecar' grep -Fq 'bash "$SCRIPT_DIR/fleet-compilation-cache.sh"' "$COMPILE" || fail 'admission must use the shared settings' -python3 "$ROOT_DIR/tests/test_ci_fleet_compilation_cache.py" echo 'PASS: fleet compiler-cache settings and host telemetry are wired' From 14a5455e226e386f34f9c37a12e00915dba1ebfb Mon Sep 17 00:00:00 2001 From: Leo Li Date: Sun, 1 Nov 2026 03:44:42 +0000 Subject: [PATCH 6/6] ci(cache): align warmer and PR Release settings Agent-Session: https://cmux.com/deeplink/agent?id=aid-de7c2a203b0e789233a0 --- .github/workflows/ci-macos.yml | 5 +++++ .github/workflows/nightly.yml | 5 +++++ tests/test_ci_fleet_compilation_cache.py | 9 +++++++++ 3 files changed, 19 insertions(+) diff --git a/.github/workflows/ci-macos.yml b/.github/workflows/ci-macos.yml index f3b7f78848ad..8ea8ef31d318 100644 --- a/.github/workflows/ci-macos.yml +++ b/.github/workflows/ci-macos.yml @@ -2519,6 +2519,10 @@ jobs: started="$(date +%s)" # Bundle cmux-tui bundles none (deferred); "Install Release helpers" # installs this tree's client (install-cmux-tui-client.sh). + fleet_cache_settings=() + while IFS= read -r setting; do + fleet_cache_settings+=("$setting") + done < <(bash scripts/ci/fleet-compilation-cache.sh) CMUX_NEXT_TUI_MODE=deferred CMUX_SKIP_ZIG_BUILD=1 xcodebuild -project cmux.xcodeproj -scheme cmux -configuration Release -derivedDataPath build-universal \ -destination 'generic/platform=macOS' \ -clonedSourcePackagesDirPath .spm-cache \ @@ -2527,6 +2531,7 @@ jobs: ONLY_ACTIVE_ARCH=NO \ COMPILATION_CACHE_ENABLE_CACHING=YES \ COMPILATION_CACHE_LIMIT_SIZE=3221225472 \ + "${fleet_cache_settings[@]}" \ COMPILER_INDEX_STORE_ENABLE=NO \ CODE_SIGNING_ALLOWED=NO ASSETCATALOG_COMPILER_APPICON_NAME=AppIcon-Nightly build ended="$(date +%s)" diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 9beca570eca6..70f02b61f88d 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -1144,6 +1144,10 @@ jobs: # The warmer only fills the Swift compilation cache; the Chromium # engine is a prebuilt artifact the shipped build embeds itself. # Bundle cmux-tui bundles none (deferred): this build only fills the cache. + fleet_cache_settings=() + while IFS= read -r setting; do + fleet_cache_settings+=("$setting") + done < <(bash scripts/ci/fleet-compilation-cache.sh) CMUX_NEXT_TUI_MODE=deferred CMUX_NEXT_SKIP_CEF=1 CMUX_SKIP_ZIG_BUILD=1 xcodebuild -scheme cmux -configuration Release -derivedDataPath build-universal \ -destination 'generic/platform=macOS' \ -clonedSourcePackagesDirPath .spm-cache \ @@ -1153,6 +1157,7 @@ jobs: ONLY_ACTIVE_ARCH=NO \ COMPILATION_CACHE_ENABLE_CACHING=YES \ COMPILATION_CACHE_LIMIT_SIZE=3221225472 \ + "${fleet_cache_settings[@]}" \ COMPILER_INDEX_STORE_ENABLE=NO \ CODE_SIGNING_ALLOWED=NO ASSETCATALOG_COMPILER_APPICON_NAME=AppIcon-Nightly build diff --git a/tests/test_ci_fleet_compilation_cache.py b/tests/test_ci_fleet_compilation_cache.py index 11dfe751ffad..536b015a1f8c 100644 --- a/tests/test_ci_fleet_compilation_cache.py +++ b/tests/test_ci_fleet_compilation_cache.py @@ -100,6 +100,15 @@ def test_nightly_xcode_command_receives_shared_settings(self): self.assertIn("CODE_SIGNING_ALLOWED=NO", args) self.assertEqual(args[-1], "build") + def test_warmer_and_pr_release_use_the_same_cache_settings(self): + nightly = (ROOT / ".github/workflows/nightly.yml").read_text() + ci_macos = (ROOT / ".github/workflows/ci-macos.yml").read_text() + warmer = nightly.split(" - name: Refresh universal nightly compilation cache\n", 1)[1].split(" - name:", 1)[0] + release = ci_macos.split(" - name: Build app (Release)\n", 1)[1].split(" - name:", 1)[0] + for step in (warmer, release): + self.assertIn("bash scripts/ci/fleet-compilation-cache.sh", step) + self.assertIn('"${fleet_cache_settings[@]}"', step) + if __name__ == "__main__": unittest.main()