@tamagui/native-ci 2.7.6 → 3.0.0-beta.637.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/android-utils.mjs +62 -0
- package/dist/android-utils.mjs.map +1 -0
- package/dist/cache.mjs +54 -70
- package/dist/cache.mjs.map +1 -1
- package/dist/cli.mjs +378 -403
- package/dist/cli.mjs.map +1 -1
- package/dist/constants.mjs +3 -2
- package/dist/constants.mjs.map +1 -1
- package/dist/deps.mjs +52 -64
- package/dist/deps.mjs.map +1 -1
- package/dist/detox.mjs +220 -202
- package/dist/detox.mjs.map +1 -1
- package/dist/fingerprint.mjs +35 -33
- package/dist/fingerprint.mjs.map +1 -1
- package/dist/index.js +10 -10
- package/dist/index.mjs +10 -10
- package/dist/ios-utils.mjs +318 -0
- package/dist/ios-utils.mjs.map +1 -0
- package/dist/metro.mjs +137 -139
- package/dist/metro.mjs.map +1 -1
- package/dist/runner.mjs +62 -65
- package/dist/runner.mjs.map +1 -1
- package/package.json +4 -4
- package/src/cli.ts +3 -3
- package/src/constants.ts +1 -1
- package/src/detox.ts +1 -0
- package/src/index.ts +2 -2
- package/src/metro.ts +2 -2
- package/src/run-detox-android.ts +1 -1
- package/src/run-detox-ios.ts +1 -1
- package/types/{android.d.ts → android-utils.d.ts} +1 -1
- package/types/android-utils.d.ts.map +1 -0
- package/types/cli.d.ts +1 -1
- package/types/constants.d.ts +2 -2
- package/types/detox.d.ts.map +1 -1
- package/types/index.d.ts +2 -2
- package/types/index.d.ts.map +1 -1
- package/types/{ios.d.ts → ios-utils.d.ts} +1 -1
- package/types/ios-utils.d.ts.map +1 -0
- package/dist/index.js.map +0 -1
- package/dist/index.mjs.map +0 -1
- package/types/android.d.ts.map +0 -1
- package/types/ios.d.ts.map +0 -1
- /package/src/{android.ts → android-utils.ts} +0 -0
- /package/src/{ios.ts → ios-utils.ts} +0 -0
package/dist/index.mjs
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { DEFAULT_KV_TTL_SECONDS, DEFAULT_METRO_TIMEOUT_MS, DEFAULT_METRO_WAIT_ATTEMPTS, DEFAULT_METRO_WAIT_INTERVAL_MS, DETOX_SERVER_PORT, METRO_HOST, METRO_PORT, METRO_URL } from "./constants.mjs";
|
|
2
2
|
import { generateFingerprint, generatePreFingerprintHash } from "./fingerprint.mjs";
|
|
3
|
-
import { createCacheKey,
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import { checkDeps,
|
|
10
|
-
|
|
11
|
-
|
|
3
|
+
import { createCacheKey, extendKVTTL, getFingerprintFromKV, loadCache, saveCache, saveFingerprintToKV } from "./cache.mjs";
|
|
4
|
+
import { isCI, isGitHubActions, runWithCache, setGitHubOutput } from "./runner.mjs";
|
|
5
|
+
import { prewarmBundle, setupSignalHandlers, startMetro, waitForMetro, withMetro } from "./metro.mjs";
|
|
6
|
+
import { buildDetoxArgs, parseDetoxArgs, runDetoxTests } from "./detox.mjs";
|
|
7
|
+
import { ensureAndroidFolder, setupAdbReverse, setupAndroidDevice, waitForDevice } from "./android-utils.mjs";
|
|
8
|
+
import { cleanupSimulators, ensureIOSApp, ensureIOSFolder } from "./ios-utils.mjs";
|
|
9
|
+
import { checkDeps, ensureAndroidDeps, ensureIosDeps, ensureMaestro, printDepsStatus } from "./deps.mjs";
|
|
10
|
+
|
|
11
|
+
export { DEFAULT_KV_TTL_SECONDS, DEFAULT_METRO_TIMEOUT_MS, DEFAULT_METRO_WAIT_ATTEMPTS, DEFAULT_METRO_WAIT_INTERVAL_MS, DETOX_SERVER_PORT, METRO_HOST, METRO_PORT, METRO_URL, buildDetoxArgs, checkDeps, cleanupSimulators, createCacheKey, ensureAndroidDeps, ensureAndroidFolder, ensureIOSApp, ensureIOSFolder, ensureIosDeps, ensureMaestro, extendKVTTL, generateFingerprint, generatePreFingerprintHash, getFingerprintFromKV, isCI, isGitHubActions, loadCache, parseDetoxArgs, prewarmBundle, printDepsStatus, runDetoxTests, runWithCache, saveCache, saveFingerprintToKV, setGitHubOutput, setupAdbReverse, setupAndroidDevice, setupSignalHandlers, startMetro, waitForDevice, waitForMetro, withMetro };
|
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { isAbsolute, join } from "node:path";
|
|
3
|
+
import { execFileSync, execSync } from "node:child_process";
|
|
4
|
+
import { $ } from "bun";
|
|
5
|
+
import { isCI } from "./runner.mjs";
|
|
6
|
+
import { generateFingerprint } from "./fingerprint.mjs";
|
|
7
|
+
|
|
8
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, { get: (a, b) => (typeof require !== "undefined" ? require : a)[b] }) : x)(function(x) {
|
|
9
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
10
|
+
throw Error("Calling `require` for \"" + x + "\" in an environment that doesn't expose the `require` function. See https://rolldown.rs/in-depth/bundling-cjs#require-external-modules for more details.");
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
function hasBootedSimulator() {
|
|
14
|
+
return !!getBootedSimulatorUDID();
|
|
15
|
+
}
|
|
16
|
+
function getBootedSimulatorUDID() {
|
|
17
|
+
try {
|
|
18
|
+
const output = execSync("xcrun simctl list devices booted", {
|
|
19
|
+
encoding: "utf-8",
|
|
20
|
+
stdio: [
|
|
21
|
+
"pipe",
|
|
22
|
+
"pipe",
|
|
23
|
+
"pipe"
|
|
24
|
+
]
|
|
25
|
+
});
|
|
26
|
+
const match = output.match(/\(([A-F0-9-]{36})\)/i);
|
|
27
|
+
return match ? match[1] : null;
|
|
28
|
+
} catch {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
function ensureBootedSimulator() {
|
|
33
|
+
if (hasBootedSimulator()) return;
|
|
34
|
+
console.info("No booted iOS simulator found, booting one...");
|
|
35
|
+
try {
|
|
36
|
+
const output = execSync("xcrun simctl list devices available", {
|
|
37
|
+
encoding: "utf-8",
|
|
38
|
+
stdio: [
|
|
39
|
+
"pipe",
|
|
40
|
+
"pipe",
|
|
41
|
+
"pipe"
|
|
42
|
+
]
|
|
43
|
+
});
|
|
44
|
+
const match = output.match(/iPhone[^\n]*\(([A-F0-9-]{36})\)/i);
|
|
45
|
+
if (!match) {
|
|
46
|
+
throw new Error("No available iPhone simulator found. Install one via Xcode.");
|
|
47
|
+
}
|
|
48
|
+
const udid = match[1];
|
|
49
|
+
console.info(`Booting simulator ${udid}...`);
|
|
50
|
+
execSync(`xcrun simctl boot ${udid}`, { stdio: "inherit" });
|
|
51
|
+
console.info("Simulator booted.");
|
|
52
|
+
} catch (err) {
|
|
53
|
+
throw new Error(`Failed to boot iOS simulator: ${err instanceof Error ? err.message : err}`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
function isAppInstalled(bundleId, udid) {
|
|
57
|
+
try {
|
|
58
|
+
execSync(`xcrun simctl get_app_container "${udid}" "${bundleId}"`, { stdio: [
|
|
59
|
+
"pipe",
|
|
60
|
+
"pipe",
|
|
61
|
+
"pipe"
|
|
62
|
+
] });
|
|
63
|
+
return true;
|
|
64
|
+
} catch {
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
function getIOSProjectName(projectRoot) {
|
|
69
|
+
const iosDir = join(projectRoot, "ios");
|
|
70
|
+
const iosEntries = readdirSync(iosDir);
|
|
71
|
+
const workspace = iosEntries.find((entry) => entry.endsWith(".xcworkspace"));
|
|
72
|
+
if (workspace) return workspace.replace(/\.xcworkspace$/, "");
|
|
73
|
+
const project = iosEntries.find((entry) => entry.endsWith(".xcodeproj"));
|
|
74
|
+
if (project) return project.replace(/\.xcodeproj$/, "");
|
|
75
|
+
throw new Error(`No iOS workspace or project found in ${iosDir}`);
|
|
76
|
+
}
|
|
77
|
+
function getIOSBuildConfiguration(config) {
|
|
78
|
+
return config.toLowerCase().includes("release") ? "Release" : "Debug";
|
|
79
|
+
}
|
|
80
|
+
function getIOSAppPath(projectRoot, config) {
|
|
81
|
+
if (process.env.DETOX_IOS_APP_PATH) return process.env.DETOX_IOS_APP_PATH;
|
|
82
|
+
const appName = getIOSProjectName(projectRoot);
|
|
83
|
+
const buildConfiguration = getIOSBuildConfiguration(config);
|
|
84
|
+
return `ios/build/Build/Products/${buildConfiguration}-iphonesimulator/${appName}.app`;
|
|
85
|
+
}
|
|
86
|
+
function getIOSFullAppPath(projectRoot, config) {
|
|
87
|
+
const appPath = getIOSAppPath(projectRoot, config);
|
|
88
|
+
return isAbsolute(appPath) ? appPath : join(projectRoot, appPath);
|
|
89
|
+
}
|
|
90
|
+
function getIOSPodTargetName(projectRoot) {
|
|
91
|
+
const podfile = readFileSync(join(projectRoot, "ios", "Podfile"), "utf-8");
|
|
92
|
+
const match = podfile.match(/^\s*target ['"]([^'"]+)['"] do/m);
|
|
93
|
+
if (!match) {
|
|
94
|
+
throw new Error(`No CocoaPods target found in ${join(projectRoot, "ios", "Podfile")}`);
|
|
95
|
+
}
|
|
96
|
+
return match[1];
|
|
97
|
+
}
|
|
98
|
+
function getIOSAppBundleId(fullAppPath) {
|
|
99
|
+
const infoPlist = join(fullAppPath, "Info.plist");
|
|
100
|
+
if (!existsSync(infoPlist)) return null;
|
|
101
|
+
try {
|
|
102
|
+
const bundleId = execFileSync("/usr/bin/plutil", [
|
|
103
|
+
"-extract",
|
|
104
|
+
"CFBundleIdentifier",
|
|
105
|
+
"raw",
|
|
106
|
+
"-o",
|
|
107
|
+
"-",
|
|
108
|
+
infoPlist
|
|
109
|
+
], {
|
|
110
|
+
encoding: "utf-8",
|
|
111
|
+
stdio: [
|
|
112
|
+
"ignore",
|
|
113
|
+
"pipe",
|
|
114
|
+
"pipe"
|
|
115
|
+
]
|
|
116
|
+
}).trim();
|
|
117
|
+
return bundleId || null;
|
|
118
|
+
} catch {
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
function isIOSAppBundleReady(fullAppPath) {
|
|
123
|
+
return existsSync(fullAppPath) && !!getIOSAppBundleId(fullAppPath);
|
|
124
|
+
}
|
|
125
|
+
async function ensureAppInstalled(opts) {
|
|
126
|
+
const { projectRoot, bundleId } = opts;
|
|
127
|
+
const udid = opts.udid || getBootedSimulatorUDID() || "booted";
|
|
128
|
+
if (isAppInstalled(bundleId, udid)) {
|
|
129
|
+
console.info(`App ${bundleId} already installed on simulator ${udid}`);
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
if (bundleId === "host.exp.Exponent") {
|
|
133
|
+
await installExpoGo(udid);
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
console.info(`App ${bundleId} not installed, building...`);
|
|
137
|
+
await ensureIOSFolder();
|
|
138
|
+
const appPath = getIOSAppPath(projectRoot, "ios.sim.debug");
|
|
139
|
+
const fullAppPath = getIOSFullAppPath(projectRoot, "ios.sim.debug");
|
|
140
|
+
if (!isIOSAppBundleReady(fullAppPath)) {
|
|
141
|
+
await ensureIOSApp("ios.sim.debug");
|
|
142
|
+
}
|
|
143
|
+
console.info(`Installing app on simulator ${udid}...`);
|
|
144
|
+
execSync(`xcrun simctl install "${udid}" "${fullAppPath}"`, { stdio: "inherit" });
|
|
145
|
+
console.info("App installed.");
|
|
146
|
+
}
|
|
147
|
+
function getMaestroBundleId(projectRoot) {
|
|
148
|
+
const flowsDir = join(projectRoot, "flows");
|
|
149
|
+
if (existsSync(flowsDir)) {
|
|
150
|
+
try {
|
|
151
|
+
const files = __require("fs").readdirSync(flowsDir);
|
|
152
|
+
for (const f of files) {
|
|
153
|
+
if (!f.endsWith(".yaml")) continue;
|
|
154
|
+
const content = readFileSync(join(flowsDir, f), "utf-8");
|
|
155
|
+
const match = content.match(/^appId:\s*(.+)$/m);
|
|
156
|
+
if (match) return match[1].trim();
|
|
157
|
+
}
|
|
158
|
+
} catch {}
|
|
159
|
+
}
|
|
160
|
+
try {
|
|
161
|
+
const appJson = JSON.parse(readFileSync(join(projectRoot, "app.json"), "utf-8"));
|
|
162
|
+
return appJson.expo?.ios?.bundleIdentifier || "host.exp.Exponent";
|
|
163
|
+
} catch {
|
|
164
|
+
return "host.exp.Exponent";
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
async function installExpoGo(udid) {
|
|
168
|
+
const tmpDir = join(__require("os").tmpdir(), "expo-go-install");
|
|
169
|
+
const appPath = join(tmpDir, "Expo Go.app");
|
|
170
|
+
if (existsSync(appPath)) {
|
|
171
|
+
console.info(`Installing cached Expo Go on simulator ${udid}...`);
|
|
172
|
+
execSync(`xcrun simctl install "${udid}" "${appPath}"`, { stdio: "inherit" });
|
|
173
|
+
console.info("Expo Go installed.");
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
console.info("Downloading Expo Go...");
|
|
177
|
+
mkdirSync(tmpDir, { recursive: true });
|
|
178
|
+
const { getVersionsAsync } = __require("@expo/cli/build/src/api/getVersions");
|
|
179
|
+
const versions = await getVersionsAsync();
|
|
180
|
+
const sdkVersions = Object.entries(versions.sdkVersions || {}).filter(([, v]) => v.iosClientUrl).sort(([a], [b]) => b.localeCompare(a, void 0, { numeric: true }));
|
|
181
|
+
const clientUrl = sdkVersions[0]?.[1]?.iosClientUrl;
|
|
182
|
+
if (!clientUrl) {
|
|
183
|
+
throw new Error("Could not find Expo Go download URL");
|
|
184
|
+
}
|
|
185
|
+
execSync(`curl -fsSL "${clientUrl}" -o "${tmpDir}/ExpoGo.tar.gz"`, { stdio: "inherit" });
|
|
186
|
+
mkdirSync(appPath, { recursive: true });
|
|
187
|
+
execSync(`tar -xzf "${tmpDir}/ExpoGo.tar.gz" -C "${appPath}"`, { stdio: "inherit" });
|
|
188
|
+
execSync(`xcrun simctl install "${udid}" "${appPath}"`, { stdio: "inherit" });
|
|
189
|
+
console.info("Expo Go installed.");
|
|
190
|
+
}
|
|
191
|
+
async function cleanupSimulators() {
|
|
192
|
+
console.info("\n--- Cleaning up simulators ---");
|
|
193
|
+
try {
|
|
194
|
+
execSync("xcrun simctl shutdown all 2>/dev/null || true", { stdio: "pipe" });
|
|
195
|
+
execSync("pkill -9 -f \"Simulator.app\" 2>/dev/null || true", { stdio: "pipe" });
|
|
196
|
+
const booted = execSync("xcrun simctl list devices | grep -i booted | wc -l", { encoding: "utf-8" }).trim();
|
|
197
|
+
console.info(`Simulators cleaned up (${booted} still booted)`);
|
|
198
|
+
} catch {
|
|
199
|
+
console.info("Simulator cleanup completed (some commands may have failed)");
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
async function ensureIOSFolder() {
|
|
203
|
+
const podfilePath = join(process.cwd(), "ios", "Podfile");
|
|
204
|
+
if (!existsSync(podfilePath)) {
|
|
205
|
+
console.info("\n--- Generating ios/ folder (for Metro) ---");
|
|
206
|
+
console.info("Note: ios/Podfile not found, running expo prebuild");
|
|
207
|
+
try {
|
|
208
|
+
await $`npx expo prebuild --platform ios --no-install`;
|
|
209
|
+
console.info("iOS folder generated!");
|
|
210
|
+
} catch (error) {
|
|
211
|
+
const err = error;
|
|
212
|
+
throw new Error(`Failed to generate ios folder: ${err.message}`);
|
|
213
|
+
}
|
|
214
|
+
} else {
|
|
215
|
+
console.info("iOS folder already exists (Podfile found)");
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
const IOS_FINGERPRINT_CACHE = ".ios-build-fingerprint.json";
|
|
219
|
+
function loadFingerprintCache(projectRoot) {
|
|
220
|
+
const cachePath = join(projectRoot, IOS_FINGERPRINT_CACHE);
|
|
221
|
+
if (!existsSync(cachePath)) {
|
|
222
|
+
return null;
|
|
223
|
+
}
|
|
224
|
+
try {
|
|
225
|
+
return JSON.parse(readFileSync(cachePath, "utf-8"));
|
|
226
|
+
} catch {
|
|
227
|
+
return null;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
function saveFingerprintCache(projectRoot, cache) {
|
|
231
|
+
const cachePath = join(projectRoot, IOS_FINGERPRINT_CACHE);
|
|
232
|
+
writeFileSync(cachePath, JSON.stringify(cache, null, 2));
|
|
233
|
+
}
|
|
234
|
+
async function ensureIOSApp(config = "ios.sim.debug") {
|
|
235
|
+
if (isCI()) {
|
|
236
|
+
console.info("CI detected - skipping local build check");
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
const projectRoot = process.cwd();
|
|
240
|
+
const appPath = getIOSAppPath(projectRoot, config);
|
|
241
|
+
const fullAppPath = getIOSFullAppPath(projectRoot, config);
|
|
242
|
+
const appExists = isIOSAppBundleReady(fullAppPath);
|
|
243
|
+
if (!appExists) {
|
|
244
|
+
console.info(`
|
|
245
|
+
--- iOS app not found at ${appPath}, building... ---`);
|
|
246
|
+
await buildIOSApp(config, projectRoot, appPath, "");
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
let currentFingerprint;
|
|
250
|
+
try {
|
|
251
|
+
const result = await generateFingerprint({
|
|
252
|
+
platform: "ios",
|
|
253
|
+
projectRoot
|
|
254
|
+
});
|
|
255
|
+
currentFingerprint = result.hash;
|
|
256
|
+
} catch (err) {
|
|
257
|
+
console.warn("Could not generate fingerprint, using existing app");
|
|
258
|
+
console.info(`iOS app found at ${appPath}`);
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
const cache = loadFingerprintCache(projectRoot);
|
|
262
|
+
const fingerprintMatch = cache?.fingerprint === currentFingerprint && currentFingerprint !== "";
|
|
263
|
+
if (fingerprintMatch) {
|
|
264
|
+
console.info(`iOS app found at ${appPath} (fingerprint matches - no native changes)`);
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
if (cache?.fingerprint) {
|
|
268
|
+
console.info(`
|
|
269
|
+
--- iOS fingerprint changed ---`);
|
|
270
|
+
console.info(`Previous: ${cache.fingerprint.slice(0, 16)}...`);
|
|
271
|
+
console.info(`Current: ${currentFingerprint.slice(0, 16)}...`);
|
|
272
|
+
if (process.env.SKIP_IOS_REBUILD) {
|
|
273
|
+
console.info("SKIP_IOS_REBUILD set, using existing app");
|
|
274
|
+
saveFingerprintCache(projectRoot, {
|
|
275
|
+
fingerprint: currentFingerprint,
|
|
276
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
277
|
+
appPath
|
|
278
|
+
});
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
console.info("Native dependencies changed, rebuilding...");
|
|
282
|
+
await buildIOSApp(config, projectRoot, appPath, currentFingerprint);
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
console.info(`iOS app found at ${appPath} (saving fingerprint)`);
|
|
286
|
+
saveFingerprintCache(projectRoot, {
|
|
287
|
+
fingerprint: currentFingerprint,
|
|
288
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
289
|
+
appPath
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
async function buildIOSApp(config, projectRoot, appPath, fingerprint) {
|
|
293
|
+
const podConfigPath = join(projectRoot, "ios", "Pods", "Target Support Files", `Pods-${getIOSPodTargetName(projectRoot)}`, `Pods-${getIOSPodTargetName(projectRoot)}.debug.xcconfig`);
|
|
294
|
+
if (!existsSync(podConfigPath)) {
|
|
295
|
+
console.info("Installing CocoaPods dependencies...");
|
|
296
|
+
await $`pod install --project-directory=ios`;
|
|
297
|
+
}
|
|
298
|
+
const scheme = getIOSProjectName(projectRoot);
|
|
299
|
+
const buildConfiguration = getIOSBuildConfiguration(config);
|
|
300
|
+
const workspace = join("ios", `${scheme}.xcworkspace`);
|
|
301
|
+
const symroot = join(projectRoot, "ios", "build", "Build", "Products");
|
|
302
|
+
const objroot = join(projectRoot, "ios", "build", "Build", "Intermediates.noindex");
|
|
303
|
+
console.info(`Building iOS app (config: ${config})...`);
|
|
304
|
+
console.info("This may take a few minutes on first run.");
|
|
305
|
+
await $`xcodebuild -workspace ${workspace} -scheme ${scheme} -configuration ${buildConfiguration} -sdk iphonesimulator SYMROOT=${symroot} OBJROOT=${objroot}`;
|
|
306
|
+
if (fingerprint) {
|
|
307
|
+
saveFingerprintCache(projectRoot, {
|
|
308
|
+
fingerprint,
|
|
309
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
310
|
+
appPath
|
|
311
|
+
});
|
|
312
|
+
console.info("Saved build fingerprint to cache");
|
|
313
|
+
}
|
|
314
|
+
console.info("iOS app built successfully!");
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
export { cleanupSimulators, ensureAppInstalled, ensureBootedSimulator, ensureIOSApp, ensureIOSFolder, getBootedSimulatorUDID, getMaestroBundleId, hasBootedSimulator };
|
|
318
|
+
//# sourceMappingURL=ios-utils.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ios-utils.js","names":[],"sources":["ios-utils.js"],"sourcesContent":["import { existsSync, readFileSync, writeFileSync, mkdirSync, readdirSync } from \"node:fs\";\nimport { isAbsolute, join } from \"node:path\";\nimport { execFileSync, execSync } from \"node:child_process\";\nimport { $ } from \"bun\";\nimport { isCI } from \"./runner\";\nimport { generateFingerprint } from \"./fingerprint\";\nfunction hasBootedSimulator() {\n return !!getBootedSimulatorUDID();\n}\nfunction getBootedSimulatorUDID() {\n try {\n const output = execSync(\"xcrun simctl list devices booted\", {\n encoding: \"utf-8\",\n stdio: [\"pipe\", \"pipe\", \"pipe\"]\n });\n const match = output.match(/\\(([A-F0-9-]{36})\\)/i);\n return match ? match[1] : null;\n } catch {\n return null;\n }\n}\nfunction ensureBootedSimulator() {\n if (hasBootedSimulator()) return;\n console.info(\"No booted iOS simulator found, booting one...\");\n try {\n const output = execSync(\"xcrun simctl list devices available\", {\n encoding: \"utf-8\",\n stdio: [\"pipe\", \"pipe\", \"pipe\"]\n });\n const match = output.match(/iPhone[^\\n]*\\(([A-F0-9-]{36})\\)/i);\n if (!match) {\n throw new Error(\"No available iPhone simulator found. Install one via Xcode.\");\n }\n const udid = match[1];\n console.info(`Booting simulator ${udid}...`);\n execSync(`xcrun simctl boot ${udid}`, { stdio: \"inherit\" });\n console.info(\"Simulator booted.\");\n } catch (err) {\n throw new Error(\n `Failed to boot iOS simulator: ${err instanceof Error ? err.message : err}`\n );\n }\n}\nfunction isAppInstalled(bundleId, udid) {\n try {\n execSync(`xcrun simctl get_app_container \"${udid}\" \"${bundleId}\"`, {\n stdio: [\"pipe\", \"pipe\", \"pipe\"]\n });\n return true;\n } catch {\n return false;\n }\n}\nfunction getIOSProjectName(projectRoot) {\n const iosDir = join(projectRoot, \"ios\");\n const iosEntries = readdirSync(iosDir);\n const workspace = iosEntries.find((entry) => entry.endsWith(\".xcworkspace\"));\n if (workspace) return workspace.replace(/\\.xcworkspace$/, \"\");\n const project = iosEntries.find((entry) => entry.endsWith(\".xcodeproj\"));\n if (project) return project.replace(/\\.xcodeproj$/, \"\");\n throw new Error(`No iOS workspace or project found in ${iosDir}`);\n}\nfunction getIOSBuildConfiguration(config) {\n return config.toLowerCase().includes(\"release\") ? \"Release\" : \"Debug\";\n}\nfunction getIOSAppPath(projectRoot, config) {\n if (process.env.DETOX_IOS_APP_PATH) return process.env.DETOX_IOS_APP_PATH;\n const appName = getIOSProjectName(projectRoot);\n const buildConfiguration = getIOSBuildConfiguration(config);\n return `ios/build/Build/Products/${buildConfiguration}-iphonesimulator/${appName}.app`;\n}\nfunction getIOSFullAppPath(projectRoot, config) {\n const appPath = getIOSAppPath(projectRoot, config);\n return isAbsolute(appPath) ? appPath : join(projectRoot, appPath);\n}\nfunction getIOSPodTargetName(projectRoot) {\n const podfile = readFileSync(join(projectRoot, \"ios\", \"Podfile\"), \"utf-8\");\n const match = podfile.match(/^\\s*target ['\"]([^'\"]+)['\"] do/m);\n if (!match) {\n throw new Error(`No CocoaPods target found in ${join(projectRoot, \"ios\", \"Podfile\")}`);\n }\n return match[1];\n}\nfunction getIOSAppBundleId(fullAppPath) {\n const infoPlist = join(fullAppPath, \"Info.plist\");\n if (!existsSync(infoPlist)) return null;\n try {\n const bundleId = execFileSync(\n \"/usr/bin/plutil\",\n [\"-extract\", \"CFBundleIdentifier\", \"raw\", \"-o\", \"-\", infoPlist],\n {\n encoding: \"utf-8\",\n stdio: [\"ignore\", \"pipe\", \"pipe\"]\n }\n ).trim();\n return bundleId || null;\n } catch {\n return null;\n }\n}\nfunction isIOSAppBundleReady(fullAppPath) {\n return existsSync(fullAppPath) && !!getIOSAppBundleId(fullAppPath);\n}\nasync function ensureAppInstalled(opts) {\n const { projectRoot, bundleId } = opts;\n const udid = opts.udid || getBootedSimulatorUDID() || \"booted\";\n if (isAppInstalled(bundleId, udid)) {\n console.info(`App ${bundleId} already installed on simulator ${udid}`);\n return;\n }\n if (bundleId === \"host.exp.Exponent\") {\n await installExpoGo(udid);\n return;\n }\n console.info(`App ${bundleId} not installed, building...`);\n await ensureIOSFolder();\n const appPath = getIOSAppPath(projectRoot, \"ios.sim.debug\");\n const fullAppPath = getIOSFullAppPath(projectRoot, \"ios.sim.debug\");\n if (!isIOSAppBundleReady(fullAppPath)) {\n await ensureIOSApp(\"ios.sim.debug\");\n }\n console.info(`Installing app on simulator ${udid}...`);\n execSync(`xcrun simctl install \"${udid}\" \"${fullAppPath}\"`, { stdio: \"inherit\" });\n console.info(\"App installed.\");\n}\nfunction getMaestroBundleId(projectRoot) {\n const flowsDir = join(projectRoot, \"flows\");\n if (existsSync(flowsDir)) {\n try {\n const files = require(\"fs\").readdirSync(flowsDir);\n for (const f of files) {\n if (!f.endsWith(\".yaml\")) continue;\n const content = readFileSync(join(flowsDir, f), \"utf-8\");\n const match = content.match(/^appId:\\s*(.+)$/m);\n if (match) return match[1].trim();\n }\n } catch {\n }\n }\n try {\n const appJson = JSON.parse(readFileSync(join(projectRoot, \"app.json\"), \"utf-8\"));\n return appJson.expo?.ios?.bundleIdentifier || \"host.exp.Exponent\";\n } catch {\n return \"host.exp.Exponent\";\n }\n}\nasync function installExpoGo(udid) {\n const tmpDir = join(require(\"os\").tmpdir(), \"expo-go-install\");\n const appPath = join(tmpDir, \"Expo Go.app\");\n if (existsSync(appPath)) {\n console.info(`Installing cached Expo Go on simulator ${udid}...`);\n execSync(`xcrun simctl install \"${udid}\" \"${appPath}\"`, { stdio: \"inherit\" });\n console.info(\"Expo Go installed.\");\n return;\n }\n console.info(\"Downloading Expo Go...\");\n mkdirSync(tmpDir, { recursive: true });\n const { getVersionsAsync } = (\n // @ts-ignore - no type declarations for internal expo module\n require(\"@expo/cli/build/src/api/getVersions\")\n );\n const versions = await getVersionsAsync();\n const sdkVersions = Object.entries(versions.sdkVersions || {}).filter(([, v]) => v.iosClientUrl).sort(([a], [b]) => b.localeCompare(a, void 0, { numeric: true }));\n const clientUrl = sdkVersions[0]?.[1]?.iosClientUrl;\n if (!clientUrl) {\n throw new Error(\"Could not find Expo Go download URL\");\n }\n execSync(`curl -fsSL \"${clientUrl}\" -o \"${tmpDir}/ExpoGo.tar.gz\"`, { stdio: \"inherit\" });\n mkdirSync(appPath, { recursive: true });\n execSync(`tar -xzf \"${tmpDir}/ExpoGo.tar.gz\" -C \"${appPath}\"`, { stdio: \"inherit\" });\n execSync(`xcrun simctl install \"${udid}\" \"${appPath}\"`, { stdio: \"inherit\" });\n console.info(\"Expo Go installed.\");\n}\nasync function cleanupSimulators() {\n console.info(\"\\n--- Cleaning up simulators ---\");\n try {\n execSync(\"xcrun simctl shutdown all 2>/dev/null || true\", { stdio: \"pipe\" });\n execSync('pkill -9 -f \"Simulator.app\" 2>/dev/null || true', { stdio: \"pipe\" });\n const booted = execSync(\"xcrun simctl list devices | grep -i booted | wc -l\", {\n encoding: \"utf-8\"\n }).trim();\n console.info(`Simulators cleaned up (${booted} still booted)`);\n } catch {\n console.info(\"Simulator cleanup completed (some commands may have failed)\");\n }\n}\nasync function ensureIOSFolder() {\n const podfilePath = join(process.cwd(), \"ios\", \"Podfile\");\n if (!existsSync(podfilePath)) {\n console.info(\"\\n--- Generating ios/ folder (for Metro) ---\");\n console.info(\"Note: ios/Podfile not found, running expo prebuild\");\n try {\n await $`npx expo prebuild --platform ios --no-install`;\n console.info(\"iOS folder generated!\");\n } catch (error) {\n const err = error;\n throw new Error(`Failed to generate ios folder: ${err.message}`);\n }\n } else {\n console.info(\"iOS folder already exists (Podfile found)\");\n }\n}\nconst IOS_FINGERPRINT_CACHE = \".ios-build-fingerprint.json\";\nfunction loadFingerprintCache(projectRoot) {\n const cachePath = join(projectRoot, IOS_FINGERPRINT_CACHE);\n if (!existsSync(cachePath)) {\n return null;\n }\n try {\n return JSON.parse(readFileSync(cachePath, \"utf-8\"));\n } catch {\n return null;\n }\n}\nfunction saveFingerprintCache(projectRoot, cache) {\n const cachePath = join(projectRoot, IOS_FINGERPRINT_CACHE);\n writeFileSync(cachePath, JSON.stringify(cache, null, 2));\n}\nasync function ensureIOSApp(config = \"ios.sim.debug\") {\n if (isCI()) {\n console.info(\"CI detected - skipping local build check\");\n return;\n }\n const projectRoot = process.cwd();\n const appPath = getIOSAppPath(projectRoot, config);\n const fullAppPath = getIOSFullAppPath(projectRoot, config);\n const appExists = isIOSAppBundleReady(fullAppPath);\n if (!appExists) {\n console.info(`\n--- iOS app not found at ${appPath}, building... ---`);\n await buildIOSApp(config, projectRoot, appPath, \"\");\n return;\n }\n let currentFingerprint;\n try {\n const result = await generateFingerprint({ platform: \"ios\", projectRoot });\n currentFingerprint = result.hash;\n } catch (err) {\n console.warn(\"Could not generate fingerprint, using existing app\");\n console.info(`iOS app found at ${appPath}`);\n return;\n }\n const cache = loadFingerprintCache(projectRoot);\n const fingerprintMatch = cache?.fingerprint === currentFingerprint && currentFingerprint !== \"\";\n if (fingerprintMatch) {\n console.info(`iOS app found at ${appPath} (fingerprint matches - no native changes)`);\n return;\n }\n if (cache?.fingerprint) {\n console.info(`\n--- iOS fingerprint changed ---`);\n console.info(`Previous: ${cache.fingerprint.slice(0, 16)}...`);\n console.info(`Current: ${currentFingerprint.slice(0, 16)}...`);\n if (process.env.SKIP_IOS_REBUILD) {\n console.info(\"SKIP_IOS_REBUILD set, using existing app\");\n saveFingerprintCache(projectRoot, {\n fingerprint: currentFingerprint,\n timestamp: (/* @__PURE__ */ new Date()).toISOString(),\n appPath\n });\n return;\n }\n console.info(\"Native dependencies changed, rebuilding...\");\n await buildIOSApp(config, projectRoot, appPath, currentFingerprint);\n return;\n }\n console.info(`iOS app found at ${appPath} (saving fingerprint)`);\n saveFingerprintCache(projectRoot, {\n fingerprint: currentFingerprint,\n timestamp: (/* @__PURE__ */ new Date()).toISOString(),\n appPath\n });\n}\nasync function buildIOSApp(config, projectRoot, appPath, fingerprint) {\n const podConfigPath = join(\n projectRoot,\n \"ios\",\n \"Pods\",\n \"Target Support Files\",\n `Pods-${getIOSPodTargetName(projectRoot)}`,\n `Pods-${getIOSPodTargetName(projectRoot)}.debug.xcconfig`\n );\n if (!existsSync(podConfigPath)) {\n console.info(\"Installing CocoaPods dependencies...\");\n await $`pod install --project-directory=ios`;\n }\n const scheme = getIOSProjectName(projectRoot);\n const buildConfiguration = getIOSBuildConfiguration(config);\n const workspace = join(\"ios\", `${scheme}.xcworkspace`);\n const symroot = join(projectRoot, \"ios\", \"build\", \"Build\", \"Products\");\n const objroot = join(projectRoot, \"ios\", \"build\", \"Build\", \"Intermediates.noindex\");\n console.info(`Building iOS app (config: ${config})...`);\n console.info(\"This may take a few minutes on first run.\");\n await $`xcodebuild -workspace ${workspace} -scheme ${scheme} -configuration ${buildConfiguration} -sdk iphonesimulator SYMROOT=${symroot} OBJROOT=${objroot}`;\n if (fingerprint) {\n saveFingerprintCache(projectRoot, {\n fingerprint,\n timestamp: (/* @__PURE__ */ new Date()).toISOString(),\n appPath\n });\n console.info(\"Saved build fingerprint to cache\");\n }\n console.info(\"iOS app built successfully!\");\n}\nexport {\n cleanupSimulators,\n ensureAppInstalled,\n ensureBootedSimulator,\n ensureIOSApp,\n ensureIOSFolder,\n getBootedSimulatorUDID,\n getMaestroBundleId,\n hasBootedSimulator\n};\n//# sourceMappingURL=ios-utils.js.map\n"],"mappings":"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"}
|