import { createWriteStream, existsSync, mkdirSync, readdirSync, unlinkSync, } from "node:fs"; import { spawnSync } from "node:child_process"; import path from "node:path"; import { fileURLToPath } from "node:url"; import { getReleaseArch } from "./release-artifact-utils.mjs"; import { fixMacosHeaderpad } from "./fix-macos-headerpad.mjs"; const rootDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), ".."); const electrobunDir = path.join(rootDir, "node_modules/electrobun"); const electrobunVersion = JSON.parse( await Bun.file(path.join(electrobunDir, "package.json")).text(), ).version; function getPlatformPaths(targetOS, targetArch) { const binExt = targetOS === "win" ? ".exe" : ""; const platformDistDir = path.join( electrobunDir, `dist-${targetOS}-${targetArch}`, ); return { platformDistDir, bunBinary: path.join(platformDistDir, "bun") + binExt, bsdiff: path.join(platformDistDir, "bsdiff") + binExt, bspatch: path.join(platformDistDir, "bspatch") + binExt, launcher: path.join(platformDistDir, "launcher") + binExt, nativeWrapperMacos: path.join(platformDistDir, "libNativeWrapper.dylib"), }; } async function downloadFile(url, destination) { const response = await fetch(url); if (!response.ok) { throw new Error(`Failed to download ${url}: ${response.status} ${response.statusText}`); } mkdirSync(path.dirname(destination), { recursive: true }); const fileStream = createWriteStream(destination); const reader = response.body?.getReader(); if (!reader) { throw new Error(`No response body for ${url}`); } while (true) { const { done, value } = await reader.read(); if (done) break; fileStream.write(Buffer.from(value)); } await new Promise((resolve, reject) => { fileStream.end((error) => (error ? reject(error) : resolve())); }); } function extractTarGz(tarPath, destination) { mkdirSync(destination, { recursive: true }); const result = spawnSync("tar", ["-xzf", tarPath, "-C", destination], { stdio: "inherit", }); if (result.status !== 0) { throw new Error(`tar extraction failed for ${tarPath}`); } } async function ensureCoreDependencies(targetOS, targetArch) { const paths = getPlatformPaths(targetOS, targetArch); const required = [ paths.bunBinary, paths.bsdiff, paths.bspatch, paths.launcher, paths.nativeWrapperMacos, ]; if (required.every((filePath) => existsSync(filePath))) { console.log(`ensure-electrobun-core: ${targetOS}-${targetArch} already present`); return; } const platformName = targetOS === "macos" ? "darwin" : targetOS === "win" ? "win" : "linux"; const url = `https://github.com/blackboardsh/electrobun/releases/download/v${electrobunVersion}/electrobun-core-${platformName}-${targetArch}.tar.gz`; const tempFile = path.join( electrobunDir, `core-${targetOS}-${targetArch}-temp.tar.gz`, ); console.log(`ensure-electrobun-core: downloading ${targetOS}-${targetArch}`); console.log(url); await downloadFile(url, tempFile); extractTarGz(tempFile, paths.platformDistDir); if (existsSync(tempFile)) { unlinkSync(tempFile); } const missing = required.filter((filePath) => !existsSync(filePath)); if (missing.length > 0) { const extracted = existsSync(paths.platformDistDir) ? readdirSync(paths.platformDistDir) : []; throw new Error( `Missing ${targetOS}-${targetArch} binaries after extract: ${missing.join(", ")}; extracted=${extracted.join(", ")}`, ); } console.log(`ensure-electrobun-core: ${targetOS}-${targetArch} ready`); } const targetOS = "macos"; const hostArch = getReleaseArch(process.arch); const targetArch = getReleaseArch( process.env.ELECTROBUN_TARGET_ARCH || process.env.ELECTROBUN_FORCE_ARCH || process.arch, ); const archesToEnsure = new Set([hostArch, targetArch]); for (const arch of archesToEnsure) { await ensureCoreDependencies(targetOS, arch); // electrobun#485: x64 core binaries lack Mach-O headerpad and get corrupted // by codesign; free a load-command slot before anything copies or signs them. fixMacosHeaderpad(getPlatformPaths(targetOS, arch).platformDistDir); }