mirror of
https://github.com/czcdr/dsh-plugin-rea.git
synced 2026-11-13 18:17:56 -10:00
fix(macos): run the frida rebuild with the vendored node on PATH; compare real paths
Two defects that only macOS arm64 can see. `npm rebuild frida` spawns the package install script in a shell, and that script resolves `node` from PATH. On the machines this bundle targets there is no `node` on PATH at all, so the rebuild died with `sh: node: command not found`, reported `fridaBindings` as unavailable, and skipped — which never fails a run. The vendored runtime is now prepended to PATH for that single spawn, and on a Mac with no node it produces `FRIDA_OK 3`. `samePath()` compared `path.resolve()` spellings, but Node resolves a module URL through its real path: a checkout under `/var/folders/...` yields `/private/var/...`, while an explicit `--root /var/folders/...` keeps what it was given. A mirrored run then decided it was not mirrored, ignored `mirror.json` and looked for the manifest only the mirror step creates. Both sides are now canonicalised, with a fallback for a directory that does not exist yet. The macOS CI workflow that exercised these is deliberately not part of this branch: it cannot obtain a runner in this environment, and a permanently red check on main is worse than no check. It lands as its own PR.
This commit is contained in:
1 parent
b3eb2b4d53
commit
2b5f0d1abc
2 files changed
+50
-6
No files matched your search
+39
-4
@@ -35,7 +35,7 @@
|
||||
*/
|
||||
import { spawnSync } from "node:child_process";
|
||||
import { createHash, randomUUID } from "node:crypto";
|
||||
import { appendFileSync, chmodSync, closeSync, copyFileSync, createWriteStream, existsSync, mkdirSync, openSync, readFileSync, readdirSync, readSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
|
||||
import { appendFileSync, chmodSync, closeSync, copyFileSync, createWriteStream, existsSync, mkdirSync, openSync, readFileSync, readdirSync, readSync, realpathSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
|
||||
import { request as httpRequest } from "node:http";
|
||||
import { request as httpsRequest } from "node:https";
|
||||
import { homedir } from "node:os";
|
||||
@@ -48,10 +48,33 @@ const PLUGIN_ROOT = path.resolve(HERE, "..");
|
||||
const IS_WINDOWS = process.platform === "win32";
|
||||
const EXE = IS_WINDOWS ? ".exe" : "";
|
||||
|
||||
/**
|
||||
* Two spellings of one directory are the same directory, not two.
|
||||
*
|
||||
* `path.resolve` alone is not enough on macOS: Node resolves a module's own URL
|
||||
* through its real path, so `fileURLToPath(import.meta.url)` for a checkout under
|
||||
* `/var/folders/...` (and any other symlinked location) yields `/private/var/...`,
|
||||
* while an explicitly passed `--root /var/folders/...` keeps the spelling it was
|
||||
* given. `samePath` then decided the run was not a mirrored one, ignored
|
||||
* `mirror.json`, and looked for the manifest the mirror step was supposed to
|
||||
* create — the BOM/mirror regression test in scripts/setup.test.mjs failed for
|
||||
* exactly that reason on macOS while passing on Linux, where /tmp is not a symlink.
|
||||
*/
|
||||
function canonicalPath(value) {
|
||||
const resolved = path.resolve(value);
|
||||
try {
|
||||
return realpathSync(resolved);
|
||||
} catch {
|
||||
// A path that does not exist yet (the bundle root on a first run) has no real
|
||||
// path to compare; the resolved spelling is still the best answer available.
|
||||
return resolved;
|
||||
}
|
||||
}
|
||||
|
||||
/** Windows treats two spellings of one path as equal; the mirror checks must too. */
|
||||
function samePath(left, right) {
|
||||
const a = path.resolve(left);
|
||||
const b = path.resolve(right);
|
||||
const a = canonicalPath(left);
|
||||
const b = canonicalPath(right);
|
||||
return IS_WINDOWS ? a.toLowerCase() === b.toLowerCase() : a === b;
|
||||
}
|
||||
|
||||
@@ -1984,10 +2007,22 @@ function ensureFridaBindings(nodeRuntime, npmCli, installRoot) {
|
||||
log(` frida has no native binding at ${bindingFile}; running its own install script via npm rebuild frida`);
|
||||
}
|
||||
|
||||
// `npm rebuild` runs the package's own install script in a shell, and that
|
||||
// script resolves `node` from PATH — not from the interpreter npm itself is
|
||||
// running under. On the machines this bundle exists for there is no `node` on
|
||||
// PATH at all (that is why it vendors one), so the rebuild died with
|
||||
// `npm rebuild frida exited 127: sh: node: command not found` and the binding
|
||||
// was reported unavailable even where prebuild-install could have fetched a
|
||||
// matching prebuilt binary. Put the vendored runtime first for this one spawn.
|
||||
const rebuild = spawnSync(
|
||||
nodeRuntime,
|
||||
[npmCli, "rebuild", "frida", "--prefix", installRoot, "--loglevel", "error"],
|
||||
{ cwd: installRoot, encoding: "utf8", timeout: 1800000 },
|
||||
{
|
||||
cwd: installRoot,
|
||||
encoding: "utf8",
|
||||
timeout: 1800000,
|
||||
env: { ...process.env, PATH: `${path.dirname(nodeRuntime)}${path.delimiter}${process.env.PATH ?? ""}` },
|
||||
},
|
||||
);
|
||||
|
||||
// 2) Hash the result BEFORE importing it too — a rebuild can produce a binding
|
||||
|
||||
+11
-2
@@ -13,7 +13,7 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { spawn, spawnSync } from "node:child_process";
|
||||
import { createHash } from "node:crypto";
|
||||
import { chmodSync, copyFileSync, existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, statSync, symlinkSync, writeFileSync } from "node:fs";
|
||||
import { chmodSync, copyFileSync, existsSync, mkdirSync, mkdtempSync, readFileSync, realpathSync, rmSync, statSync, symlinkSync, writeFileSync } from "node:fs";
|
||||
import { createServer } from "node:http";
|
||||
import { tmpdir } from "node:os";
|
||||
import path from "node:path";
|
||||
@@ -230,7 +230,16 @@ test("a mirrored shim takes its bundle root from its own location, not DSH_HOME"
|
||||
const mirror = path.join(root, "plugins", "rea-bundle");
|
||||
mkdirSync(path.join(mirror, "bin"), { recursive: true });
|
||||
copyFileSync(SHIM_SOURCE, path.join(mirror, "bin", "rea-shim.mjs"));
|
||||
assert.equal(shimBundleRoot(path.join(mirror, "bin", "rea-shim.mjs"), { DSH_HOME: path.join(root, "elsewhere") }), mirror);
|
||||
// The shim derives its root from its own module URL, which Node resolves
|
||||
// through the real path: on macOS `tmpdir()` is /var/folders/... and that is a
|
||||
// symlink to /private/var/folders/..., so the reported root is the canonical
|
||||
// spelling while the fixture path is not. Compare the same thing the shim
|
||||
// reports rather than failing on the spelling — the other assertions in this
|
||||
// test are about which of the two roots wins, not about how it is spelled.
|
||||
assert.equal(
|
||||
shimBundleRoot(path.join(mirror, "bin", "rea-shim.mjs"), { DSH_HOME: path.join(root, "elsewhere") }),
|
||||
realpathSync(mirror),
|
||||
);
|
||||
} finally {
|
||||
rmTreeQuiet(root);
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user