mirror of
https://github.com/hanzoai/dev.git
synced 2026-10-25 03:32:32 +00:00
fix(ci): fix codespell and prettier issues
- Fix typos: parellel→parallel, neccessary→necessary, mulitple→multiple, responible→responsible, geniune→genuine, yeild→yield - Add ignore words: Nd, nd, edn, lightening (valid terms) - Run prettier --write to fix all formatting issues
This commit is contained in:
+399
-185
@@ -1,26 +1,42 @@
|
||||
#!/usr/bin/env node
|
||||
// Postinstall script for @hanzo/dev
|
||||
|
||||
import { existsSync, mkdirSync, createWriteStream, chmodSync, readFileSync, readSync, writeFileSync, unlinkSync, statSync, openSync, closeSync, copyFileSync, fsyncSync, renameSync, realpathSync } from 'fs';
|
||||
import { join, dirname, resolve } from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
import { get } from 'https';
|
||||
import { platform, arch, tmpdir } from 'os';
|
||||
import { execSync } from 'child_process';
|
||||
import { createRequire } from 'module';
|
||||
import {
|
||||
existsSync,
|
||||
mkdirSync,
|
||||
createWriteStream,
|
||||
chmodSync,
|
||||
readFileSync,
|
||||
readSync,
|
||||
writeFileSync,
|
||||
unlinkSync,
|
||||
statSync,
|
||||
openSync,
|
||||
closeSync,
|
||||
copyFileSync,
|
||||
fsyncSync,
|
||||
renameSync,
|
||||
realpathSync,
|
||||
} from "fs";
|
||||
import { join, dirname, resolve } from "path";
|
||||
import { fileURLToPath } from "url";
|
||||
import { get } from "https";
|
||||
import { platform, arch, tmpdir } from "os";
|
||||
import { execSync } from "child_process";
|
||||
import { createRequire } from "module";
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
function getTargetTriple() {
|
||||
const platformMap = {
|
||||
'darwin': 'apple-darwin',
|
||||
'linux': 'unknown-linux-musl',
|
||||
'win32': 'pc-windows-msvc'
|
||||
darwin: "apple-darwin",
|
||||
linux: "unknown-linux-musl",
|
||||
win32: "pc-windows-msvc",
|
||||
};
|
||||
|
||||
const archMap = {
|
||||
'x64': 'x86_64',
|
||||
'arm64': 'aarch64'
|
||||
x64: "x86_64",
|
||||
arm64: "aarch64",
|
||||
};
|
||||
|
||||
const rustArch = archMap[arch()] || arch();
|
||||
@@ -31,47 +47,54 @@ function getTargetTriple() {
|
||||
|
||||
function getCacheDir(version) {
|
||||
const plt = platform();
|
||||
const home = process.env.HOME || process.env.USERPROFILE || '';
|
||||
let base = '';
|
||||
if (plt === 'win32') {
|
||||
base = process.env.LOCALAPPDATA || join(home, 'AppData', 'Local');
|
||||
} else if (plt === 'darwin') {
|
||||
base = join(home, 'Library', 'Caches');
|
||||
const home = process.env.HOME || process.env.USERPROFILE || "";
|
||||
let base = "";
|
||||
if (plt === "win32") {
|
||||
base = process.env.LOCALAPPDATA || join(home, "AppData", "Local");
|
||||
} else if (plt === "darwin") {
|
||||
base = join(home, "Library", "Caches");
|
||||
} else {
|
||||
base = process.env.XDG_CACHE_HOME || join(home, '.cache');
|
||||
base = process.env.XDG_CACHE_HOME || join(home, ".cache");
|
||||
}
|
||||
const dir = join(base, 'hanzo', 'dev', version);
|
||||
const dir = join(base, "hanzo", "dev", version);
|
||||
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
||||
return dir;
|
||||
}
|
||||
|
||||
function getCachedBinaryPath(version, targetTriple, isWindows) {
|
||||
const ext = isWindows ? '.exe' : '';
|
||||
const ext = isWindows ? ".exe" : "";
|
||||
const cacheDir = getCacheDir(version);
|
||||
return join(cacheDir, `dev-${targetTriple}${ext}`);
|
||||
}
|
||||
|
||||
function isWSL() {
|
||||
if (platform() !== 'linux') return false;
|
||||
if (platform() !== "linux") return false;
|
||||
try {
|
||||
const ver = readFileSync('/proc/version', 'utf8').toLowerCase();
|
||||
return ver.includes('microsoft') || !!process.env.WSL_DISTRO_NAME;
|
||||
} catch { return false; }
|
||||
const ver = readFileSync("/proc/version", "utf8").toLowerCase();
|
||||
return ver.includes("microsoft") || !!process.env.WSL_DISTRO_NAME;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function isPathOnWindowsFs(p) {
|
||||
try {
|
||||
const mounts = readFileSync('/proc/mounts', 'utf8').split(/\n/).filter(Boolean);
|
||||
let best = { mount: '/', type: 'unknown', len: 1 };
|
||||
const mounts = readFileSync("/proc/mounts", "utf8")
|
||||
.split(/\n/)
|
||||
.filter(Boolean);
|
||||
let best = { mount: "/", type: "unknown", len: 1 };
|
||||
for (const line of mounts) {
|
||||
const parts = line.split(' ');
|
||||
const parts = line.split(" ");
|
||||
if (parts.length < 3) continue;
|
||||
const mnt = parts[1];
|
||||
const typ = parts[2];
|
||||
if (p.startsWith(mnt) && mnt.length > best.len) best = { mount: mnt, type: typ, len: mnt.length };
|
||||
if (p.startsWith(mnt) && mnt.length > best.len)
|
||||
best = { mount: mnt, type: typ, len: mnt.length };
|
||||
}
|
||||
return best.type === 'drvfs' || best.type === 'cifs';
|
||||
} catch { return false; }
|
||||
return best.type === "drvfs" || best.type === "cifs";
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async function writeCacheAtomic(srcPath, cachePath) {
|
||||
@@ -82,141 +105,185 @@ async function writeCacheAtomic(srcPath, cachePath) {
|
||||
}
|
||||
} catch {}
|
||||
const dir = dirname(cachePath);
|
||||
if (!existsSync(dir)) { try { mkdirSync(dir, { recursive: true }); } catch {} }
|
||||
const tmp = cachePath + '.tmp-' + Math.random().toString(36).slice(2, 8);
|
||||
if (!existsSync(dir)) {
|
||||
try {
|
||||
mkdirSync(dir, { recursive: true });
|
||||
} catch {}
|
||||
}
|
||||
const tmp = cachePath + ".tmp-" + Math.random().toString(36).slice(2, 8);
|
||||
copyFileSync(srcPath, tmp);
|
||||
try { const fd = openSync(tmp, 'r'); try { fsyncSync(fd); } finally { closeSync(fd); } } catch {}
|
||||
try {
|
||||
const fd = openSync(tmp, "r");
|
||||
try {
|
||||
fsyncSync(fd);
|
||||
} finally {
|
||||
closeSync(fd);
|
||||
}
|
||||
} catch {}
|
||||
const delays = [100, 200, 400, 800, 1200, 1600];
|
||||
for (let i = 0; i < delays.length; i++) {
|
||||
try {
|
||||
if (existsSync(cachePath)) { try { unlinkSync(cachePath); } catch {} }
|
||||
if (existsSync(cachePath)) {
|
||||
try {
|
||||
unlinkSync(cachePath);
|
||||
} catch {}
|
||||
}
|
||||
renameSync(tmp, cachePath);
|
||||
return;
|
||||
} catch {
|
||||
await new Promise(r => setTimeout(r, delays[i]));
|
||||
await new Promise((r) => setTimeout(r, delays[i]));
|
||||
}
|
||||
}
|
||||
if (existsSync(cachePath)) { try { unlinkSync(cachePath); } catch {} }
|
||||
if (existsSync(cachePath)) {
|
||||
try {
|
||||
unlinkSync(cachePath);
|
||||
} catch {}
|
||||
}
|
||||
renameSync(tmp, cachePath);
|
||||
}
|
||||
|
||||
function resolveGlobalBinDir() {
|
||||
const plt = platform();
|
||||
const userAgent = process.env.npm_config_user_agent || '';
|
||||
const userAgent = process.env.npm_config_user_agent || "";
|
||||
|
||||
const fromPrefix = (prefixPath) => {
|
||||
if (!prefixPath) return '';
|
||||
return plt === 'win32' ? prefixPath : join(prefixPath, 'bin');
|
||||
if (!prefixPath) return "";
|
||||
return plt === "win32" ? prefixPath : join(prefixPath, "bin");
|
||||
};
|
||||
|
||||
const prefixEnv = process.env.npm_config_prefix || process.env.PREFIX || '';
|
||||
const prefixEnv = process.env.npm_config_prefix || process.env.PREFIX || "";
|
||||
const direct = fromPrefix(prefixEnv);
|
||||
if (direct) return direct;
|
||||
|
||||
const tryExec = (command) => {
|
||||
try {
|
||||
return execSync(command, {
|
||||
stdio: ['ignore', 'pipe', 'ignore'],
|
||||
stdio: ["ignore", "pipe", "ignore"],
|
||||
shell: true,
|
||||
}).toString().trim();
|
||||
})
|
||||
.toString()
|
||||
.trim();
|
||||
} catch {
|
||||
return '';
|
||||
return "";
|
||||
}
|
||||
};
|
||||
|
||||
const prefixFromNpm = fromPrefix(tryExec('npm prefix -g'));
|
||||
const prefixFromNpm = fromPrefix(tryExec("npm prefix -g"));
|
||||
if (prefixFromNpm) return prefixFromNpm;
|
||||
|
||||
const binFromNpm = tryExec('npm bin -g');
|
||||
const binFromNpm = tryExec("npm bin -g");
|
||||
if (binFromNpm) return binFromNpm;
|
||||
|
||||
if (userAgent.includes('pnpm')) {
|
||||
const pnpmBin = tryExec('pnpm bin --global');
|
||||
if (userAgent.includes("pnpm")) {
|
||||
const pnpmBin = tryExec("pnpm bin --global");
|
||||
if (pnpmBin) return pnpmBin;
|
||||
}
|
||||
|
||||
if (userAgent.includes('yarn')) {
|
||||
const yarnBin = tryExec('yarn global bin');
|
||||
if (userAgent.includes("yarn")) {
|
||||
const yarnBin = tryExec("yarn global bin");
|
||||
if (yarnBin) return yarnBin;
|
||||
}
|
||||
|
||||
return '';
|
||||
return "";
|
||||
}
|
||||
|
||||
async function downloadBinary(url, dest, maxRedirects = 5, maxRetries = 3) {
|
||||
const sleep = (ms) => new Promise(r => setTimeout(r, ms));
|
||||
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
||||
|
||||
const doAttempt = () => new Promise((resolve, reject) => {
|
||||
const attempt = (currentUrl, redirectsLeft) => {
|
||||
const req = get(currentUrl, (response) => {
|
||||
const status = response.statusCode || 0;
|
||||
const location = response.headers.location;
|
||||
const doAttempt = () =>
|
||||
new Promise((resolve, reject) => {
|
||||
const attempt = (currentUrl, redirectsLeft) => {
|
||||
const req = get(currentUrl, (response) => {
|
||||
const status = response.statusCode || 0;
|
||||
const location = response.headers.location;
|
||||
|
||||
if ((status === 301 || status === 302 || status === 303 || status === 307 || status === 308) && location) {
|
||||
if (redirectsLeft <= 0) {
|
||||
reject(new Error(`Too many redirects while downloading ${currentUrl}`));
|
||||
if (
|
||||
(status === 301 ||
|
||||
status === 302 ||
|
||||
status === 303 ||
|
||||
status === 307 ||
|
||||
status === 308) &&
|
||||
location
|
||||
) {
|
||||
if (redirectsLeft <= 0) {
|
||||
reject(
|
||||
new Error(`Too many redirects while downloading ${currentUrl}`),
|
||||
);
|
||||
return;
|
||||
}
|
||||
attempt(location, redirectsLeft - 1);
|
||||
return;
|
||||
}
|
||||
attempt(location, redirectsLeft - 1);
|
||||
return;
|
||||
}
|
||||
|
||||
if (status === 200) {
|
||||
const expected = parseInt(response.headers['content-length'] || '0', 10) || 0;
|
||||
let bytes = 0;
|
||||
let timer;
|
||||
const timeoutMs = 30000;
|
||||
if (status === 200) {
|
||||
const expected =
|
||||
parseInt(response.headers["content-length"] || "0", 10) || 0;
|
||||
let bytes = 0;
|
||||
let timer;
|
||||
const timeoutMs = 30000;
|
||||
|
||||
const resetTimer = () => {
|
||||
if (timer) clearTimeout(timer);
|
||||
timer = setTimeout(() => {
|
||||
req.destroy(new Error('download stalled'));
|
||||
}, timeoutMs);
|
||||
};
|
||||
const resetTimer = () => {
|
||||
if (timer) clearTimeout(timer);
|
||||
timer = setTimeout(() => {
|
||||
req.destroy(new Error("download stalled"));
|
||||
}, timeoutMs);
|
||||
};
|
||||
|
||||
resetTimer();
|
||||
response.on('data', (chunk) => {
|
||||
bytes += chunk.length;
|
||||
resetTimer();
|
||||
});
|
||||
response.on("data", (chunk) => {
|
||||
bytes += chunk.length;
|
||||
resetTimer();
|
||||
});
|
||||
|
||||
const file = createWriteStream(dest);
|
||||
response.pipe(file);
|
||||
file.on('finish', () => {
|
||||
if (timer) clearTimeout(timer);
|
||||
file.close();
|
||||
if (expected && bytes !== expected) {
|
||||
try { unlinkSync(dest); } catch {}
|
||||
reject(new Error(`incomplete download: got ${bytes} of ${expected} bytes`));
|
||||
} else if (bytes === 0) {
|
||||
try { unlinkSync(dest); } catch {}
|
||||
reject(new Error('empty download'));
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
file.on('error', (err) => {
|
||||
if (timer) clearTimeout(timer);
|
||||
try { unlinkSync(dest); } catch {}
|
||||
reject(err);
|
||||
});
|
||||
} else {
|
||||
reject(new Error(`Failed to download: HTTP ${status}`));
|
||||
}
|
||||
});
|
||||
const file = createWriteStream(dest);
|
||||
response.pipe(file);
|
||||
file.on("finish", () => {
|
||||
if (timer) clearTimeout(timer);
|
||||
file.close();
|
||||
if (expected && bytes !== expected) {
|
||||
try {
|
||||
unlinkSync(dest);
|
||||
} catch {}
|
||||
reject(
|
||||
new Error(
|
||||
`incomplete download: got ${bytes} of ${expected} bytes`,
|
||||
),
|
||||
);
|
||||
} else if (bytes === 0) {
|
||||
try {
|
||||
unlinkSync(dest);
|
||||
} catch {}
|
||||
reject(new Error("empty download"));
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
file.on("error", (err) => {
|
||||
if (timer) clearTimeout(timer);
|
||||
try {
|
||||
unlinkSync(dest);
|
||||
} catch {}
|
||||
reject(err);
|
||||
});
|
||||
} else {
|
||||
reject(new Error(`Failed to download: HTTP ${status}`));
|
||||
}
|
||||
});
|
||||
|
||||
req.on('error', (err) => {
|
||||
try { unlinkSync(dest); } catch {}
|
||||
reject(err);
|
||||
});
|
||||
req.on("error", (err) => {
|
||||
try {
|
||||
unlinkSync(dest);
|
||||
} catch {}
|
||||
reject(err);
|
||||
});
|
||||
|
||||
req.setTimeout(120000, () => {
|
||||
req.destroy(new Error('download timed out'));
|
||||
});
|
||||
};
|
||||
req.setTimeout(120000, () => {
|
||||
req.destroy(new Error("download timed out"));
|
||||
});
|
||||
};
|
||||
|
||||
attempt(url, maxRedirects);
|
||||
});
|
||||
attempt(url, maxRedirects);
|
||||
});
|
||||
|
||||
let attemptNum = 0;
|
||||
while (true) {
|
||||
@@ -235,22 +302,38 @@ function validateDownloadedBinary(p) {
|
||||
try {
|
||||
const st = statSync(p);
|
||||
if (!st.isFile() || st.size === 0) {
|
||||
return { ok: false, reason: 'empty or not a regular file' };
|
||||
return { ok: false, reason: "empty or not a regular file" };
|
||||
}
|
||||
const fd = openSync(p, 'r');
|
||||
const fd = openSync(p, "r");
|
||||
try {
|
||||
const buf = Buffer.alloc(4);
|
||||
const n = readSync(fd, buf, 0, 4, 0);
|
||||
if (n < 2) return { ok: false, reason: 'too short' };
|
||||
if (n < 2) return { ok: false, reason: "too short" };
|
||||
const plt = platform();
|
||||
if (plt === 'win32') {
|
||||
if (!(buf[0] === 0x4d && buf[1] === 0x5a)) return { ok: false, reason: 'invalid PE header (missing MZ)' };
|
||||
} else if (plt === 'linux' || plt === 'android') {
|
||||
if (!(buf[0] === 0x7f && buf[1] === 0x45 && buf[2] === 0x4c && buf[3] === 0x46)) return { ok: false, reason: 'invalid ELF header' };
|
||||
} else if (plt === 'darwin') {
|
||||
const isMachO = (buf[0] === 0xcf && buf[1] === 0xfa && buf[2] === 0xed && buf[3] === 0xfe) ||
|
||||
(buf[0] === 0xca && buf[1] === 0xfe && buf[2] === 0xba && buf[3] === 0xbe);
|
||||
if (!isMachO) return { ok: false, reason: 'invalid Mach-O header' };
|
||||
if (plt === "win32") {
|
||||
if (!(buf[0] === 0x4d && buf[1] === 0x5a))
|
||||
return { ok: false, reason: "invalid PE header (missing MZ)" };
|
||||
} else if (plt === "linux" || plt === "android") {
|
||||
if (
|
||||
!(
|
||||
buf[0] === 0x7f &&
|
||||
buf[1] === 0x45 &&
|
||||
buf[2] === 0x4c &&
|
||||
buf[3] === 0x46
|
||||
)
|
||||
)
|
||||
return { ok: false, reason: "invalid ELF header" };
|
||||
} else if (plt === "darwin") {
|
||||
const isMachO =
|
||||
(buf[0] === 0xcf &&
|
||||
buf[1] === 0xfa &&
|
||||
buf[2] === 0xed &&
|
||||
buf[3] === 0xfe) ||
|
||||
(buf[0] === 0xca &&
|
||||
buf[1] === 0xfe &&
|
||||
buf[2] === 0xba &&
|
||||
buf[3] === 0xbe);
|
||||
if (!isMachO) return { ok: false, reason: "invalid Mach-O header" };
|
||||
}
|
||||
return { ok: true };
|
||||
} finally {
|
||||
@@ -263,32 +346,35 @@ function validateDownloadedBinary(p) {
|
||||
|
||||
export async function runPostinstall(options = {}) {
|
||||
const { skipGlobalAlias = false, invokedByRuntime = false } = options;
|
||||
if (process.env.DEV_POSTINSTALL_DRY_RUN === '1') {
|
||||
if (process.env.DEV_POSTINSTALL_DRY_RUN === "1") {
|
||||
return { skipped: true };
|
||||
}
|
||||
|
||||
if (invokedByRuntime) {
|
||||
process.env.DEV_RUNTIME_POSTINSTALL = process.env.DEV_RUNTIME_POSTINSTALL || '1';
|
||||
process.env.DEV_RUNTIME_POSTINSTALL =
|
||||
process.env.DEV_RUNTIME_POSTINSTALL || "1";
|
||||
}
|
||||
|
||||
const ua = process.env.npm_config_user_agent || '';
|
||||
const isNpx = ua.includes('npx');
|
||||
const isGlobal = process.env.npm_config_global === 'true';
|
||||
const ua = process.env.npm_config_user_agent || "";
|
||||
const isNpx = ua.includes("npx");
|
||||
const isGlobal = process.env.npm_config_global === "true";
|
||||
|
||||
const targetTriple = getTargetTriple();
|
||||
const isWindows = platform() === 'win32';
|
||||
const binaryExt = isWindows ? '.exe' : '';
|
||||
const isWindows = platform() === "win32";
|
||||
const binaryExt = isWindows ? ".exe" : "";
|
||||
|
||||
const binDir = join(__dirname, 'bin');
|
||||
const binDir = join(__dirname, "bin");
|
||||
if (!existsSync(binDir)) {
|
||||
mkdirSync(binDir, { recursive: true });
|
||||
}
|
||||
|
||||
const packageJson = JSON.parse(readFileSync(join(__dirname, 'package.json'), 'utf8'));
|
||||
const packageJson = JSON.parse(
|
||||
readFileSync(join(__dirname, "package.json"), "utf8"),
|
||||
);
|
||||
const version = packageJson.version;
|
||||
|
||||
// The release produces 'code-*' binaries; we'll download and rename to 'dev-*'
|
||||
const binaries = ['code'];
|
||||
const binaries = ["code"];
|
||||
|
||||
console.log(`Installing @hanzo/dev v${version} for ${targetTriple}...`);
|
||||
|
||||
@@ -304,11 +390,22 @@ export async function runPostinstall(options = {}) {
|
||||
const valid = validateDownloadedBinary(cachePath);
|
||||
if (valid.ok) {
|
||||
const wsl = isWSL();
|
||||
const binDirReal = (() => { try { return realpathSync(binDir); } catch { return binDir; } })();
|
||||
const mirrorToLocal = !(isWindows || (wsl && isPathOnWindowsFs(binDirReal)));
|
||||
const binDirReal = (() => {
|
||||
try {
|
||||
return realpathSync(binDir);
|
||||
} catch {
|
||||
return binDir;
|
||||
}
|
||||
})();
|
||||
const mirrorToLocal = !(
|
||||
isWindows ||
|
||||
(wsl && isPathOnWindowsFs(binDirReal))
|
||||
);
|
||||
if (mirrorToLocal) {
|
||||
copyFileSync(cachePath, localPath);
|
||||
try { chmodSync(localPath, 0o755); } catch {}
|
||||
try {
|
||||
chmodSync(localPath, 0o755);
|
||||
} catch {}
|
||||
}
|
||||
console.log(`✓ ${devBinaryName} ready from user cache`);
|
||||
continue;
|
||||
@@ -322,13 +419,16 @@ export async function runPostinstall(options = {}) {
|
||||
const require = createRequire(import.meta.url);
|
||||
const platformPkg = (() => {
|
||||
const name = (() => {
|
||||
if (isWindows) return '@hanzo/dev-win32-x64';
|
||||
if (isWindows) return "@hanzo/dev-win32-x64";
|
||||
const plt = platform();
|
||||
const cpu = arch();
|
||||
if (plt === 'darwin' && cpu === 'arm64') return '@hanzo/dev-darwin-arm64';
|
||||
if (plt === 'darwin' && cpu === 'x64') return '@hanzo/dev-darwin-x64';
|
||||
if (plt === 'linux' && cpu === 'x64') return '@hanzo/dev-linux-x64-musl';
|
||||
if (plt === 'linux' && cpu === 'arm64') return '@hanzo/dev-linux-arm64-musl';
|
||||
if (plt === "darwin" && cpu === "arm64")
|
||||
return "@hanzo/dev-darwin-arm64";
|
||||
if (plt === "darwin" && cpu === "x64") return "@hanzo/dev-darwin-x64";
|
||||
if (plt === "linux" && cpu === "x64")
|
||||
return "@hanzo/dev-linux-x64-musl";
|
||||
if (plt === "linux" && cpu === "arm64")
|
||||
return "@hanzo/dev-linux-arm64-musl";
|
||||
return null;
|
||||
})();
|
||||
if (!name) return null;
|
||||
@@ -343,81 +443,155 @@ export async function runPostinstall(options = {}) {
|
||||
|
||||
if (platformPkg) {
|
||||
try {
|
||||
const src = join(platformPkg.dir, 'bin', devBinaryName);
|
||||
const src = join(platformPkg.dir, "bin", devBinaryName);
|
||||
if (!existsSync(src)) {
|
||||
throw new Error(`platform package missing binary: ${platformPkg.name}`);
|
||||
throw new Error(
|
||||
`platform package missing binary: ${platformPkg.name}`,
|
||||
);
|
||||
}
|
||||
await writeCacheAtomic(src, cachePath);
|
||||
const wsl = isWSL();
|
||||
const binDirReal = (() => { try { return realpathSync(binDir); } catch { return binDir; } })();
|
||||
const mirrorToLocal = !(isWindows || (wsl && isPathOnWindowsFs(binDirReal)));
|
||||
const binDirReal = (() => {
|
||||
try {
|
||||
return realpathSync(binDir);
|
||||
} catch {
|
||||
return binDir;
|
||||
}
|
||||
})();
|
||||
const mirrorToLocal = !(
|
||||
isWindows ||
|
||||
(wsl && isPathOnWindowsFs(binDirReal))
|
||||
);
|
||||
if (mirrorToLocal) {
|
||||
copyFileSync(cachePath, localPath);
|
||||
try { chmodSync(localPath, 0o755); } catch {}
|
||||
try {
|
||||
chmodSync(localPath, 0o755);
|
||||
} catch {}
|
||||
}
|
||||
console.log(`✓ Installed ${devBinaryName} from ${platformPkg.name} (cached)`);
|
||||
console.log(
|
||||
`✓ Installed ${devBinaryName} from ${platformPkg.name} (cached)`,
|
||||
);
|
||||
continue;
|
||||
} catch (e) {
|
||||
console.warn(`⚠ Failed platform package install (${e.message}), falling back to GitHub download`);
|
||||
console.warn(
|
||||
`⚠ Failed platform package install (${e.message}), falling back to GitHub download`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Download from GitHub release
|
||||
const isWin = isWindows;
|
||||
const detectedWSL = isWSL();
|
||||
const binDirReal = (() => { try { return realpathSync(binDir); } catch { return binDir; } })();
|
||||
const mirrorToLocal = !(isWin || (detectedWSL && isPathOnWindowsFs(binDirReal)));
|
||||
const binDirReal = (() => {
|
||||
try {
|
||||
return realpathSync(binDir);
|
||||
} catch {
|
||||
return binDir;
|
||||
}
|
||||
})();
|
||||
const mirrorToLocal = !(
|
||||
isWin ||
|
||||
(detectedWSL && isPathOnWindowsFs(binDirReal))
|
||||
);
|
||||
let useZst = false;
|
||||
if (!isWin) {
|
||||
try {
|
||||
execSync('zstd --version', { stdio: 'ignore', shell: true });
|
||||
execSync("zstd --version", { stdio: "ignore", shell: true });
|
||||
useZst = true;
|
||||
} catch {
|
||||
useZst = false;
|
||||
}
|
||||
}
|
||||
const archiveName = isWin ? `${binaryName}.zip` : (useZst ? `${binaryName}.zst` : `${binaryName}.tar.gz`);
|
||||
const archiveName = isWin
|
||||
? `${binaryName}.zip`
|
||||
: useZst
|
||||
? `${binaryName}.zst`
|
||||
: `${binaryName}.tar.gz`;
|
||||
const downloadUrl = `https://github.com/hanzoai/dev/releases/download/v${version}/${archiveName}`;
|
||||
|
||||
console.log(`Downloading ${archiveName}...`);
|
||||
try {
|
||||
const needsIsolation = isWin || (!isWin && !mirrorToLocal);
|
||||
let safeTempDir = needsIsolation ? join(tmpdir(), 'hanzo', 'dev', version) : binDir;
|
||||
let safeTempDir = needsIsolation
|
||||
? join(tmpdir(), "hanzo", "dev", version)
|
||||
: binDir;
|
||||
if (needsIsolation) {
|
||||
try {
|
||||
if (!existsSync(safeTempDir)) mkdirSync(safeTempDir, { recursive: true });
|
||||
if (!existsSync(safeTempDir))
|
||||
mkdirSync(safeTempDir, { recursive: true });
|
||||
} catch {
|
||||
try {
|
||||
safeTempDir = getCacheDir(version);
|
||||
if (!existsSync(safeTempDir)) mkdirSync(safeTempDir, { recursive: true });
|
||||
if (!existsSync(safeTempDir))
|
||||
mkdirSync(safeTempDir, { recursive: true });
|
||||
} catch {}
|
||||
}
|
||||
}
|
||||
const tmpPath = join(needsIsolation ? safeTempDir : binDir, `.${archiveName}.part`);
|
||||
const tmpPath = join(
|
||||
needsIsolation ? safeTempDir : binDir,
|
||||
`.${archiveName}.part`,
|
||||
);
|
||||
await downloadBinary(downloadUrl, tmpPath);
|
||||
|
||||
if (isWin) {
|
||||
const unzipDest = safeTempDir;
|
||||
try {
|
||||
const sysRoot = process.env.SystemRoot || process.env.windir || 'C:\\Windows';
|
||||
const psFull = join(sysRoot, 'System32', 'WindowsPowerShell', 'v1.0', 'powershell.exe');
|
||||
const sysRoot =
|
||||
process.env.SystemRoot || process.env.windir || "C:\\Windows";
|
||||
const psFull = join(
|
||||
sysRoot,
|
||||
"System32",
|
||||
"WindowsPowerShell",
|
||||
"v1.0",
|
||||
"powershell.exe",
|
||||
);
|
||||
const psCmd = `Expand-Archive -Path '${tmpPath}' -DestinationPath '${unzipDest}' -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 "${tmpPath}" -C "${unzipDest}"`, { stdio: 'ignore', shell: true }); }
|
||||
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 "${tmpPath}" -C "${unzipDest}"`, {
|
||||
stdio: "ignore",
|
||||
shell: true,
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
throw new Error(`failed to unzip archive: ${e.message}`);
|
||||
} finally {
|
||||
try { unlinkSync(tmpPath); } catch {}
|
||||
try {
|
||||
unlinkSync(tmpPath);
|
||||
} catch {}
|
||||
}
|
||||
try {
|
||||
const extractedPath = join(unzipDest, binaryName);
|
||||
// Rename code-* to dev-* in cache
|
||||
const devCachePath = cachePath;
|
||||
await writeCacheAtomic(extractedPath, devCachePath);
|
||||
try { unlinkSync(extractedPath); } catch {}
|
||||
try {
|
||||
unlinkSync(extractedPath);
|
||||
} catch {}
|
||||
} catch (e) {
|
||||
throw new Error(`failed to move binary to cache: ${e.message}`);
|
||||
}
|
||||
@@ -425,20 +599,36 @@ export async function runPostinstall(options = {}) {
|
||||
const downloadedPath = join(binDir, binaryName);
|
||||
if (useZst) {
|
||||
try {
|
||||
execSync(`zstd -d '${tmpPath}' -o '${downloadedPath}'`, { stdio: 'ignore', shell: true });
|
||||
execSync(`zstd -d '${tmpPath}' -o '${downloadedPath}'`, {
|
||||
stdio: "ignore",
|
||||
shell: true,
|
||||
});
|
||||
} catch (e) {
|
||||
try { unlinkSync(tmpPath); } catch {}
|
||||
throw new Error(`failed to decompress .zst (need zstd CLI): ${e.message}`);
|
||||
try {
|
||||
unlinkSync(tmpPath);
|
||||
} catch {}
|
||||
throw new Error(
|
||||
`failed to decompress .zst (need zstd CLI): ${e.message}`,
|
||||
);
|
||||
}
|
||||
try { unlinkSync(tmpPath); } catch {}
|
||||
try {
|
||||
unlinkSync(tmpPath);
|
||||
} catch {}
|
||||
} else {
|
||||
try {
|
||||
execSync(`tar -xzf '${tmpPath}' -C '${binDir}'`, { stdio: 'ignore', shell: true });
|
||||
execSync(`tar -xzf '${tmpPath}' -C '${binDir}'`, {
|
||||
stdio: "ignore",
|
||||
shell: true,
|
||||
});
|
||||
} catch (e) {
|
||||
try { unlinkSync(tmpPath); } catch {}
|
||||
try {
|
||||
unlinkSync(tmpPath);
|
||||
} catch {}
|
||||
throw new Error(`failed to extract .tar.gz: ${e.message}`);
|
||||
}
|
||||
try { unlinkSync(tmpPath); } catch {}
|
||||
try {
|
||||
unlinkSync(tmpPath);
|
||||
} catch {}
|
||||
}
|
||||
// Rename code-* to dev-*
|
||||
if (existsSync(downloadedPath)) {
|
||||
@@ -447,14 +637,22 @@ export async function runPostinstall(options = {}) {
|
||||
} else {
|
||||
const extractedPath = downloadedPath;
|
||||
await writeCacheAtomic(extractedPath, cachePath);
|
||||
try { unlinkSync(extractedPath); } catch {}
|
||||
try {
|
||||
unlinkSync(extractedPath);
|
||||
} catch {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const valid = validateDownloadedBinary(isWin ? cachePath : (mirrorToLocal ? localPath : cachePath));
|
||||
const valid = validateDownloadedBinary(
|
||||
isWin ? cachePath : mirrorToLocal ? localPath : cachePath,
|
||||
);
|
||||
if (!valid.ok) {
|
||||
try { (isWin || !mirrorToLocal) ? unlinkSync(cachePath) : unlinkSync(localPath); } catch {}
|
||||
try {
|
||||
isWin || !mirrorToLocal
|
||||
? unlinkSync(cachePath)
|
||||
: unlinkSync(localPath);
|
||||
} catch {}
|
||||
throw new Error(`invalid binary (${valid.reason})`);
|
||||
}
|
||||
|
||||
@@ -462,9 +660,13 @@ export async function runPostinstall(options = {}) {
|
||||
chmodSync(localPath, 0o755);
|
||||
}
|
||||
|
||||
console.log(`✓ Installed ${devBinaryName}${(isWin || !mirrorToLocal) ? ' (cached)' : ''}`);
|
||||
console.log(
|
||||
`✓ Installed ${devBinaryName}${isWin || !mirrorToLocal ? " (cached)" : ""}`,
|
||||
);
|
||||
if (!isWin && mirrorToLocal) {
|
||||
try { await writeCacheAtomic(localPath, cachePath); } catch {}
|
||||
try {
|
||||
await writeCacheAtomic(localPath, cachePath);
|
||||
} catch {}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`✗ Failed to install ${devBinaryName}: ${error.message}`);
|
||||
@@ -475,23 +677,35 @@ export async function runPostinstall(options = {}) {
|
||||
const mainBinary = `dev-${targetTriple}${binaryExt}`;
|
||||
const mainBinaryPath = join(binDir, mainBinary);
|
||||
|
||||
if (existsSync(mainBinaryPath) || existsSync(getCachedBinaryPath(version, targetTriple, platform() === 'win32'))) {
|
||||
if (
|
||||
existsSync(mainBinaryPath) ||
|
||||
existsSync(
|
||||
getCachedBinaryPath(version, targetTriple, platform() === "win32"),
|
||||
)
|
||||
) {
|
||||
try {
|
||||
const probePath = existsSync(mainBinaryPath) ? mainBinaryPath : getCachedBinaryPath(version, targetTriple, platform() === 'win32');
|
||||
const probePath = existsSync(mainBinaryPath)
|
||||
? mainBinaryPath
|
||||
: getCachedBinaryPath(version, targetTriple, platform() === "win32");
|
||||
const stats = statSync(probePath);
|
||||
if (!stats.size) throw new Error('binary is empty (download likely failed)');
|
||||
if (!stats.size)
|
||||
throw new Error("binary is empty (download likely failed)");
|
||||
const valid = validateDownloadedBinary(probePath);
|
||||
if (!valid.ok) {
|
||||
console.warn(`⚠ Main dev binary appears invalid: ${valid.reason}`);
|
||||
console.warn(' Try reinstalling or check your network/proxy settings.');
|
||||
console.warn(
|
||||
" Try reinstalling or check your network/proxy settings.",
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn(`⚠ Main dev binary appears invalid: ${e.message}`);
|
||||
console.warn(' Try reinstalling or check your network/proxy settings.');
|
||||
console.warn(" Try reinstalling or check your network/proxy settings.");
|
||||
}
|
||||
console.log('✓ Installation complete!');
|
||||
console.log("✓ Installation complete!");
|
||||
} else {
|
||||
console.warn('⚠ Main dev binary not found. You may need to build from source.');
|
||||
console.warn(
|
||||
"⚠ Main dev binary not found. You may need to build from source.",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -506,8 +720,8 @@ function isExecutedDirectly() {
|
||||
}
|
||||
|
||||
if (isExecutedDirectly()) {
|
||||
runPostinstall().catch(error => {
|
||||
console.error('Installation failed:', error);
|
||||
runPostinstall().catch((error) => {
|
||||
console.error("Installation failed:", error);
|
||||
process.exit(1);
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user