Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .fallowrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
"src/daemon.ts",
"packages/capture-kit/src/png-worker.ts",
"scripts/patch-xcuitest-runner-icon.ts",
"scripts/write-xcuitest-cache-metadata.ts",
"scripts/xcuitest-build-settings.ts",
"packages/capture-kit/src/ios-snapshot-engine/replay.ts",
// #1596 regression fixture: runs as a real `node --experimental-strip-types`
// subprocess (test/integration/daemon-replace-exit-flush.test.ts), so
Expand All @@ -41,7 +43,6 @@
"scripts/di-seams/**",
"scripts/maestro-conformance/corpus/**",
"apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests.xctestplan",
"scripts/write-xcuitest-cache-metadata.mjs",
"scripts/help-conformance-sample-outputs.d.mts",
"scripts/help-conformance-runner-output.d.mts"
],
Expand Down
21 changes: 15 additions & 6 deletions .github/actions/setup-apple-runner-build/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,11 @@ inputs:
description: 'Registered check id built through `pnpm gate`'
required: true
xcuitest-platform:
description: 'Optional AGENT_DEVICE_XCUITEST_PLATFORM value'
required: false
default: ''
description: 'AGENT_DEVICE_XCUITEST_PLATFORM value the runner build and its cache metadata are keyed by'
required: true
xcuitest-destination:
description: 'Optional AGENT_DEVICE_XCUITEST_DESTINATION value'
required: false
default: ''
description: 'AGENT_DEVICE_XCUITEST_DESTINATION value, which must carry a platform= token'
required: true
outputs:
cache-hit:
description: 'Whether an exact Apple runner build cache was restored'
Expand Down Expand Up @@ -108,6 +106,17 @@ runs:
shell: bash
run: node --experimental-strip-types scripts/patch-xcuitest-runner-icon.ts "${{ inputs.derived-path }}"

# The icon patch rewrites product bytes after the cache was saved unpatched, so the
# content manifest on this machine must certify the tree that will actually run.
- name: Re-publish Apple runner cache metadata
shell: bash
run: |
Comment thread
cubic-dev-ai[bot] marked this conversation as resolved.
node --experimental-strip-types scripts/write-xcuitest-cache-metadata.ts \
"${{ inputs.xcuitest-platform }}" \
"${{ inputs.derived-path }}" \
"${{ inputs.xcuitest-destination }}" \
"${{ inputs.derived-path }}/Logs/agent-device-build-for-testing.log"

- name: Report Apple runner build cache
env:
CACHE_HIT: ${{ steps.restore-runner-build.outputs.cache-hit }}
Expand Down
2 changes: 1 addition & 1 deletion packages/contracts/src/prepare.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { DeviceKind, PublicPlatform } from '@agent-device/kernel/device';
import type { JsonObject } from './json.ts';

export type PrepareIosRunnerCacheKind = 'exact' | 'restore-key' | 'miss' | 'external';
export type PrepareIosRunnerCacheKind = 'exact' | 'miss' | 'external';
export type PrepareIosRunnerArtifactState = 'valid' | 'rebuilt';

export type PrepareIosRunnerTiming = {
Expand Down
19 changes: 19 additions & 0 deletions packages/platform-apple/src/core/runner-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,22 @@ export {
hasCachedAppleRunnerArtifact,
resolveRunnerAppBundleId,
} from '../runner/runner-xctestrun.ts';
export { findXctestrun as findRunnerXctestrun } from '../runner/runner-artifact.ts';
export { resolveExistingXctestrunProductPaths as resolveExistingRunnerProductPaths } from '../runner/runner-xctestrun-products.ts';
export {
requireRunnerBuildSettingsMatchBuildLog,
resolveExpectedRunnerCacheMetadata,
resolveRunnerArchBuildSettings,
resolveRunnerBundleBuildSettings,
resolveRunnerPerformanceBuildSettings,
resolveRunnerSandboxBuildArgs,
resolveRunnerSigningBuildSettings,
} from '../runner/runner-cache-metadata.ts';
export {
isRunnerXcuitestScriptPlatform,
resolveRunnerScriptDevice,
} from '../runner/apple-runner-platform.ts';
export {
requireCertifiedRunnerCacheArtifacts,
writeRunnerCacheMetadataForArtifacts,
} from '../runner/runner-cache.ts';
13 changes: 13 additions & 0 deletions packages/platform-apple/src/runner-operations-facade.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export {
applyXctestRunnerAppIconFromDerivedPath,
detachIosRunnerSessionsForShutdown,
findRunnerXctestrun,
hasLiveIosRunnerSession,
notifyIosRunnerAppRelaunched,
prepareIosRunner,
Expand All @@ -10,11 +11,23 @@ export {
readStaleRunnerLease,
releaseIosRunnerOnClose,
releaseSpeculativeIosRunnerSessionFor,
resolveExistingRunnerProductPaths,
resolveExpectedRunnerCacheMetadata,
resolveRunnerAppBundleId,
resolveRunnerArchBuildSettings,
resolveRunnerBundleBuildSettings,
resolveRunnerPerformanceBuildSettings,
resolveRunnerSandboxBuildArgs,
isRunnerXcuitestScriptPlatform,
resolveRunnerScriptDevice,
resolveRunnerSigningBuildSettings,
requireCertifiedRunnerCacheArtifacts,
requireRunnerBuildSettingsMatchBuildLog,
runAppleRunnerCommand,
stopAllIosRunnerSessions,
stopIosRunnerSession,
verifyLeaseRunnerPidIdentity,
writeRunnerCacheMetadataForArtifacts,
} from './core/runner-client.ts';
export { queryAppleRunnerSelector } from './core/runner-selector-query.ts';
export { restoreLegacyXctestDeviceSetRedirect } from './runner/runner-device-set.ts';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { test } from 'vitest';
import assert from 'node:assert/strict';
import {
isRunnerXcuitestScriptPlatform,
resolveRunnerDestination,
resolveRunnerScriptDevice,
resolveRunnerHandoffTarget,
resolveRunnerPlatformName,
resolveRunnerSdkName,
Expand Down Expand Up @@ -168,6 +170,47 @@ test('the usbmux-only xctest backend is refused while coredevice is named explic
});
});

test('resolveRunnerScriptDevice reads the simulator kind from the destination platform token', () => {
const udid = '5AF10197-87C1-4799-835E-3C6CBF9F3163';

assert.equal(
resolveRunnerScriptDevice('ios', `platform=iOS Simulator,id=${udid}`).kind,
'simulator',
);
// xcodebuild matches the platform token case-insensitively, so a lowercase spelling still
// builds the simulator SDK and must not be certified as a physical-device runner.
assert.equal(
resolveRunnerScriptDevice('ios', `platform=iOS simulator,id=${udid}`).kind,
'simulator',
);
assert.equal(
resolveRunnerScriptDevice('ios', 'generic/platform=iOS Simulator').kind,
'simulator',
);
assert.equal(resolveRunnerScriptDevice('ios', 'generic/platform=iOS').kind, 'device');
assert.equal(resolveRunnerScriptDevice('tvos', 'platform=tvOS Simulator,id=x').kind, 'simulator');
});

test('resolveRunnerScriptDevice records a macOS build as the host device', () => {
assert.equal(resolveRunnerScriptDevice('macos', 'platform=macOS,arch=arm64').kind, 'device');
assert.equal(resolveRunnerScriptDevice('macos', 'platform=macOS,arch=arm64').target, 'desktop');
});

test('resolveRunnerScriptDevice refuses a destination that leaves the SDK to the scheme', () => {
assert.throws(
() => resolveRunnerScriptDevice('ios', 'id=5AF10197-87C1-4799-835E-3C6CBF9F3163'),
/must name its platform/,
);
});

test('isRunnerXcuitestScriptPlatform accepts only the platforms the build script knows', () => {
assert.equal(isRunnerXcuitestScriptPlatform('ios'), true);
assert.equal(isRunnerXcuitestScriptPlatform('visionos'), true);
assert.equal(isRunnerXcuitestScriptPlatform('watchos'), false);
assert.equal(isRunnerXcuitestScriptPlatform('iOS'), false);
assert.equal(isRunnerXcuitestScriptPlatform(''), false);
});

test('a non-Apple target is refused instead of defaulting into the physical lane', () => {
assert.deepEqual(
resolveRunnerHandoffTarget({
Expand Down
7 changes: 7 additions & 0 deletions packages/platform-apple/src/runner/__tests__/digest-file.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import crypto from 'node:crypto';
import fs from 'node:fs';

/** sha256 of a file's bytes, the same digest a cache manifest records. */
export function digestFile(filePath: string): string {
return crypto.createHash('sha256').update(fs.readFileSync(filePath)).digest('hex');
}
Loading
Loading