d68f003000
CI / Change detection (push) Has been cancelled
CI / Version drift (push) Has been cancelled
CI / Lint (push) Has been cancelled
CI / Workflow RLM cache (push) Has been cancelled
CI / Test (macos-latest) (push) Has been cancelled
CI / Test (ubuntu-latest) (push) Has been cancelled
CI / Test (windows-latest) (push) Has been cancelled
CI / npm wrapper smoke (push) Has been cancelled
CI / Mobile runtime smoke (push) Has been cancelled
CI / Workflow lint (push) Has been cancelled
CI / Documentation (push) Has been cancelled
Web Frontend / Lint & Type Check (push) Failing after 1s
Auto-close harvested PRs / close (push) Failing after 1s
cargo-deny / cargo-deny (bans licenses sources) (push) Failing after 1s
Security audit / cargo-audit (push) Failing after 1s
Sync to CNB / sync (push) Failing after 1s
cargo-deny / cargo-deny (advisories) (push) Failing after 3s
Web Frontend / Deploy to Cloudflare (push) Has been cancelled
146 lines
4.0 KiB
JavaScript
146 lines
4.0 KiB
JavaScript
const fs = require("fs");
|
|
const { execFileSync } = require("child_process");
|
|
|
|
const GLIBC_VERSION_RE = /GLIBC_(\d+)\.(\d+)(?:\.(\d+))?/g;
|
|
|
|
function isLinux() {
|
|
return process.platform === "linux";
|
|
}
|
|
|
|
function parseVersion(text) {
|
|
const match = String(text || "").match(/(\d+)\.(\d+)(?:\.(\d+))?/);
|
|
if (!match) return null;
|
|
return [Number(match[1]), Number(match[2]), Number(match[3] || 0)];
|
|
}
|
|
|
|
function compareVersion(a, b) {
|
|
for (let i = 0; i < 3; i += 1) {
|
|
if (a[i] !== b[i]) return a[i] - b[i];
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
function formatVersion(version) {
|
|
return version[2] ? `${version[0]}.${version[1]}.${version[2]}` : `${version[0]}.${version[1]}`;
|
|
}
|
|
|
|
function detectHostGlibc() {
|
|
try {
|
|
const out = execFileSync("getconf", ["GNU_LIBC_VERSION"], {
|
|
encoding: "utf8",
|
|
stdio: ["ignore", "pipe", "ignore"],
|
|
});
|
|
const version = parseVersion(out);
|
|
if (version) return version;
|
|
} catch {
|
|
// fall through to ldd
|
|
}
|
|
try {
|
|
const out = execFileSync("ldd", ["--version"], {
|
|
encoding: "utf8",
|
|
stdio: ["ignore", "pipe", "ignore"],
|
|
});
|
|
const firstLine = out.split("\n", 1)[0];
|
|
const version = parseVersion(firstLine);
|
|
if (version) return version;
|
|
} catch {
|
|
// glibc not present (e.g. musl / Alpine)
|
|
}
|
|
return null;
|
|
}
|
|
|
|
function detectBinaryRequiredGlibc(filePath) {
|
|
const buf = fs.readFileSync(filePath);
|
|
const text = buf.toString("latin1");
|
|
let highest = null;
|
|
GLIBC_VERSION_RE.lastIndex = 0;
|
|
let match;
|
|
while ((match = GLIBC_VERSION_RE.exec(text)) !== null) {
|
|
const version = [Number(match[1]), Number(match[2]), Number(match[3] || 0)];
|
|
if (!highest || compareVersion(version, highest) > 0) {
|
|
highest = version;
|
|
}
|
|
}
|
|
return highest;
|
|
}
|
|
|
|
function buildFromSourceHint() {
|
|
return [
|
|
"You can still run codewhale by building from source with Cargo:",
|
|
"",
|
|
" # Requires Rust 1.88+ (https://rustup.rs)",
|
|
" cargo install codewhale-cli --locked # provides `codewhale` and `codew`",
|
|
" cargo install codewhale-tui --locked # provides `codewhale-tui`",
|
|
"",
|
|
"Or build from a checkout:",
|
|
"",
|
|
" git clone https://github.com/Hmbown/CodeWhale.git",
|
|
" cd CodeWhale",
|
|
" cargo install --path crates/cli --locked",
|
|
" cargo install --path crates/tui --locked",
|
|
"",
|
|
"See https://github.com/Hmbown/CodeWhale/blob/main/docs/INSTALL.md",
|
|
].join("\n");
|
|
}
|
|
|
|
function skipGlibcCheck() {
|
|
return (
|
|
process.env.CODEWHALE_SKIP_GLIBC_CHECK === "1" ||
|
|
process.env.DEEPSEEK_TUI_SKIP_GLIBC_CHECK === "1" ||
|
|
process.env.DEEPSEEK_SKIP_GLIBC_CHECK === "1"
|
|
);
|
|
}
|
|
|
|
function glibcCompatibilityMessage(required, host) {
|
|
const hostLine = host
|
|
? `this system has glibc ${formatVersion(host)}, which is too old for that asset.`
|
|
: "this system does not appear to provide GNU libc.";
|
|
return [
|
|
`Prebuilt CodeWhale Linux binaries require GLIBC_${formatVersion(required)}, but ${hostLine}`,
|
|
"",
|
|
"The Linux x64 release asset is a static (musl) build that runs on any glibc,",
|
|
"but the Linux arm64 asset is a GNU libc build linked against",
|
|
"Ubuntu 24.04/glibc 2.39, which Ubuntu 22.04 (glibc 2.35) cannot run.",
|
|
"",
|
|
buildFromSourceHint(),
|
|
"",
|
|
"Set CODEWHALE_SKIP_GLIBC_CHECK=1 to bypass this check at your own risk.",
|
|
].join("\n");
|
|
}
|
|
|
|
function preflightGlibc(filePath) {
|
|
if (!isLinux()) return;
|
|
if (skipGlibcCheck()) {
|
|
return;
|
|
}
|
|
|
|
const required = detectBinaryRequiredGlibc(filePath);
|
|
if (!required) {
|
|
// Statically linked / musl binary, or no GLIBC_* version dependencies present.
|
|
return;
|
|
}
|
|
|
|
const host = detectHostGlibc();
|
|
if (!host) {
|
|
throw new Error(glibcCompatibilityMessage(required, null));
|
|
}
|
|
|
|
if (compareVersion(host, required) < 0) {
|
|
throw new Error(glibcCompatibilityMessage(required, host));
|
|
}
|
|
}
|
|
|
|
module.exports = {
|
|
preflightGlibc,
|
|
detectHostGlibc,
|
|
detectBinaryRequiredGlibc,
|
|
// exported for tests
|
|
_internal: {
|
|
parseVersion,
|
|
compareVersion,
|
|
formatVersion,
|
|
glibcCompatibilityMessage,
|
|
skipGlibcCheck,
|
|
},
|
|
};
|