fallow
Version:
Codebase intelligence for TypeScript and JavaScript: health, complexity, duplication, architecture, styling drift, and unused code from one graph. CLI, LSP, and MCP server. Zero config for over 100 frameworks.
254 lines (230 loc) • 9.68 kB
JavaScript
// Shared launcher used by bin/fallow, bin/fallow-lsp, and bin/fallow-mcp.
//
// 1. Resolves the platform package for the current process (platform + arch + libc).
// 2. Runs ensureVerified (Ed25519 + SHA-256 lazy first-run verify).
// 3. Execs the platform binary.
// 4. For `<bin> --version`, appends a `verified: ...` status line to stdout
// so procurement teams have a single command that surfaces the integrity
// posture (replaces the install-time confirmation message removed when
// postinstall verification was retired for RFC 868 readiness).
const { execFileSync } = require("node:child_process");
const path = require("node:path");
const fs = require("node:fs");
const os = require("node:os");
const { getPlatformPackage } = require("./platform-package");
const { ensureVerified } = require("./lazy-verify");
const { isPreSigningVersion } = require("./verify-binary");
function resolvePlatformPackageName() {
if (process.platform !== "linux") {
return getPlatformPackage(process.platform, process.arch);
}
try {
const { familySync } = require("detect-libc");
return getPlatformPackage(process.platform, process.arch, familySync());
} catch {
// musl binaries are statically linked and work on both glibc and musl
return getPlatformPackage(process.platform, process.arch, "musl");
}
}
function isVersionQuery(argv) {
// The root command answers all three version flags (--version, -V, and the
// TS/JS-toolchain-style -v), so the verified-status line must be appended for
// every one of them, not just --version / -V.
const tail = argv.slice(2);
if (tail.length === 0) return false;
return tail[0] === "--version" || tail[0] === "-V" || tail[0] === "-v";
}
// Signing status of the resolved CLI version, appended to the `verified:` line.
// Most informative on the `skipped` path: a fleet running with
// FALLOW_SKIP_BINARY_VERIFY can see whether the pinned version is even signable
// (pre-signing versions predate the 2.77.0 epoch and have no signature to
// verify). Best-effort: an unknown/unreadable version yields no annotation.
function describeSigning(version) {
if (typeof version !== "string" || version.length === 0) {
return "";
}
return isPreSigningVersion(version)
? `; fallow ${version} unsigned (predates 2.77.0)`
: `; fallow ${version} signed`;
}
function describeVerified(result, version) {
const status = describeVerifiedStatus(result);
return `${status}${describeSigning(version)}`;
}
function describeVerifiedStatus(result) {
if (result.skipped) {
return `verified: skipped (${result.reason})`;
}
if (result.ok) {
if (result.cached) {
return `verified: yes (cache hit at ${result.sentinelPath})`;
}
if (result.sentinelPath) {
return `verified: yes (sentinel ${result.sentinelPath})`;
}
return "verified: yes (sentinel not persisted)";
}
return `verified: no (${result.code})`;
}
// Resolve the platform package directory + manifest path, or print an
// actionable error and exit. Keeps `runBinary` a flat top-level sequence.
function resolvePlatformPaths() {
const pkg = resolvePlatformPackageName();
if (!pkg) {
process.stderr.write(`Unsupported platform: ${process.platform}-${process.arch}\n`);
process.exit(1);
}
try {
const manifestPath = require.resolve(`${pkg}/package.json`);
return { pkg, manifestPath, platformPkgDir: path.dirname(manifestPath) };
} catch {
process.stderr.write(
`Could not find ${pkg}. Run 'npm install' to install the platform-specific binary.\n`,
);
process.exit(1);
}
}
function printVerifyError(verifyResult) {
const where = verifyResult.binary ? ` ${verifyResult.binary}` : "";
process.stderr.write(
`fallow: binary verification failed${where} (${verifyResult.code}): ${verifyResult.message}\n` +
`See https://github.com/fallow-rs/fallow/blob/main/SECURITY.md for the trust model. ` +
`Set FALLOW_SKIP_BINARY_VERIFY=1 only when you deliberately replace the published binary.\n`,
);
}
function writeVerifiedLineIfVersionQuery(verifyResult, version) {
if (isVersionQuery(process.argv)) {
process.stdout.write(`${describeVerified(verifyResult, version)}\n`);
}
}
// Exit code for a child failure caught from execFileSync. A signal death has
// status === null; map it to the shell convention 128 + signal number so CI
// gates see a crash, never a success.
function exitCodeForChildFailure(e) {
if (e.status !== null) return e.status;
const signalNumber = os.constants.signals[e.signal];
return signalNumber ? 128 + signalNumber : 1;
}
// Read the resolved CLI version from the platform package manifest (its version
// is released in lockstep with the CLI). Best-effort: never throws, so a
// missing/garbled manifest just omits the signing annotation on --version.
function readResolvedVersion(manifestPath) {
try {
const version = JSON.parse(fs.readFileSync(manifestPath, "utf8")).version;
return typeof version === "string" ? version : undefined;
} catch {
return undefined;
}
}
function resolveTypeAwareCompanion(
fallowVersion,
resolvePackage = require.resolve,
readFile = fs.readFileSync,
) {
try {
const manifestPath = resolvePackage("fallow-type-aware/package.json");
const manifest = JSON.parse(readFile(manifestPath, "utf8"));
if (manifest.version !== fallowVersion) return undefined;
const companion = path.join(path.dirname(manifestPath), "fallow-type-aware.mjs");
return fs.existsSync(companion) ? companion : undefined;
} catch {
return undefined;
}
}
function typeAwareCommand(
companion,
{ platform = process.platform, execPath = process.execPath } = {},
) {
return platform === "win32"
? { binary: execPath, script: companion }
: { binary: companion, script: undefined };
}
function childEnvironment(
resolvedVersion,
resolveCompanion = resolveTypeAwareCompanion,
commandOptions,
) {
if (process.env.FALLOW_TYPE_AWARE_BIN) return process.env;
const companion = resolveCompanion(resolvedVersion);
if (companion === undefined) return process.env;
const command = typeAwareCommand(companion, commandOptions);
const environment = {
...process.env,
FALLOW_TYPE_AWARE_BIN: command.binary,
// Marks the wiring as launcher-provided node_modules resolution so
// `type-aware status` does not report it as a user-set override.
FALLOW_TYPE_AWARE_BIN_SOURCE: "npm-wrapper",
};
if (command.script) {
environment.FALLOW_TYPE_AWARE_SCRIPT = command.script;
} else {
delete environment.FALLOW_TYPE_AWARE_SCRIPT;
}
return environment;
}
// Swallow EPIPE on stdout. When fallow's output is piped into a reader that
// closes early (e.g. `fallow --version | head`), the trailing `verified:`
// status line would otherwise surface as an unhandled EPIPE 'error' event and
// dump a Node stack trace. EPIPE arrives as an async 'error' event on the
// stdout stream, not as a throw, so a try/catch around the write cannot catch
// it. The child binary's primary output is already written via inherited
// stdio; the status line is best-effort, so exit cleanly once the reader is
// gone. Scoped to stdout so a genuine error write to stderr still sets exit 1.
function guardBrokenStdout() {
process.stdout.on("error", (err) => {
if (err && err.code === "EPIPE") {
process.exit(0);
}
throw err;
});
}
// Run the resolved platform binary. `options.prependArgs` (default none) is a
// list of leading arguments inserted before the process argv, used by the
// `fallow-lsp` / `fallow-mcp` launcher shims to spawn the multicall binary as
// `fallow lsp-server` / `fallow mcp-server`. Because the platform packages ship
// a single `fallow` binary, those shims pass `binaryBaseName = "fallow"` and
// the subcommand via `prependArgs`; signal, exit-code, and version-line
// handling stay identical to a bare `fallow` invocation.
function runBinary(binaryBaseName, options = {}) {
const prependArgs = Array.isArray(options.prependArgs) ? options.prependArgs : [];
guardBrokenStdout();
const { pkg, manifestPath, platformPkgDir } = resolvePlatformPaths();
const resolvedVersion = readResolvedVersion(manifestPath);
const binaryName = process.platform === "win32" ? `${binaryBaseName}.exe` : binaryBaseName;
const binaryPath = path.join(platformPkgDir, binaryName);
if (!fs.existsSync(binaryPath)) {
process.stderr.write(`Binary not found at ${binaryPath}\n`);
process.exit(1);
}
// Lazy first-run verify. Errors are user-facing.
const verifyResult = ensureVerified({ platformPkgDir, packageName: pkg, manifestPath });
if (!verifyResult.ok) {
printVerifyError(verifyResult);
process.exit(1);
}
try {
execFileSync(binaryPath, [...prependArgs, ...process.argv.slice(2)], {
stdio: "inherit",
env: childEnvironment(resolvedVersion),
});
} catch (e) {
if (e.status === undefined) throw e;
if (e.status === null) {
process.stderr.write(`fallow binary terminated by signal ${e.signal ?? "unknown"}\n`);
}
// Child has already written its --version line via inherited stdio;
// append the verified line here only on a clean exit.
if (e.status === 0) writeVerifiedLineIfVersionQuery(verifyResult, resolvedVersion);
process.exit(exitCodeForChildFailure(e));
}
writeVerifiedLineIfVersionQuery(verifyResult, resolvedVersion);
}
module.exports = {
runBinary,
describeVerified, // test-only
isVersionQuery, // test-only
guardBrokenStdout, // test-only
exitCodeForChildFailure, // test-only
resolveTypeAwareCompanion, // test-only
childEnvironment, // test-only
};