mirror of
https://github.com/hanzoai/dev.git
synced 2026-10-25 03:32:32 +00:00
Conflict resolution: - Accepted remote version bumps to package.json files (0.6.51) - Kept our workflow fix (Homebrew continue-on-error) - Included new platform components from parallel agents
216 lines
7.1 KiB
JavaScript
216 lines
7.1 KiB
JavaScript
// postinstall.js - Download and extract platform-specific binary for hanzo-node
|
|
|
|
import { platform as nodePlatform, arch as nodeArch } from "os";
|
|
import path from "path";
|
|
import { fileURLToPath } from "url";
|
|
import { existsSync, mkdirSync, copyFileSync, chmodSync, readFileSync, unlinkSync, createWriteStream } from "fs";
|
|
import { execSync } from "child_process";
|
|
import { get as httpsGet } from "https";
|
|
|
|
const __filename = fileURLToPath(import.meta.url);
|
|
const __dirname = path.dirname(__filename);
|
|
|
|
const { platform, arch } = process;
|
|
|
|
let targetTriple = null;
|
|
switch (platform) {
|
|
case "linux":
|
|
case "android":
|
|
switch (arch) {
|
|
case "x64":
|
|
targetTriple = "x86_64-unknown-linux-musl";
|
|
break;
|
|
case "arm64":
|
|
targetTriple = "aarch64-unknown-linux-musl";
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
case "darwin":
|
|
switch (arch) {
|
|
case "x64":
|
|
targetTriple = "x86_64-apple-darwin";
|
|
break;
|
|
case "arm64":
|
|
targetTriple = "aarch64-apple-darwin";
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
case "win32":
|
|
switch (arch) {
|
|
case "x64":
|
|
targetTriple = "x86_64-pc-windows-msvc.exe";
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
const getPlatformPackageName = () => {
|
|
const plt = nodePlatform();
|
|
const cpu = nodeArch();
|
|
if (plt === "win32") return "hanzo-node-win32-x64";
|
|
if (plt === "darwin" && cpu === "arm64") return "hanzo-node-darwin-arm64";
|
|
if (plt === "darwin" && cpu === "x64") return "hanzo-node-darwin-x64";
|
|
if (plt === "linux" && cpu === "x64") return "hanzo-node-linux-x64-musl";
|
|
if (plt === "linux" && cpu === "arm64") return "hanzo-node-linux-arm64-musl";
|
|
return null;
|
|
};
|
|
|
|
const getCacheDir = (version) => {
|
|
const plt = nodePlatform();
|
|
const home = process.env.HOME || process.env.USERPROFILE || "";
|
|
let base = "";
|
|
if (plt === "win32") {
|
|
base = process.env.LOCALAPPDATA || path.join(home, "AppData", "Local");
|
|
} else if (plt === "darwin") {
|
|
base = path.join(home, "Library", "Caches");
|
|
} else {
|
|
base = process.env.XDG_CACHE_HOME || path.join(home, ".cache");
|
|
}
|
|
const dir = path.join(base, "hanzo", "node", version);
|
|
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
|
return dir;
|
|
};
|
|
|
|
const httpsDownload = (url, dest) => new Promise((resolve, reject) => {
|
|
const req = httpsGet(url, (res) => {
|
|
const status = res.statusCode || 0;
|
|
if (status >= 300 && status < 400 && res.headers.location) {
|
|
return resolve(httpsDownload(res.headers.location, dest));
|
|
}
|
|
if (status !== 200) {
|
|
return reject(new Error(`HTTP ${status}`));
|
|
}
|
|
const out = createWriteStream(dest);
|
|
res.pipe(out);
|
|
out.on("finish", () => out.close(resolve));
|
|
out.on("error", (e) => {
|
|
try { unlinkSync(dest); } catch {}
|
|
reject(e);
|
|
});
|
|
});
|
|
req.on("error", (e) => {
|
|
try { unlinkSync(dest); } catch {}
|
|
reject(e);
|
|
});
|
|
req.setTimeout(120000, () => {
|
|
req.destroy(new Error("download timed out"));
|
|
});
|
|
});
|
|
|
|
export async function runPostinstall(opts = {}) {
|
|
const { invokedByRuntime = false, skipGlobalAlias = false } = opts;
|
|
|
|
if (!targetTriple) {
|
|
if (!invokedByRuntime) {
|
|
console.log(`hanzo-node: unsupported platform ${platform}/${arch}, skipping postinstall`);
|
|
}
|
|
return;
|
|
}
|
|
|
|
const binDir = path.join(__dirname, "bin");
|
|
if (!existsSync(binDir)) mkdirSync(binDir, { recursive: true });
|
|
|
|
const binaryPath = path.join(binDir, `hanzo-${targetTriple}`);
|
|
const legacyBinaryPath = path.join(binDir, `dev-${targetTriple}`);
|
|
|
|
// Already present?
|
|
if (existsSync(binaryPath) || existsSync(legacyBinaryPath)) {
|
|
if (!invokedByRuntime) {
|
|
console.log("hanzo-node: binary already present");
|
|
}
|
|
return;
|
|
}
|
|
|
|
// Try platform package first
|
|
const pkgName = getPlatformPackageName();
|
|
if (pkgName) {
|
|
try {
|
|
const req = (await import("module")).createRequire(import.meta.url);
|
|
const pkgJson = req.resolve(`${pkgName}/package.json`);
|
|
const pkgDir = path.dirname(pkgJson);
|
|
const src = path.join(pkgDir, "bin", `hanzo-${targetTriple}`);
|
|
if (existsSync(src)) {
|
|
copyFileSync(src, binaryPath);
|
|
if (platform !== "win32") {
|
|
try { chmodSync(binaryPath, 0o755); } catch {}
|
|
}
|
|
if (!invokedByRuntime) {
|
|
console.log(`hanzo-node: installed from ${pkgName}`);
|
|
}
|
|
return;
|
|
}
|
|
} catch (e) {
|
|
// Fall through to download
|
|
}
|
|
}
|
|
|
|
// Download from GitHub release
|
|
try {
|
|
const pkg = JSON.parse(readFileSync(path.join(__dirname, "package.json"), "utf8"));
|
|
const version = pkg.version;
|
|
|
|
const isWin = platform === "win32";
|
|
const binaryName = `code-${targetTriple}`;
|
|
const archiveName = isWin
|
|
? `${binaryName}.zip`
|
|
: (() => { try { execSync("zstd --version", { stdio: "ignore", shell: true }); return `${binaryName}.zst`; } catch { return `${binaryName}.tar.gz`; } })();
|
|
const url = `https://github.com/hanzoai/dev/releases/download/v${version}/${archiveName}`;
|
|
|
|
if (!invokedByRuntime) {
|
|
console.log(`hanzo-node: downloading ${archiveName}...`);
|
|
}
|
|
|
|
const tmp = path.join(binDir, `.${archiveName}.part`);
|
|
await httpsDownload(url, tmp);
|
|
|
|
if (isWin) {
|
|
const sysRoot = process.env.SystemRoot || process.env.windir || 'C:\\Windows';
|
|
const psFull = path.join(sysRoot, 'System32', 'WindowsPowerShell', 'v1.0', 'powershell.exe');
|
|
const psCmd = `Expand-Archive -Path '${tmp}' -DestinationPath '${binDir}' -Force`;
|
|
let ok = false;
|
|
try { execSync(`"${psFull}" -NoProfile -NonInteractive -Command "${psCmd}"`, { stdio: 'ignore' }); ok = true; } catch {}
|
|
if (!ok) { try { execSync(`powershell -NoProfile -NonInteractive -Command "${psCmd}"`, { stdio: 'ignore' }); ok = true; } catch {} }
|
|
if (!ok) { try { execSync(`pwsh -NoProfile -NonInteractive -Command "${psCmd}"`, { stdio: 'ignore' }); ok = true; } catch {} }
|
|
if (!ok) { execSync(`tar -xf "${tmp}" -C "${binDir}"`, { stdio: 'ignore', shell: true }); }
|
|
try { unlinkSync(tmp); } catch {}
|
|
} else {
|
|
if (archiveName.endsWith(".zst")) {
|
|
execSync(`zstd -d '${tmp}' -o '${binaryPath}'`, { stdio: 'ignore', shell: true });
|
|
try { unlinkSync(tmp); } catch {}
|
|
} else {
|
|
execSync(`tar -xzf '${tmp}' -C '${binDir}'`, { stdio: 'ignore', shell: true });
|
|
try { unlinkSync(tmp); } catch {}
|
|
}
|
|
try { chmodSync(binaryPath, 0o755); } catch {}
|
|
}
|
|
|
|
// Cache for future use
|
|
const cacheDir = getCacheDir(version);
|
|
const cachePath = path.join(cacheDir, `hanzo-${targetTriple}`);
|
|
try { copyFileSync(binaryPath, cachePath); } catch {}
|
|
|
|
if (!invokedByRuntime) {
|
|
console.log("hanzo-node: installation complete");
|
|
}
|
|
} catch (e) {
|
|
if (!invokedByRuntime) {
|
|
console.error(`hanzo-node: postinstall failed: ${e.message}`);
|
|
console.error("You can try running the CLI directly, which will attempt to bootstrap the binary.");
|
|
}
|
|
throw e;
|
|
}
|
|
}
|
|
|
|
// Run if invoked directly
|
|
if (process.argv[1] === fileURLToPath(import.meta.url)) {
|
|
runPostinstall().catch(() => process.exit(0)); // Don't fail npm install
|
|
}
|