Files
OmniRoute/scripts/build/prepare-electron-standalone.mjs
dependabot[bot] 067d0d6ed7 chore(deps): bump github/codeql-action/analyze from 4.36.3 to 4.37.0 (#6831)
* fix(electron): bump electron 42→43 + build better-sqlite3 from source (ABI 148) (#6605)

fix(electron): bump electron 42→43 + rebuild better-sqlite3 from source against the Electron ABI (148).

Electron 43 raises NODE_MODULE_VERSION to 148; better-sqlite3@12.11.1 has no electron-v148 prebuild, so the packaged app died with 'Nenhum driver SQLite disponível'. prepare-electron-standalone now compiles better-sqlite3 from source against the electron headers into build/Release (where 'bindings' resolves it). Validated by Electron Package Smoke (green) + local (node_register_module_v148).

Supersedes #6378. (--admin: the only reds are SonarQube/SonarCloud failing on a coverage-report artifact digest-mismatch — a GitHub Actions infra flake, not this diff; Sonar is green on main and the diff touches only the electron build.)

* deps: bump the development group across 1 directory with 6 updates (#6588)

deps: bump the development group (6 updates). Rebased onto current main; all checks green after the electron-smoke fix (#6605).

* fix(proxy): force CONNECT tunnel for HTTP proxied requests (undici 8.7) + production deps bump (#6620)

fix(proxy): force CONNECT tunnel for HTTP proxied requests (undici 8.7) + production deps bump.

undici 8.6+ changed ProxyAgent to forward plain-HTTP via request-proxy instead of CONNECT, breaking OAuth refresh through a connection proxy (501). proxyDispatcher now passes proxyTunnel:true. Validated: Unit Tests 3/8 (the OAuth-proxy test) green, new regression test green (fails without the fix on undici 8.7), SonarQube green.

Supersedes #6380. (--admin: the only red is Electron Package Smoke failing on a next-build artifact 'digest-mismatch' — a GitHub Actions infra flake corrupting the asar ('file data stream has unexpected number of bytes'); the better-sqlite3 rebuild itself succeeded (gyp ok) and the electron path is unchanged from #6605 which passed the smoke. Not this diff.)

* chore(deps): bump github/codeql-action/analyze from 4.36.3 to 4.37.0

Bumps [github/codeql-action/analyze](https://github.com/github/codeql-action) from 4.36.3 to 4.37.0.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](54f647b7e1...99df26d4f1)

---
updated-dependencies:
- dependency-name: github/codeql-action/analyze
  dependency-version: 4.37.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: Diego Rodrigues de Sa e Souza <8016841+diegosouzapw@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-12 01:58:43 -03:00

196 lines
7.5 KiB
JavaScript

#!/usr/bin/env node
import { cpSync, existsSync, lstatSync, readFileSync, readdirSync, rmSync } from "node:fs";
import { basename, dirname, join, relative } from "node:path";
import { fileURLToPath } from "node:url";
import { spawnSync } from "node:child_process";
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, prefixes = ["keytar"]) {
if (!existsSync(baseDir)) return;
const dirs = readdirSync(baseDir);
for (const dir of dirs) {
if (prefixes.some((p) => dir.startsWith(p))) {
const fullPath = join(baseDir, dir);
rmSync(fullPath, { recursive: true, force: true });
}
}
}
// --- Electron-UNIQUE: rebuild better-sqlite3 against the Electron ABI --------
//
// The `npm ci` at the repo root compiles better-sqlite3 for the CI *Node* ABI
// (e.g. 137 for Node 24). The packaged app runs its Next.js server via
// ELECTRON_RUN_AS_NODE, so it needs the *Electron* ABI (146 for electron 42,
// 148 for electron 43). We cannot rely on electron-builder's @electron/rebuild
// here: it searches `electron/node_modules` (where better-sqlite3 does not live)
// and, with the default prebuild path, tries to fetch a prebuilt binary — but
// better-sqlite3@12.11.1 only ships prebuilds up to electron-v146, so electron
// 43 (v148) silently gets no rebuild and the app dies with "Nenhum driver
// SQLite disponível — better-sqlite3 (falhou)".
//
// Instead we copy the *full* module (source + binding.gyp) from the root into
// the standalone and compile it from source against the Electron headers, so
// `bindings` finds a correct build/Release/better_sqlite3.node regardless of
// prebuild availability. Robust to any current/future electron version.
function readElectronVersion() {
const pkg = JSON.parse(readFileSync(join(ROOT, "electron", "package.json"), "utf8"));
const raw = pkg.devDependencies?.electron || pkg.dependencies?.electron || "";
return String(raw).replace(/^[\^~]/, "");
}
function rebuildBetterSqlite3ForElectron(standaloneNodeModules) {
const srcMod = join(ROOT, "node_modules", "better-sqlite3");
if (!existsSync(srcMod)) {
console.warn("[electron] better-sqlite3 not found at repo root — skipping ABI rebuild.");
return;
}
const electronVersion = readElectronVersion();
if (!electronVersion) {
throw new Error("[electron] could not resolve electron version for better-sqlite3 rebuild.");
}
const destMod = join(standaloneNodeModules, "better-sqlite3");
// copyNatives only copies build/; we need the full module (src + binding.gyp)
// to compile from source. Overwrite the copied Node-ABI build in the process.
cpSync(srcMod, destMod, { recursive: true, force: true });
rmSync(join(destMod, "build"), { recursive: true, force: true });
console.log(`[electron] rebuilding better-sqlite3 against electron ${electronVersion} ABI…`);
const result = spawnSync(
process.platform === "win32" ? "npx.cmd" : "npx",
["--yes", "node-gyp", "rebuild"],
{
cwd: destMod,
stdio: "inherit",
// Compile against the Electron headers (not Node's) so the .node lands in
// build/Release with the Electron NODE_MODULE_VERSION. No shell interpolation.
env: {
...process.env,
npm_config_runtime: "electron",
npm_config_target: electronVersion,
npm_config_disturl: "https://electronjs.org/headers",
npm_config_arch: process.arch,
npm_config_build_from_source: "true",
},
}
);
if (result.status !== 0) {
throw new Error(
`[electron] better-sqlite3 rebuild against electron ${electronVersion} failed (exit ${result.status}).`
);
}
// Drop the now-unneeded compile inputs to keep the packaged app lean.
for (const dir of ["deps", "src", "build/Debug", "build/obj.target"]) {
rmSync(join(destMod, dir), { 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();
// Rebuild better-sqlite3 from source against the Electron ABI in the primary
// node_modules (where the standalone server resolves it). keytar is still
// stripped so electron-builder's @electron/rebuild handles it (it has electron
// prebuilds); also drop any stray Node-ABI better-sqlite3 under .next/node_modules
// so it cannot shadow the rebuilt one.
rebuildBetterSqlite3ForElectron(join(ELECTRON_STANDALONE_DIR, "node_modules"));
removeNativeModules(join(ELECTRON_STANDALONE_DIR, "node_modules"), ["keytar"]);
removeNativeModules(join(ELECTRON_STANDALONE_DIR, ".next", "node_modules"), [
"better-sqlite3",
"keytar",
]);
console.log(
`[electron] prepared standalone bundle: ${relative(ROOT, ELECTRON_STANDALONE_DIR) || "."}`
);