yaver-cli 1.99.28 → 1.99.30
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/hermesc/README.md +29 -0
- package/hermesc/darwin-x64/hermesc +0 -0
- package/package.json +1 -1
- package/src/bundler.js +47 -12
- package/src/hermesc-runtime.js +330 -0
- package/src/postinstall.js +85 -0
- /package/hermesc/{hermesc → darwin-arm64/hermesc} +0 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# hermesc binaries for yaver-cli
|
|
2
|
+
|
|
3
|
+
Hermes bytecode compiler binaries matched to `react-native@0.81.5`
|
|
4
|
+
(Hermes BC version 96, the version baked into the Yaver container app).
|
|
5
|
+
|
|
6
|
+
## How this directory is populated
|
|
7
|
+
|
|
8
|
+
- `darwin-arm64/` and `darwin-x64/` ship with the tarball as a macOS
|
|
9
|
+
universal binary. They're small (~6 MB) and cover the vast majority
|
|
10
|
+
of `yaver-push` users.
|
|
11
|
+
- Other platforms (`linux-x64`, `win32-x64`) are installed at
|
|
12
|
+
`npm install -g yaver-cli` time by `src/postinstall.js` → it extracts
|
|
13
|
+
the right binary from the `react-native` npm tarball.
|
|
14
|
+
- `linux-arm64` has no upstream prebuilt. On first push, `bundler.js`
|
|
15
|
+
falls back to `getHermescPathAsync({ allowBuildFromSource: true })`,
|
|
16
|
+
which builds hermesc from the project's own
|
|
17
|
+
`node_modules/react-native/sdks/hermes/` sources via CMake. Takes
|
|
18
|
+
~3–5 min, cached afterwards.
|
|
19
|
+
|
|
20
|
+
## How bundler.js resolves the right one
|
|
21
|
+
|
|
22
|
+
1. Platform-matched cache (this dir, per `<platform>-<arch>` key)
|
|
23
|
+
2. `~/.yaver/hermesc/<key>/hermesc` (per-user mirror)
|
|
24
|
+
3. Legacy flat layout (top-level `hermesc` or `hermesc.exe`)
|
|
25
|
+
4. Project-local `node_modules/react-native/sdks/hermesc/...`
|
|
26
|
+
|
|
27
|
+
Never commit binaries into platform subdirs other than the two
|
|
28
|
+
macOS ones — the installer writes them at install time and
|
|
29
|
+
committing them would make every `git pull` churn large files.
|
|
Binary file
|
package/package.json
CHANGED
package/src/bundler.js
CHANGED
|
@@ -2,31 +2,57 @@ const { execSync, execFileSync } = require('child_process');
|
|
|
2
2
|
const fs = require('fs');
|
|
3
3
|
const path = require('path');
|
|
4
4
|
const os = require('os');
|
|
5
|
+
const { findExistingHermesc, ensureHermesc } = require('./hermesc-runtime');
|
|
5
6
|
|
|
6
7
|
function getHermescPath() {
|
|
7
8
|
const key = `${os.platform()}-${os.arch()}`;
|
|
8
9
|
const dir = path.join(__dirname, '..', 'hermesc');
|
|
9
10
|
const ext = os.platform() === 'win32' ? 'hermesc.exe' : 'hermesc';
|
|
10
11
|
|
|
12
|
+
// 1. Cache populated by postinstall (or previous on-demand install).
|
|
13
|
+
// Covers the common case without any network access at push time.
|
|
14
|
+
const cached = findExistingHermesc();
|
|
15
|
+
if (cached) return cached;
|
|
16
|
+
|
|
17
|
+
// 2. Legacy/redundant lookup: packages built before the platform-aware
|
|
18
|
+
// install may have raw hermesc subdirs left over, and third-party
|
|
19
|
+
// forks sometimes drop their own binaries into hermesc/ directly.
|
|
11
20
|
const candidates = [
|
|
12
21
|
path.join(dir, key, ext),
|
|
13
22
|
path.join(dir, 'darwin-arm64', ext),
|
|
14
23
|
path.join(dir, 'linux-x64', ext),
|
|
24
|
+
path.join(dir, ext),
|
|
15
25
|
];
|
|
16
|
-
|
|
17
26
|
const found = candidates.find(p => fs.existsSync(p));
|
|
18
|
-
if (
|
|
19
|
-
|
|
27
|
+
if (found) {
|
|
28
|
+
try { fs.chmodSync(found, 0o755); } catch {}
|
|
29
|
+
return found;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// 3. Project's own react-native installation (ships hermesc at
|
|
33
|
+
// RN 0.81+). Covers `--ignore-scripts` installs and linux-arm64
|
|
34
|
+
// where we don't have a prebuilt binary.
|
|
35
|
+
const rnHermesc = findRNHermesc();
|
|
36
|
+
if (rnHermesc) return rnHermesc;
|
|
37
|
+
|
|
38
|
+
throw new Error(
|
|
39
|
+
`hermesc not found for ${key}. Run \`npm rebuild yaver-cli\` to re-trigger the platform-aware installer, or ensure react-native is installed locally.`,
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Async variant that will provision hermesc on demand if the sync path
|
|
44
|
+
// can't find one. Callers that can `await` (push command, etc.) should
|
|
45
|
+
// prefer this so the first push works even when postinstall was skipped.
|
|
46
|
+
async function getHermescPathAsync({ quiet = true, allowBuildFromSource = false } = {}) {
|
|
47
|
+
try {
|
|
48
|
+
return getHermescPath();
|
|
49
|
+
} catch (_err) {
|
|
50
|
+
const provisioned = await ensureHermesc({ quiet, allowBuildFromSource });
|
|
51
|
+
if (provisioned) return provisioned;
|
|
20
52
|
const rnHermesc = findRNHermesc();
|
|
21
53
|
if (rnHermesc) return rnHermesc;
|
|
22
|
-
|
|
23
|
-
throw new Error(
|
|
24
|
-
`hermesc not found for ${key}. Install yaver-cli hermesc binaries or ensure react-native is installed.`
|
|
25
|
-
);
|
|
54
|
+
throw _err;
|
|
26
55
|
}
|
|
27
|
-
|
|
28
|
-
try { fs.chmodSync(found, 0o755); } catch {}
|
|
29
|
-
return found;
|
|
30
56
|
}
|
|
31
57
|
|
|
32
58
|
/** Find hermesc from the project's react-native installation */
|
|
@@ -84,7 +110,10 @@ async function bundle({ platform, entryFile, outputDir, dev = false, minify = tr
|
|
|
84
110
|
}
|
|
85
111
|
|
|
86
112
|
async function compileHermes({ inputPath, outputPath }) {
|
|
87
|
-
|
|
113
|
+
// Prefer the async resolver: if the cache is empty (install did
|
|
114
|
+
// --ignore-scripts, or this is linux-arm64 and we need a build-
|
|
115
|
+
// from-source) it'll provision one on demand instead of throwing.
|
|
116
|
+
const hermesc = await getHermescPathAsync({ quiet: false, allowBuildFromSource: true });
|
|
88
117
|
const tmp = inputPath + '.tmp';
|
|
89
118
|
fs.renameSync(inputPath, tmp);
|
|
90
119
|
|
|
@@ -112,4 +141,10 @@ function readBytecodeVersion(hbcPath) {
|
|
|
112
141
|
return buf.readUInt32LE(8);
|
|
113
142
|
}
|
|
114
143
|
|
|
115
|
-
module.exports = {
|
|
144
|
+
module.exports = {
|
|
145
|
+
bundle,
|
|
146
|
+
compileHermes,
|
|
147
|
+
readBytecodeVersion,
|
|
148
|
+
getHermescPath,
|
|
149
|
+
getHermescPathAsync,
|
|
150
|
+
};
|
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
// Platform-aware hermesc provisioner for yaver-cli.
|
|
2
|
+
//
|
|
3
|
+
// Runs from `postinstall` on global installs and on-demand from the
|
|
4
|
+
// bundler. Avoids shipping a ~6 MB binary inside the tarball that's
|
|
5
|
+
// only correct for one platform — instead we detect the host and
|
|
6
|
+
// install a matching hermesc at install time.
|
|
7
|
+
//
|
|
8
|
+
// Lookup order (redundant by design; any one succeeding is enough):
|
|
9
|
+
// 1. Cached install under node_modules/yaver-cli/hermesc/<key>/hermesc
|
|
10
|
+
// 2. Per-user cache under ~/.yaver/hermesc/<key>/hermesc
|
|
11
|
+
// 3. Download from the react-native npm tarball and extract just
|
|
12
|
+
// sdks/hermesc/<rn-dir>/hermesc for this platform
|
|
13
|
+
// 4. Build from the project's node_modules/react-native hermes
|
|
14
|
+
// sources (handles linux-arm64 where no prebuilt exists)
|
|
15
|
+
// 5. Fall through — bundler.js then resolves from project-local RN
|
|
16
|
+
//
|
|
17
|
+
// Must never block `npm install`. Every failure is logged + swallowed;
|
|
18
|
+
// the CLI still installs fine, the bundler just takes the runtime
|
|
19
|
+
// fallback path the next time someone actually tries to push a bundle.
|
|
20
|
+
|
|
21
|
+
const fs = require("fs");
|
|
22
|
+
const os = require("os");
|
|
23
|
+
const path = require("path");
|
|
24
|
+
const https = require("https");
|
|
25
|
+
const { spawnSync } = require("child_process");
|
|
26
|
+
|
|
27
|
+
const HERMES_RN_VERSION = process.env.YAVER_HERMES_RN_VERSION || "0.81.5";
|
|
28
|
+
|
|
29
|
+
// Maps node's platform-arch to the subdirectory react-native ships its
|
|
30
|
+
// prebuilt hermesc under in the npm tarball (sdks/hermesc/<dir>/hermesc).
|
|
31
|
+
// react-native 0.81.x does NOT ship a prebuilt for linux-arm64 —
|
|
32
|
+
// those machines build from source instead (see buildFromProject()).
|
|
33
|
+
const RN_PREBUILT_DIR = {
|
|
34
|
+
"darwin-arm64": "osx-bin",
|
|
35
|
+
"darwin-x64": "osx-bin",
|
|
36
|
+
"linux-x64": "linux64-bin",
|
|
37
|
+
"win32-x64": "win64-bin",
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
function platformKey() {
|
|
41
|
+
return `${process.platform}-${process.arch}`;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function hermescBasename() {
|
|
45
|
+
return process.platform === "win32" ? "hermesc.exe" : "hermesc";
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function cliInstallDir() {
|
|
49
|
+
return path.join(__dirname, "..", "hermesc", platformKey());
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function cliInstallPath() {
|
|
53
|
+
return path.join(cliInstallDir(), hermescBasename());
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function userCacheDir() {
|
|
57
|
+
return path.join(os.homedir(), ".yaver", "hermesc", platformKey());
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function userCachePath() {
|
|
61
|
+
return path.join(userCacheDir(), hermescBasename());
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function hermescBinaryRunnable(binaryPath) {
|
|
65
|
+
try {
|
|
66
|
+
const res = spawnSync(binaryPath, ["--version"], {
|
|
67
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
68
|
+
timeout: 5000,
|
|
69
|
+
});
|
|
70
|
+
if (res.status !== 0) return false;
|
|
71
|
+
const out = (res.stdout?.toString() || "") + (res.stderr?.toString() || "");
|
|
72
|
+
return /hermes/i.test(out) || /bytecode/i.test(out);
|
|
73
|
+
} catch (_err) {
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function chmodExec(binaryPath) {
|
|
79
|
+
if (process.platform === "win32") return;
|
|
80
|
+
try {
|
|
81
|
+
fs.chmodSync(binaryPath, 0o755);
|
|
82
|
+
} catch (_err) {
|
|
83
|
+
/* ignore */
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function log(message, { quiet }) {
|
|
88
|
+
if (quiet) return;
|
|
89
|
+
console.error(`[yaver hermesc] ${message}`);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async function downloadToFile(url, outPath, { maxRedirects = 5 } = {}) {
|
|
93
|
+
return new Promise((resolve, reject) => {
|
|
94
|
+
const fetch = (u, redirectsLeft) => {
|
|
95
|
+
const req = https.get(u, (res) => {
|
|
96
|
+
if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
|
|
97
|
+
if (redirectsLeft <= 0) {
|
|
98
|
+
reject(new Error(`too many redirects for ${url}`));
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
res.resume();
|
|
102
|
+
const next = new URL(res.headers.location, u).toString();
|
|
103
|
+
fetch(next, redirectsLeft - 1);
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
if (res.statusCode !== 200) {
|
|
107
|
+
reject(new Error(`HTTP ${res.statusCode} for ${u}`));
|
|
108
|
+
res.resume();
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
const out = fs.createWriteStream(outPath);
|
|
112
|
+
res.pipe(out);
|
|
113
|
+
out.on("finish", () => out.close(() => resolve()));
|
|
114
|
+
out.on("error", (err) => {
|
|
115
|
+
try {
|
|
116
|
+
fs.rmSync(outPath, { force: true });
|
|
117
|
+
} catch (_err) {
|
|
118
|
+
/* ignore */
|
|
119
|
+
}
|
|
120
|
+
reject(err);
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
req.on("error", reject);
|
|
124
|
+
req.setTimeout(60000, () => {
|
|
125
|
+
req.destroy(new Error(`download timed out: ${u}`));
|
|
126
|
+
});
|
|
127
|
+
};
|
|
128
|
+
fetch(url, maxRedirects);
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
async function downloadFromRNTarball(destPath, { quiet }) {
|
|
133
|
+
const rnDir = RN_PREBUILT_DIR[platformKey()];
|
|
134
|
+
if (!rnDir) {
|
|
135
|
+
throw new Error(`no react-native prebuilt for ${platformKey()}`);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Redundant registries — npm.js primary, unpkg as fallback CDN.
|
|
139
|
+
const candidates = [
|
|
140
|
+
`https://registry.npmjs.org/react-native/-/react-native-${HERMES_RN_VERSION}.tgz`,
|
|
141
|
+
`https://unpkg.com/react-native@${HERMES_RN_VERSION}/react-native-${HERMES_RN_VERSION}.tgz`,
|
|
142
|
+
];
|
|
143
|
+
|
|
144
|
+
const tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), "yaver-hermesc-"));
|
|
145
|
+
const tarball = path.join(tmpRoot, "rn.tgz");
|
|
146
|
+
|
|
147
|
+
let lastErr = null;
|
|
148
|
+
for (const url of candidates) {
|
|
149
|
+
try {
|
|
150
|
+
log(`fetching ${url}`, { quiet });
|
|
151
|
+
await downloadToFile(url, tarball);
|
|
152
|
+
lastErr = null;
|
|
153
|
+
break;
|
|
154
|
+
} catch (err) {
|
|
155
|
+
lastErr = err;
|
|
156
|
+
log(`download failed: ${err.message}`, { quiet });
|
|
157
|
+
try {
|
|
158
|
+
fs.rmSync(tarball, { force: true });
|
|
159
|
+
} catch (_) {
|
|
160
|
+
/* ignore */
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
if (lastErr) {
|
|
165
|
+
try {
|
|
166
|
+
fs.rmSync(tmpRoot, { recursive: true, force: true });
|
|
167
|
+
} catch (_) {
|
|
168
|
+
/* ignore */
|
|
169
|
+
}
|
|
170
|
+
throw lastErr;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const rel = `package/sdks/hermesc/${rnDir}/${hermescBasename()}`;
|
|
174
|
+
const res = spawnSync("tar", ["-xzf", tarball, "-C", tmpRoot, rel], {
|
|
175
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
176
|
+
});
|
|
177
|
+
if (res.status !== 0) {
|
|
178
|
+
const stderr = res.stderr?.toString() || "unknown tar failure";
|
|
179
|
+
try {
|
|
180
|
+
fs.rmSync(tmpRoot, { recursive: true, force: true });
|
|
181
|
+
} catch (_) {
|
|
182
|
+
/* ignore */
|
|
183
|
+
}
|
|
184
|
+
throw new Error(`tar extract failed: ${stderr}`);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const extracted = path.join(tmpRoot, rel);
|
|
188
|
+
if (!fs.existsSync(extracted)) {
|
|
189
|
+
try {
|
|
190
|
+
fs.rmSync(tmpRoot, { recursive: true, force: true });
|
|
191
|
+
} catch (_) {
|
|
192
|
+
/* ignore */
|
|
193
|
+
}
|
|
194
|
+
throw new Error(`extracted hermesc missing: ${extracted}`);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
fs.mkdirSync(path.dirname(destPath), { recursive: true });
|
|
198
|
+
fs.copyFileSync(extracted, destPath);
|
|
199
|
+
chmodExec(destPath);
|
|
200
|
+
|
|
201
|
+
try {
|
|
202
|
+
fs.rmSync(tmpRoot, { recursive: true, force: true });
|
|
203
|
+
} catch (_) {
|
|
204
|
+
/* ignore */
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// Build hermesc from the project's own node_modules/react-native
|
|
209
|
+
// sources. Covers linux-arm64 and any other platform where we don't
|
|
210
|
+
// have a prebuilt. Mirrors desktop/agent/hermesc_resolver.go's
|
|
211
|
+
// buildProjectHermesc path.
|
|
212
|
+
function buildFromProject(destPath, { quiet }) {
|
|
213
|
+
const projectRoots = [
|
|
214
|
+
process.cwd(),
|
|
215
|
+
path.join(process.cwd(), ".."),
|
|
216
|
+
];
|
|
217
|
+
for (const root of projectRoots) {
|
|
218
|
+
const hermesSrc = path.join(
|
|
219
|
+
root,
|
|
220
|
+
"node_modules",
|
|
221
|
+
"react-native",
|
|
222
|
+
"sdks",
|
|
223
|
+
"hermes",
|
|
224
|
+
);
|
|
225
|
+
if (!fs.existsSync(path.join(hermesSrc, "CMakeLists.txt"))) continue;
|
|
226
|
+
try {
|
|
227
|
+
log(`building hermesc from ${hermesSrc} (one-time, ~3–5 min)…`, { quiet });
|
|
228
|
+
const buildDir = fs.mkdtempSync(path.join(os.tmpdir(), "hermes-build-"));
|
|
229
|
+
const cmake = spawnSync(
|
|
230
|
+
"cmake",
|
|
231
|
+
["-S", hermesSrc, "-B", buildDir, "-DCMAKE_BUILD_TYPE=Release"],
|
|
232
|
+
{ stdio: quiet ? "ignore" : "inherit" },
|
|
233
|
+
);
|
|
234
|
+
if (cmake.status !== 0) throw new Error(`cmake configure failed`);
|
|
235
|
+
const build = spawnSync(
|
|
236
|
+
"cmake",
|
|
237
|
+
["--build", buildDir, "--target", "hermesc", "--config", "Release", "-j"],
|
|
238
|
+
{ stdio: quiet ? "ignore" : "inherit" },
|
|
239
|
+
);
|
|
240
|
+
if (build.status !== 0) throw new Error(`cmake build failed`);
|
|
241
|
+
const built = path.join(buildDir, "bin", hermescBasename());
|
|
242
|
+
if (!fs.existsSync(built)) throw new Error(`hermesc missing after build: ${built}`);
|
|
243
|
+
fs.mkdirSync(path.dirname(destPath), { recursive: true });
|
|
244
|
+
fs.copyFileSync(built, destPath);
|
|
245
|
+
chmodExec(destPath);
|
|
246
|
+
try {
|
|
247
|
+
fs.rmSync(buildDir, { recursive: true, force: true });
|
|
248
|
+
} catch (_) {
|
|
249
|
+
/* ignore */
|
|
250
|
+
}
|
|
251
|
+
return true;
|
|
252
|
+
} catch (err) {
|
|
253
|
+
log(`build-from-source failed at ${hermesSrc}: ${err.message}`, { quiet });
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
return false;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
function findExistingHermesc() {
|
|
260
|
+
const candidates = [cliInstallPath(), userCachePath()];
|
|
261
|
+
for (const p of candidates) {
|
|
262
|
+
if (fs.existsSync(p)) {
|
|
263
|
+
chmodExec(p);
|
|
264
|
+
if (hermescBinaryRunnable(p)) return p;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
return null;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
async function ensureHermesc({ quiet = false, allowBuildFromSource = false } = {}) {
|
|
271
|
+
const existing = findExistingHermesc();
|
|
272
|
+
if (existing) return existing;
|
|
273
|
+
|
|
274
|
+
const key = platformKey();
|
|
275
|
+
|
|
276
|
+
if (RN_PREBUILT_DIR[key]) {
|
|
277
|
+
// Prefer cli-local install so `yaver-push` works offline after the
|
|
278
|
+
// first install (user cache also works; we write to both so either
|
|
279
|
+
// path resolves).
|
|
280
|
+
const dest = cliInstallPath();
|
|
281
|
+
try {
|
|
282
|
+
await downloadFromRNTarball(dest, { quiet });
|
|
283
|
+
if (hermescBinaryRunnable(dest)) {
|
|
284
|
+
// Also mirror into ~/.yaver so a later `npm uninstall` + reinstall
|
|
285
|
+
// doesn't re-download. Redundancy is the point.
|
|
286
|
+
try {
|
|
287
|
+
fs.mkdirSync(userCacheDir(), { recursive: true });
|
|
288
|
+
fs.copyFileSync(dest, userCachePath());
|
|
289
|
+
chmodExec(userCachePath());
|
|
290
|
+
} catch (_) {
|
|
291
|
+
/* ignore mirror errors */
|
|
292
|
+
}
|
|
293
|
+
log(`installed at ${dest}`, { quiet });
|
|
294
|
+
return dest;
|
|
295
|
+
}
|
|
296
|
+
log(`downloaded binary at ${dest} did not run — discarding`, { quiet });
|
|
297
|
+
try {
|
|
298
|
+
fs.rmSync(dest, { force: true });
|
|
299
|
+
} catch (_) {
|
|
300
|
+
/* ignore */
|
|
301
|
+
}
|
|
302
|
+
} catch (err) {
|
|
303
|
+
log(`prebuilt download failed: ${err.message}`, { quiet });
|
|
304
|
+
}
|
|
305
|
+
} else {
|
|
306
|
+
log(`no prebuilt available for ${key} — will try build-from-source on demand`, { quiet });
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
if (allowBuildFromSource) {
|
|
310
|
+
const dest = cliInstallPath();
|
|
311
|
+
if (buildFromProject(dest, { quiet }) && hermescBinaryRunnable(dest)) {
|
|
312
|
+
log(`built hermesc from source at ${dest}`, { quiet });
|
|
313
|
+
return dest;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// Fall through — caller should rely on bundler.js's project-local RN
|
|
318
|
+
// fallback (node_modules/react-native/sdks/hermesc/...). Return null
|
|
319
|
+
// rather than throwing so postinstall never fails npm install.
|
|
320
|
+
return null;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
module.exports = {
|
|
324
|
+
ensureHermesc,
|
|
325
|
+
findExistingHermesc,
|
|
326
|
+
cliInstallPath,
|
|
327
|
+
userCachePath,
|
|
328
|
+
platformKey,
|
|
329
|
+
hermescBasename,
|
|
330
|
+
};
|
package/src/postinstall.js
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
// Must NEVER fail npm install. This is best-effort bootstrap only.
|
|
8
8
|
|
|
9
9
|
const { ensureAgentBinary, runAgentCommand } = require("./agent-runtime");
|
|
10
|
+
const { ensureHermesc } = require("./hermesc-runtime");
|
|
10
11
|
const { execSync } = require("child_process");
|
|
11
12
|
const fs = require("fs");
|
|
12
13
|
const os = require("os");
|
|
@@ -69,6 +70,71 @@ function installMissingCodingRunners() {
|
|
|
69
70
|
}
|
|
70
71
|
}
|
|
71
72
|
|
|
73
|
+
function ensureLinuxRunnerSandboxPackages() {
|
|
74
|
+
try {
|
|
75
|
+
if (process.platform !== "linux") return;
|
|
76
|
+
if (typeof process.geteuid !== "function" || process.geteuid() !== 0) return;
|
|
77
|
+
const missing = [];
|
|
78
|
+
if (!commandExists("bwrap")) missing.push("bubblewrap");
|
|
79
|
+
if (!commandExists("newuidmap")) missing.push("uidmap");
|
|
80
|
+
if (missing.length === 0) return;
|
|
81
|
+
if (!commandExists("apt-get")) {
|
|
82
|
+
log(`Runner sandbox packages missing (${missing.join(", ")}) and no apt-get is available for auto-install.`);
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
execSync("apt-get update -y", { stdio: ["ignore", "ignore", "ignore"] });
|
|
86
|
+
execSync(`DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends ${missing.join(" ")}`, {
|
|
87
|
+
stdio: "inherit",
|
|
88
|
+
});
|
|
89
|
+
log(`Installed Linux runner sandbox packages: ${missing.join(", ")}.`);
|
|
90
|
+
} catch (error) {
|
|
91
|
+
log(`Skipping Linux runner sandbox package bootstrap: ${error.message}`);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function ensureLinuxRunnerSandboxSupport() {
|
|
96
|
+
try {
|
|
97
|
+
if (process.platform !== "linux") return;
|
|
98
|
+
if (typeof process.geteuid !== "function" || process.geteuid() !== 0) return;
|
|
99
|
+
const confPath = "/etc/sysctl.d/99-yaver-runner-sandbox.conf";
|
|
100
|
+
let body = "kernel.unprivileged_userns_clone=1\nuser.max_user_namespaces=1048576\n";
|
|
101
|
+
if (fs.existsSync("/proc/sys/kernel/apparmor_restrict_unprivileged_userns")) {
|
|
102
|
+
body += "kernel.apparmor_restrict_unprivileged_userns=0\n";
|
|
103
|
+
}
|
|
104
|
+
fs.writeFileSync(confPath, body);
|
|
105
|
+
execSync("sysctl --system", { stdio: ["ignore", "ignore", "ignore"] });
|
|
106
|
+
log("Enabled Linux user-namespace prerequisites for Codex/runner sandboxes.");
|
|
107
|
+
} catch (error) {
|
|
108
|
+
log(`Skipping Linux runner sandbox bootstrap: ${error.message}`);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function reportLinuxRunnerSandboxStatus() {
|
|
113
|
+
try {
|
|
114
|
+
if (process.platform !== "linux") return;
|
|
115
|
+
const issues = [];
|
|
116
|
+
if (!commandExists("bwrap")) issues.push("bubblewrap");
|
|
117
|
+
if (!commandExists("newuidmap")) issues.push("uidmap");
|
|
118
|
+
try {
|
|
119
|
+
const userns = fs.readFileSync("/proc/sys/kernel/unprivileged_userns_clone", "utf8").trim();
|
|
120
|
+
if (userns === "0") issues.push("kernel.unprivileged_userns_clone=0");
|
|
121
|
+
} catch (_) {}
|
|
122
|
+
try {
|
|
123
|
+
const maxUserns = fs.readFileSync("/proc/sys/user/max_user_namespaces", "utf8").trim();
|
|
124
|
+
if (!maxUserns || maxUserns === "0") issues.push("user.max_user_namespaces=0");
|
|
125
|
+
} catch (_) {}
|
|
126
|
+
try {
|
|
127
|
+
const apparmor = fs.readFileSync("/proc/sys/kernel/apparmor_restrict_unprivileged_userns", "utf8").trim();
|
|
128
|
+
if (apparmor === "1") issues.push("kernel.apparmor_restrict_unprivileged_userns=1");
|
|
129
|
+
} catch (_) {}
|
|
130
|
+
if (issues.length > 0) {
|
|
131
|
+
log(`Linux runner sandbox still has blockers: ${issues.join(", ")}. Yaver will mark Codex blocked until the host allows it.`);
|
|
132
|
+
}
|
|
133
|
+
} catch (_) {
|
|
134
|
+
// Best-effort only.
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
72
138
|
function installMissingMobileTools() {
|
|
73
139
|
const missing = MOBILE_TOOL_BOOTSTRAP.filter((entry) => !commandExists(entry.command));
|
|
74
140
|
if (missing.length === 0) {
|
|
@@ -157,7 +223,26 @@ async function main() {
|
|
|
157
223
|
return;
|
|
158
224
|
}
|
|
159
225
|
|
|
226
|
+
// Platform-aware hermesc provisioning. Downloads the binary matching
|
|
227
|
+
// this host from the react-native npm tarball, caches it under the
|
|
228
|
+
// CLI install dir + ~/.yaver so `yaver-push` works offline after.
|
|
229
|
+
// Never blocks install — the bundler falls back to project-local RN
|
|
230
|
+
// if this step skipped or failed.
|
|
231
|
+
try {
|
|
232
|
+
const hermescPath = await ensureHermesc({ quiet: true });
|
|
233
|
+
if (hermescPath) {
|
|
234
|
+
log(`Hermes compiler ready at ${hermescPath}.`);
|
|
235
|
+
} else {
|
|
236
|
+
log(`Hermes compiler not pre-provisioned for ${process.platform}-${process.arch}; bundler will resolve project-local hermesc at push time.`);
|
|
237
|
+
}
|
|
238
|
+
} catch (error) {
|
|
239
|
+
log(`Skipping hermesc install: ${error.message}`);
|
|
240
|
+
}
|
|
241
|
+
|
|
160
242
|
ensurePathOnUnix();
|
|
243
|
+
ensureLinuxRunnerSandboxPackages();
|
|
244
|
+
ensureLinuxRunnerSandboxSupport();
|
|
245
|
+
reportLinuxRunnerSandboxStatus();
|
|
161
246
|
|
|
162
247
|
if (process.platform !== "linux" && process.platform !== "darwin") {
|
|
163
248
|
return;
|
|
File without changes
|