@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 +26 -13
- package/dist/cache.mjs.map +1 -1
- package/dist/cli.mjs +268 -117
- package/dist/cli.mjs.map +1 -1
- package/dist/constants.mjs +8 -8
- package/dist/constants.mjs.map +1 -1
- package/dist/deps.mjs +46 -19
- package/dist/deps.mjs.map +1 -1
- package/dist/detox.mjs +85 -56
- package/dist/detox.mjs.map +1 -1
- package/dist/fingerprint.mjs +13 -9
- package/dist/fingerprint.mjs.map +1 -1
- package/dist/index.js +9 -95
- package/dist/index.js.map +1 -6
- package/dist/metro.mjs +91 -30
- package/dist/metro.mjs.map +1 -1
- package/dist/runner.mjs +27 -19
- package/dist/runner.mjs.map +1 -1
- package/package.json +4 -3
- package/src/cli.ts +24 -2
- package/src/detox.ts +10 -1
- package/src/ios.ts +192 -15
- package/src/metro.ts +49 -2
- package/src/run-detox-android.ts +1 -0
- package/src/run-detox-ios.ts +1 -0
- package/types/detox.d.ts +3 -0
- package/types/detox.d.ts.map +1 -1
- package/types/ios.d.ts +29 -0
- package/types/ios.d.ts.map +1 -1
- package/types/metro.d.ts.map +1 -1
- package/dist/cache.js +0 -71
- package/dist/cache.js.map +0 -6
- package/dist/cli.js +0 -298
- package/dist/cli.js.map +0 -6
- package/dist/constants.js +0 -12
- package/dist/constants.js.map +0 -6
- package/dist/deps.js +0 -44
- package/dist/deps.js.map +0 -6
- package/dist/detox.js +0 -73
- package/dist/detox.js.map +0 -6
- package/dist/fingerprint.js +0 -43
- package/dist/fingerprint.js.map +0 -6
- package/dist/metro.js +0 -102
- package/dist/metro.js.map +0 -6
- package/dist/runner.js +0 -73
- package/dist/runner.js.map +0 -6
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)
|
|
20
|
+
if (!response.ok) {
|
|
21
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
22
|
+
}
|
|
21
23
|
} catch (error) {
|
|
22
|
-
|
|
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)
|
|
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
|
-
|
|
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
|
-
|
|
54
|
-
|
|
55
|
-
|
|
63
|
+
cacheDir
|
|
64
|
+
} = options;
|
|
65
|
+
const cachePath = cacheDir ? join(process.cwd(), cacheDir, filePath) : join(process.cwd(), filePath);
|
|
56
66
|
mkdirSync(dirname(cachePath), {
|
|
57
|
-
recursive:
|
|
58
|
-
})
|
|
67
|
+
recursive: true
|
|
68
|
+
});
|
|
69
|
+
writeFileSync(cachePath, JSON.stringify(data, null, 2));
|
|
59
70
|
}
|
|
60
71
|
function loadCache(filePath, options = {}) {
|
|
61
72
|
const {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
if (!existsSync(cachePath))
|
|
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 {
|
package/dist/cache.mjs.map
CHANGED
|
@@ -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":"AAAA,SAASA,UAAA,EAAYC,YAAA,EAAcC,aAAA,EAAeC,SAAA,QAAiB;AACnE,SAASC,OAAA,EAASC,IAAA,QAAY;AAC9B,SAASC,sBAAA,QAA6C;AAgB/C,SAASC,eAAeC,OAAA,EAA+B;EAC5D,MAAM;IAAEC,QAAA;IAAUC,WAAA;IAAaC,MAAA,GAAS;EAAe,IAAIH,OAAA;EAC3D,OAAO,GAAGG,MAAM,IAAIF,QAAQ,IAAIC,WAAW;AAC7C;AAMA,eAAsBE,oBACpBC,EAAA,EACAC,GAAA,EACAJ,WAAA,EACAK,UAAA,GAAaT,sBAAA,EACE;EACf,IAAI;IACF,MAAMU,QAAA,GAAW,MAAMC,KAAA,CAAM,GAAGJ,EAAA,CAAGK,GAAG,UAAUJ,GAAG,IAAIC,UAAU,IAAIL,WAAW,IAAI;MAClFS,MAAA,EAAQ;MACRC,OAAA,EAAS;QACPC,aAAA,EAAe,UAAUR,EAAA,CAAGS,KAAK;MACnC;IACF,CAAC;IAED,IAAI,CAACN,QAAA,CAASO,EAAA,
|
|
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
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
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
|
-
|
|
76
|
+
while (i < argv.length) {
|
|
77
77
|
const arg = argv[i];
|
|
78
|
-
if (arg === "--project-root" && 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
|
|
81
|
-
|
|
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
|
-
|
|
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
|
-
|
|
97
|
-
|
|
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)
|
|
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
|
|
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 ===")
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
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 ===")
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
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 ===")
|
|
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
|
-
|
|
149
|
-
|
|
150
|
-
|
|
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
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
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 () =>
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
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)
|
|
235
|
+
console.error("iOS tests failed:", err);
|
|
236
|
+
iosExit = 1;
|
|
168
237
|
}
|
|
169
|
-
await ensureAndroidDeps()
|
|
170
|
-
--- Android Tests
|
|
171
|
-
|
|
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 () =>
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
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)
|
|
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 ===`)
|
|
187
|
-
|
|
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"
|
|
196
|
-
|
|
197
|
-
|
|
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
|
-
|
|
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
|
-
|
|
213
|
-
|
|
214
|
-
|
|
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(
|
|
221
|
-
`);
|
|
322
|
+
console.info("Generating fingerprints...\n");
|
|
222
323
|
const iosResult = await generateFingerprint({
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
console.info("Current fingerprints:")
|
|
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):")
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
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
|
-
})
|
|
245
|
-
|
|
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
|
|
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())
|
|
261
|
-
hash,
|
|
262
|
-
|
|
263
|
-
|
|
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
|
-
|
|
270
|
-
|
|
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())
|
|
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
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
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
|
-
|
|
295
|
-
(!key || !value)
|
|
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)
|
|
444
|
+
await saveFingerprintToKV(kv, key, value);
|
|
445
|
+
console.info("OK");
|
|
298
446
|
break;
|
|
299
447
|
}
|
|
300
448
|
default:
|
|
301
|
-
console.error(`Unknown command: ${command}`)
|
|
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)
|
|
454
|
+
console.error("Error:", error.message);
|
|
455
|
+
process.exit(1);
|
|
305
456
|
}
|
|
306
457
|
//# sourceMappingURL=cli.mjs.map
|