farmcontrol-server/scripts/expand-macos-bundle.mjs
Tom Butcher 3107a517c3
Some checks failed
farmcontrol/farmcontrol-server/pipeline/head There was a failure building this commit
Refactor zstd decompression in expand-macos-bundle script
- Replaced the findZstd function with a new decompressTarZst function to streamline the zstd decompression process.
- Improved error handling by logging an error message and exiting the process if zstd is not found.
- Updated the script to directly call the new decompression function, enhancing clarity and maintainability.
2026-08-01 21:39:47 +01:00

143 lines
4.4 KiB
JavaScript

import {
cpSync,
existsSync,
mkdirSync,
readFileSync,
readdirSync,
rmSync,
} from "node:fs";
import path from "node:path";
import { spawnSync } from "node:child_process";
import { tmpdir } from "node:os";
import { fileURLToPath } from "node:url";
const rootDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
const buildEnv = process.env.ELECTROBUN_BUILD_ENV || "dev";
const targetOs = process.env.ELECTROBUN_OS || "macos";
const wrapperPath = process.env.ELECTROBUN_WRAPPER_BUNDLE_PATH;
if (buildEnv === "dev" || targetOs !== "macos") {
console.log("expand-macos-bundle: skipping (dev or non-macOS build)");
process.exit(0);
}
if (!wrapperPath || !existsSync(wrapperPath)) {
console.error("expand-macos-bundle: ELECTROBUN_WRAPPER_BUNDLE_PATH not set or missing");
process.exit(1);
}
const resolvedWrapperPath = path.resolve(wrapperPath);
const contentsPath = path.join(resolvedWrapperPath, "Contents");
const resourcesPath = path.join(contentsPath, "Resources");
const metadataPath = path.join(resourcesPath, "metadata.json");
if (!existsSync(metadataPath)) {
console.log("expand-macos-bundle: no extractor metadata, assuming already expanded");
process.exit(0);
}
const metadata = JSON.parse(readFileSync(metadataPath, "utf8"));
const hash = metadata.hash;
const appName = metadata.name || "Farm Control Server";
const tarZstPath = path.resolve(resourcesPath, `${hash}.tar.zst`);
if (!existsSync(tarZstPath)) {
console.log(`expand-macos-bundle: ${hash}.tar.zst not found, skipping`);
process.exit(0);
}
function decompressTarZst(inputPath, outputPath) {
const systemZstd = spawnSync("zstd", ["--version"], { encoding: "utf8" });
if (systemZstd.status === 0) {
return spawnSync("zstd", ["-d", "-f", "-o", outputPath, inputPath], {
stdio: "inherit",
});
}
const hostArch = process.arch === "arm64" ? "arm64" : "x64";
const zigZstd = path.join(
rootDir,
"node_modules/electrobun/dist-macos-" + hostArch,
"zig-zstd",
);
if (existsSync(zigZstd)) {
return spawnSync(
zigZstd,
["decompress", "-i", inputPath, "-o", outputPath, "--no-timing"],
{ stdio: "inherit" },
);
}
console.error(
"expand-macos-bundle: zstd not found (install zstd or use electrobun dist binaries)",
);
process.exit(1);
}
const workDir = path.join(tmpdir(), `farmcontrol-server-expand-${hash}`);
const tarPath = path.join(workDir, `${hash}.tar`);
rmSync(workDir, { recursive: true, force: true });
mkdirSync(workDir, { recursive: true });
const decompress = decompressTarZst(tarZstPath, tarPath);
if (decompress.status !== 0) {
console.error("expand-macos-bundle: zstd decompression failed");
process.exit(decompress.status ?? 1);
}
const extractTar = spawnSync("tar", ["-xf", tarPath, "-C", workDir], {
stdio: "inherit",
});
if (extractTar.status !== 0) {
console.error("expand-macos-bundle: tar extraction failed");
process.exit(extractTar.status ?? 1);
}
const innerAppPath = path.join(workDir, `${appName}.app`);
if (!existsSync(innerAppPath)) {
const appBundle = readdirSync(workDir).find((entry) => entry.endsWith(".app"));
if (!appBundle) {
console.error("expand-macos-bundle: extracted app bundle not found");
process.exit(1);
}
}
const resolvedInnerApp = existsSync(innerAppPath)
? innerAppPath
: path.join(workDir, readdirSync(workDir).find((e) => e.endsWith(".app")));
const innerContentsPath = path.join(resolvedInnerApp, "Contents");
function replaceDirectory(sourceDir, destinationDir) {
rmSync(destinationDir, { recursive: true, force: true });
cpSync(sourceDir, destinationDir, { recursive: true, dereference: true });
}
replaceDirectory(
path.join(innerContentsPath, "MacOS"),
path.join(contentsPath, "MacOS"),
);
rmSync(resourcesPath, { recursive: true, force: true });
cpSync(path.join(innerContentsPath, "Resources"), resourcesPath, {
recursive: true,
dereference: true,
});
const innerFrameworks = path.join(innerContentsPath, "Frameworks");
const wrapperFrameworks = path.join(contentsPath, "Frameworks");
if (existsSync(innerFrameworks)) {
replaceDirectory(innerFrameworks, wrapperFrameworks);
}
cpSync(path.join(innerContentsPath, "Info.plist"), path.join(contentsPath, "Info.plist"));
rmSync(workDir, { recursive: true, force: true });
if (process.platform === "darwin") {
spawnSync("xattr", ["-cr", resolvedWrapperPath], { stdio: "inherit" });
}
console.log(`expand-macos-bundle: pre-expanded ${resolvedWrapperPath}`);