@tamagui/native-ci 2.0.0-rc.4 → 2.0.0-rc.40

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/cache.mjs CHANGED
@@ -17,9 +17,14 @@ async function saveFingerprintToKV(kv, key, fingerprint, ttlSeconds = DEFAULT_KV
17
17
  Authorization: `Bearer ${kv.token}`
18
18
  }
19
19
  });
20
- if (!response.ok) throw new Error(`HTTP ${response.status}: ${response.statusText}`);
20
+ if (!response.ok) {
21
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
22
+ }
21
23
  } catch (error) {
22
- throw error instanceof TypeError ? new Error(`Network error connecting to KV store: ${error.message}`) : new Error(`Failed to save fingerprint to KV: ${error.message}`);
24
+ if (error instanceof TypeError) {
25
+ throw new Error(`Network error connecting to KV store: ${error.message}`);
26
+ }
27
+ throw new Error(`Failed to save fingerprint to KV: ${error.message}`);
23
28
  }
24
29
  }
25
30
  async function getFingerprintFromKV(kv, key) {
@@ -29,11 +34,16 @@ async function getFingerprintFromKV(kv, key) {
29
34
  Authorization: `Bearer ${kv.token}`
30
35
  }
31
36
  });
32
- if (!response.ok) throw new Error(`HTTP ${response.status}: ${response.statusText}`);
37
+ if (!response.ok) {
38
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
39
+ }
33
40
  const data = await response.json();
34
41
  return data.result === "null" ? null : data.result;
35
42
  } catch (error) {
36
- throw error instanceof TypeError ? new Error(`Network error connecting to KV store: ${error.message}`) : new Error(`Failed to get fingerprint from KV: ${error.message}`);
43
+ if (error instanceof TypeError) {
44
+ throw new Error(`Network error connecting to KV store: ${error.message}`);
45
+ }
46
+ throw new Error(`Failed to get fingerprint from KV: ${error.message}`);
37
47
  }
38
48
  }
39
49
  async function extendKVTTL(kv, key, ttlSeconds = DEFAULT_KV_TTL_SECONDS) {
@@ -50,19 +60,22 @@ async function extendKVTTL(kv, key, ttlSeconds = DEFAULT_KV_TTL_SECONDS) {
50
60
  }
51
61
  function saveCache(filePath, data, options = {}) {
52
62
  const {
53
- cacheDir
54
- } = options,
55
- cachePath = cacheDir ? join(process.cwd(), cacheDir, filePath) : join(process.cwd(), filePath);
63
+ cacheDir
64
+ } = options;
65
+ const cachePath = cacheDir ? join(process.cwd(), cacheDir, filePath) : join(process.cwd(), filePath);
56
66
  mkdirSync(dirname(cachePath), {
57
- recursive: !0
58
- }), writeFileSync(cachePath, JSON.stringify(data, null, 2));
67
+ recursive: true
68
+ });
69
+ writeFileSync(cachePath, JSON.stringify(data, null, 2));
59
70
  }
60
71
  function loadCache(filePath, options = {}) {
61
72
  const {
62
- cacheDir
63
- } = options,
64
- cachePath = cacheDir ? join(process.cwd(), cacheDir, filePath) : join(process.cwd(), filePath);
65
- if (!existsSync(cachePath)) return null;
73
+ cacheDir
74
+ } = options;
75
+ const cachePath = cacheDir ? join(process.cwd(), cacheDir, filePath) : join(process.cwd(), filePath);
76
+ if (!existsSync(cachePath)) {
77
+ return null;
78
+ }
66
79
  try {
67
80
  return JSON.parse(readFileSync(cachePath, "utf-8"));
68
81
  } catch {
@@ -1 +1 @@
1
- {"version":3,"names":["existsSync","readFileSync","writeFileSync","mkdirSync","dirname","join","DEFAULT_KV_TTL_SECONDS","createCacheKey","options","platform","fingerprint","prefix","saveFingerprintToKV","kv","key","ttlSeconds","response","fetch","url","method","headers","Authorization","token","ok","Error","status","statusText","error","TypeError","message","getFingerprintFromKV","data","json","result","extendKVTTL","console","warn","saveCache","filePath","cacheDir","cachePath","process","cwd","recursive","JSON","stringify","loadCache","parse"],"sources":["../src/cache.ts"],"sourcesContent":[null],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":["existsSync","readFileSync","writeFileSync","mkdirSync","dirname","join","DEFAULT_KV_TTL_SECONDS","createCacheKey","options","platform","fingerprint","prefix","saveFingerprintToKV","kv","key","ttlSeconds","response","fetch","url","method","headers","Authorization","token","ok","Error","status","statusText","error","TypeError","message","getFingerprintFromKV","data","json","result","extendKVTTL","console","warn","saveCache","filePath","cacheDir","cachePath","process","cwd","recursive","JSON","stringify","loadCache","parse"],"sources":["../src/cache.ts"],"sourcesContent":[null],"mappings":"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","ignoreList":[]}
package/dist/cli.mjs CHANGED
@@ -5,7 +5,7 @@ import { setGitHubOutput, isGitHubActions, isCI } from "./runner.mjs";
5
5
  import { ensureIosDeps, ensureAndroidDeps, ensureMaestro, printDepsStatus } from "./deps.mjs";
6
6
  import { withMetro } from "./metro.mjs";
7
7
  import { runDetoxTests } from "./detox.mjs";
8
- import { ensureIOSFolder, ensureIOSApp } from "./ios";
8
+ import { ensureIOSFolder, ensureIOSApp, ensureBootedSimulator, ensureAppInstalled, getBootedSimulatorUDID, getMaestroBundleId } from "./ios";
9
9
  import { setupAndroidDevice, ensureAndroidFolder } from "./android";
10
10
  const HELP = `
11
11
  native-ci - Native CI/CD helpers for Expo apps
@@ -60,25 +60,43 @@ EXAMPLES:
60
60
  native-ci fingerprint-test
61
61
  `;
62
62
  function parseArgs(argv) {
63
- const args2 = [],
64
- options2 = {
65
- projectRoot: process.cwd(),
66
- config: "",
67
- recordLogs: "all",
68
- retries: 0,
69
- headless: !1,
70
- prefix: "native-build",
71
- githubOutput: !1,
72
- json: !1,
73
- help: !1
74
- };
63
+ const args2 = [];
64
+ const options2 = {
65
+ projectRoot: process.cwd(),
66
+ config: "",
67
+ recordLogs: "all",
68
+ retries: 0,
69
+ headless: false,
70
+ prefix: "native-build",
71
+ githubOutput: false,
72
+ json: false,
73
+ help: false
74
+ };
75
75
  let i = 0;
76
- for (; i < argv.length;) {
76
+ while (i < argv.length) {
77
77
  const arg = argv[i];
78
- if (arg === "--project-root" && argv[i + 1]) options2.projectRoot = argv[++i];else if (arg === "--config" && argv[i + 1]) options2.config = argv[++i];else if (arg === "--record-logs" && argv[i + 1]) options2.recordLogs = argv[++i];else if (arg === "--retries" && argv[i + 1]) {
78
+ if (arg === "--project-root" && argv[i + 1]) {
79
+ options2.projectRoot = argv[++i];
80
+ } else if (arg === "--config" && argv[i + 1]) {
81
+ options2.config = argv[++i];
82
+ } else if (arg === "--record-logs" && argv[i + 1]) {
83
+ options2.recordLogs = argv[++i];
84
+ } else if (arg === "--retries" && argv[i + 1]) {
79
85
  const val = Number.parseInt(argv[++i], 10);
80
- !Number.isNaN(val) && val >= 0 && (options2.retries = val);
81
- } else arg === "--prefix" && argv[i + 1] ? options2.prefix = argv[++i] : arg === "--github-output" ? options2.githubOutput = !0 : arg === "--json" ? options2.json = !0 : arg === "--help" || arg === "-h" ? options2.help = !0 : arg.startsWith("-") || args2.push(arg);
86
+ if (!Number.isNaN(val) && val >= 0) {
87
+ options2.retries = val;
88
+ }
89
+ } else if (arg === "--prefix" && argv[i + 1]) {
90
+ options2.prefix = argv[++i];
91
+ } else if (arg === "--github-output") {
92
+ options2.githubOutput = true;
93
+ } else if (arg === "--json") {
94
+ options2.json = true;
95
+ } else if (arg === "--help" || arg === "-h") {
96
+ options2.help = true;
97
+ } else if (!arg.startsWith("-")) {
98
+ args2.push(arg);
99
+ }
82
100
  i++;
83
101
  }
84
102
  return {
@@ -89,12 +107,20 @@ function parseArgs(argv) {
89
107
  };
90
108
  }
91
109
  function validatePlatform(value) {
92
- return value !== "ios" && value !== "android" && (console.error('Error: platform must be "ios" or "android"'), process.exit(1)), value;
110
+ if (value !== "ios" && value !== "android") {
111
+ console.error('Error: platform must be "ios" or "android"');
112
+ process.exit(1);
113
+ }
114
+ return value;
93
115
  }
94
116
  function getKVCredentials() {
95
- const url = process.env.KV_STORE_REDIS_REST_URL,
96
- token = process.env.KV_STORE_REDIS_REST_TOKEN;
97
- return (!url || !token) && (console.error("Error: KV_STORE_REDIS_REST_URL and KV_STORE_REDIS_REST_TOKEN required"), process.exit(1)), {
117
+ const url = process.env.KV_STORE_REDIS_REST_URL;
118
+ const token = process.env.KV_STORE_REDIS_REST_TOKEN;
119
+ if (!url || !token) {
120
+ console.error("Error: KV_STORE_REDIS_REST_URL and KV_STORE_REDIS_REST_TOKEN required");
121
+ process.exit(1);
122
+ }
123
+ return {
98
124
  url,
99
125
  token
100
126
  };
@@ -105,7 +131,10 @@ const {
105
131
  args,
106
132
  options
107
133
  } = parseArgs(process.argv.slice(2));
108
- (options.help || !command) && (console.info(HELP), process.exit(options.help ? 0 : 1));
134
+ if (options.help || !command) {
135
+ console.info(HELP);
136
+ process.exit(options.help ? 0 : 1);
137
+ }
109
138
  try {
110
139
  switch (command) {
111
140
  // ========================================
@@ -113,78 +142,129 @@ try {
113
142
  // ========================================
114
143
  case "test":
115
144
  {
116
- isCI() && !process.env.NATIVE_CI_FORCE_RUN && (console.info("Skipping native tests in CI (handled by separate workflow)"), console.info("Set NATIVE_CI_FORCE_RUN=1 to force run"), process.exit(0));
145
+ if (isCI() && !process.env.NATIVE_CI_FORCE_RUN) {
146
+ console.info("Skipping native tests in CI (handled by separate workflow)");
147
+ console.info("Set NATIVE_CI_FORCE_RUN=1 to force run");
148
+ process.exit(0);
149
+ }
117
150
  const platform = subcommand || "ios";
118
151
  if (platform === "ios") {
152
+ ensureBootedSimulator();
119
153
  await ensureIosDeps();
120
154
  const config = options.config || "ios.sim.debug";
121
- console.info("=== iOS Detox Test Runner ==="), console.info(`Config: ${config}`), console.info(`Project root: ${options.projectRoot}`), process.chdir(options.projectRoot), await ensureIOSFolder(), await ensureIOSApp(config);
122
- const exitCode = await withMetro("ios", async () => runDetoxTests({
123
- config,
124
- projectRoot: options.projectRoot,
125
- recordLogs: options.recordLogs,
126
- retries: options.retries
127
- }));
155
+ console.info("=== iOS Detox Test Runner ===");
156
+ console.info(`Config: ${config}`);
157
+ console.info(`Project root: ${options.projectRoot}`);
158
+ process.chdir(options.projectRoot);
159
+ await ensureIOSFolder();
160
+ await ensureIOSApp(config);
161
+ const exitCode = await withMetro("ios", async () => {
162
+ return runDetoxTests({
163
+ config,
164
+ projectRoot: options.projectRoot,
165
+ recordLogs: options.recordLogs,
166
+ retries: options.retries
167
+ });
168
+ });
128
169
  process.exit(exitCode);
129
170
  } else if (platform === "android") {
130
171
  await ensureAndroidDeps();
131
172
  const config = options.config || "android.emu.debug";
132
- console.info("=== Android Detox Test Runner ==="), console.info(`Config: ${config}`), console.info(`Project root: ${options.projectRoot}`), console.info(`Headless: ${options.headless}`), process.chdir(options.projectRoot), await ensureAndroidFolder(), await setupAndroidDevice();
133
- const exitCode = await withMetro("android", async () => runDetoxTests({
134
- config,
135
- projectRoot: options.projectRoot,
136
- recordLogs: options.recordLogs,
137
- retries: options.retries,
138
- headless: options.headless
139
- }));
173
+ console.info("=== Android Detox Test Runner ===");
174
+ console.info(`Config: ${config}`);
175
+ console.info(`Project root: ${options.projectRoot}`);
176
+ console.info(`Headless: ${options.headless}`);
177
+ process.chdir(options.projectRoot);
178
+ await ensureAndroidFolder();
179
+ await setupAndroidDevice();
180
+ const exitCode = await withMetro("android", async () => {
181
+ return runDetoxTests({
182
+ config,
183
+ projectRoot: options.projectRoot,
184
+ recordLogs: options.recordLogs,
185
+ retries: options.retries,
186
+ headless: options.headless
187
+ });
188
+ });
140
189
  process.exit(exitCode);
141
190
  } else if (platform === "maestro") {
191
+ ensureBootedSimulator();
142
192
  await ensureMaestro();
143
193
  const flow = args[0] || "";
144
- console.info("=== Maestro Test Runner ==="), console.info(`Flow: ${flow || "all"}`), console.info(`Project root: ${options.projectRoot}`), process.chdir(options.projectRoot);
194
+ console.info("=== Maestro Test Runner ===");
195
+ console.info(`Flow: ${flow || "all"}`);
196
+ console.info(`Project root: ${options.projectRoot}`);
197
+ process.chdir(options.projectRoot);
198
+ const udid = getBootedSimulatorUDID();
199
+ const bundleId = getMaestroBundleId(options.projectRoot);
200
+ await ensureAppInstalled({
201
+ projectRoot: options.projectRoot,
202
+ bundleId,
203
+ udid
204
+ });
145
205
  const exitCode = await withMetro("ios", async () => {
146
206
  const {
147
- $
148
- } = await import("bun"),
149
- flowArg = flow ? `./flows/${flow}` : "./flows";
150
- return (await $`maestro test ${flowArg} --exclude-tags=util --no-ansi`.nothrow()).exitCode;
207
+ $
208
+ } = await import("bun");
209
+ const flowArg = flow ? `./flows/${flow}` : "./flows";
210
+ const udidArgs = ["--udid", udid];
211
+ const debugDir = `${options.projectRoot}/.maestro-debug`;
212
+ const result = await $`maestro test ${flowArg} --exclude-tags=util --no-ansi --debug-output=${debugDir} ${udidArgs}`.nothrow();
213
+ return result.exitCode;
151
214
  });
152
215
  process.exit(exitCode);
153
216
  } else if (platform === "all") {
154
- console.info(`=== Running All Native Tests ===
155
- `), await ensureIosDeps(), console.info(`
156
- --- iOS Tests ---
157
- `), process.chdir(options.projectRoot), await ensureIOSFolder(), await ensureIOSApp(options.config || "ios.sim.debug");
217
+ ensureBootedSimulator();
218
+ console.info("=== Running All Native Tests ===\n");
219
+ await ensureIosDeps();
220
+ console.info("\n--- iOS Tests ---\n");
221
+ process.chdir(options.projectRoot);
222
+ await ensureIOSFolder();
223
+ await ensureIOSApp(options.config || "ios.sim.debug");
158
224
  let iosExit = 0;
159
225
  try {
160
- iosExit = await withMetro("ios", async () => runDetoxTests({
161
- config: options.config || "ios.sim.debug",
162
- projectRoot: options.projectRoot,
163
- recordLogs: options.recordLogs,
164
- retries: options.retries
165
- }));
226
+ iosExit = await withMetro("ios", async () => {
227
+ return runDetoxTests({
228
+ config: options.config || "ios.sim.debug",
229
+ projectRoot: options.projectRoot,
230
+ recordLogs: options.recordLogs,
231
+ retries: options.retries
232
+ });
233
+ });
166
234
  } catch (err) {
167
- console.error("iOS tests failed:", err), iosExit = 1;
235
+ console.error("iOS tests failed:", err);
236
+ iosExit = 1;
168
237
  }
169
- await ensureAndroidDeps(), console.info(`
170
- --- Android Tests ---
171
- `), await ensureAndroidFolder(), await setupAndroidDevice();
238
+ await ensureAndroidDeps();
239
+ console.info("\n--- Android Tests ---\n");
240
+ await ensureAndroidFolder();
241
+ await setupAndroidDevice();
172
242
  let androidExit = 0;
173
243
  try {
174
- androidExit = await withMetro("android", async () => runDetoxTests({
175
- config: options.config || "android.emu.debug",
176
- projectRoot: options.projectRoot,
177
- recordLogs: options.recordLogs,
178
- retries: options.retries,
179
- headless: options.headless
180
- }));
244
+ androidExit = await withMetro("android", async () => {
245
+ return runDetoxTests({
246
+ config: options.config || "android.emu.debug",
247
+ projectRoot: options.projectRoot,
248
+ recordLogs: options.recordLogs,
249
+ retries: options.retries,
250
+ headless: options.headless
251
+ });
252
+ });
181
253
  } catch (err) {
182
- console.error("Android tests failed:", err), androidExit = 1;
254
+ console.error("Android tests failed:", err);
255
+ androidExit = 1;
183
256
  }
184
257
  const success = iosExit === 0 && androidExit === 0;
185
258
  console.info(`
186
- === Test Results ===`), console.info(`iOS: ${iosExit === 0 ? "PASSED" : "FAILED"}`), console.info(`Android: ${androidExit === 0 ? "PASSED" : "FAILED"}`), process.exit(success ? 0 : 1);
187
- } else console.error(`Unknown test platform: ${platform}`), console.info("Usage: native-ci test [ios|android|maestro|all]"), process.exit(1);
259
+ === Test Results ===`);
260
+ console.info(`iOS: ${iosExit === 0 ? "PASSED" : "FAILED"}`);
261
+ console.info(`Android: ${androidExit === 0 ? "PASSED" : "FAILED"}`);
262
+ process.exit(success ? 0 : 1);
263
+ } else {
264
+ console.error(`Unknown test platform: ${platform}`);
265
+ console.info("Usage: native-ci test [ios|android|maestro|all]");
266
+ process.exit(1);
267
+ }
188
268
  break;
189
269
  }
190
270
  // ========================================
@@ -192,9 +272,23 @@ try {
192
272
  // ========================================
193
273
  case "deps":
194
274
  {
195
- !subcommand || subcommand === "status" ? printDepsStatus() : subcommand === "install" ? (console.info(`Installing all dependencies...
196
- `), await ensureIosDeps(), await ensureMaestro(), console.info(`
197
- All dependencies installed!`)) : subcommand === "install-ios" ? await ensureIosDeps() : subcommand === "install-android" ? await ensureAndroidDeps() : subcommand === "install-maestro" ? await ensureMaestro() : (console.error(`Unknown deps subcommand: ${subcommand}`), process.exit(1));
275
+ if (!subcommand || subcommand === "status") {
276
+ printDepsStatus();
277
+ } else if (subcommand === "install") {
278
+ console.info("Installing all dependencies...\n");
279
+ await ensureIosDeps();
280
+ await ensureMaestro();
281
+ console.info("\nAll dependencies installed!");
282
+ } else if (subcommand === "install-ios") {
283
+ await ensureIosDeps();
284
+ } else if (subcommand === "install-android") {
285
+ await ensureAndroidDeps();
286
+ } else if (subcommand === "install-maestro") {
287
+ await ensureMaestro();
288
+ } else {
289
+ console.error(`Unknown deps subcommand: ${subcommand}`);
290
+ process.exit(1);
291
+ }
198
292
  break;
199
293
  }
200
294
  // ========================================
@@ -202,47 +296,70 @@ All dependencies installed!`)) : subcommand === "install-ios" ? await ensureIosD
202
296
  // ========================================
203
297
  case "fingerprint":
204
298
  {
205
- const platform = validatePlatform(subcommand),
206
- result = await generateFingerprint({
207
- platform,
208
- projectRoot: options.projectRoot
209
- });
210
- (options.githubOutput || isGitHubActions()) && (setGitHubOutput("fingerprint", result.hash), setGitHubOutput("cache-key", createCacheKey({
299
+ const platform = validatePlatform(subcommand);
300
+ const result = await generateFingerprint({
211
301
  platform,
212
- fingerprint: result.hash,
213
- prefix: options.prefix
214
- }))), options.json ? console.info(JSON.stringify(result, null, 2)) : console.info(result.hash);
302
+ projectRoot: options.projectRoot
303
+ });
304
+ if (options.githubOutput || isGitHubActions()) {
305
+ setGitHubOutput("fingerprint", result.hash);
306
+ setGitHubOutput("cache-key", createCacheKey({
307
+ platform,
308
+ fingerprint: result.hash,
309
+ prefix: options.prefix
310
+ }));
311
+ }
312
+ if (options.json) {
313
+ console.info(JSON.stringify(result, null, 2));
314
+ } else {
315
+ console.info(result.hash);
316
+ }
215
317
  break;
216
318
  }
217
319
  case "fingerprint-test":
218
320
  {
219
321
  const CACHE_FILE = ".fingerprint-cache.json";
220
- console.info(`Generating fingerprints...
221
- `);
322
+ console.info("Generating fingerprints...\n");
222
323
  const iosResult = await generateFingerprint({
223
- platform: "ios",
224
- projectRoot: options.projectRoot
225
- }),
226
- androidResult = await generateFingerprint({
227
- platform: "android",
228
- projectRoot: options.projectRoot
229
- }),
230
- iosFingerprint = iosResult.hash,
231
- androidFingerprint = androidResult.hash;
232
- console.info("Current fingerprints:"), console.info(` iOS: ${iosFingerprint}`), console.info(` Android: ${androidFingerprint}`), console.info("");
324
+ platform: "ios",
325
+ projectRoot: options.projectRoot
326
+ });
327
+ const androidResult = await generateFingerprint({
328
+ platform: "android",
329
+ projectRoot: options.projectRoot
330
+ });
331
+ const iosFingerprint = iosResult.hash;
332
+ const androidFingerprint = androidResult.hash;
333
+ console.info("Current fingerprints:");
334
+ console.info(` iOS: ${iosFingerprint}`);
335
+ console.info(` Android: ${androidFingerprint}`);
336
+ console.info("");
233
337
  const cache = loadCache(CACHE_FILE);
234
338
  if (cache?.ios && cache?.android) {
235
- console.info("Previous fingerprints (from cache):"), console.info(` iOS: ${cache.ios}`), console.info(` Android: ${cache.android}`), console.info("");
236
- const iosChanged = cache.ios !== iosFingerprint,
237
- androidChanged = cache.android !== androidFingerprint;
238
- iosChanged || androidChanged ? (console.info("Fingerprints changed!"), iosChanged && console.info(" - iOS fingerprint changed (would trigger iOS rebuild)"), androidChanged && console.info(" - Android fingerprint changed (would trigger Android rebuild)")) : console.info("Fingerprints match - no rebuild needed");
239
- } else console.info("No previous fingerprints cached.");
339
+ console.info("Previous fingerprints (from cache):");
340
+ console.info(` iOS: ${cache.ios}`);
341
+ console.info(` Android: ${cache.android}`);
342
+ console.info("");
343
+ const iosChanged = cache.ios !== iosFingerprint;
344
+ const androidChanged = cache.android !== androidFingerprint;
345
+ if (iosChanged || androidChanged) {
346
+ console.info("Fingerprints changed!");
347
+ if (iosChanged) console.info(" - iOS fingerprint changed (would trigger iOS rebuild)");
348
+ if (androidChanged) console.info(" - Android fingerprint changed (would trigger Android rebuild)");
349
+ } else {
350
+ console.info("Fingerprints match - no rebuild needed");
351
+ }
352
+ } else {
353
+ console.info("No previous fingerprints cached.");
354
+ }
240
355
  saveCache(CACHE_FILE, {
241
356
  ios: iosFingerprint,
242
357
  android: androidFingerprint,
243
358
  timestamp: (/* @__PURE__ */new Date()).toISOString()
244
- }), console.info(`
245
- Saved fingerprints to ${CACHE_FILE}`), console.info(`
359
+ });
360
+ console.info(`
361
+ Saved fingerprints to ${CACHE_FILE}`);
362
+ console.info(`
246
363
  To test cache invalidation:
247
364
  1. Add a native dependency: yarn add react-native-mmkv
248
365
  2. Run this script again: native-ci fingerprint-test
@@ -255,25 +372,41 @@ To test cache invalidation:
255
372
  case "pre-hash":
256
373
  {
257
374
  const files = [subcommand, ...args].filter(Boolean);
258
- files.length === 0 && (console.error("Error: at least one file required"), process.exit(1));
375
+ if (files.length === 0) {
376
+ console.error("Error: at least one file required");
377
+ process.exit(1);
378
+ }
259
379
  const hash = generatePreFingerprintHash(files, options.projectRoot);
260
- (options.githubOutput || isGitHubActions()) && setGitHubOutput("pre-fingerprint-hash", hash), options.json ? console.info(JSON.stringify({
261
- hash,
262
- files
263
- }, null, 2)) : console.info(hash);
380
+ if (options.githubOutput || isGitHubActions()) {
381
+ setGitHubOutput("pre-fingerprint-hash", hash);
382
+ }
383
+ if (options.json) {
384
+ console.info(JSON.stringify({
385
+ hash,
386
+ files
387
+ }, null, 2));
388
+ } else {
389
+ console.info(hash);
390
+ }
264
391
  break;
265
392
  }
266
393
  case "cache-key":
267
394
  {
268
- const platform = validatePlatform(subcommand),
269
- fingerprint = args[0];
270
- fingerprint || (console.error("Error: fingerprint is required"), process.exit(1));
395
+ const platform = validatePlatform(subcommand);
396
+ const fingerprint = args[0];
397
+ if (!fingerprint) {
398
+ console.error("Error: fingerprint is required");
399
+ process.exit(1);
400
+ }
271
401
  const cacheKey = createCacheKey({
272
402
  platform,
273
403
  fingerprint,
274
404
  prefix: options.prefix
275
405
  });
276
- (options.githubOutput || isGitHubActions()) && setGitHubOutput("cache-key", cacheKey), console.info(cacheKey);
406
+ if (options.githubOutput || isGitHubActions()) {
407
+ setGitHubOutput("cache-key", cacheKey);
408
+ }
409
+ console.info(cacheKey);
277
410
  break;
278
411
  }
279
412
  // ========================================
@@ -282,25 +415,43 @@ To test cache invalidation:
282
415
  case "kv-get":
283
416
  {
284
417
  const key = subcommand;
285
- key || (console.error("Error: key is required"), process.exit(1));
286
- const kv = getKVCredentials(),
287
- value = await getFingerprintFromKV(kv, key);
288
- (options.githubOutput || isGitHubActions()) && (setGitHubOutput("value", value || ""), setGitHubOutput("found", value ? "true" : "false")), value ? console.info(value) : process.exit(1);
418
+ if (!key) {
419
+ console.error("Error: key is required");
420
+ process.exit(1);
421
+ }
422
+ const kv = getKVCredentials();
423
+ const value = await getFingerprintFromKV(kv, key);
424
+ if (options.githubOutput || isGitHubActions()) {
425
+ setGitHubOutput("value", value || "");
426
+ setGitHubOutput("found", value ? "true" : "false");
427
+ }
428
+ if (value) {
429
+ console.info(value);
430
+ } else {
431
+ process.exit(1);
432
+ }
289
433
  break;
290
434
  }
291
435
  case "kv-set":
292
436
  {
293
- const key = subcommand,
294
- value = args[0];
295
- (!key || !value) && (console.error("Error: key and value are required"), process.exit(1));
437
+ const key = subcommand;
438
+ const value = args[0];
439
+ if (!key || !value) {
440
+ console.error("Error: key and value are required");
441
+ process.exit(1);
442
+ }
296
443
  const kv = getKVCredentials();
297
- await saveFingerprintToKV(kv, key, value), console.info("OK");
444
+ await saveFingerprintToKV(kv, key, value);
445
+ console.info("OK");
298
446
  break;
299
447
  }
300
448
  default:
301
- console.error(`Unknown command: ${command}`), console.info(HELP), process.exit(1);
449
+ console.error(`Unknown command: ${command}`);
450
+ console.info(HELP);
451
+ process.exit(1);
302
452
  }
303
453
  } catch (error) {
304
- console.error("Error:", error.message), process.exit(1);
454
+ console.error("Error:", error.message);
455
+ process.exit(1);
305
456
  }
306
457
  //# sourceMappingURL=cli.mjs.map