@stim-cli/expo-build-cache 1.8.0 → 1.10.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 +12 -1
- package/dist/index.mjs +88 -10
- package/package.json +2 -3
package/README.md
CHANGED
|
@@ -28,10 +28,21 @@ For current Expo SDK versions:
|
|
|
28
28
|
|
|
29
29
|
Expo SDK 53 reads this value under `expo.experiments.buildCacheProvider`.
|
|
30
30
|
|
|
31
|
+
Expo builds a debug APK without `--all-arch` only for the ABI of the device it
|
|
32
|
+
targets. The provider keys those builds on that ABI, which it reads with `adb`
|
|
33
|
+
(from `ANDROID_HOME`, `ANDROID_SDK_ROOT`, the default SDK location, or `PATH`)
|
|
34
|
+
when exactly one device is online. With several devices online, or when `adb`
|
|
35
|
+
does not answer, it skips the cache for that run and logs `[build-cache] skip
|
|
36
|
+
android`. `--all-arch` builds have their own entry. These keys match the
|
|
37
|
+
per-ABI builds `stim android` makes by default; a universal debug build that
|
|
38
|
+
`stim android` makes with `optimizations.android.targetAbiOnly: false` is not
|
|
39
|
+
shared with Expo runs.
|
|
40
|
+
|
|
31
41
|
The provider works without the `stim` npm package. When Stim is available,
|
|
32
42
|
the provider registers its cache so `stim gc` can report and trim it.
|
|
33
43
|
|
|
34
|
-
Set `STIM_BUILD_CACHE` to override the cache location.
|
|
44
|
+
Set `STIM_BUILD_CACHE` to an absolute path to override the cache location. A
|
|
45
|
+
relative value is ignored with a warning.
|
|
35
46
|
|
|
36
47
|
See the [cache package documentation](https://stim.appandflow.com/docs/cache-packages)
|
|
37
48
|
for current setup and cleanup guidance.
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
|
+
import os from "node:os";
|
|
2
3
|
import path from "node:path";
|
|
3
4
|
import { artifactIn, buildCacheKey, buildCacheRoot, registerCache, resolveArtifact, storeArtifact } from "@stim-cli/core";
|
|
4
5
|
import { execFileSync } from "node:child_process";
|
|
@@ -13,6 +14,67 @@ function entryDir(platform, key) {
|
|
|
13
14
|
function shortKey(key, fingerprintHash) {
|
|
14
15
|
return `${String(fingerprintHash).slice(0, 12)}${key.slice(String(fingerprintHash).length)}`;
|
|
15
16
|
}
|
|
17
|
+
const ANDROID_ABIS = [
|
|
18
|
+
"armeabi-v7a",
|
|
19
|
+
"arm64-v8a",
|
|
20
|
+
"x86",
|
|
21
|
+
"x86_64"
|
|
22
|
+
];
|
|
23
|
+
function adbPath() {
|
|
24
|
+
const configured = process.env.ANDROID_HOME || process.env.ANDROID_SDK_ROOT;
|
|
25
|
+
const home = os.homedir();
|
|
26
|
+
const sdk = configured ? configured : [
|
|
27
|
+
path.join(home, "Library", "Android", "sdk"),
|
|
28
|
+
path.join(home, "Android", "Sdk"),
|
|
29
|
+
path.join(home, "Android", "sdk"),
|
|
30
|
+
path.join(home, "AppData", "Local", "Android", "Sdk")
|
|
31
|
+
].find((candidate) => fs.existsSync(path.join(candidate, "platform-tools")));
|
|
32
|
+
return sdk ? path.join(sdk, "platform-tools", "adb") : "adb";
|
|
33
|
+
}
|
|
34
|
+
function connectedDeviceAbi() {
|
|
35
|
+
try {
|
|
36
|
+
const adb = adbPath();
|
|
37
|
+
const serials = execFileSync(adb, ["devices"], {
|
|
38
|
+
encoding: "utf8",
|
|
39
|
+
timeout: 1e4,
|
|
40
|
+
stdio: [
|
|
41
|
+
"ignore",
|
|
42
|
+
"pipe",
|
|
43
|
+
"ignore"
|
|
44
|
+
]
|
|
45
|
+
}).split("\n").slice(1).map((line) => line.trim().split(/\s+/)).filter(([, state]) => state === "device").map(([serial]) => serial);
|
|
46
|
+
if (serials.length !== 1) return null;
|
|
47
|
+
return execFileSync(adb, [
|
|
48
|
+
"-s",
|
|
49
|
+
serials[0],
|
|
50
|
+
"shell",
|
|
51
|
+
"getprop",
|
|
52
|
+
"ro.product.cpu.abilist"
|
|
53
|
+
], {
|
|
54
|
+
encoding: "utf8",
|
|
55
|
+
timeout: 1e4,
|
|
56
|
+
stdio: [
|
|
57
|
+
"ignore",
|
|
58
|
+
"pipe",
|
|
59
|
+
"ignore"
|
|
60
|
+
]
|
|
61
|
+
}).trim().split(",").find((abi) => ANDROID_ABIS.includes(abi)) ?? null;
|
|
62
|
+
} catch {
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
function keyOptions(platform, runOptions = {}) {
|
|
67
|
+
if (platform !== "android" || runOptions.abi || runOptions.allArch) return runOptions;
|
|
68
|
+
const parts = (runOptions.variant || "debug").split(/(?=[A-Z])/);
|
|
69
|
+
let buildType = parts.pop();
|
|
70
|
+
if (parts.length > 0 && buildType === "Optimized") buildType = parts.pop() + buildType;
|
|
71
|
+
if (!/^debug(optimized)?$/i.test(buildType)) return runOptions;
|
|
72
|
+
const abi = connectedDeviceAbi();
|
|
73
|
+
return abi ? {
|
|
74
|
+
...runOptions,
|
|
75
|
+
abi
|
|
76
|
+
} : null;
|
|
77
|
+
}
|
|
16
78
|
function registerOnce() {
|
|
17
79
|
const root = cacheRoot();
|
|
18
80
|
if (registeredDir === root) return;
|
|
@@ -27,7 +89,12 @@ function registerOnce() {
|
|
|
27
89
|
}
|
|
28
90
|
async function resolveBuildCache({ platform, fingerprintHash, runOptions }) {
|
|
29
91
|
registerOnce();
|
|
30
|
-
const
|
|
92
|
+
const options = keyOptions(platform, runOptions);
|
|
93
|
+
if (!options) {
|
|
94
|
+
console.log(`[build-cache] skip ${platform}: cannot tell which ABI this debug build targets`);
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
const key = buildCacheKey(platform, fingerprintHash, options);
|
|
31
98
|
const hit = resolveArtifact(entryDir(platform, key));
|
|
32
99
|
if (hit) {
|
|
33
100
|
console.log(`[build-cache] hit ${platform} ${shortKey(key, fingerprintHash)}`);
|
|
@@ -39,17 +106,28 @@ async function resolveBuildCache({ platform, fingerprintHash, runOptions }) {
|
|
|
39
106
|
async function uploadBuildCache({ platform, fingerprintHash, buildPath, runOptions }) {
|
|
40
107
|
registerOnce();
|
|
41
108
|
if (!buildPath || !fs.existsSync(buildPath)) return null;
|
|
42
|
-
const
|
|
109
|
+
const options = keyOptions(platform, runOptions);
|
|
110
|
+
if (!options) {
|
|
111
|
+
console.log(`[build-cache] skip ${platform}: cannot tell which ABI this debug build targets`);
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
const key = buildCacheKey(platform, fingerprintHash, options);
|
|
43
115
|
const dest = entryDir(platform, key);
|
|
44
116
|
if (artifactIn(dest)) return artifactIn(dest);
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
117
|
+
let stored;
|
|
118
|
+
try {
|
|
119
|
+
stored = storeArtifact(dest, buildPath, {
|
|
120
|
+
runFile: execFileSync,
|
|
121
|
+
onRenameError: (staging) => fs.rmSync(staging, {
|
|
122
|
+
recursive: true,
|
|
123
|
+
force: true
|
|
124
|
+
})
|
|
125
|
+
});
|
|
126
|
+
} catch (error) {
|
|
127
|
+
console.warn(`[build-cache] could not store ${platform} ${shortKey(key, fingerprintHash)}: ${error.message}`);
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
if (stored) console.log(`[build-cache] stored ${platform} ${shortKey(key, fingerprintHash)}`);
|
|
53
131
|
return stored;
|
|
54
132
|
}
|
|
55
133
|
//#endregion
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stim-cli/expo-build-cache",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.10.0",
|
|
4
4
|
"description": "Local Expo build cache provider: installs a cached .app/.apk instead of compiling when no native input changed.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -24,13 +24,12 @@
|
|
|
24
24
|
}
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@stim-cli/core": "^1.
|
|
27
|
+
"@stim-cli/core": "^1.10.0"
|
|
28
28
|
},
|
|
29
29
|
"engines": {
|
|
30
30
|
"node": ">=22.12.0"
|
|
31
31
|
},
|
|
32
32
|
"scripts": {
|
|
33
|
-
"test": "node --test test/*.test.js",
|
|
34
33
|
"build": "tsdown"
|
|
35
34
|
}
|
|
36
35
|
}
|