mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-01 21:02:12 +03:00
Layer 3: Dockerfile COPY .next/standalone -> .build/next/standalone (+cache mount); electron stage -> .build/electron-standalone + extraResources; CI asserts dist/server.js; eslint ignores .build/**+dist/**. Layer 2: deploy-vps-* skills use build:release + rsync(dist) -> remote app/ + pm2 restart + BUILD_SHA verify (drop npm-i-g/legacy-peer-deps/manual-wreq). Docs (RELEASE_CHECKLIST/CONTRIBUTING/AGENTS/CHANGELOG) describe src/+.build/+dist/ layout. Verified: docker build exit 0 + health 200; electron stage OK; no stale build-output refs.
118 lines
3.8 KiB
JavaScript
118 lines
3.8 KiB
JavaScript
#!/usr/bin/env node
|
|
|
|
import { existsSync, lstatSync, readdirSync, rmSync } from "node:fs";
|
|
import { basename, dirname, join, relative } from "node:path";
|
|
import { fileURLToPath } from "node:url";
|
|
import { assembleStandalone } from "./assembleStandalone.mjs";
|
|
|
|
const __filename = fileURLToPath(import.meta.url);
|
|
const __dirname = dirname(__filename);
|
|
const ROOT = join(__dirname, "..", "..");
|
|
|
|
const NEXT_DIST_DIR = process.env.NEXT_DIST_DIR || ".build/next";
|
|
const DIST_DIR = join(ROOT, NEXT_DIST_DIR);
|
|
const STANDALONE_DIR = join(DIST_DIR, "standalone");
|
|
const ELECTRON_STANDALONE_DIR = join(ROOT, ".build", "electron-standalone");
|
|
|
|
// --- Electron-UNIQUE: resolve the nested server.js location ----------------
|
|
|
|
function resolveStandaloneBundleDir() {
|
|
const directServer = join(STANDALONE_DIR, "server.js");
|
|
if (existsSync(directServer)) {
|
|
return STANDALONE_DIR;
|
|
}
|
|
|
|
const nestedCandidates = [
|
|
join(STANDALONE_DIR, "projects", "OmniRoute"),
|
|
join(STANDALONE_DIR, basename(ROOT)),
|
|
];
|
|
|
|
for (const candidate of nestedCandidates) {
|
|
if (existsSync(join(candidate, "server.js"))) {
|
|
return candidate;
|
|
}
|
|
}
|
|
|
|
throw new Error(
|
|
`Standalone server bundle not found in ${STANDALONE_DIR}. Run \`npm run build\` first.`
|
|
);
|
|
}
|
|
|
|
// --- Electron-UNIQUE: symlink guard (electron-builder fails on symlinked node_modules) ---
|
|
|
|
function assertBundleIsPackagable(bundleDir) {
|
|
const nodeModulesPath = join(bundleDir, "node_modules");
|
|
if (!existsSync(nodeModulesPath)) return;
|
|
|
|
if (lstatSync(nodeModulesPath).isSymbolicLink()) {
|
|
throw new Error(
|
|
[
|
|
"Next standalone emitted app/node_modules as a symlink.",
|
|
"electron-builder preserves extraResources symlinks, which would make the packaged app",
|
|
"depend on the original build machine path at runtime.",
|
|
"",
|
|
`Offending path: ${nodeModulesPath}`,
|
|
"Use a real node_modules directory in the build worktree before packaging Electron.",
|
|
].join("\n")
|
|
);
|
|
}
|
|
}
|
|
|
|
// --- Electron-UNIQUE: strip generated electron artifacts from staged dir ---
|
|
|
|
function removeGeneratedElectronArtifacts() {
|
|
const generatedDirs = [join(ELECTRON_STANDALONE_DIR, "electron", "dist-electron")];
|
|
|
|
for (const dir of generatedDirs) {
|
|
rmSync(dir, { recursive: true, force: true });
|
|
}
|
|
}
|
|
|
|
// --- Electron-UNIQUE: remove native modules for electron-builder ABI rebuild ---
|
|
|
|
function removeNativeModules(baseDir) {
|
|
if (!existsSync(baseDir)) return;
|
|
const dirs = readdirSync(baseDir);
|
|
for (const dir of dirs) {
|
|
if (dir.startsWith("better-sqlite3") || dir.startsWith("keytar")) {
|
|
const fullPath = join(baseDir, dir);
|
|
rmSync(fullPath, { recursive: true, force: true });
|
|
}
|
|
}
|
|
}
|
|
|
|
function logContextualError(error) {
|
|
const message = error instanceof Error ? error.message : String(error);
|
|
console.error(`[electron] failed to prepare standalone bundle: ${message}`);
|
|
process.exitCode = 1;
|
|
}
|
|
|
|
process.on("uncaughtException", logContextualError);
|
|
|
|
// Resolve the bundle dir (handles nested project layout) and check for symlinks
|
|
const bundleDir = resolveStandaloneBundleDir();
|
|
assertBundleIsPackagable(bundleDir);
|
|
|
|
// Clean the stage dir before assembly
|
|
rmSync(ELECTRON_STANDALONE_DIR, { recursive: true, force: true });
|
|
|
|
// Shared assembly: standalone copy + .next/static + public + abs-path sanitization + natives/@swc/helpers
|
|
assembleStandalone({
|
|
distDir: DIST_DIR,
|
|
outDir: ELECTRON_STANDALONE_DIR,
|
|
projectRoot: ROOT,
|
|
sanitizePaths: true,
|
|
copyNatives: true,
|
|
});
|
|
|
|
// Electron-UNIQUE post-assembly steps
|
|
removeGeneratedElectronArtifacts();
|
|
|
|
// Strip better-sqlite3 and keytar so electron-builder rebuilds them against Electron ABI
|
|
removeNativeModules(join(ELECTRON_STANDALONE_DIR, "node_modules"));
|
|
removeNativeModules(join(ELECTRON_STANDALONE_DIR, ".next", "node_modules"));
|
|
|
|
console.log(
|
|
`[electron] prepared standalone bundle: ${relative(ROOT, ELECTRON_STANDALONE_DIR) || "."}`
|
|
);
|