@tamagui/native-ci 1.139.0
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/README.md +320 -0
- package/action.yml +98 -0
- package/actions/fingerprint/action.yml +110 -0
- package/actions/test-detox-android/action.yml +70 -0
- package/actions/test-detox-ios/action.yml +66 -0
- package/dist/cache.js +71 -0
- package/dist/cache.js.map +6 -0
- package/dist/cache.mjs +73 -0
- package/dist/cache.mjs.map +1 -0
- package/dist/cli.js +275 -0
- package/dist/cli.js.map +6 -0
- package/dist/cli.mjs +306 -0
- package/dist/cli.mjs.map +1 -0
- package/dist/constants.js +12 -0
- package/dist/constants.js.map +6 -0
- package/dist/constants.mjs +10 -0
- package/dist/constants.mjs.map +1 -0
- package/dist/deps.js +44 -0
- package/dist/deps.js.map +6 -0
- package/dist/deps.mjs +53 -0
- package/dist/deps.mjs.map +1 -0
- package/dist/detox.js +49 -0
- package/dist/detox.js.map +6 -0
- package/dist/detox.mjs +55 -0
- package/dist/detox.mjs.map +1 -0
- package/dist/fingerprint.js +43 -0
- package/dist/fingerprint.js.map +6 -0
- package/dist/fingerprint.mjs +40 -0
- package/dist/fingerprint.mjs.map +1 -0
- package/dist/index.js +90 -0
- package/dist/index.js.map +6 -0
- package/dist/index.mjs +11 -0
- package/dist/index.mjs.map +1 -0
- package/dist/metro.js +79 -0
- package/dist/metro.js.map +6 -0
- package/dist/metro.mjs +75 -0
- package/dist/metro.mjs.map +1 -0
- package/dist/runner.js +73 -0
- package/dist/runner.js.map +6 -0
- package/dist/runner.mjs +73 -0
- package/dist/runner.mjs.map +1 -0
- package/package.json +50 -0
- package/src/android.ts +103 -0
- package/src/cache.ts +144 -0
- package/src/cli.ts +513 -0
- package/src/constants.ts +30 -0
- package/src/deps.ts +109 -0
- package/src/detox.ts +102 -0
- package/src/fingerprint.ts +77 -0
- package/src/index.ts +86 -0
- package/src/ios.ts +38 -0
- package/src/metro.ts +157 -0
- package/src/run-detox-android.ts +49 -0
- package/src/run-detox-ios.ts +40 -0
- package/src/runner.ts +123 -0
- package/types/android.d.ts +32 -0
- package/types/android.d.ts.map +1 -0
- package/types/cache.d.ts +41 -0
- package/types/cache.d.ts.map +1 -0
- package/types/cli.d.ts +11 -0
- package/types/cli.d.ts.map +1 -0
- package/types/constants.d.ts +18 -0
- package/types/constants.d.ts.map +1 -0
- package/types/deps.d.ts +32 -0
- package/types/deps.d.ts.map +1 -0
- package/types/detox.d.ts +39 -0
- package/types/detox.d.ts.map +1 -0
- package/types/fingerprint.d.ts +21 -0
- package/types/fingerprint.d.ts.map +1 -0
- package/types/index.d.ts +16 -0
- package/types/index.d.ts.map +1 -0
- package/types/ios.d.ts +18 -0
- package/types/ios.d.ts.map +1 -0
- package/types/metro.d.ts +51 -0
- package/types/metro.d.ts.map +1 -0
- package/types/run-detox-android.d.ts +15 -0
- package/types/run-detox-android.d.ts.map +1 -0
- package/types/run-detox-ios.d.ts +14 -0
- package/types/run-detox-ios.d.ts.map +1 -0
- package/types/runner.d.ts +35 -0
- package/types/runner.d.ts.map +1 -0
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/fingerprint.ts"],
|
|
4
|
+
"mappings": "AAAA,SAAS,gBAAsC;AAC/C,SAAS,kBAAkB;AAC3B,SAAS,YAAY,oBAAoB;AACzC,SAAS,YAAY;AAkBrB,eAAsB,oBACpB,SAC4B;AAC5B,QAAM,EAAE,UAAU,cAAc,QAAQ,IAAI,GAAG,QAAQ,GAAM,IAAI,SAG3D,cAAc,KAAK,aAAa,UAAU;AAChD,MAAI,CAAC,WAAW,WAAW;AACzB,UAAM,IAAI,MAAM,wBAAwB,WAAW,4BAA4B;AAGjF,QAAM,cAA+B;AAAA,IACnC,KAAK;AAAA,IACL,UAAU;AAAA,IACV,OAAO,QAAQ,YAAY,CAAC,QAAQ,QAAQ,MAAM;AAAA,EACpD;AAEA,MAAI;AACF,UAAM,SAAS;AAAA,MACb,yDAAyD,QAAQ;AAAA,MACjE;AAAA,IACF,GAEM,SAAS,KAAK,MAAM,MAAM;AAEhC,WAAO;AAAA,MACL,MAAM,OAAO;AAAA,MACb,SAAS,OAAO,WAAW,CAAC;AAAA,IAC9B;AAAA,EACF,SAAS,OAAO;AACd,UAAM,MAAM;AACZ,UAAM,IAAI;AAAA,MACR,sBAAsB,QAAQ,iBAAiB,IAAI,OAAO,GAAG,IAAI,SAAS;AAAA,EAAK,IAAI,MAAM,KAAK,EAAE;AAAA,IAClG;AAAA,EACF;AACF;AAMO,SAAS,2BACd,OACA,cAAsB,QAAQ,IAAI,GAC1B;AACR,QAAM,OAAO,WAAW,QAAQ;AAEhC,aAAW,QAAQ,OAAO;AACxB,UAAM,WAAW,KAAK,aAAa,IAAI;AACvC,IAAI,WAAW,QAAQ,KACrB,KAAK,OAAO,aAAa,QAAQ,CAAC;AAAA,EAEtC;AAEA,SAAO,KAAK,OAAO,KAAK,EAAE,MAAM,GAAG,EAAE;AACvC;",
|
|
5
|
+
"names": []
|
|
6
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { execSync } from "node:child_process";
|
|
2
|
+
import { createHash } from "node:crypto";
|
|
3
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
async function generateFingerprint(options) {
|
|
6
|
+
const {
|
|
7
|
+
platform,
|
|
8
|
+
projectRoot = process.cwd(),
|
|
9
|
+
debug = !1
|
|
10
|
+
} = options,
|
|
11
|
+
appJsonPath = join(projectRoot, "app.json");
|
|
12
|
+
if (!existsSync(appJsonPath)) throw new Error(`No app.json found at ${projectRoot}. Is this an Expo project?`);
|
|
13
|
+
const execOptions = {
|
|
14
|
+
cwd: projectRoot,
|
|
15
|
+
encoding: "utf-8",
|
|
16
|
+
stdio: debug ? "inherit" : ["pipe", "pipe", "pipe"]
|
|
17
|
+
};
|
|
18
|
+
try {
|
|
19
|
+
const result = execSync(`npx @expo/fingerprint fingerprint:generate --platform ${platform}`, execOptions),
|
|
20
|
+
parsed = JSON.parse(result);
|
|
21
|
+
return {
|
|
22
|
+
hash: parsed.hash,
|
|
23
|
+
sources: parsed.sources || []
|
|
24
|
+
};
|
|
25
|
+
} catch (error) {
|
|
26
|
+
const err = error;
|
|
27
|
+
throw new Error(`Failed to generate ${platform} fingerprint: ${err.message}${err.stderr ? `
|
|
28
|
+
${err.stderr}` : ""}`);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
function generatePreFingerprintHash(files, projectRoot = process.cwd()) {
|
|
32
|
+
const hash = createHash("sha256");
|
|
33
|
+
for (const file of files) {
|
|
34
|
+
const filePath = join(projectRoot, file);
|
|
35
|
+
existsSync(filePath) && hash.update(readFileSync(filePath));
|
|
36
|
+
}
|
|
37
|
+
return hash.digest("hex").slice(0, 16);
|
|
38
|
+
}
|
|
39
|
+
export { generateFingerprint, generatePreFingerprintHash };
|
|
40
|
+
//# sourceMappingURL=fingerprint.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["execSync","createHash","existsSync","readFileSync","join","generateFingerprint","options","platform","projectRoot","process","cwd","debug","appJsonPath","Error","execOptions","encoding","stdio","result","parsed","JSON","parse","hash","sources","error","err","message","stderr","generatePreFingerprintHash","files","file","filePath","update","digest","slice"],"sources":["../src/fingerprint.ts"],"sourcesContent":[null],"mappings":"AAAA,SAASA,QAAA,QAAsC;AAC/C,SAASC,UAAA,QAAkB;AAC3B,SAASC,UAAA,EAAYC,YAAA,QAAoB;AACzC,SAASC,IAAA,QAAY;AAkBrB,eAAsBC,oBACpBC,OAAA,EAC4B;EAC5B,MAAM;MAAEC,QAAA;MAAUC,WAAA,GAAcC,OAAA,CAAQC,GAAA,CAAI;MAAGC,KAAA,GAAQ;IAAM,IAAIL,OAAA;IAG3DM,WAAA,GAAcR,IAAA,CAAKI,WAAA,EAAa,UAAU;EAChD,IAAI,CAACN,UAAA,CAAWU,WAAW,GACzB,MAAM,IAAIC,KAAA,CAAM,wBAAwBL,WAAW,4BAA4B;EAGjF,MAAMM,WAAA,GAA+B;IACnCJ,GAAA,EAAKF,WAAA;IACLO,QAAA,EAAU;IACVC,KAAA,EAAOL,KAAA,GAAQ,YAAY,CAAC,QAAQ,QAAQ,MAAM;EACpD;EAEA,IAAI;IACF,MAAMM,MAAA,GAASjB,QAAA,CACb,yDAAyDO,QAAQ,IACjEO,WACF;MAEMI,MAAA,GAASC,IAAA,CAAKC,KAAA,CAAMH,MAAM;IAEhC,OAAO;MACLI,IAAA,EAAMH,MAAA,CAAOG,IAAA;MACbC,OAAA,EAASJ,MAAA,CAAOI,OAAA,IAAW;IAC7B;EACF,SAASC,KAAA,EAAO;IACd,MAAMC,GAAA,GAAMD,KAAA;IACZ,MAAM,IAAIV,KAAA,CACR,sBAAsBN,QAAQ,iBAAiBiB,GAAA,CAAIC,OAAO,GAAGD,GAAA,CAAIE,MAAA,GAAS;AAAA,EAAKF,GAAA,CAAIE,MAAM,KAAK,EAAE,EAClG;EACF;AACF;AAMO,SAASC,2BACdC,KAAA,EACApB,WAAA,GAAsBC,OAAA,CAAQC,GAAA,CAAI,GAC1B;EACR,MAAMW,IAAA,GAAOpB,UAAA,CAAW,QAAQ;EAEhC,WAAW4B,IAAA,IAAQD,KAAA,EAAO;IACxB,MAAME,QAAA,GAAW1B,IAAA,CAAKI,WAAA,EAAaqB,IAAI;IACnC3B,UAAA,CAAW4B,QAAQ,KACrBT,IAAA,CAAKU,MAAA,CAAO5B,YAAA,CAAa2B,QAAQ,CAAC;EAEtC;EAEA,OAAOT,IAAA,CAAKW,MAAA,CAAO,KAAK,EAAEC,KAAA,CAAM,GAAG,EAAE;AACvC","ignoreList":[]}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import {
|
|
2
|
+
METRO_HOST,
|
|
3
|
+
METRO_PORT,
|
|
4
|
+
METRO_URL,
|
|
5
|
+
DETOX_SERVER_PORT,
|
|
6
|
+
DEFAULT_METRO_WAIT_ATTEMPTS,
|
|
7
|
+
DEFAULT_METRO_WAIT_INTERVAL_MS,
|
|
8
|
+
DEFAULT_METRO_TIMEOUT_MS,
|
|
9
|
+
DEFAULT_KV_TTL_SECONDS
|
|
10
|
+
} from "./constants";
|
|
11
|
+
import {
|
|
12
|
+
generateFingerprint,
|
|
13
|
+
generatePreFingerprintHash
|
|
14
|
+
} from "./fingerprint";
|
|
15
|
+
import {
|
|
16
|
+
createCacheKey,
|
|
17
|
+
saveFingerprintToKV,
|
|
18
|
+
getFingerprintFromKV,
|
|
19
|
+
extendKVTTL,
|
|
20
|
+
saveCache,
|
|
21
|
+
loadCache
|
|
22
|
+
} from "./cache";
|
|
23
|
+
import {
|
|
24
|
+
runWithCache,
|
|
25
|
+
setGitHubOutput,
|
|
26
|
+
isGitHubActions,
|
|
27
|
+
isCI
|
|
28
|
+
} from "./runner";
|
|
29
|
+
import {
|
|
30
|
+
waitForMetro,
|
|
31
|
+
prewarmBundle,
|
|
32
|
+
startMetro,
|
|
33
|
+
setupSignalHandlers,
|
|
34
|
+
withMetro
|
|
35
|
+
} from "./metro";
|
|
36
|
+
import {
|
|
37
|
+
parseDetoxArgs,
|
|
38
|
+
buildDetoxArgs,
|
|
39
|
+
runDetoxTests
|
|
40
|
+
} from "./detox";
|
|
41
|
+
import { waitForDevice, setupAdbReverse, setupAndroidDevice, ensureAndroidFolder } from "./android";
|
|
42
|
+
import { ensureIOSFolder } from "./ios";
|
|
43
|
+
import {
|
|
44
|
+
checkDeps,
|
|
45
|
+
ensureIosDeps,
|
|
46
|
+
ensureAndroidDeps,
|
|
47
|
+
ensureMaestro,
|
|
48
|
+
printDepsStatus
|
|
49
|
+
} from "./deps";
|
|
50
|
+
export {
|
|
51
|
+
DEFAULT_KV_TTL_SECONDS,
|
|
52
|
+
DEFAULT_METRO_TIMEOUT_MS,
|
|
53
|
+
DEFAULT_METRO_WAIT_ATTEMPTS,
|
|
54
|
+
DEFAULT_METRO_WAIT_INTERVAL_MS,
|
|
55
|
+
DETOX_SERVER_PORT,
|
|
56
|
+
METRO_HOST,
|
|
57
|
+
METRO_PORT,
|
|
58
|
+
METRO_URL,
|
|
59
|
+
buildDetoxArgs,
|
|
60
|
+
checkDeps,
|
|
61
|
+
createCacheKey,
|
|
62
|
+
ensureAndroidDeps,
|
|
63
|
+
ensureAndroidFolder,
|
|
64
|
+
ensureIOSFolder,
|
|
65
|
+
ensureIosDeps,
|
|
66
|
+
ensureMaestro,
|
|
67
|
+
extendKVTTL,
|
|
68
|
+
generateFingerprint,
|
|
69
|
+
generatePreFingerprintHash,
|
|
70
|
+
getFingerprintFromKV,
|
|
71
|
+
isCI,
|
|
72
|
+
isGitHubActions,
|
|
73
|
+
loadCache,
|
|
74
|
+
parseDetoxArgs,
|
|
75
|
+
prewarmBundle,
|
|
76
|
+
printDepsStatus,
|
|
77
|
+
runDetoxTests,
|
|
78
|
+
runWithCache,
|
|
79
|
+
saveCache,
|
|
80
|
+
saveFingerprintToKV,
|
|
81
|
+
setGitHubOutput,
|
|
82
|
+
setupAdbReverse,
|
|
83
|
+
setupAndroidDevice,
|
|
84
|
+
setupSignalHandlers,
|
|
85
|
+
startMetro,
|
|
86
|
+
waitForDevice,
|
|
87
|
+
waitForMetro,
|
|
88
|
+
withMetro
|
|
89
|
+
};
|
|
90
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/index.ts"],
|
|
4
|
+
"mappings": "AAQA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAGK;AAGP;AAAA,EACE;AAAA,EACA;AAAA,OAGK;AAGP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAIK;AAGP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAGK;AAGP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAGK;AAGP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OAEK;AAGP,SAAS,eAAe,iBAAiB,oBAAoB,2BAA2B;AAGxF,SAAS,uBAAuB;AAGhC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAEK;",
|
|
5
|
+
"names": []
|
|
6
|
+
}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { METRO_HOST, METRO_PORT, METRO_URL, DETOX_SERVER_PORT, DEFAULT_METRO_WAIT_ATTEMPTS, DEFAULT_METRO_WAIT_INTERVAL_MS, DEFAULT_METRO_TIMEOUT_MS, DEFAULT_KV_TTL_SECONDS } from "./constants.mjs";
|
|
2
|
+
import { generateFingerprint, generatePreFingerprintHash } from "./fingerprint.mjs";
|
|
3
|
+
import { createCacheKey, saveFingerprintToKV, getFingerprintFromKV, extendKVTTL, saveCache, loadCache } from "./cache.mjs";
|
|
4
|
+
import { runWithCache, setGitHubOutput, isGitHubActions, isCI } from "./runner.mjs";
|
|
5
|
+
import { waitForMetro, prewarmBundle, startMetro, setupSignalHandlers, withMetro } from "./metro.mjs";
|
|
6
|
+
import { parseDetoxArgs, buildDetoxArgs, runDetoxTests } from "./detox.mjs";
|
|
7
|
+
import { waitForDevice, setupAdbReverse, setupAndroidDevice, ensureAndroidFolder } from "./android";
|
|
8
|
+
import { ensureIOSFolder } from "./ios";
|
|
9
|
+
import { checkDeps, ensureIosDeps, ensureAndroidDeps, ensureMaestro, printDepsStatus } from "./deps.mjs";
|
|
10
|
+
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, createCacheKey, ensureAndroidDeps, ensureAndroidFolder, 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 };
|
|
11
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["METRO_HOST","METRO_PORT","METRO_URL","DETOX_SERVER_PORT","DEFAULT_METRO_WAIT_ATTEMPTS","DEFAULT_METRO_WAIT_INTERVAL_MS","DEFAULT_METRO_TIMEOUT_MS","DEFAULT_KV_TTL_SECONDS","generateFingerprint","generatePreFingerprintHash","createCacheKey","saveFingerprintToKV","getFingerprintFromKV","extendKVTTL","saveCache","loadCache","runWithCache","setGitHubOutput","isGitHubActions","isCI","waitForMetro","prewarmBundle","startMetro","setupSignalHandlers","withMetro","parseDetoxArgs","buildDetoxArgs","runDetoxTests","waitForDevice","setupAdbReverse","setupAndroidDevice","ensureAndroidFolder","ensureIOSFolder","checkDeps","ensureIosDeps","ensureAndroidDeps","ensureMaestro","printDepsStatus"],"sources":["../src/index.ts"],"sourcesContent":[null],"mappings":"AAQA,SACEA,UAAA,EACAC,UAAA,EACAC,SAAA,EACAC,iBAAA,EACAC,2BAAA,EACAC,8BAAA,EACAC,wBAAA,EACAC,sBAAA,QAGK;AAGP,SACEC,mBAAA,EACAC,0BAAA,QAGK;AAGP,SACEC,cAAA,EACAC,mBAAA,EACAC,oBAAA,EACAC,WAAA,EACAC,SAAA,EACAC,SAAA,QAIK;AAGP,SACEC,YAAA,EACAC,eAAA,EACAC,eAAA,EACAC,IAAA,QAGK;AAGP,SACEC,YAAA,EACAC,aAAA,EACAC,UAAA,EACAC,mBAAA,EACAC,SAAA,QAGK;AAGP,SACEC,cAAA,EACAC,cAAA,EACAC,aAAA,QAEK;AAGP,SAASC,aAAA,EAAeC,eAAA,EAAiBC,kBAAA,EAAoBC,mBAAA,QAA2B;AAGxF,SAASC,eAAA,QAAuB;AAGhC,SACEC,SAAA,EACAC,aAAA,EACAC,iBAAA,EACAC,aAAA,EACAC,eAAA,QAEK","ignoreList":[]}
|
package/dist/metro.js
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import {
|
|
2
|
+
METRO_URL,
|
|
3
|
+
DEFAULT_METRO_WAIT_ATTEMPTS,
|
|
4
|
+
DEFAULT_METRO_WAIT_INTERVAL_MS
|
|
5
|
+
} from "./constants";
|
|
6
|
+
async function waitForMetro(options) {
|
|
7
|
+
const {
|
|
8
|
+
platform,
|
|
9
|
+
maxAttempts = DEFAULT_METRO_WAIT_ATTEMPTS,
|
|
10
|
+
intervalMs = DEFAULT_METRO_WAIT_INTERVAL_MS
|
|
11
|
+
} = options;
|
|
12
|
+
console.info("Waiting for Metro to start...");
|
|
13
|
+
for (let i = 1; i <= maxAttempts; i++) {
|
|
14
|
+
try {
|
|
15
|
+
if ((await fetch(`${METRO_URL}/`, {
|
|
16
|
+
headers: { "Expo-Platform": platform }
|
|
17
|
+
})).ok)
|
|
18
|
+
return console.info("Metro is responding!"), !0;
|
|
19
|
+
} catch {
|
|
20
|
+
}
|
|
21
|
+
console.info(`Waiting for Metro... (${i}/${maxAttempts})`), await Bun.sleep(intervalMs);
|
|
22
|
+
}
|
|
23
|
+
return !1;
|
|
24
|
+
}
|
|
25
|
+
async function prewarmBundle(platform) {
|
|
26
|
+
console.info("Pre-warming bundle...");
|
|
27
|
+
try {
|
|
28
|
+
const response = await fetch(`${METRO_URL}/`, {
|
|
29
|
+
headers: { "Expo-Platform": platform }
|
|
30
|
+
});
|
|
31
|
+
if (response.ok) {
|
|
32
|
+
const bundleUrl = (await response.json())?.launchAsset?.url;
|
|
33
|
+
bundleUrl ? (console.info(`Fetching bundle from: ${bundleUrl}`), await fetch(bundleUrl), console.info("Bundle pre-warmed!")) : console.info("No bundle URL found in manifest, skipping pre-warm");
|
|
34
|
+
}
|
|
35
|
+
} catch {
|
|
36
|
+
console.info("Bundle pre-warm completed (with error, continuing)");
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
function startMetro() {
|
|
40
|
+
console.info(`
|
|
41
|
+
--- Starting Metro bundler ---`);
|
|
42
|
+
const proc = Bun.spawn(["yarn", "expo", "start", "--dev-client", "--offline", "--clear"], {
|
|
43
|
+
env: { ...process.env, EXPO_NO_TELEMETRY: "true" },
|
|
44
|
+
stdout: "inherit",
|
|
45
|
+
stderr: "inherit"
|
|
46
|
+
});
|
|
47
|
+
return {
|
|
48
|
+
proc,
|
|
49
|
+
kill: () => {
|
|
50
|
+
proc.kill(), console.info("Metro stopped");
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
function setupSignalHandlers(metro) {
|
|
55
|
+
const cleanup = (signal) => {
|
|
56
|
+
console.info(`
|
|
57
|
+
Received ${signal}, cleaning up...`), metro.kill(), process.exit(signal === "SIGINT" ? 130 : 143);
|
|
58
|
+
};
|
|
59
|
+
process.on("SIGINT", () => cleanup("SIGINT")), process.on("SIGTERM", () => cleanup("SIGTERM"));
|
|
60
|
+
}
|
|
61
|
+
async function withMetro(platform, fn) {
|
|
62
|
+
const metro = startMetro();
|
|
63
|
+
setupSignalHandlers(metro);
|
|
64
|
+
try {
|
|
65
|
+
if (!await waitForMetro({ platform }))
|
|
66
|
+
throw new Error("Metro failed to start within timeout");
|
|
67
|
+
return await prewarmBundle(platform), await fn();
|
|
68
|
+
} finally {
|
|
69
|
+
metro.kill();
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
export {
|
|
73
|
+
prewarmBundle,
|
|
74
|
+
setupSignalHandlers,
|
|
75
|
+
startMetro,
|
|
76
|
+
waitForMetro,
|
|
77
|
+
withMetro
|
|
78
|
+
};
|
|
79
|
+
//# sourceMappingURL=metro.js.map
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/metro.ts"],
|
|
4
|
+
"mappings": "AAQA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OAGK;AAuBP,eAAsB,aAAa,SAAyC;AAC1E,QAAM;AAAA,IACJ;AAAA,IACA,cAAc;AAAA,IACd,aAAa;AAAA,EACf,IAAI;AAEJ,UAAQ,KAAK,+BAA+B;AAE5C,WAAS,IAAI,GAAG,KAAK,aAAa,KAAK;AACrC,QAAI;AAIF,WAHiB,MAAM,MAAM,GAAG,SAAS,KAAK;AAAA,QAC5C,SAAS,EAAE,iBAAiB,SAAS;AAAA,MACvC,CAAC,GACY;AACX,uBAAQ,KAAK,sBAAsB,GAC5B;AAAA,IAEX,QAAQ;AAAA,IAER;AACA,YAAQ,KAAK,yBAAyB,CAAC,IAAI,WAAW,GAAG,GACzD,MAAM,IAAI,MAAM,UAAU;AAAA,EAC5B;AAEA,SAAO;AACT;AAMA,eAAsB,cAAc,UAAmC;AACrE,UAAQ,KAAK,uBAAuB;AAEpC,MAAI;AACF,UAAM,WAAW,MAAM,MAAM,GAAG,SAAS,KAAK;AAAA,MAC5C,SAAS,EAAE,iBAAiB,SAAS;AAAA,IACvC,CAAC;AAED,QAAI,SAAS,IAAI;AAEf,YAAM,aADY,MAAM,SAAS,KAAK,IACV,aAAa;AAEzC,MAAI,aACF,QAAQ,KAAK,yBAAyB,SAAS,EAAE,GACjD,MAAM,MAAM,SAAS,GACrB,QAAQ,KAAK,oBAAoB,KAEjC,QAAQ,KAAK,oDAAoD;AAAA,IAErE;AAAA,EACF,QAAgB;AAEd,YAAQ,KAAK,oDAAoD;AAAA,EACnE;AACF;AAOO,SAAS,aAA2B;AACzC,UAAQ,KAAK;AAAA,+BAAkC;AAI/C,QAAM,OAAO,IAAI,MAAM,CAAC,QAAQ,QAAQ,SAAS,gBAAgB,aAAa,SAAS,GAAG;AAAA,IACxF,KAAK,EAAE,GAAG,QAAQ,KAAK,mBAAmB,OAAO;AAAA,IACjD,QAAQ;AAAA,IACR,QAAQ;AAAA,EACV,CAAC;AAED,SAAO;AAAA,IACL;AAAA,IACA,MAAM,MAAM;AACV,WAAK,KAAK,GACV,QAAQ,KAAK,eAAe;AAAA,IAC9B;AAAA,EACF;AACF;AAMO,SAAS,oBAAoB,OAA2B;AAC7D,QAAM,UAAU,CAAC,WAAmB;AAClC,YAAQ,KAAK;AAAA,WAAc,MAAM,kBAAkB,GACnD,MAAM,KAAK,GACX,QAAQ,KAAK,WAAW,WAAW,MAAM,GAAG;AAAA,EAC9C;AAEA,UAAQ,GAAG,UAAU,MAAM,QAAQ,QAAQ,CAAC,GAC5C,QAAQ,GAAG,WAAW,MAAM,QAAQ,SAAS,CAAC;AAChD;AAOA,eAAsB,UAAa,UAAoB,IAAkC;AACvF,QAAM,QAAQ,WAAW;AACzB,sBAAoB,KAAK;AAEzB,MAAI;AAEF,QAAI,CADe,MAAM,aAAa,EAAE,SAAS,CAAC;AAEhD,YAAM,IAAI,MAAM,sCAAsC;AAGxD,iBAAM,cAAc,QAAQ,GAErB,MAAM,GAAG;AAAA,EAClB,UAAE;AACA,UAAM,KAAK;AAAA,EACb;AACF;",
|
|
5
|
+
"names": []
|
|
6
|
+
}
|
package/dist/metro.mjs
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { METRO_URL, DEFAULT_METRO_WAIT_ATTEMPTS, DEFAULT_METRO_WAIT_INTERVAL_MS } from "./constants.mjs";
|
|
2
|
+
async function waitForMetro(options) {
|
|
3
|
+
const {
|
|
4
|
+
platform,
|
|
5
|
+
maxAttempts = DEFAULT_METRO_WAIT_ATTEMPTS,
|
|
6
|
+
intervalMs = DEFAULT_METRO_WAIT_INTERVAL_MS
|
|
7
|
+
} = options;
|
|
8
|
+
console.info("Waiting for Metro to start...");
|
|
9
|
+
for (let i = 1; i <= maxAttempts; i++) {
|
|
10
|
+
try {
|
|
11
|
+
if ((await fetch(`${METRO_URL}/`, {
|
|
12
|
+
headers: {
|
|
13
|
+
"Expo-Platform": platform
|
|
14
|
+
}
|
|
15
|
+
})).ok) return console.info("Metro is responding!"), !0;
|
|
16
|
+
} catch {}
|
|
17
|
+
console.info(`Waiting for Metro... (${i}/${maxAttempts})`), await Bun.sleep(intervalMs);
|
|
18
|
+
}
|
|
19
|
+
return !1;
|
|
20
|
+
}
|
|
21
|
+
async function prewarmBundle(platform) {
|
|
22
|
+
console.info("Pre-warming bundle...");
|
|
23
|
+
try {
|
|
24
|
+
const response = await fetch(`${METRO_URL}/`, {
|
|
25
|
+
headers: {
|
|
26
|
+
"Expo-Platform": platform
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
if (response.ok) {
|
|
30
|
+
const bundleUrl = (await response.json())?.launchAsset?.url;
|
|
31
|
+
bundleUrl ? (console.info(`Fetching bundle from: ${bundleUrl}`), await fetch(bundleUrl), console.info("Bundle pre-warmed!")) : console.info("No bundle URL found in manifest, skipping pre-warm");
|
|
32
|
+
}
|
|
33
|
+
} catch {
|
|
34
|
+
console.info("Bundle pre-warm completed (with error, continuing)");
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
function startMetro() {
|
|
38
|
+
console.info(`
|
|
39
|
+
--- Starting Metro bundler ---`);
|
|
40
|
+
const proc = Bun.spawn(["yarn", "expo", "start", "--dev-client", "--offline", "--clear"], {
|
|
41
|
+
env: {
|
|
42
|
+
...process.env,
|
|
43
|
+
EXPO_NO_TELEMETRY: "true"
|
|
44
|
+
},
|
|
45
|
+
stdout: "inherit",
|
|
46
|
+
stderr: "inherit"
|
|
47
|
+
});
|
|
48
|
+
return {
|
|
49
|
+
proc,
|
|
50
|
+
kill: () => {
|
|
51
|
+
proc.kill(), console.info("Metro stopped");
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
function setupSignalHandlers(metro) {
|
|
56
|
+
const cleanup = signal => {
|
|
57
|
+
console.info(`
|
|
58
|
+
Received ${signal}, cleaning up...`), metro.kill(), process.exit(signal === "SIGINT" ? 130 : 143);
|
|
59
|
+
};
|
|
60
|
+
process.on("SIGINT", () => cleanup("SIGINT")), process.on("SIGTERM", () => cleanup("SIGTERM"));
|
|
61
|
+
}
|
|
62
|
+
async function withMetro(platform, fn) {
|
|
63
|
+
const metro = startMetro();
|
|
64
|
+
setupSignalHandlers(metro);
|
|
65
|
+
try {
|
|
66
|
+
if (!(await waitForMetro({
|
|
67
|
+
platform
|
|
68
|
+
}))) throw new Error("Metro failed to start within timeout");
|
|
69
|
+
return await prewarmBundle(platform), await fn();
|
|
70
|
+
} finally {
|
|
71
|
+
metro.kill();
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
export { prewarmBundle, setupSignalHandlers, startMetro, waitForMetro, withMetro };
|
|
75
|
+
//# sourceMappingURL=metro.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["METRO_URL","DEFAULT_METRO_WAIT_ATTEMPTS","DEFAULT_METRO_WAIT_INTERVAL_MS","waitForMetro","options","platform","maxAttempts","intervalMs","console","info","i","fetch","headers","ok","Bun","sleep","prewarmBundle","response","bundleUrl","json","launchAsset","url","startMetro","proc","spawn","env","process","EXPO_NO_TELEMETRY","stdout","stderr","kill","setupSignalHandlers","metro","cleanup","signal","exit","on","withMetro","fn","Error"],"sources":["../src/metro.ts"],"sourcesContent":[null],"mappings":"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","ignoreList":[]}
|
package/dist/runner.js
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { execSync } from "node:child_process";
|
|
2
|
+
import { appendFileSync } from "node:fs";
|
|
3
|
+
import { createCacheKey, loadCache, saveCache } from "./cache";
|
|
4
|
+
import { generateFingerprint } from "./fingerprint";
|
|
5
|
+
async function runWithCache(options) {
|
|
6
|
+
const {
|
|
7
|
+
platform,
|
|
8
|
+
buildCommand,
|
|
9
|
+
outputPaths,
|
|
10
|
+
projectRoot = process.cwd(),
|
|
11
|
+
cachePrefix = "native-build",
|
|
12
|
+
debug = !1
|
|
13
|
+
} = options, log = debug ? console.log : () => {
|
|
14
|
+
};
|
|
15
|
+
log(`Generating ${platform} fingerprint...`);
|
|
16
|
+
const { hash: fingerprint } = await generateFingerprint({
|
|
17
|
+
platform,
|
|
18
|
+
projectRoot,
|
|
19
|
+
debug
|
|
20
|
+
});
|
|
21
|
+
log(`Fingerprint: ${fingerprint}`);
|
|
22
|
+
const cacheKey = createCacheKey({ platform, fingerprint, prefix: cachePrefix });
|
|
23
|
+
log(`Cache key: ${cacheKey}`);
|
|
24
|
+
const cached = loadCache(cacheKey);
|
|
25
|
+
if (cached && cached.fingerprint === fingerprint)
|
|
26
|
+
return log("Cache hit! Skipping build."), {
|
|
27
|
+
cacheHit: !0,
|
|
28
|
+
fingerprint,
|
|
29
|
+
cacheKey,
|
|
30
|
+
outputPaths
|
|
31
|
+
};
|
|
32
|
+
log(`Running build: ${buildCommand}`);
|
|
33
|
+
const execOptions = {
|
|
34
|
+
cwd: projectRoot,
|
|
35
|
+
stdio: debug ? "inherit" : ["pipe", "pipe", "pipe"]
|
|
36
|
+
};
|
|
37
|
+
try {
|
|
38
|
+
execSync(buildCommand, execOptions);
|
|
39
|
+
} catch (error) {
|
|
40
|
+
const err = error;
|
|
41
|
+
throw new Error(`Build failed: ${err.message}${err.stderr ? `
|
|
42
|
+
${err.stderr}` : ""}`);
|
|
43
|
+
}
|
|
44
|
+
return saveCache(cacheKey, {
|
|
45
|
+
fingerprint,
|
|
46
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
47
|
+
platform,
|
|
48
|
+
outputPaths
|
|
49
|
+
}), {
|
|
50
|
+
cacheHit: !1,
|
|
51
|
+
fingerprint,
|
|
52
|
+
cacheKey,
|
|
53
|
+
outputPaths
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
function setGitHubOutput(name, value) {
|
|
57
|
+
const outputFile = process.env.GITHUB_OUTPUT;
|
|
58
|
+
outputFile ? appendFileSync(outputFile, `${name}=${value}
|
|
59
|
+
`) : console.info(`[GitHub Output] ${name}=${value}`);
|
|
60
|
+
}
|
|
61
|
+
function isGitHubActions() {
|
|
62
|
+
return !!process.env.GITHUB_ACTIONS;
|
|
63
|
+
}
|
|
64
|
+
function isCI() {
|
|
65
|
+
return !!process.env.CI;
|
|
66
|
+
}
|
|
67
|
+
export {
|
|
68
|
+
isCI,
|
|
69
|
+
isGitHubActions,
|
|
70
|
+
runWithCache,
|
|
71
|
+
setGitHubOutput
|
|
72
|
+
};
|
|
73
|
+
//# sourceMappingURL=runner.js.map
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/runner.ts"],
|
|
4
|
+
"mappings": "AAAA,SAAS,gBAAsC;AAC/C,SAAS,sBAAsB;AAC/B,SAAS,gBAAgB,WAAW,iBAAiB;AACrD,SAAS,2BAA2B;AAwBpC,eAAsB,aACpB,SAC6B;AAC7B,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA,cAAc,QAAQ,IAAI;AAAA,IAC1B,cAAc;AAAA,IACd,QAAQ;AAAA,EACV,IAAI,SAEE,MAAM,QAAQ,QAAQ,MAAM,MAAM;AAAA,EAAC;AAGzC,MAAI,cAAc,QAAQ,iBAAiB;AAC3C,QAAM,EAAE,MAAM,YAAY,IAAI,MAAM,oBAAoB;AAAA,IACtD;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AACD,MAAI,gBAAgB,WAAW,EAAE;AAGjC,QAAM,WAAW,eAAe,EAAE,UAAU,aAAa,QAAQ,YAAY,CAAC;AAC9E,MAAI,cAAc,QAAQ,EAAE;AAE5B,QAAM,SAAS,UAAsD,QAAQ;AAE7E,MAAI,UAAU,OAAO,gBAAgB;AACnC,eAAI,4BAA4B,GACzB;AAAA,MACL,UAAU;AAAA,MACV;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAIF,MAAI,kBAAkB,YAAY,EAAE;AACpC,QAAM,cAA+B;AAAA,IACnC,KAAK;AAAA,IACL,OAAO,QAAQ,YAAY,CAAC,QAAQ,QAAQ,MAAM;AAAA,EACpD;AAEA,MAAI;AACF,aAAS,cAAc,WAAW;AAAA,EACpC,SAAS,OAAO;AACd,UAAM,MAAM;AACZ,UAAM,IAAI,MAAM,iBAAiB,IAAI,OAAO,GAAG,IAAI,SAAS;AAAA,EAAK,IAAI,MAAM,KAAK,EAAE,EAAE;AAAA,EACtF;AAGA,mBAAU,UAAU;AAAA,IAClB;AAAA,IACA,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,IAClC;AAAA,IACA;AAAA,EACF,CAAC,GAEM;AAAA,IACL,UAAU;AAAA,IACV;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAMO,SAAS,gBAAgB,MAAc,OAAqB;AACjE,QAAM,aAAa,QAAQ,IAAI;AAC/B,EAAI,aACF,eAAe,YAAY,GAAG,IAAI,IAAI,KAAK;AAAA,CAAI,IAG/C,QAAQ,KAAK,mBAAmB,IAAI,IAAI,KAAK,EAAE;AAEnD;AAKO,SAAS,kBAA2B;AACzC,SAAO,CAAC,CAAC,QAAQ,IAAI;AACvB;AAKO,SAAS,OAAgB;AAC9B,SAAO,CAAC,CAAC,QAAQ,IAAI;AACvB;",
|
|
5
|
+
"names": []
|
|
6
|
+
}
|
package/dist/runner.mjs
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { execSync } from "node:child_process";
|
|
2
|
+
import { appendFileSync } from "node:fs";
|
|
3
|
+
import { createCacheKey, loadCache, saveCache } from "./cache.mjs";
|
|
4
|
+
import { generateFingerprint } from "./fingerprint.mjs";
|
|
5
|
+
async function runWithCache(options) {
|
|
6
|
+
const {
|
|
7
|
+
platform,
|
|
8
|
+
buildCommand,
|
|
9
|
+
outputPaths,
|
|
10
|
+
projectRoot = process.cwd(),
|
|
11
|
+
cachePrefix = "native-build",
|
|
12
|
+
debug = !1
|
|
13
|
+
} = options,
|
|
14
|
+
log = debug ? console.log : () => {};
|
|
15
|
+
log(`Generating ${platform} fingerprint...`);
|
|
16
|
+
const {
|
|
17
|
+
hash: fingerprint
|
|
18
|
+
} = await generateFingerprint({
|
|
19
|
+
platform,
|
|
20
|
+
projectRoot,
|
|
21
|
+
debug
|
|
22
|
+
});
|
|
23
|
+
log(`Fingerprint: ${fingerprint}`);
|
|
24
|
+
const cacheKey = createCacheKey({
|
|
25
|
+
platform,
|
|
26
|
+
fingerprint,
|
|
27
|
+
prefix: cachePrefix
|
|
28
|
+
});
|
|
29
|
+
log(`Cache key: ${cacheKey}`);
|
|
30
|
+
const cached = loadCache(cacheKey);
|
|
31
|
+
if (cached && cached.fingerprint === fingerprint) return log("Cache hit! Skipping build."), {
|
|
32
|
+
cacheHit: !0,
|
|
33
|
+
fingerprint,
|
|
34
|
+
cacheKey,
|
|
35
|
+
outputPaths
|
|
36
|
+
};
|
|
37
|
+
log(`Running build: ${buildCommand}`);
|
|
38
|
+
const execOptions = {
|
|
39
|
+
cwd: projectRoot,
|
|
40
|
+
stdio: debug ? "inherit" : ["pipe", "pipe", "pipe"]
|
|
41
|
+
};
|
|
42
|
+
try {
|
|
43
|
+
execSync(buildCommand, execOptions);
|
|
44
|
+
} catch (error) {
|
|
45
|
+
const err = error;
|
|
46
|
+
throw new Error(`Build failed: ${err.message}${err.stderr ? `
|
|
47
|
+
${err.stderr}` : ""}`);
|
|
48
|
+
}
|
|
49
|
+
return saveCache(cacheKey, {
|
|
50
|
+
fingerprint,
|
|
51
|
+
timestamp: (/* @__PURE__ */new Date()).toISOString(),
|
|
52
|
+
platform,
|
|
53
|
+
outputPaths
|
|
54
|
+
}), {
|
|
55
|
+
cacheHit: !1,
|
|
56
|
+
fingerprint,
|
|
57
|
+
cacheKey,
|
|
58
|
+
outputPaths
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
function setGitHubOutput(name, value) {
|
|
62
|
+
const outputFile = process.env.GITHUB_OUTPUT;
|
|
63
|
+
outputFile ? appendFileSync(outputFile, `${name}=${value}
|
|
64
|
+
`) : console.info(`[GitHub Output] ${name}=${value}`);
|
|
65
|
+
}
|
|
66
|
+
function isGitHubActions() {
|
|
67
|
+
return !!process.env.GITHUB_ACTIONS;
|
|
68
|
+
}
|
|
69
|
+
function isCI() {
|
|
70
|
+
return !!process.env.CI;
|
|
71
|
+
}
|
|
72
|
+
export { isCI, isGitHubActions, runWithCache, setGitHubOutput };
|
|
73
|
+
//# sourceMappingURL=runner.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["execSync","appendFileSync","createCacheKey","loadCache","saveCache","generateFingerprint","runWithCache","options","platform","buildCommand","outputPaths","projectRoot","process","cwd","cachePrefix","debug","log","console","hash","fingerprint","cacheKey","prefix","cached","cacheHit","execOptions","stdio","error","err","Error","message","stderr","timestamp","Date","toISOString","setGitHubOutput","name","value","outputFile","env","GITHUB_OUTPUT","info","isGitHubActions","GITHUB_ACTIONS","isCI","CI"],"sources":["../src/runner.ts"],"sourcesContent":[null],"mappings":"AAAA,SAASA,QAAA,QAAsC;AAC/C,SAASC,cAAA,QAAsB;AAC/B,SAASC,cAAA,EAAgBC,SAAA,EAAWC,SAAA,QAAiB;AACrD,SAASC,mBAAA,QAA2B;AAwBpC,eAAsBC,aACpBC,OAAA,EAC6B;EAC7B,MAAM;MACJC,QAAA;MACAC,YAAA;MACAC,WAAA;MACAC,WAAA,GAAcC,OAAA,CAAQC,GAAA,CAAI;MAC1BC,WAAA,GAAc;MACdC,KAAA,GAAQ;IACV,IAAIR,OAAA;IAEES,GAAA,GAAMD,KAAA,GAAQE,OAAA,CAAQD,GAAA,GAAM,MAAM,CAAC;EAGzCA,GAAA,CAAI,cAAcR,QAAQ,iBAAiB;EAC3C,MAAM;IAAEU,IAAA,EAAMC;EAAY,IAAI,MAAMd,mBAAA,CAAoB;IACtDG,QAAA;IACAG,WAAA;IACAI;EACF,CAAC;EACDC,GAAA,CAAI,gBAAgBG,WAAW,EAAE;EAGjC,MAAMC,QAAA,GAAWlB,cAAA,CAAe;IAAEM,QAAA;IAAUW,WAAA;IAAaE,MAAA,EAAQP;EAAY,CAAC;EAC9EE,GAAA,CAAI,cAAcI,QAAQ,EAAE;EAE5B,MAAME,MAAA,GAASnB,SAAA,CAAsDiB,QAAQ;EAE7E,IAAIE,MAAA,IAAUA,MAAA,CAAOH,WAAA,KAAgBA,WAAA,EACnC,OAAAH,GAAA,CAAI,4BAA4B,GACzB;IACLO,QAAA,EAAU;IACVJ,WAAA;IACAC,QAAA;IACAV;EACF;EAIFM,GAAA,CAAI,kBAAkBP,YAAY,EAAE;EACpC,MAAMe,WAAA,GAA+B;IACnCX,GAAA,EAAKF,WAAA;IACLc,KAAA,EAAOV,KAAA,GAAQ,YAAY,CAAC,QAAQ,QAAQ,MAAM;EACpD;EAEA,IAAI;IACFf,QAAA,CAASS,YAAA,EAAce,WAAW;EACpC,SAASE,KAAA,EAAO;IACd,MAAMC,GAAA,GAAMD,KAAA;IACZ,MAAM,IAAIE,KAAA,CAAM,iBAAiBD,GAAA,CAAIE,OAAO,GAAGF,GAAA,CAAIG,MAAA,GAAS;AAAA,EAAKH,GAAA,CAAIG,MAAM,KAAK,EAAE,EAAE;EACtF;EAGA,OAAA1B,SAAA,CAAUgB,QAAA,EAAU;IAClBD,WAAA;IACAY,SAAA,GAAW,mBAAIC,IAAA,CAAK,GAAEC,WAAA,CAAY;IAClCzB,QAAA;IACAE;EACF,CAAC,GAEM;IACLa,QAAA,EAAU;IACVJ,WAAA;IACAC,QAAA;IACAV;EACF;AACF;AAMO,SAASwB,gBAAgBC,IAAA,EAAcC,KAAA,EAAqB;EACjE,MAAMC,UAAA,GAAazB,OAAA,CAAQ0B,GAAA,CAAIC,aAAA;EAC3BF,UAAA,GACFpC,cAAA,CAAeoC,UAAA,EAAY,GAAGF,IAAI,IAAIC,KAAK;AAAA,CAAI,IAG/CnB,OAAA,CAAQuB,IAAA,CAAK,mBAAmBL,IAAI,IAAIC,KAAK,EAAE;AAEnD;AAKO,SAASK,gBAAA,EAA2B;EACzC,OAAO,CAAC,CAAC7B,OAAA,CAAQ0B,GAAA,CAAII,cAAA;AACvB;AAKO,SAASC,KAAA,EAAgB;EAC9B,OAAO,CAAC,CAAC/B,OAAA,CAAQ0B,GAAA,CAAIM,EAAA;AACvB","ignoreList":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@tamagui/native-ci",
|
|
3
|
+
"version": "1.139.0",
|
|
4
|
+
"description": "Native CI/CD helpers for React Native apps with Expo - fingerprinting, caching, and build optimization",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"source": "src/index.ts",
|
|
8
|
+
"types": "./types/index.d.ts",
|
|
9
|
+
"module": "dist",
|
|
10
|
+
"bin": "./dist/cli.mjs",
|
|
11
|
+
"engines": {
|
|
12
|
+
"node": ">=18"
|
|
13
|
+
},
|
|
14
|
+
"publishConfig": {
|
|
15
|
+
"access": "public"
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"src",
|
|
19
|
+
"types",
|
|
20
|
+
"dist",
|
|
21
|
+
"actions",
|
|
22
|
+
"action.yml"
|
|
23
|
+
],
|
|
24
|
+
"scripts": {
|
|
25
|
+
"build": "tamagui-build --skip-native",
|
|
26
|
+
"watch": "yarn build --watch",
|
|
27
|
+
"clean": "tamagui-build clean",
|
|
28
|
+
"clean:build": "tamagui-build clean:build"
|
|
29
|
+
},
|
|
30
|
+
"exports": {
|
|
31
|
+
"./package.json": "./package.json",
|
|
32
|
+
".": {
|
|
33
|
+
"types": "./types/index.d.ts",
|
|
34
|
+
"import": "./dist/index.mjs"
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
"dependencies": {
|
|
38
|
+
"@expo/fingerprint": "^0.15.3"
|
|
39
|
+
},
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"@tamagui/build": "1.139.0",
|
|
42
|
+
"@types/bun": "^1.1.0",
|
|
43
|
+
"@types/node": "^22.1.0"
|
|
44
|
+
},
|
|
45
|
+
"repository": {
|
|
46
|
+
"type": "git",
|
|
47
|
+
"url": "https://github.com/tamagui/tamagui.git",
|
|
48
|
+
"directory": "code/packages/native-ci"
|
|
49
|
+
}
|
|
50
|
+
}
|
package/src/android.ts
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Android-specific utilities for Detox test runners
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { existsSync } from 'node:fs'
|
|
6
|
+
import { join } from 'node:path'
|
|
7
|
+
import { $ } from 'bun'
|
|
8
|
+
import { METRO_PORT, DETOX_SERVER_PORT } from './constants'
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Wait for Android device/emulator to be ready.
|
|
12
|
+
* Times out after 30 seconds if no device is available.
|
|
13
|
+
*/
|
|
14
|
+
export async function waitForDevice(): Promise<void> {
|
|
15
|
+
console.info('\n--- Waiting for device ---')
|
|
16
|
+
|
|
17
|
+
// Check if any device is connected first (with a quick timeout)
|
|
18
|
+
try {
|
|
19
|
+
const result = await $`adb devices`.quiet()
|
|
20
|
+
const lines = result.stdout.toString().split('\n').filter(line => line.includes('\tdevice'))
|
|
21
|
+
if (lines.length === 0) {
|
|
22
|
+
throw new Error('No Android device/emulator connected. Please start an emulator first.')
|
|
23
|
+
}
|
|
24
|
+
} catch (error) {
|
|
25
|
+
const err = error as Error
|
|
26
|
+
throw new Error(`No Android device available: ${err.message}`)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
try {
|
|
30
|
+
// Wait for device to be fully booted (with timeout)
|
|
31
|
+
await $`timeout 30 adb wait-for-device`.quiet()
|
|
32
|
+
await $`timeout 60 adb shell 'while [ -z "$(getprop sys.boot_completed)" ]; do sleep 1; done'`.quiet()
|
|
33
|
+
console.info('Device is ready!')
|
|
34
|
+
} catch (error) {
|
|
35
|
+
const err = error as Error
|
|
36
|
+
throw new Error(`Failed to wait for Android device: ${err.message}`)
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Setup ADB reverse port forwarding for Metro and Detox server.
|
|
42
|
+
* This allows the emulator to connect to services on the host machine.
|
|
43
|
+
*/
|
|
44
|
+
export async function setupAdbReverse(): Promise<void> {
|
|
45
|
+
console.info('\n--- Setting up ADB reverse ---')
|
|
46
|
+
|
|
47
|
+
try {
|
|
48
|
+
// Metro bundler port
|
|
49
|
+
await $`adb reverse tcp:${METRO_PORT} tcp:${METRO_PORT}`
|
|
50
|
+
console.info(`Reversed port ${METRO_PORT} (Metro)`)
|
|
51
|
+
|
|
52
|
+
// Detox server port
|
|
53
|
+
await $`adb reverse tcp:${DETOX_SERVER_PORT} tcp:${DETOX_SERVER_PORT}`
|
|
54
|
+
console.info(`Reversed port ${DETOX_SERVER_PORT} (Detox)`)
|
|
55
|
+
} catch (error) {
|
|
56
|
+
const err = error as Error
|
|
57
|
+
throw new Error(
|
|
58
|
+
`Failed to setup ADB reverse ports: ${err.message}\n` +
|
|
59
|
+
'Make sure the Android emulator is running and adb is available.'
|
|
60
|
+
)
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Full Android device setup - wait for device and setup port forwarding.
|
|
66
|
+
*/
|
|
67
|
+
export async function setupAndroidDevice(): Promise<void> {
|
|
68
|
+
await waitForDevice()
|
|
69
|
+
await setupAdbReverse()
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Ensure the android/ folder has full prebuild structure for Metro.
|
|
74
|
+
* In CI, the build job caches the entire android/ folder (including APKs and test files).
|
|
75
|
+
* The test job restores this cache and should NOT regenerate it.
|
|
76
|
+
*
|
|
77
|
+
* Why we DON'T always regenerate (unlike a previous approach):
|
|
78
|
+
* - The cached android/ folder includes DetoxTest.java and other manually-added test files
|
|
79
|
+
* - Running `expo prebuild --clean` would DELETE these critical test infrastructure files
|
|
80
|
+
* - The cached folder's fingerprint already ensures it's in sync with node_modules
|
|
81
|
+
*
|
|
82
|
+
* We check for android/build.gradle as the indicator of a complete prebuild.
|
|
83
|
+
* Only regenerate if the folder is missing or incomplete.
|
|
84
|
+
*/
|
|
85
|
+
export async function ensureAndroidFolder(): Promise<void> {
|
|
86
|
+
const buildGradlePath = join(process.cwd(), 'android', 'build.gradle')
|
|
87
|
+
|
|
88
|
+
if (existsSync(buildGradlePath)) {
|
|
89
|
+
console.info('Android folder already exists (build.gradle found)')
|
|
90
|
+
return
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
console.info('\n--- Generating android/ folder (for Metro) ---')
|
|
94
|
+
console.info('Note: android/build.gradle not found, running expo prebuild')
|
|
95
|
+
|
|
96
|
+
try {
|
|
97
|
+
await $`npx expo prebuild --platform android`
|
|
98
|
+
console.info('Android folder generated!')
|
|
99
|
+
} catch (error) {
|
|
100
|
+
const err = error as Error
|
|
101
|
+
throw new Error(`Failed to generate android folder: ${err.message}`)
|
|
102
|
+
}
|
|
103
|
+
}
|