@tamagui/native-ci 2.7.7 → 3.0.0-beta.643.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/cli.mjs
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
|
-
#!/usr/bin/env
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
2
|
import { generateFingerprint, generatePreFingerprintHash } from "./fingerprint.mjs";
|
|
3
|
-
import { createCacheKey,
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
3
|
+
import { createCacheKey, getFingerprintFromKV, loadCache, saveCache, saveFingerprintToKV } from "./cache.mjs";
|
|
4
|
+
import { isCI, isGitHubActions, setGitHubOutput } from "./runner.mjs";
|
|
5
|
+
import { ensureAndroidDeps, ensureIosDeps, ensureMaestro, printDepsStatus } from "./deps.mjs";
|
|
6
6
|
import { withMetro } from "./metro.mjs";
|
|
7
7
|
import { runDetoxTests } from "./detox.mjs";
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
8
|
+
import { ensureAppInstalled, ensureBootedSimulator, ensureIOSApp, ensureIOSFolder, getBootedSimulatorUDID, getMaestroBundleId } from "./ios-utils.mjs";
|
|
9
|
+
import { ensureAndroidFolder, setupAndroidDevice } from "./android-utils.mjs";
|
|
10
10
|
import { chmodSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
11
11
|
import { tmpdir } from "node:os";
|
|
12
12
|
import { join } from "node:path";
|
|
13
|
+
|
|
13
14
|
const HELP = `
|
|
14
15
|
native-ci - Native CI/CD helpers for Expo apps
|
|
15
16
|
|
|
@@ -63,9 +64,9 @@ EXAMPLES:
|
|
|
63
64
|
native-ci fingerprint-test
|
|
64
65
|
`;
|
|
65
66
|
function createMaestroXcrunShim() {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
67
|
+
const binDir = mkdtempSync(join(tmpdir(), "native-ci-maestro-"));
|
|
68
|
+
const xcrunPath = join(binDir, "xcrun");
|
|
69
|
+
writeFileSync(xcrunPath, `#!/bin/sh
|
|
69
70
|
if [ "$1" = "devicectl" ] && [ "$2" = "--json-output" ] && [ "$4" = "list" ] && [ "$5" = "devices" ]; then
|
|
70
71
|
printf '{"result":{"devices":[]}}\\n' > "$3"
|
|
71
72
|
exit 0
|
|
@@ -73,327 +74,307 @@ fi
|
|
|
73
74
|
|
|
74
75
|
exec /usr/bin/xcrun "$@"
|
|
75
76
|
`);
|
|
76
|
-
|
|
77
|
-
|
|
77
|
+
chmodSync(xcrunPath, 493);
|
|
78
|
+
return binDir;
|
|
78
79
|
}
|
|
79
80
|
function parseArgs(argv) {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
81
|
+
const args2 = [];
|
|
82
|
+
const options2 = {
|
|
83
|
+
projectRoot: process.cwd(),
|
|
84
|
+
config: "",
|
|
85
|
+
recordLogs: "all",
|
|
86
|
+
retries: 0,
|
|
87
|
+
headless: false,
|
|
88
|
+
prefix: "native-build",
|
|
89
|
+
githubOutput: false,
|
|
90
|
+
json: false,
|
|
91
|
+
help: false
|
|
92
|
+
};
|
|
93
|
+
let i = 0;
|
|
94
|
+
while (i < argv.length) {
|
|
95
|
+
const arg = argv[i];
|
|
96
|
+
if (arg === "--project-root" && argv[i + 1]) {
|
|
97
|
+
options2.projectRoot = argv[++i];
|
|
98
|
+
} else if (arg === "--config" && argv[i + 1]) {
|
|
99
|
+
options2.config = argv[++i];
|
|
100
|
+
} else if (arg === "--record-logs" && argv[i + 1]) {
|
|
101
|
+
options2.recordLogs = argv[++i];
|
|
102
|
+
} else if (arg === "--retries" && argv[i + 1]) {
|
|
103
|
+
const val = Number.parseInt(argv[++i], 10);
|
|
104
|
+
if (!Number.isNaN(val) && val >= 0) {
|
|
105
|
+
options2.retries = val;
|
|
106
|
+
}
|
|
107
|
+
} else if (arg === "--prefix" && argv[i + 1]) {
|
|
108
|
+
options2.prefix = argv[++i];
|
|
109
|
+
} else if (arg === "--github-output") {
|
|
110
|
+
options2.githubOutput = true;
|
|
111
|
+
} else if (arg === "--json") {
|
|
112
|
+
options2.json = true;
|
|
113
|
+
} else if (arg === "--help" || arg === "-h") {
|
|
114
|
+
options2.help = true;
|
|
115
|
+
} else if (!arg.startsWith("-")) {
|
|
116
|
+
args2.push(arg);
|
|
117
|
+
}
|
|
118
|
+
i++;
|
|
119
|
+
}
|
|
120
|
+
return {
|
|
121
|
+
command: args2[0] || "",
|
|
122
|
+
subcommand: args2[1] || "",
|
|
123
|
+
args: args2.slice(2),
|
|
124
|
+
options: options2
|
|
125
|
+
};
|
|
125
126
|
}
|
|
126
127
|
function validatePlatform(value) {
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
128
|
+
if (value !== "ios" && value !== "android") {
|
|
129
|
+
console.error("Error: platform must be \"ios\" or \"android\"");
|
|
130
|
+
process.exit(1);
|
|
131
|
+
}
|
|
132
|
+
return value;
|
|
132
133
|
}
|
|
133
134
|
function getKVCredentials() {
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
135
|
+
const url = process.env.KV_STORE_REDIS_REST_URL;
|
|
136
|
+
const token = process.env.KV_STORE_REDIS_REST_TOKEN;
|
|
137
|
+
if (!url || !token) {
|
|
138
|
+
console.error("Error: KV_STORE_REDIS_REST_URL and KV_STORE_REDIS_REST_TOKEN required");
|
|
139
|
+
process.exit(1);
|
|
140
|
+
}
|
|
141
|
+
return {
|
|
142
|
+
url,
|
|
143
|
+
token
|
|
144
|
+
};
|
|
144
145
|
}
|
|
145
|
-
const {
|
|
146
|
-
command,
|
|
147
|
-
subcommand,
|
|
148
|
-
args,
|
|
149
|
-
options
|
|
150
|
-
} = parseArgs(process.argv.slice(2));
|
|
146
|
+
const { command, subcommand, args, options } = parseArgs(process.argv.slice(2));
|
|
151
147
|
if (options.help || !command) {
|
|
152
|
-
|
|
153
|
-
|
|
148
|
+
console.info(HELP);
|
|
149
|
+
process.exit(options.help ? 0 : 1);
|
|
154
150
|
}
|
|
155
151
|
try {
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
} catch (err) {
|
|
288
|
-
console.error("Android tests failed:", err);
|
|
289
|
-
androidExit = 1;
|
|
290
|
-
}
|
|
291
|
-
const success = iosExit === 0 && androidExit === 0;
|
|
292
|
-
console.info(`
|
|
152
|
+
switch (command) {
|
|
153
|
+
case "test": {
|
|
154
|
+
if (isCI() && !process.env.NATIVE_CI_FORCE_RUN) {
|
|
155
|
+
console.info("Skipping native tests in CI (handled by separate workflow)");
|
|
156
|
+
console.info("Set NATIVE_CI_FORCE_RUN=1 to force run");
|
|
157
|
+
process.exit(0);
|
|
158
|
+
}
|
|
159
|
+
const platform = subcommand || "ios";
|
|
160
|
+
if (platform === "ios") {
|
|
161
|
+
ensureBootedSimulator();
|
|
162
|
+
await ensureIosDeps();
|
|
163
|
+
const config = options.config || "ios.sim.debug";
|
|
164
|
+
console.info("=== iOS Detox Test Runner ===");
|
|
165
|
+
console.info(`Config: ${config}`);
|
|
166
|
+
console.info(`Project root: ${options.projectRoot}`);
|
|
167
|
+
process.chdir(options.projectRoot);
|
|
168
|
+
await ensureIOSFolder();
|
|
169
|
+
await ensureIOSApp(config);
|
|
170
|
+
const exitCode = await withMetro("ios", async () => {
|
|
171
|
+
return runDetoxTests({
|
|
172
|
+
config,
|
|
173
|
+
projectRoot: options.projectRoot,
|
|
174
|
+
recordLogs: options.recordLogs,
|
|
175
|
+
retries: options.retries,
|
|
176
|
+
testFiles: args.length > 0 ? args : void 0
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
process.exit(exitCode);
|
|
180
|
+
} else if (platform === "android") {
|
|
181
|
+
await ensureAndroidDeps();
|
|
182
|
+
const config = options.config || "android.emu.debug";
|
|
183
|
+
console.info("=== Android Detox Test Runner ===");
|
|
184
|
+
console.info(`Config: ${config}`);
|
|
185
|
+
console.info(`Project root: ${options.projectRoot}`);
|
|
186
|
+
console.info(`Headless: ${options.headless}`);
|
|
187
|
+
process.chdir(options.projectRoot);
|
|
188
|
+
await ensureAndroidFolder();
|
|
189
|
+
await setupAndroidDevice();
|
|
190
|
+
const exitCode = await withMetro("android", async () => {
|
|
191
|
+
return runDetoxTests({
|
|
192
|
+
config,
|
|
193
|
+
projectRoot: options.projectRoot,
|
|
194
|
+
recordLogs: options.recordLogs,
|
|
195
|
+
retries: options.retries,
|
|
196
|
+
headless: options.headless,
|
|
197
|
+
testFiles: args.length > 0 ? args : void 0
|
|
198
|
+
});
|
|
199
|
+
});
|
|
200
|
+
process.exit(exitCode);
|
|
201
|
+
} else if (platform === "maestro") {
|
|
202
|
+
ensureBootedSimulator();
|
|
203
|
+
await ensureMaestro();
|
|
204
|
+
const flow = args[0] || "";
|
|
205
|
+
console.info("=== Maestro Test Runner ===");
|
|
206
|
+
console.info(`Flow: ${flow || "all"}`);
|
|
207
|
+
console.info(`Project root: ${options.projectRoot}`);
|
|
208
|
+
process.chdir(options.projectRoot);
|
|
209
|
+
const udid = getBootedSimulatorUDID();
|
|
210
|
+
const bundleId = getMaestroBundleId(options.projectRoot);
|
|
211
|
+
await ensureAppInstalled({
|
|
212
|
+
projectRoot: options.projectRoot,
|
|
213
|
+
bundleId,
|
|
214
|
+
udid
|
|
215
|
+
});
|
|
216
|
+
const exitCode = await withMetro("ios", async () => {
|
|
217
|
+
const { $ } = await import("bun");
|
|
218
|
+
const flowArg = flow ? `./flows/${flow}` : "./flows";
|
|
219
|
+
const udidArgs = ["--udid", udid];
|
|
220
|
+
const debugDir = `${options.projectRoot}/.maestro-debug`;
|
|
221
|
+
const xcrunShimDir = createMaestroXcrunShim();
|
|
222
|
+
const previousPath = process.env.PATH;
|
|
223
|
+
try {
|
|
224
|
+
process.env.PATH = `${xcrunShimDir}:${previousPath ?? ""}`;
|
|
225
|
+
const result = await $`maestro test ${flowArg} --exclude-tags=util --no-ansi --debug-output=${debugDir} ${udidArgs}`.nothrow();
|
|
226
|
+
return result.exitCode;
|
|
227
|
+
} finally {
|
|
228
|
+
if (previousPath === void 0) {
|
|
229
|
+
delete process.env.PATH;
|
|
230
|
+
} else {
|
|
231
|
+
process.env.PATH = previousPath;
|
|
232
|
+
}
|
|
233
|
+
rmSync(xcrunShimDir, {
|
|
234
|
+
recursive: true,
|
|
235
|
+
force: true
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
});
|
|
239
|
+
process.exit(exitCode);
|
|
240
|
+
} else if (platform === "all") {
|
|
241
|
+
ensureBootedSimulator();
|
|
242
|
+
console.info("=== Running All Native Tests ===\n");
|
|
243
|
+
await ensureIosDeps();
|
|
244
|
+
console.info("\n--- iOS Tests ---\n");
|
|
245
|
+
process.chdir(options.projectRoot);
|
|
246
|
+
await ensureIOSFolder();
|
|
247
|
+
await ensureIOSApp(options.config || "ios.sim.debug");
|
|
248
|
+
let iosExit = 0;
|
|
249
|
+
try {
|
|
250
|
+
iosExit = await withMetro("ios", async () => {
|
|
251
|
+
return runDetoxTests({
|
|
252
|
+
config: options.config || "ios.sim.debug",
|
|
253
|
+
projectRoot: options.projectRoot,
|
|
254
|
+
recordLogs: options.recordLogs,
|
|
255
|
+
retries: options.retries
|
|
256
|
+
});
|
|
257
|
+
});
|
|
258
|
+
} catch (err) {
|
|
259
|
+
console.error("iOS tests failed:", err);
|
|
260
|
+
iosExit = 1;
|
|
261
|
+
}
|
|
262
|
+
await ensureAndroidDeps();
|
|
263
|
+
console.info("\n--- Android Tests ---\n");
|
|
264
|
+
await ensureAndroidFolder();
|
|
265
|
+
await setupAndroidDevice();
|
|
266
|
+
let androidExit = 0;
|
|
267
|
+
try {
|
|
268
|
+
androidExit = await withMetro("android", async () => {
|
|
269
|
+
return runDetoxTests({
|
|
270
|
+
config: options.config || "android.emu.debug",
|
|
271
|
+
projectRoot: options.projectRoot,
|
|
272
|
+
recordLogs: options.recordLogs,
|
|
273
|
+
retries: options.retries,
|
|
274
|
+
headless: options.headless
|
|
275
|
+
});
|
|
276
|
+
});
|
|
277
|
+
} catch (err) {
|
|
278
|
+
console.error("Android tests failed:", err);
|
|
279
|
+
androidExit = 1;
|
|
280
|
+
}
|
|
281
|
+
const success = iosExit === 0 && androidExit === 0;
|
|
282
|
+
console.info(`
|
|
293
283
|
=== Test Results ===`);
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
} else {
|
|
387
|
-
console.info("No previous fingerprints cached.");
|
|
388
|
-
}
|
|
389
|
-
saveCache(CACHE_FILE, {
|
|
390
|
-
ios: iosFingerprint,
|
|
391
|
-
android: androidFingerprint,
|
|
392
|
-
timestamp: (/* @__PURE__ */new Date()).toISOString()
|
|
393
|
-
});
|
|
394
|
-
console.info(`
|
|
284
|
+
console.info(`iOS: ${iosExit === 0 ? "PASSED" : "FAILED"}`);
|
|
285
|
+
console.info(`Android: ${androidExit === 0 ? "PASSED" : "FAILED"}`);
|
|
286
|
+
process.exit(success ? 0 : 1);
|
|
287
|
+
} else {
|
|
288
|
+
console.error(`Unknown test platform: ${platform}`);
|
|
289
|
+
console.info("Usage: native-ci test [ios|android|maestro|all]");
|
|
290
|
+
process.exit(1);
|
|
291
|
+
}
|
|
292
|
+
break;
|
|
293
|
+
}
|
|
294
|
+
case "deps": {
|
|
295
|
+
if (!subcommand || subcommand === "status") {
|
|
296
|
+
printDepsStatus();
|
|
297
|
+
} else if (subcommand === "install") {
|
|
298
|
+
console.info("Installing all dependencies...\n");
|
|
299
|
+
await ensureIosDeps();
|
|
300
|
+
await ensureMaestro();
|
|
301
|
+
console.info("\nAll dependencies installed!");
|
|
302
|
+
} else if (subcommand === "install-ios") {
|
|
303
|
+
await ensureIosDeps();
|
|
304
|
+
} else if (subcommand === "install-android") {
|
|
305
|
+
await ensureAndroidDeps();
|
|
306
|
+
} else if (subcommand === "install-maestro") {
|
|
307
|
+
await ensureMaestro();
|
|
308
|
+
} else {
|
|
309
|
+
console.error(`Unknown deps subcommand: ${subcommand}`);
|
|
310
|
+
process.exit(1);
|
|
311
|
+
}
|
|
312
|
+
break;
|
|
313
|
+
}
|
|
314
|
+
case "fingerprint": {
|
|
315
|
+
const platform = validatePlatform(subcommand);
|
|
316
|
+
const result = await generateFingerprint({
|
|
317
|
+
platform,
|
|
318
|
+
projectRoot: options.projectRoot
|
|
319
|
+
});
|
|
320
|
+
if (options.githubOutput || isGitHubActions()) {
|
|
321
|
+
setGitHubOutput("fingerprint", result.hash);
|
|
322
|
+
setGitHubOutput("cache-key", createCacheKey({
|
|
323
|
+
platform,
|
|
324
|
+
fingerprint: result.hash,
|
|
325
|
+
prefix: options.prefix
|
|
326
|
+
}));
|
|
327
|
+
}
|
|
328
|
+
if (options.json) {
|
|
329
|
+
console.info(JSON.stringify(result, null, 2));
|
|
330
|
+
} else {
|
|
331
|
+
console.info(result.hash);
|
|
332
|
+
}
|
|
333
|
+
break;
|
|
334
|
+
}
|
|
335
|
+
case "fingerprint-test": {
|
|
336
|
+
const CACHE_FILE = ".fingerprint-cache.json";
|
|
337
|
+
console.info("Generating fingerprints...\n");
|
|
338
|
+
const iosResult = await generateFingerprint({
|
|
339
|
+
platform: "ios",
|
|
340
|
+
projectRoot: options.projectRoot
|
|
341
|
+
});
|
|
342
|
+
const androidResult = await generateFingerprint({
|
|
343
|
+
platform: "android",
|
|
344
|
+
projectRoot: options.projectRoot
|
|
345
|
+
});
|
|
346
|
+
const iosFingerprint = iosResult.hash;
|
|
347
|
+
const androidFingerprint = androidResult.hash;
|
|
348
|
+
console.info("Current fingerprints:");
|
|
349
|
+
console.info(` iOS: ${iosFingerprint}`);
|
|
350
|
+
console.info(` Android: ${androidFingerprint}`);
|
|
351
|
+
console.info("");
|
|
352
|
+
const cache = loadCache(CACHE_FILE);
|
|
353
|
+
if (cache?.ios && cache?.android) {
|
|
354
|
+
console.info("Previous fingerprints (from cache):");
|
|
355
|
+
console.info(` iOS: ${cache.ios}`);
|
|
356
|
+
console.info(` Android: ${cache.android}`);
|
|
357
|
+
console.info("");
|
|
358
|
+
const iosChanged = cache.ios !== iosFingerprint;
|
|
359
|
+
const androidChanged = cache.android !== androidFingerprint;
|
|
360
|
+
if (iosChanged || androidChanged) {
|
|
361
|
+
console.info("Fingerprints changed!");
|
|
362
|
+
if (iosChanged) console.info(" - iOS fingerprint changed (would trigger iOS rebuild)");
|
|
363
|
+
if (androidChanged) console.info(" - Android fingerprint changed (would trigger Android rebuild)");
|
|
364
|
+
} else {
|
|
365
|
+
console.info("Fingerprints match - no rebuild needed");
|
|
366
|
+
}
|
|
367
|
+
} else {
|
|
368
|
+
console.info("No previous fingerprints cached.");
|
|
369
|
+
}
|
|
370
|
+
saveCache(CACHE_FILE, {
|
|
371
|
+
ios: iosFingerprint,
|
|
372
|
+
android: androidFingerprint,
|
|
373
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
374
|
+
});
|
|
375
|
+
console.info(`
|
|
395
376
|
Saved fingerprints to ${CACHE_FILE}`);
|
|
396
|
-
|
|
377
|
+
console.info(`
|
|
397
378
|
To test cache invalidation:
|
|
398
379
|
1. Add a native dependency: yarn add react-native-mmkv
|
|
399
380
|
2. Run this script again: native-ci fingerprint-test
|
|
@@ -401,91 +382,85 @@ To test cache invalidation:
|
|
|
401
382
|
4. Remove the dependency: yarn remove react-native-mmkv
|
|
402
383
|
5. Run this script again - fingerprints should match original
|
|
403
384
|
`);
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
break;
|
|
481
|
-
}
|
|
482
|
-
default:
|
|
483
|
-
console.error(`Unknown command: ${command}`);
|
|
484
|
-
console.info(HELP);
|
|
485
|
-
process.exit(1);
|
|
486
|
-
}
|
|
385
|
+
break;
|
|
386
|
+
}
|
|
387
|
+
case "pre-hash": {
|
|
388
|
+
const files = [subcommand, ...args].filter(Boolean);
|
|
389
|
+
if (files.length === 0) {
|
|
390
|
+
console.error("Error: at least one file required");
|
|
391
|
+
process.exit(1);
|
|
392
|
+
}
|
|
393
|
+
const hash = generatePreFingerprintHash(files, options.projectRoot);
|
|
394
|
+
if (options.githubOutput || isGitHubActions()) {
|
|
395
|
+
setGitHubOutput("pre-fingerprint-hash", hash);
|
|
396
|
+
}
|
|
397
|
+
if (options.json) {
|
|
398
|
+
console.info(JSON.stringify({
|
|
399
|
+
hash,
|
|
400
|
+
files
|
|
401
|
+
}, null, 2));
|
|
402
|
+
} else {
|
|
403
|
+
console.info(hash);
|
|
404
|
+
}
|
|
405
|
+
break;
|
|
406
|
+
}
|
|
407
|
+
case "cache-key": {
|
|
408
|
+
const platform = validatePlatform(subcommand);
|
|
409
|
+
const fingerprint = args[0];
|
|
410
|
+
if (!fingerprint) {
|
|
411
|
+
console.error("Error: fingerprint is required");
|
|
412
|
+
process.exit(1);
|
|
413
|
+
}
|
|
414
|
+
const cacheKey = createCacheKey({
|
|
415
|
+
platform,
|
|
416
|
+
fingerprint,
|
|
417
|
+
prefix: options.prefix
|
|
418
|
+
});
|
|
419
|
+
if (options.githubOutput || isGitHubActions()) {
|
|
420
|
+
setGitHubOutput("cache-key", cacheKey);
|
|
421
|
+
}
|
|
422
|
+
console.info(cacheKey);
|
|
423
|
+
break;
|
|
424
|
+
}
|
|
425
|
+
case "kv-get": {
|
|
426
|
+
const key = subcommand;
|
|
427
|
+
if (!key) {
|
|
428
|
+
console.error("Error: key is required");
|
|
429
|
+
process.exit(1);
|
|
430
|
+
}
|
|
431
|
+
const kv = getKVCredentials();
|
|
432
|
+
const value = await getFingerprintFromKV(kv, key);
|
|
433
|
+
if (options.githubOutput || isGitHubActions()) {
|
|
434
|
+
setGitHubOutput("value", value || "");
|
|
435
|
+
setGitHubOutput("found", value ? "true" : "false");
|
|
436
|
+
}
|
|
437
|
+
if (value) {
|
|
438
|
+
console.info(value);
|
|
439
|
+
} else {
|
|
440
|
+
process.exit(1);
|
|
441
|
+
}
|
|
442
|
+
break;
|
|
443
|
+
}
|
|
444
|
+
case "kv-set": {
|
|
445
|
+
const key = subcommand;
|
|
446
|
+
const value = args[0];
|
|
447
|
+
if (!key || !value) {
|
|
448
|
+
console.error("Error: key and value are required");
|
|
449
|
+
process.exit(1);
|
|
450
|
+
}
|
|
451
|
+
const kv = getKVCredentials();
|
|
452
|
+
await saveFingerprintToKV(kv, key, value);
|
|
453
|
+
console.info("OK");
|
|
454
|
+
break;
|
|
455
|
+
}
|
|
456
|
+
default:
|
|
457
|
+
console.error(`Unknown command: ${command}`);
|
|
458
|
+
console.info(HELP);
|
|
459
|
+
process.exit(1);
|
|
460
|
+
}
|
|
487
461
|
} catch (error) {
|
|
488
|
-
|
|
489
|
-
|
|
462
|
+
console.error("Error:", error.message);
|
|
463
|
+
process.exit(1);
|
|
490
464
|
}
|
|
465
|
+
|
|
491
466
|
//# sourceMappingURL=cli.mjs.map
|