Skip to content

Commit fef2e6d

Browse files
committed
ci: harden iOS prebuild cache keys and remove derived-data copy path
1 parent 09ac465 commit fef2e6d

3 files changed

Lines changed: 45 additions & 35 deletions

File tree

.github/workflows/ios-runner-prebuild.yml

Lines changed: 15 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@ name: iOS Runner Prebuild (CI-only)
22

33
on:
44
workflow_dispatch:
5+
inputs:
6+
cache_buster:
7+
description: Optional suffix to force a fresh prebuild cache entry
8+
required: false
9+
default: stable
510

611
permissions:
712
contents: read
@@ -18,7 +23,6 @@ jobs:
1823
env:
1924
IOS_RUNTIME_VERSION: '26.2'
2025
IOS_DEVICE_NAME: 'iPhone 17 Pro'
21-
PREBUILT_DIR: ${{ github.workspace }}/.tmp/ios-runner-prebuilt
2226
DERIVED_DATA_PATH: ${{ github.workspace }}/.tmp/ios-runner-derived
2327
steps:
2428
- name: Checkout
@@ -36,48 +40,39 @@ jobs:
3640
echo "version=$XCODE_VERSION" >> "$GITHUB_OUTPUT"
3741
echo "key=$XCODE_KEY" >> "$GITHUB_OUTPUT"
3842
43+
- name: Resolve prebuild source hash
44+
id: source-hash
45+
run: echo "value=${{ hashFiles('ios-runner/**', 'package.json', 'pnpm-lock.yaml') }}" >> "$GITHUB_OUTPUT"
46+
3947
- name: Restore prebuilt cache
4048
id: restore-prebuilt
4149
uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.2.3
4250
with:
43-
path: ${{ env.PREBUILT_DIR }}
44-
key: ios-runner-prebuilt-${{ steps.xcode.outputs.key }}-ios-${{ env.IOS_RUNTIME_VERSION }}
51+
path: ${{ env.DERIVED_DATA_PATH }}
52+
key: ios-runner-prebuilt-${{ steps.xcode.outputs.key }}-ios-${{ env.IOS_RUNTIME_VERSION }}-${{ steps.source-hash.outputs.value }}-${{ github.event.inputs.cache_buster }}
4553

4654
- name: Build ios-runner for testing
4755
if: steps.restore-prebuilt.outputs.cache-hit != 'true'
4856
run: |
4957
set -euo pipefail
50-
rm -rf "$DERIVED_DATA_PATH" "$PREBUILT_DIR"
58+
rm -rf "$DERIVED_DATA_PATH"
5159
xcodebuild build-for-testing \
5260
-project ios-runner/AgentDeviceRunner/AgentDeviceRunner.xcodeproj \
5361
-scheme AgentDeviceRunner \
5462
-destination "platform=iOS Simulator,name=${IOS_DEVICE_NAME},OS=${IOS_RUNTIME_VERSION}" \
5563
-derivedDataPath "$DERIVED_DATA_PATH"
5664
57-
- name: Package prebuilt cache payload
58-
if: steps.restore-prebuilt.outputs.cache-hit != 'true'
59-
run: |
60-
set -euo pipefail
61-
mkdir -p "$PREBUILT_DIR"
62-
cp -R "$DERIVED_DATA_PATH" "$PREBUILT_DIR/derived-data"
63-
cat > "$PREBUILT_DIR/metadata.txt" <<EOF
64-
runtime_version=${IOS_RUNTIME_VERSION}
65-
device_name=${IOS_DEVICE_NAME}
66-
xcode_version=${{ steps.xcode.outputs.version }}
67-
github_run_id=${GITHUB_RUN_ID}
68-
github_sha=${GITHUB_SHA}
69-
EOF
70-
7165
- name: Save prebuilt cache
7266
if: steps.restore-prebuilt.outputs.cache-hit != 'true'
7367
uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.2.3
7468
with:
75-
path: ${{ env.PREBUILT_DIR }}
76-
key: ios-runner-prebuilt-${{ steps.xcode.outputs.key }}-ios-${{ env.IOS_RUNTIME_VERSION }}
69+
path: ${{ env.DERIVED_DATA_PATH }}
70+
key: ios-runner-prebuilt-${{ steps.xcode.outputs.key }}-ios-${{ env.IOS_RUNTIME_VERSION }}-${{ steps.source-hash.outputs.value }}-${{ github.event.inputs.cache_buster }}
7771

7872
- name: Report prebuild cache status
7973
run: |
8074
set -euo pipefail
75+
echo "cache_key=ios-runner-prebuilt-${{ steps.xcode.outputs.key }}-ios-${{ env.IOS_RUNTIME_VERSION }}-${{ steps.source-hash.outputs.value }}-${{ github.event.inputs.cache_buster }}"
8176
if [ "${{ steps.restore-prebuilt.outputs.cache-hit }}" = "true" ]; then
8277
echo "Reused existing prebuild cache."
8378
else

.github/workflows/ios.yml

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ jobs:
2121
continue-on-error: true
2222
env:
2323
IOS_RUNTIME_VERSION: '26.2'
24-
PREBUILT_DIR: ${{ github.workspace }}/.tmp/ios-runner-prebuilt
24+
DERIVED_DATA_PATH: ${{ github.workspace }}/.tmp/ios-runner-derived
25+
AGENT_DEVICE_IOS_RUNNER_DERIVED_PATH: ${{ github.workspace }}/.tmp/ios-runner-derived
2526
AGENT_DEVICE_IOS_SIMCTL_LIST_TIMEOUT_MS: "60000"
2627
AGENT_DEVICE_DAEMON_TIMEOUT_MS: "300000"
2728
AGENT_DEVICE_IOS_BOOT_TIMEOUT_MS: "180000"
@@ -40,20 +41,18 @@ jobs:
4041
XCODE_KEY="$(echo "$XCODE_VERSION" | tr ' ' '-' | tr -cd '[:alnum:]._-')"
4142
echo "key=$XCODE_KEY" >> "$GITHUB_OUTPUT"
4243
44+
- name: Resolve prebuild source hash
45+
id: source-hash
46+
run: echo "value=${{ hashFiles('ios-runner/**', 'package.json', 'pnpm-lock.yaml') }}" >> "$GITHUB_OUTPUT"
47+
4348
- name: Restore iOS runner prebuilt cache
4449
id: restore-prebuilt
4550
uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.2.3
4651
with:
47-
path: ${{ env.PREBUILT_DIR }}
48-
key: ios-runner-prebuilt-${{ steps.xcode.outputs.key }}-ios-${{ env.IOS_RUNTIME_VERSION }}
49-
50-
- name: Hydrate derived data from prebuilt cache
51-
if: steps.restore-prebuilt.outputs.cache-hit == 'true'
52-
run: |
53-
set -euo pipefail
54-
mkdir -p "$HOME/.agent-device/ios-runner"
55-
rm -rf "$HOME/.agent-device/ios-runner/derived"
56-
cp -R "$PREBUILT_DIR/derived-data" "$HOME/.agent-device/ios-runner/derived"
52+
path: ${{ env.DERIVED_DATA_PATH }}
53+
key: ios-runner-prebuilt-${{ steps.xcode.outputs.key }}-ios-${{ env.IOS_RUNTIME_VERSION }}-${{ steps.source-hash.outputs.value }}-stable
54+
restore-keys: |
55+
ios-runner-prebuilt-${{ steps.xcode.outputs.key }}-ios-${{ env.IOS_RUNTIME_VERSION }}-${{ steps.source-hash.outputs.value }}-
5756
5857
- name: Resolve agent-device home
5958
id: ios-agent-home
@@ -66,7 +65,8 @@ jobs:
6665
xcrun simctl list devices -j | node -e "
6766
const fs = require('node:fs');
6867
const payload = JSON.parse(fs.readFileSync(0, 'utf8'));
69-
const runtimeKey = 'com.apple.CoreSimulator.SimRuntime.iOS-26-2';
68+
const runtimeVersion = process.env.IOS_RUNTIME_VERSION;
69+
const runtimeKey = 'com.apple.CoreSimulator.SimRuntime.iOS-' + runtimeVersion.replace(/\./g, '-');
7070
const devices = payload.devices?.[runtimeKey] ?? [];
7171
const available = devices.filter((d) => d.isAvailable && d.name === 'iPhone 17 Pro');
7272
const preferred = available.find((d) => d.state === 'Booted') ?? available[0];
@@ -82,7 +82,14 @@ jobs:
8282
8383
- name: Build iOS integration artifacts
8484
if: steps.restore-prebuilt.outputs.cache-hit != 'true'
85-
run: pnpm build:xcuitest
85+
run: |
86+
set -euo pipefail
87+
rm -rf "$DERIVED_DATA_PATH"
88+
xcodebuild build-for-testing \
89+
-project ios-runner/AgentDeviceRunner/AgentDeviceRunner.xcodeproj \
90+
-scheme AgentDeviceRunner \
91+
-destination "platform=iOS Simulator,name=iPhone 17 Pro,OS=${IOS_RUNTIME_VERSION}" \
92+
-derivedDataPath "$DERIVED_DATA_PATH"
8693
8794
- name: Run iOS integration test
8895
run: node --test test/integration/ios.test.ts

src/platforms/ios/runner-client.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -292,8 +292,7 @@ async function ensureXctestrun(
292292
udid: string,
293293
options: { verbose?: boolean; logPath?: string; traceLogPath?: string },
294294
): Promise<string> {
295-
const base = path.join(os.homedir(), '.agent-device', 'ios-runner');
296-
const derived = path.join(base, 'derived');
295+
const derived = resolveRunnerDerivedPath();
297296
if (shouldCleanDerived()) {
298297
try {
299298
fs.rmSync(derived, { recursive: true, force: true });
@@ -354,6 +353,15 @@ async function ensureXctestrun(
354353
return built;
355354
}
356355

356+
function resolveRunnerDerivedPath(): string {
357+
const override = process.env.AGENT_DEVICE_IOS_RUNNER_DERIVED_PATH?.trim();
358+
if (override) {
359+
return path.resolve(override);
360+
}
361+
const base = path.join(os.homedir(), '.agent-device', 'ios-runner');
362+
return path.join(base, 'derived');
363+
}
364+
357365
function findXctestrun(root: string): string | null {
358366
if (!fs.existsSync(root)) return null;
359367
const candidates: { path: string; mtimeMs: number }[] = [];

0 commit comments

Comments
 (0)