refactor: colocate native platform sources under android/, apple/, linux/ (#1273)

Move the scattered root-level native projects into per-platform folders and drop
the now-redundant platform prefix:

- android-ime-helper/        -> android/ime-helper/
- android-multitouch-helper/ -> android/multitouch-helper/
- android-snapshot-helper/   -> android/snapshot-helper/
- apple-runner/              -> apple/runner/
- macos-helper/              -> apple/macos-helper/
- src/platforms/linux/atspi-dump.py -> linux/atspi-dump.py

Only repo source paths move. Identity surfaces stay frozen so no user's runner
cache is invalidated on upgrade: the derived-cache key hashes source paths
relative to AgentDeviceRunner and excludes packageVersion, and the
~/.agent-device/{apple-runner,macos-helper} namespaces, the
agent-device-android-*-helper artifact/manifest/protocol names, the
AgentDeviceRunner Xcode project, and the `prepare ios-runner` CLI command are
unchanged. Updates build/package scripts, CI, package.json files+scripts,
ignore/attr/fallow configs, runtime path resolvers, and test fixtures.

Also: re-base repo-root-relative refs inside the moved apple/runner for the
added nesting level (gated XCUITest fixture walk + two doc links), and clean the
legacy dist/apple-runner packaged output so the relocated runner can't
double-ship into the wholesale-included dist (with a regression test).
This commit is contained in:
Michał Pierzchała
2026-07-15 21:47:38 +02:00
committed by GitHub
parent 37895caf99
commit e58cbcdb5f
102 changed files with 145 additions and 107 deletions
+1 -1
View File
@@ -28,7 +28,7 @@
"examples/test-app/**",
"scripts/perf/**",
"scripts/layering/**",
"apple-runner/AgentDeviceRunner/AgentDeviceRunnerUITests.xctestplan",
"apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests.xctestplan",
"scripts/write-xcuitest-cache-metadata.mjs"
],
"ignoreDependencies": ["@theme"],
+1 -1
View File
@@ -1 +1 @@
android-snapshot-helper/debug.keystore binary
android/snapshot-helper/debug.keystore binary
@@ -52,7 +52,7 @@ runs:
id: source-hash
run: |
set -euo pipefail
echo "value=${{ hashFiles('apple-runner/**', 'scripts/build-xcuitest-apple.sh', 'scripts/patch-xcuitest-runner-icon.ts', 'scripts/write-xcuitest-cache-metadata.mjs', 'src/platforms/ios/apple-runner-platform.ts', 'src/platforms/ios/runner-icon.ts', 'src/platforms/ios/runner-xctestrun.ts', 'src/platforms/ios/runner-xctestrun-products.ts', '.github/actions/setup-apple-replay/action.yml', 'package.json', 'pnpm-lock.yaml') }}" >> "$GITHUB_OUTPUT"
echo "value=${{ hashFiles('apple/runner/**', 'scripts/build-xcuitest-apple.sh', 'scripts/patch-xcuitest-runner-icon.ts', 'scripts/write-xcuitest-cache-metadata.mjs', 'src/platforms/ios/apple-runner-platform.ts', 'src/platforms/ios/runner-icon.ts', 'src/platforms/ios/runner-xctestrun.ts', 'src/platforms/ios/runner-xctestrun-products.ts', '.github/actions/setup-apple-replay/action.yml', 'package.json', 'pnpm-lock.yaml') }}" >> "$GITHUB_OUTPUT"
shell: bash
- name: Cache replay prebuilt
+1 -1
View File
@@ -32,7 +32,7 @@ jobs:
- name: Disallow trailing commas before closing parenthesis in Swift
run: |
if rg -nU --glob '*.swift' ',\s*\n\s*\)' apple-runner; then
if rg -nU --glob '*.swift' ',\s*\n\s*\)' apple/runner; then
echo "Found trailing commas before ')' in Swift files. This syntax requires Swift 6.1+ and breaks older Xcode toolchains."
exit 1
fi
+1 -1
View File
@@ -97,7 +97,7 @@ jobs:
echo "=== AT-SPI2 Python bindings ==="
python3 -c "import gi; gi.require_version('Atspi', '2.0'); from gi.repository import Atspi; print('OK')"
echo "=== AT-SPI2 tree dump (quick test) ==="
python3 src/platforms/linux/atspi-dump.py --surface desktop --max-nodes 5 | python3 -m json.tool | head -20 || echo "::warning::AT-SPI2 tree dump returned no nodes (expected before any app is launched)"
python3 linux/atspi-dump.py --surface desktop --max-nodes 5 | python3 -m json.tool | head -20 || echo "::warning::AT-SPI2 tree dump returned no nodes (expected before any app is launched)"
echo "=== xdotool ==="
xdotool version
+6 -6
View File
@@ -34,9 +34,9 @@ xcuserdata/
*.xctestrun
*.xcarchive
.skillgym-results/
android-snapshot-helper/build/
android-snapshot-helper/dist/
android-multitouch-helper/build/
android-multitouch-helper/dist/
android-ime-helper/build/
android-ime-helper/dist/
android/snapshot-helper/build/
android/snapshot-helper/dist/
android/multitouch-helper/build/
android/multitouch-helper/dist/
android/ime-helper/build/
android/ime-helper/dist/
+4 -4
View File
@@ -8,10 +8,10 @@ examples/test-app/.expo/
.build/
.swiftpm/
DerivedData/
android-snapshot-helper/build/
android-snapshot-helper/dist/
android-multitouch-helper/build/
android-multitouch-helper/dist/
android/snapshot-helper/build/
android/snapshot-helper/dist/
android/multitouch-helper/build/
android/multitouch-helper/dist/
# Tool caches
.fallow/
@@ -20,7 +20,7 @@ sh ./scripts/build-android-snapshot-helper.sh "$VERSION" .tmp/android-snapshot-h
The build uses Android SDK command-line tools directly. It expects `ANDROID_HOME` or
`ANDROID_SDK_ROOT` to point at an SDK with `platforms/android-36` and matching build tools.
`pnpm prepack` builds the npm-bundled helper into `android-snapshot-helper/dist`; npm users get
`pnpm prepack` builds the npm-bundled helper into `android/snapshot-helper/dist`; npm users get
that APK in the package and the first helper-backed `snapshot` installs it automatically when
missing or outdated.
@@ -53,7 +53,8 @@ extension RunnerTests {
let fixtureURL = URL(fileURLWithPath: #filePath)
.deletingLastPathComponent() // AgentDeviceRunnerUITests
.deletingLastPathComponent() // AgentDeviceRunner
.deletingLastPathComponent() // apple-runner
.deletingLastPathComponent() // runner
.deletingLastPathComponent() // apple
.deletingLastPathComponent() // repo root
.appendingPathComponent("contracts")
.appendingPathComponent("fixtures")
@@ -15,7 +15,7 @@ Current internal runner for iOS, tvOS, and macOS desktop automation.
Protocol and maintenance references:
- Protocol overview: [`RUNNER_PROTOCOL.md`](RUNNER_PROTOCOL.md)
- TypeScript client: [`../src/platforms/apple/core/runner/runner-client.ts`](../src/platforms/apple/core/runner/runner-client.ts)
- TypeScript client: [`../../src/platforms/apple/core/runner/runner-client.ts`](../../src/platforms/apple/core/runner/runner-client.ts)
- Swift wire models: [`AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Models.swift`](AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Models.swift)
## UITest Runner File Map
@@ -46,7 +46,7 @@ serialize reliably. In those cases interactive snapshots may return a sparse roo
full snapshots preserve the XCTest error. A penalized simulator can recover through private AX;
physical devices use a short XCTest probe because no non-XCTest semantic backend is available
there. See
[`../docs/adr/0004-ios-snapshot-backend-strategy.md`](../docs/adr/0004-ios-snapshot-backend-strategy.md)
[`../../docs/adr/0004-ios-snapshot-backend-strategy.md`](../../docs/adr/0004-ios-snapshot-backend-strategy.md)
for the backend boundary and future simulator AX-service direction.
## Protocol Notes
@@ -41,7 +41,7 @@ Examples:
The current command names are defined in:
- [`../src/platforms/apple/core/runner/runner-client.ts`](../src/platforms/apple/core/runner/runner-client.ts)
- [`../../src/platforms/apple/core/runner/runner-client.ts`](../../src/platforms/apple/core/runner/runner-client.ts)
- [`AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Models.swift`](AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Models.swift)
## Response Shape
+16 -16
View File
@@ -90,16 +90,16 @@
"build:xcuitest:visionos:clean": "pnpm clean:xcuitest:visionos && pnpm build:xcuitest:visionos",
"build:android-snapshot-helper": "sh ./scripts/build-android-snapshot-helper.sh $(node -p \"require('./package.json').version\") .tmp/android-snapshot-helper",
"package:android-snapshot-helper": "sh ./scripts/package-android-snapshot-helper.sh $(node -p \"require('./package.json').version\") v$(node -p \"require('./package.json').version\") .tmp/android-snapshot-helper",
"package:android-snapshot-helper:npm": "rm -rf android-snapshot-helper/dist && sh ./scripts/package-android-snapshot-helper.sh $(node -p \"require('./package.json').version\") v$(node -p \"require('./package.json').version\") android-snapshot-helper/dist",
"package:android-snapshot-helper:npm": "rm -rf android/snapshot-helper/dist && sh ./scripts/package-android-snapshot-helper.sh $(node -p \"require('./package.json').version\") v$(node -p \"require('./package.json').version\") android/snapshot-helper/dist",
"package:apple-runner:npm": "node scripts/package-apple-runner-source.mjs",
"build:android-multitouch-helper": "sh ./scripts/build-android-multitouch-helper.sh $(node -p \"require('./package.json').version\") .tmp/android-multitouch-helper",
"package:android-multitouch-helper": "sh ./scripts/package-android-multitouch-helper.sh $(node -p \"require('./package.json').version\") .tmp/android-multitouch-helper",
"package:android-multitouch-helper:npm": "rm -rf android-multitouch-helper/dist && sh ./scripts/package-android-multitouch-helper.sh $(node -p \"require('./package.json').version\") android-multitouch-helper/dist",
"package:android-multitouch-helper:npm": "rm -rf android/multitouch-helper/dist && sh ./scripts/package-android-multitouch-helper.sh $(node -p \"require('./package.json').version\") android/multitouch-helper/dist",
"build:android": "pnpm package:android-snapshot-helper:npm && pnpm package:android-multitouch-helper:npm && pnpm package:android-ime-helper:npm",
"build:android-ime-helper": "sh ./scripts/build-android-ime-helper.sh $(node -p \"require('./package.json').version\") .tmp/android-ime-helper",
"package:android-ime-helper": "sh ./scripts/package-android-ime-helper.sh $(node -p \"require('./package.json').version\") .tmp/android-ime-helper",
"package:android-ime-helper:npm": "rm -rf android-ime-helper/dist && sh ./scripts/package-android-ime-helper.sh $(node -p \"require('./package.json').version\") android-ime-helper/dist",
"build:macos-helper": "swift build -c release --package-path macos-helper",
"package:android-ime-helper:npm": "rm -rf android/ime-helper/dist && sh ./scripts/package-android-ime-helper.sh $(node -p \"require('./package.json').version\") android/ime-helper/dist",
"build:macos-helper": "swift build -c release --package-path apple/macos-helper",
"build:all": "pnpm build:node && pnpm build:xcuitest",
"ad": "node bin/agent-device.mjs",
"bench:help-conformance": "node scripts/help-conformance-bench.mjs",
@@ -163,18 +163,18 @@
"files": [
"bin",
"dist",
"macos-helper",
"!macos-helper/**/.build",
"android-snapshot-helper/dist",
"!android-snapshot-helper/dist/*.idsig",
"!android-snapshot-helper/README.md",
"android-multitouch-helper/dist",
"!android-multitouch-helper/dist/*.idsig",
"!android-multitouch-helper/README.md",
"android-ime-helper/dist",
"!android-ime-helper/dist/*.idsig",
"!android-ime-helper/README.md",
"src/platforms/linux/atspi-dump.py",
"apple/macos-helper",
"!apple/macos-helper/**/.build",
"android/snapshot-helper/dist",
"!android/snapshot-helper/dist/*.idsig",
"!android/snapshot-helper/README.md",
"android/multitouch-helper/dist",
"!android/multitouch-helper/dist/*.idsig",
"!android/multitouch-helper/README.md",
"android/ime-helper/dist",
"!android/ime-helper/dist/*.idsig",
"!android/ime-helper/README.md",
"linux/atspi-dump.py",
"skills",
"server.json",
"smithery.yaml",
+2 -2
View File
@@ -9,7 +9,7 @@ fi
VERSION="$1"
OUTPUT_DIR="$2"
PROJECT_DIR="$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)"
HELPER_DIR="$PROJECT_DIR/android-ime-helper"
HELPER_DIR="$PROJECT_DIR/android/ime-helper"
PACKAGE_NAME="com.callstack.agentdevice.imehelper"
MIN_SDK=23
TARGET_SDK=36
@@ -49,7 +49,7 @@ BUILD_DIR="$HELPER_DIR/build"
CLASSES_DIR="$BUILD_DIR/classes"
DEX_DIR="$BUILD_DIR/dex"
RES_COMPILED_DIR="$BUILD_DIR/res-compiled"
KEYSTORE="$PROJECT_DIR/android-snapshot-helper/debug.keystore"
KEYSTORE="$PROJECT_DIR/android/snapshot-helper/debug.keystore"
UNSIGNED_APK="$BUILD_DIR/helper-unsigned.apk"
ALIGNED_APK="$BUILD_DIR/helper-aligned.apk"
APK_PATH="$OUTPUT_DIR/$APK_BASENAME"
+2 -2
View File
@@ -9,7 +9,7 @@ fi
VERSION="$1"
OUTPUT_DIR="$2"
PROJECT_DIR="$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)"
HELPER_DIR="$PROJECT_DIR/android-multitouch-helper"
HELPER_DIR="$PROJECT_DIR/android/multitouch-helper"
PACKAGE_NAME="com.callstack.agentdevice.multitouchhelper"
MIN_SDK=23
TARGET_SDK=36
@@ -49,7 +49,7 @@ BUILD_DIR="$HELPER_DIR/build"
CLASSES_DIR="$BUILD_DIR/classes"
TEST_CLASSES_DIR="$BUILD_DIR/test-classes"
DEX_DIR="$BUILD_DIR/dex"
KEYSTORE="$PROJECT_DIR/android-snapshot-helper/debug.keystore"
KEYSTORE="$PROJECT_DIR/android/snapshot-helper/debug.keystore"
UNSIGNED_APK="$BUILD_DIR/helper-unsigned.apk"
ALIGNED_APK="$BUILD_DIR/helper-aligned.apk"
APK_PATH="$OUTPUT_DIR/$APK_BASENAME"
+1 -1
View File
@@ -9,7 +9,7 @@ fi
VERSION="$1"
OUTPUT_DIR="$2"
PROJECT_DIR="$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)"
HELPER_DIR="$PROJECT_DIR/android-snapshot-helper"
HELPER_DIR="$PROJECT_DIR/android/snapshot-helper"
PACKAGE_NAME="com.callstack.agentdevice.snapshothelper"
MIN_SDK=23
TARGET_SDK=36
+1 -1
View File
@@ -2,7 +2,7 @@
set -eu
PLATFORM="${AGENT_DEVICE_XCUITEST_PLATFORM:-}"
PROJECT_PATH="apple-runner/AgentDeviceRunner/AgentDeviceRunner.xcodeproj"
PROJECT_PATH="apple/runner/AgentDeviceRunner/AgentDeviceRunner.xcodeproj"
SCHEME="AgentDeviceRunner"
DEFAULT_IOS_RUNNER_APP_BUNDLE_ID="com.callstack.agentdevice.runner"
+3 -3
View File
@@ -75,13 +75,13 @@ test('android-adb stub test delegates project ownership to Vitest', () => {
});
test('Swift runner change selects the swift-runner build', () => {
assert.deepEqual(ids(['apple-runner/Sources/Runner/Main.swift']), ['swift-runner']);
assert.deepEqual(ids(['apple/runner/Sources/Runner/Main.swift']), ['swift-runner']);
assert.ok(ids(['src/platforms/apple/core/runner/Support.swift']).includes('swift-runner'));
});
test('Android helper change selects the android-helpers build', () => {
assert.deepEqual(ids(['android-snapshot-helper/src/Main.kt']), ['android-helpers']);
assert.deepEqual(ids(['android-multitouch-helper/build.gradle']), ['android-helpers']);
assert.deepEqual(ids(['android/snapshot-helper/src/Main.kt']), ['android-helpers']);
assert.deepEqual(ids(['android/multitouch-helper/build.gradle']), ['android-helpers']);
});
test('MCP metadata change selects the mcp-metadata check', () => {
+4 -3
View File
@@ -244,20 +244,21 @@ const BUILD_OWNERSHIP: ReadonlyArray<{
check: 'swift-runner',
rule: 'own:swift',
detail: 'Swift runner sources require the XCUITest build',
owns: (file) => file.startsWith('apple-runner/') || file.endsWith('.swift'),
owns: (file) => file.startsWith('apple/runner/') || file.endsWith('.swift'),
},
{
check: 'android-helpers',
rule: 'own:android-helpers',
detail: 'Android helper packages have their own build',
owns: (file) =>
file.startsWith('android-snapshot-helper/') || file.startsWith('android-multitouch-helper/'),
file.startsWith('android/snapshot-helper/') ||
file.startsWith('android/multitouch-helper/'),
},
{
check: 'macos-helper',
rule: 'own:macos-helper',
detail: 'macOS helper is a separate Swift package build',
owns: (file) => file.startsWith('macos-helper/'),
owns: (file) => file.startsWith('apple/macos-helper/'),
},
{
check: 'mcp-metadata',
+13 -2
View File
@@ -4,8 +4,16 @@ import path from 'node:path';
import { fileURLToPath } from 'node:url';
const UNIT_TEST_CONDITION = 'AGENT_DEVICE_RUNNER_UNIT_TESTS';
const SOURCE_DIR = path.join('apple-runner');
const OUTPUT_DIR = path.join('dist', 'apple-runner');
const SOURCE_DIR = path.join('apple', 'runner');
const OUTPUT_DIR = path.join('dist', 'apple', 'runner');
// Packaged-runner locations from before the apple-runner/ -> apple/runner/ move. `dist` ships
// wholesale, so a stale tree left by an older build/checkout would double-ship into the npm
// package (and inflate the bundle-size diff, which packages the base then the PR into one dist).
// Always remove them so only the current OUTPUT_DIR survives.
const LEGACY_OUTPUT_DIRS = [
path.join('dist', 'apple-runner'),
path.join('dist', 'apple', 'apple-runner'),
];
const SKIPPED_DIR_NAMES = new Set(['.build', '.swiftpm', 'xcuserdata']);
const SKIPPED_ROOT_FILES = new Set(['README.md', 'RUNNER_PROTOCOL.md']);
@@ -18,6 +26,9 @@ export function packageAppleRunnerSource(options = {}) {
}
fs.rmSync(outputRoot, { recursive: true, force: true });
for (const legacyDir of LEGACY_OUTPUT_DIRS) {
fs.rmSync(path.join(root, legacyDir), { recursive: true, force: true });
}
const summary = {
outputRoot,
copiedFiles: 0,
+1 -1
View File
@@ -49,7 +49,7 @@ function resolveRunnerTestBundleId() {
}
function computeRunnerSourceFingerprint() {
const runnerRoot = path.join(projectRoot, 'apple-runner', 'AgentDeviceRunner');
const runnerRoot = path.join(projectRoot, 'apple', 'runner', 'AgentDeviceRunner');
const files = collectRunnerSourceFiles(runnerRoot);
const hash = crypto.createHash('sha256');
for (const file of files) {
@@ -10,28 +10,28 @@ const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../
const packageScript = path.join(repoRoot, 'scripts', 'package-apple-runner-source.mjs');
const runnerSnapshotSwiftPath = path.join(
repoRoot,
'apple-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Snapshot.swift',
'apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Snapshot.swift',
);
test('package apple runner source strips unit-test blocks without mutating checkout source', async () => {
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'agent-device-runner-package-'));
onTestFinished(() => fs.rmSync(root, { recursive: true, force: true }));
writeFixtureFile(root, 'apple-runner/README.md', 'developer docs\n');
writeFixtureFile(root, 'apple-runner/.build/cache.txt', 'cache\n');
writeFixtureFile(root, 'apple/runner/README.md', 'developer docs\n');
writeFixtureFile(root, 'apple/runner/.build/cache.txt', 'cache\n');
writeFixtureFile(
root,
'apple-runner/AgentDeviceRunner/AgentDeviceRunner.xcodeproj/project.pbxproj',
'apple/runner/AgentDeviceRunner/AgentDeviceRunner.xcodeproj/project.pbxproj',
'',
);
writeFixtureFile(
root,
'apple-runner/AgentDeviceRunner/AgentDeviceRunner.xcodeproj/xcuserdata/user.xcuserstate',
'apple/runner/AgentDeviceRunner/AgentDeviceRunner.xcodeproj/xcuserdata/user.xcuserstate',
'state\n',
);
writeFixtureFile(
root,
'apple-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Feature.swift',
'apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Feature.swift',
[
'extension RunnerTests {',
' func runtimeHelper() {}',
@@ -54,11 +54,11 @@ test('package apple runner source strips unit-test blocks without mutating check
const sourceSwiftPath = path.join(
root,
'apple-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Feature.swift',
'apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Feature.swift',
);
const packagedSwiftPath = path.join(
root,
'dist/apple-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Feature.swift',
'dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Feature.swift',
);
const sourceSwift = fs.readFileSync(sourceSwiftPath, 'utf8');
const packagedSwift = fs.readFileSync(packagedSwiftPath, 'utf8');
@@ -70,22 +70,52 @@ test('package apple runner source strips unit-test blocks without mutating check
assert.match(packagedSwift, /#if os\(macOS\)/);
assert.ok(
fs.existsSync(
path.join(root, 'dist/apple-runner/AgentDeviceRunner/AgentDeviceRunner.xcodeproj'),
path.join(root, 'dist/apple/runner/AgentDeviceRunner/AgentDeviceRunner.xcodeproj'),
),
);
assert.equal(fs.existsSync(path.join(root, 'dist/apple-runner/README.md')), false);
assert.equal(fs.existsSync(path.join(root, 'dist/apple-runner/.build/cache.txt')), false);
assert.equal(fs.existsSync(path.join(root, 'dist/apple/runner/README.md')), false);
assert.equal(fs.existsSync(path.join(root, 'dist/apple/runner/.build/cache.txt')), false);
assert.equal(
fs.existsSync(
path.join(
root,
'dist/apple-runner/AgentDeviceRunner/AgentDeviceRunner.xcodeproj/xcuserdata/user.xcuserstate',
'dist/apple/runner/AgentDeviceRunner/AgentDeviceRunner.xcodeproj/xcuserdata/user.xcuserstate',
),
),
false,
);
});
test('package apple runner source removes legacy dist/apple-runner output before shipping', async () => {
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'agent-device-runner-package-legacy-'));
onTestFinished(() => fs.rmSync(root, { recursive: true, force: true }));
// Minimal current-layout source so packaging succeeds.
writeFixtureFile(
root,
'apple/runner/AgentDeviceRunner/AgentDeviceRunner.xcodeproj/project.pbxproj',
'',
);
// Stale packaged trees left by builds/checkouts predating the apple-runner -> apple/runner
// move. `dist` ships wholesale, so these must not survive packaging or they double-ship.
writeFixtureFile(
root,
'dist/apple-runner/AgentDeviceRunner/RunnerTests+Legacy.swift',
'legacy\n',
);
writeFixtureFile(
root,
'dist/apple/apple-runner/AgentDeviceRunner/RunnerTests+Mid.swift',
'mid\n',
);
await runCmd(process.execPath, [packageScript, '--root', root, '--quiet']);
assert.equal(fs.existsSync(path.join(root, 'dist/apple-runner')), false);
assert.equal(fs.existsSync(path.join(root, 'dist/apple/apple-runner')), false);
assert.ok(fs.existsSync(path.join(root, 'dist/apple/runner/AgentDeviceRunner')));
});
test('apple runner tree snapshot capture stays on the main queue', () => {
const source = fs.readFileSync(runnerSnapshotSwiftPath, 'utf8');
const boundedCapture = extractSwiftFunction(source, 'captureSnapshotRootBounded');
@@ -6,7 +6,8 @@ export const PROJECT_ROOT = path.resolve(import.meta.dirname, '..', '..', '..');
const RUNNER_SOURCES_DIR = path.join(
PROJECT_ROOT,
'apple-runner',
'apple',
'runner',
'AgentDeviceRunner',
'AgentDeviceRunnerUITests',
);
+1 -1
View File
@@ -20,7 +20,7 @@ import {
const IOS_RUNNER_XCODEBUILD_KILL_PATTERNS = [
'xcodebuild .*AgentDeviceRunnerUITests/RunnerTests/testCommand',
'xcodebuild .*AgentDeviceRunner\\.env\\.session-',
'xcodebuild build-for-testing .*apple-runner/AgentDeviceRunner/AgentDeviceRunner\\.xcodeproj',
'xcodebuild build-for-testing .*apple/runner/AgentDeviceRunner/AgentDeviceRunner\\.xcodeproj',
];
type BoundedTimeoutPolicy = CommandTimeoutPolicy & { envelopeMs: number };
@@ -6,7 +6,7 @@ const NORMAL_IME = 'com.google.android.inputmethod.latin/.LatinIME';
// probeAndroidTestIme reads the helper's service component from the bundled artifact; inject a
// fixture so the orphan-detection checks pass on a fresh checkout that hasn't packaged
// android-ime-helper/dist (CI's Coverage job runs no packaging step).
// android/ime-helper/dist (CI's Coverage job runs no packaging step).
vi.mock('../../../platforms/android/ime-helper.ts', async (importOriginal) => {
const actual = await importOriginal<typeof import('../../../platforms/android/ime-helper.ts')>();
return {
@@ -11,7 +11,7 @@ import { findProjectRoot } from '../../../utils/version.ts';
const SERVICE_SRC = path.join(
findProjectRoot(),
'android-ime-helper/src/main/java/com/callstack/agentdevice/imehelper/TestInputMethodService.java',
'android/ime-helper/src/main/java/com/callstack/agentdevice/imehelper/TestInputMethodService.java',
);
function readService(): string {
@@ -9,7 +9,7 @@ const SETTINGS_KEY = 'agent_device_ime_helper_previous_ime';
const PENDING_DIR = 'android-test-ime-pending';
// activateAndroidTestIme reads the bundled artifact for the service component; inject a fixture so
// the suite passes on a fresh checkout that hasn't packaged android-ime-helper/dist (CI Coverage).
// the suite passes on a fresh checkout that hasn't packaged android/ime-helper/dist (CI Coverage).
vi.mock('../ime-helper.ts', async (importOriginal) => {
const actual = await importOriginal<typeof import('../ime-helper.ts')>();
const fixture = await import('../../../__tests__/test-utils/android-snapshot-helper.ts');
@@ -3,7 +3,7 @@ import assert from 'node:assert/strict';
const PACKAGE = 'com.callstack.agentdevice.imehelper';
// Inject a fixture artifact so the tests never read android-ime-helper/dist from disk (which a
// Inject a fixture artifact so the tests never read android/ime-helper/dist from disk (which a
// fresh checkout that hasn't packaged the helper won't have — CI's Coverage job included).
vi.mock('../ime-helper.ts', async (importOriginal) => {
const actual = await importOriginal<typeof import('../ime-helper.ts')>();
@@ -141,7 +141,7 @@ export async function resolveAndroidHelperArtifact<
unavailableMessage: string;
}): Promise<{ apkPath: string; manifest: Manifest }> {
const version = readVersion();
const helperDir = path.join(findProjectRoot(), options.helperDirName, 'dist');
const helperDir = path.join(findProjectRoot(), 'android', options.helperDirName, 'dist');
const manifestPath = path.join(helperDir, options.manifestFileName(version));
try {
const manifest = options.parseManifest(JSON.parse(await fs.readFile(manifestPath, 'utf8')));
+1 -1
View File
@@ -49,7 +49,7 @@ export type AndroidImeHelperArtifact = {
export async function resolveAndroidImeHelperArtifact(): Promise<AndroidImeHelperArtifact> {
return await resolveAndroidHelperArtifact({
helperDirName: 'android-ime-helper',
helperDirName: 'ime-helper',
manifestFileName: (version) => `agent-device-android-ime-helper-${version}.manifest.json`,
parseManifest: parseAndroidImeHelperManifest,
unavailableMessage:
@@ -36,7 +36,7 @@ export type AndroidMultiTouchHelperArtifact = {
export async function resolveAndroidMultiTouchHelperArtifact(): Promise<AndroidMultiTouchHelperArtifact> {
return await resolveAndroidHelperArtifact({
helperDirName: 'android-multitouch-helper',
helperDirName: 'multitouch-helper',
manifestFileName: (version) =>
`agent-device-android-multitouch-helper-${version}.manifest.json`,
parseManifest: parseAndroidMultiTouchHelperManifest,
+1 -1
View File
@@ -513,7 +513,7 @@ async function resolveAndroidSnapshotHelperArtifact(
}
const version = readVersion();
const helperDir = path.join(findProjectRoot(), 'android-snapshot-helper', 'dist');
const helperDir = path.join(findProjectRoot(), 'android', 'snapshot-helper', 'dist');
const manifestPath = path.join(
helperDir,
`agent-device-android-snapshot-helper-${version}.manifest.json`,
@@ -183,7 +183,7 @@ beforeEach(() => {
test('resolveMacOsHelperPackageRootFrom finds helper package from source and dist-like paths', async () => {
const repoRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'agent-device-helper-root-'));
const helperRoot = path.join(repoRoot, 'macos-helper');
const helperRoot = path.join(repoRoot, 'apple', 'macos-helper');
await fs.mkdir(helperRoot, { recursive: true });
await fs.writeFile(path.join(helperRoot, 'Package.swift'), '// test\n', 'utf8');
@@ -975,7 +975,7 @@ test('xctestrunReferencesProjectRoot rejects stale worktree artifacts', async ()
const xctestrunPath = path.join(tmpDir, 'AgentDeviceRunner.xctestrun');
fs.writeFileSync(
xctestrunPath,
'<plist><dict><key>SourceFilesCommonPathPrefix</key><string>/tmp/other-worktree/agent-device/apple-runner/AgentDeviceRunner</string></dict></plist>',
'<plist><dict><key>SourceFilesCommonPathPrefix</key><string>/tmp/other-worktree/agent-device/apple/runner/AgentDeviceRunner</string></dict></plist>',
'utf8',
);
@@ -1026,7 +1026,8 @@ test('resolveRunnerDerivedPath reuses cache path for identical runner source fin
const firstRoot = path.join(tmpDir, 'first');
const secondRoot = path.join(tmpDir, 'second');
const runnerRelativePath = path.join(
'apple-runner',
'apple',
'runner',
'AgentDeviceRunner',
'AgentDeviceRunnerUITests',
'RunnerTests.swift',
@@ -1283,7 +1284,8 @@ test('ensureXctestrunArtifact rebuilds after cached macOS runner repair failure'
const { derivedPath, existingXctestrunPath } = await makeCachedRunnerXctestrun();
const projectPath = path.join(
projectRoot,
'apple-runner',
'apple',
'runner',
'AgentDeviceRunner',
'AgentDeviceRunner.xcodeproj',
);
@@ -10,8 +10,8 @@ import {
test('resolveAppleRunnerSourceRoot prefers checkout source over packaged source', () => {
const root = makeTempRoot();
const checkoutSource = path.join(root, 'apple-runner', 'AgentDeviceRunner');
const packagedSource = path.join(root, 'dist', 'apple-runner', 'AgentDeviceRunner');
const checkoutSource = path.join(root, 'apple', 'runner', 'AgentDeviceRunner');
const packagedSource = path.join(root, 'dist', 'apple', 'runner', 'AgentDeviceRunner');
fs.mkdirSync(path.join(checkoutSource, 'AgentDeviceRunner.xcodeproj'), { recursive: true });
fs.mkdirSync(path.join(packagedSource, 'AgentDeviceRunner.xcodeproj'), { recursive: true });
@@ -24,7 +24,7 @@ test('resolveAppleRunnerSourceRoot prefers checkout source over packaged source'
test('resolveAppleRunnerSourceRoot falls back to packaged source', () => {
const root = makeTempRoot();
const packagedSource = path.join(root, 'dist', 'apple-runner', 'AgentDeviceRunner');
const packagedSource = path.join(root, 'dist', 'apple', 'runner', 'AgentDeviceRunner');
fs.mkdirSync(path.join(packagedSource, 'AgentDeviceRunner.xcodeproj'), { recursive: true });
assert.equal(resolveAppleRunnerSourceRoot(root), packagedSource);
@@ -215,12 +215,12 @@ test('setup metadata script matches expected iOS simulator cache metadata', asyn
const binDir = path.join(root, 'bin');
fs.mkdirSync(binDir, { recursive: true });
fs.mkdirSync(derivedRoot, { recursive: true });
fs.mkdirSync(path.join(projectRoot, 'apple-runner', 'AgentDeviceRunner'), {
fs.mkdirSync(path.join(projectRoot, 'apple', 'runner', 'AgentDeviceRunner'), {
recursive: true,
});
fs.writeFileSync(path.join(projectRoot, 'package.json'), '{"version":"0.19.0"}\n');
fs.writeFileSync(
path.join(projectRoot, 'apple-runner', 'AgentDeviceRunner', 'Runner.swift'),
path.join(projectRoot, 'apple', 'runner', 'AgentDeviceRunner', 'Runner.swift'),
'final class Runner {}\n',
);
writeExecutable(
@@ -1,8 +1,8 @@
import fs from 'node:fs';
import path from 'node:path';
const APPLE_RUNNER_SOURCE_ROOT = path.join('apple-runner', 'AgentDeviceRunner');
const PACKAGED_APPLE_RUNNER_SOURCE_ROOT = path.join('dist', 'apple-runner', 'AgentDeviceRunner');
const APPLE_RUNNER_SOURCE_ROOT = path.join('apple', 'runner', 'AgentDeviceRunner');
const PACKAGED_APPLE_RUNNER_SOURCE_ROOT = path.join('dist', 'apple', 'runner', 'AgentDeviceRunner');
export function resolveAppleRunnerSourceRoot(projectRoot: string): string {
const checkoutSourceRoot = path.join(projectRoot, APPLE_RUNNER_SOURCE_ROOT);
+2 -2
View File
@@ -19,7 +19,7 @@ import {
export type MacOsPermissionTarget = 'accessibility' | 'screen-recording' | 'input-monitoring';
// Keep this shape aligned with macOS helper SnapshotNodeResponse in
// macos-helper/Sources/AgentDeviceMacOSHelper/SnapshotTraversal.swift.
// apple/macos-helper/Sources/AgentDeviceMacOSHelper/SnapshotTraversal.swift.
export type MacOsSnapshotNode = {
index: number;
type?: string;
@@ -99,7 +99,7 @@ function appendMacOsHelperContextArgs(
export function resolveMacOsHelperPackageRootFrom(modulePath: string): string {
let currentDir = path.dirname(modulePath);
while (true) {
const candidate = path.join(currentDir, 'macos-helper');
const candidate = path.join(currentDir, 'apple', 'macos-helper');
if (existsSync(path.join(candidate, 'Package.swift'))) {
return candidate;
}
+6 -14
View File
@@ -30,29 +30,21 @@ const SCRIPT_NAME = 'atspi-dump.py';
let cachedScriptPath: string | null = null;
/** Resolve atspi-dump.py relative to this module, checking both source and dist layouts. */
/** Resolve atspi-dump.py, which lives under `linux/` at the repo/package root. */
function resolveScriptPath(): string {
if (cachedScriptPath) return cachedScriptPath;
const thisDir = path.dirname(fileURLToPath(import.meta.url));
// Walk upward looking for the script — handles both:
// src/platforms/linux/ (source)
// dist/src/ (bundled, .py lives in package root under src/platforms/linux/)
// Walk upward looking for linux/atspi-dump.py — handles both:
// <repo root>/linux/ (source; this module lives at src/platforms/linux/)
// <package root>/linux/ (bundled; this module lives under dist/src/)
let dir = thisDir;
for (let i = 0; i < 5; i++) {
const candidate = path.join(dir, 'src', 'platforms', 'linux', SCRIPT_NAME);
for (let i = 0; i < 6; i++) {
const candidate = path.join(dir, 'linux', SCRIPT_NAME);
if (fs.existsSync(candidate)) {
cachedScriptPath = candidate;
return candidate;
}
// Also check same-directory (running from source dir directly)
if (i === 0) {
const sameDir = path.join(dir, SCRIPT_NAME);
if (fs.existsSync(sameDir)) {
cachedScriptPath = sameDir;
return sameDir;
}
}
dir = path.dirname(dir);
}
+1 -1
View File
@@ -160,7 +160,7 @@ test('recording script candidates include packaged dist apple-runner source', ()
const packageRoot = path.join(tmpDir, 'package');
const scriptPath = path.join(
packageRoot,
'dist/apple-runner/AgentDeviceRunner/RecordingScripts/recording-overlay.swift',
'dist/apple/runner/AgentDeviceRunner/RecordingScripts/recording-overlay.swift',
);
fs.mkdirSync(path.dirname(scriptPath), { recursive: true });
fs.writeFileSync(scriptPath, 'print("overlay")\n');
@@ -8,7 +8,7 @@ import { getRecordingOverlaySupportWarning } from '../overlay.ts';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const recordingScriptsDir = path.resolve(
__dirname,
'../../../apple-runner/AgentDeviceRunner/RecordingScripts',
'../../../apple/runner/AgentDeviceRunner/RecordingScripts',
);
const recordingTestSupportDir = path.resolve(__dirname, '../../../test/integration/support');
const SWIFT_TYPECHECK_TIMEOUT_MS = 60_000;
+2 -2
View File
@@ -17,7 +17,7 @@ export function buildRecordingScriptPathCandidates(
projectRoot: string,
cwd: string,
): string[] {
const sourceScriptPath = `apple-runner/AgentDeviceRunner/RecordingScripts/${scriptName}`;
const sourceScriptPath = `apple/runner/AgentDeviceRunner/RecordingScripts/${scriptName}`;
const packagedScriptPath = `dist/${sourceScriptPath}`;
return [
path.resolve(moduleDir, scriptName),
@@ -46,7 +46,7 @@ function resolveRecordingScriptPath(scriptName: string): string {
}
throw new AppError('COMMAND_FAILED', `Missing recording helper script: ${scriptName}`, {
hint: 'Ensure apple-runner/AgentDeviceRunner/RecordingScripts is present in this checkout or bundled under dist/apple-runner in the package.',
hint: 'Ensure apple/runner/AgentDeviceRunner/RecordingScripts is present in this checkout or bundled under dist/apple/runner in the package.',
scriptName,
searchedPaths: scriptCandidates,
});
@@ -6,7 +6,7 @@ import { isTapPointInsideViewport } from '../mobile-snapshot-semantics.ts';
import type { Rect } from '../../kernel/snapshot.ts';
// ADR 0011 Layer 2 golden parity table: the SAME JSON is asserted against the
// Swift twin (TapPointPolicy in apple-runner/AgentDeviceRunner/
// Swift twin (TapPointPolicy in apple/runner/AgentDeviceRunner/
// AgentDeviceRunnerUITests/RunnerTapPointPolicy.swift, gated XCTest in the
// same file), so drift between the runner's ELEMENT_OFFSCREEN guard and the
// runtime's offscreen rule turns CI red on whichever side changed.

Some files were not shown because too many files have changed in this diff Show More