import assert from "node:assert/strict"; import { execFileSync, spawnSync } from "node:child_process"; import { fileURLToPath } from "node:url"; import { existsSync, openSync, closeSync, readFileSync, readSync, readdirSync, rmSync, statSync } from "node:fs"; import { join } from "node:path"; import { listPackage } from "@electron/asar"; import { assertMacIcnsComplete } from "./desktop-icns.mjs"; import { isVisualCppRuntimeDll, readPeImportedDlls } from "./pe-imports.mjs"; import { assertDesktopProductionFiles } from "./desktop-production-files.mjs"; const outputDirectory = join(process.cwd(), "release", "desktop"); const version = JSON.parse( readFileSync(join(process.cwd(), "apps", "desktop", "package.json"), "utf8"), ).version; const walk = (directory) => { if (!existsSync(directory)) return []; const result = []; for (const entry of readdirSync(directory)) { const path = join(directory, entry); if (statSync(path).isDirectory()) result.push(...walk(path)); else result.push(path); } return result; }; const files = walk(outputDirectory); const requestedPlatform = process.env.EDGE_EVER_VERIFY_TARGET ?? process.platform; const requestedArch = process.env.EDGE_EVER_DESKTOP_ARCH ?? process.arch; const listAsarFiles = (asarPath, executable) => { const paths = new Set(listPackage(asarPath).map((path) => path.replaceAll("\\", "/"))); assertDesktopProductionFiles(paths); execFileSync(executable, [fileURLToPath(new URL("./verify-desktop-production-runtime.cjs", import.meta.url)), asarPath], { env: { ...process.env, ELECTRON_RUN_AS_NODE: "1" }, timeout: 150_000, stdio: "inherit", }); return paths; }; const verifyMachOArch = (path, arch, label) => { const result = spawnSync("lipo", ["-archs", path], { encoding: "utf8" }); const expectedArch = arch === "x64" ? "x86_64" : arch; assert.equal(result.status, 0, `${label} architecture inspection failed: ${result.stderr || result.stdout}`); assert.deepEqual(result.stdout.trim().split(/\s+/), [expectedArch], `${label} must contain only ${expectedArch}`); }; const verifyPeX64 = (path, label) => { const descriptor = openSync(path, "r"); try { const dosHeader = Buffer.alloc(64); assert.equal(readSync(descriptor, dosHeader, 0, dosHeader.length, 0), dosHeader.length, `${label} DOS header is incomplete`); assert.equal(dosHeader.toString("ascii", 0, 2), "MZ", `${label} is not a PE executable`); const peOffset = dosHeader.readUInt32LE(0x3c); const peHeader = Buffer.alloc(6); assert.equal(readSync(descriptor, peHeader, 0, peHeader.length, peOffset), peHeader.length, `${label} PE header is incomplete`); assert.equal(peHeader.toString("ascii", 0, 4), "PE\0\0", `${label} PE signature is invalid`); assert.equal(peHeader.readUInt16LE(4), 0x8664, `${label} must target Windows x64`); } finally { closeSync(descriptor); } }; const verifyElfX64 = (path, label) => { const result = spawnSync("readelf", ["-h", path], { encoding: "utf8" }); assert.equal(result.status, 0, `${label} ELF inspection failed: ${result.stderr || result.stdout}`); assert.match(result.stdout, /Class:\s+ELF64/, `${label} must be a 64-bit ELF executable`); assert.match(result.stdout, /Machine:\s+Advanced Micro Devices X86-64/, `${label} must target Linux x64`); }; const verifyGlibcBaseline = (path, maximum = [2, 35]) => { const result = spawnSync("readelf", ["--version-info", path], { encoding: "utf8" }); assert.equal(result.status, 0, `Rust sidecar glibc inspection failed: ${result.stderr || result.stdout}`); const versions = [...result.stdout.matchAll(/GLIBC_(\d+)\.(\d+)/g)] .map((match) => [Number(match[1]), Number(match[2])]); assert.ok(versions.length > 0, "Rust sidecar must declare its glibc requirements"); const exceedsBaseline = versions.some(([major, minor]) => major > maximum[0] || (major === maximum[0] && minor > maximum[1]) ); assert.equal(exceedsBaseline, false, `Rust sidecar must not require glibc newer than ${maximum.join(".")}`); }; for (const sidecarPath of files.filter((path) => /[\\/]resources[\\/]sidecar[\\/]edgeever-sidecar(?:\.exe)?$/i.test(path))) { const bundleRoot = sidecarPath.replace(/[\\/]resources[\\/]sidecar[\\/]edgeever-sidecar(?:\.exe)?$/i, ""); assert.ok(existsSync(join(bundleRoot, "resources", "web", "index.html")), `Desktop bundle is missing the Web renderer: ${bundleRoot}`); assert.ok(existsSync(join(bundleRoot, "resources", "web", "desktop-storage-bridge.html")), `Desktop bundle is missing the renderer storage bridge: ${bundleRoot}`); assert.ok(existsSync(join(bundleRoot, "resources", "migrations")), `Desktop bundle is missing migrations: ${bundleRoot}`); } if (requestedPlatform === "darwin") { assert.ok(["arm64", "x64"].includes(requestedArch), `Unsupported macOS architecture: ${requestedArch}`); assert.ok(existsSync(join(outputDirectory, `EdgeEver-${version}-mac-${requestedArch}.dmg`)), `macOS package must contain the current ${requestedArch} DMG`); assert.ok(existsSync(join(outputDirectory, `EdgeEver-${version}-mac-${requestedArch}.dmg.blockmap`)), `macOS package must contain the current ${requestedArch} DMG blockmap`); assert.ok(existsSync(join(outputDirectory, `EdgeEver-${version}-mac-${requestedArch}.zip`)), `macOS package must contain the current ${requestedArch} update ZIP`); assert.ok(existsSync(join(outputDirectory, `EdgeEver-${version}-mac-${requestedArch}.zip.blockmap`)), `macOS package must contain the current ${requestedArch} ZIP blockmap`); const unpackedDirectory = requestedArch === "x64" ? "mac" : `mac-${requestedArch}`; const unpackedApp = join(outputDirectory, unpackedDirectory, "EdgeEver.app"); assert.ok(existsSync(unpackedApp), "macOS package must contain the unpacked app bundle"); const executable = join(unpackedApp, "Contents", "MacOS", "EdgeEver"); const appResources = join(unpackedApp, "Contents", "Resources"); const sidecar = join(appResources, "sidecar", "edgeever-sidecar"); assert.ok(existsSync(sidecar), `macOS app bundle is missing the sidecar: ${sidecar}`); verifyMachOArch(executable, requestedArch, "Electron executable"); verifyMachOArch(sidecar, requestedArch, "Rust sidecar"); const shareExtension = join(unpackedApp, "Contents", "PlugIns", "EdgeEverShare.appex", "Contents", "MacOS", "EdgeEverShare"); assert.ok(existsSync(shareExtension), `macOS app bundle is missing the WeChat share extension: ${shareExtension}`); verifyMachOArch(shareExtension, requestedArch, "WeChat share extension"); const extensionSymbols = spawnSync("nm", ["-u", shareExtension], { encoding: "utf8" }); assert.equal(extensionSymbols.status, 0, `WeChat share extension symbol inspection failed: ${extensionSymbols.stderr || extensionSymbols.stdout}`); assert.match(extensionSymbols.stdout, /\b_NSExtensionMain\b/, "WeChat share extension must use the system extension entry point"); const shareExtensionInfo = readFileSync(join(unpackedApp, "Contents", "PlugIns", "EdgeEverShare.appex", "Contents", "Info.plist"), "utf8"); assert.match(shareExtensionInfo, /com\.apple\.share-services/, "WeChat share extension must register a share service"); assert.match(shareExtensionInfo, /NSExtensionActivationSupportsFileWithMaxCount/, "WeChat share extension must accept shared files"); const asarPath = join(appResources, "app.asar"); assert.ok(existsSync(asarPath), `macOS app bundle is missing app.asar: ${asarPath}`); const asarFiles = listAsarFiles(asarPath, executable); assert.ok(asarFiles.has("/src/preload/index.cjs"), "macOS app bundle must contain the sandbox-compatible CommonJS preload"); assert.ok(!asarFiles.has("/src/preload/index.mjs"), "macOS app bundle must not contain the unsupported ESM preload"); const appIconPath = join(appResources, "icon.icns"); assert.ok(existsSync(appIconPath), `macOS app bundle is missing icon.icns: ${appIconPath}`); assertMacIcnsComplete(readFileSync(appIconPath), appIconPath); const infoPlist = readFileSync(join(unpackedApp, "Contents", "Info.plist"), "utf8"); assert.match(infoPlist, /CFBundleIconFile/, "macOS Info.plist must declare CFBundleIconFile"); // LaunchServices registers unpacked build products as additional document // handlers. The signed DMG/ZIP are the release artifacts, so discard the // disposable unpacked bundle once its contents have passed verification. rmSync(unpackedApp, { recursive: true, force: true }); } else if (requestedPlatform === "win32") { assert.equal(requestedArch, "x64", `Windows release package must target x64, received: ${requestedArch}`); const installer = join(outputDirectory, `EdgeEver-${version}-windows-x64.exe`); const unpackedDirectory = join(outputDirectory, "win-unpacked"); const executable = join(unpackedDirectory, "EdgeEver.exe"); const resources = join(unpackedDirectory, "resources"); const sidecar = join(resources, "sidecar", "edgeever-sidecar.exe"); assert.ok(existsSync(installer), `Windows package must contain the current x64 NSIS installer: ${installer}`); assert.ok(existsSync(executable), `Windows package must contain the unpacked x64 executable: ${executable}`); assert.ok(existsSync(sidecar), `Windows package must contain the x64 sidecar: ${sidecar}`); assert.ok(existsSync(join(resources, "web", "index.html")), "Windows app bundle is missing the Web renderer"); assert.ok(existsSync(join(resources, "migrations")), "Windows app bundle is missing migrations"); verifyPeX64(executable, "Electron executable"); verifyPeX64(sidecar, "Rust sidecar"); const sidecarRuntimeImports = readPeImportedDlls(sidecar).filter(isVisualCppRuntimeDll); assert.deepEqual( sidecarRuntimeImports, [], `Windows sidecar must statically link the Visual C++ runtime; found: ${sidecarRuntimeImports.join(", ")}`, ); const asarPath = join(resources, "app.asar"); assert.ok(existsSync(asarPath), `Windows app bundle is missing app.asar: ${asarPath}`); const asarFiles = listAsarFiles(asarPath, executable); assert.ok(asarFiles.has("/src/preload/index.cjs"), "Windows app bundle must contain the sandbox-compatible CommonJS preload"); assert.ok(!asarFiles.has("/src/preload/index.mjs"), "Windows app bundle must not contain the unsupported ESM preload"); } else if (requestedPlatform === "linux") { assert.equal(requestedArch, "x64", `Linux Preview package must target x64, received: ${requestedArch}`); const appImage = join(outputDirectory, `EdgeEver-${version}-linux-x64.AppImage`); const unpackedDirectory = join(outputDirectory, "linux-unpacked"); const executable = ["EdgeEver", "edgeever"] .map((name) => join(unpackedDirectory, name)) .find(existsSync); const resources = join(unpackedDirectory, "resources"); const sidecar = join(resources, "sidecar", "edgeever-sidecar"); assert.ok(existsSync(appImage), `Linux package must contain the current x64 AppImage: ${appImage}`); assert.ok(statSync(appImage).mode & 0o111, "Linux AppImage must be executable"); assert.ok(executable, "Linux package must contain the unpacked EdgeEver executable"); assert.ok(existsSync(sidecar), `Linux app bundle is missing the sidecar: ${sidecar}`); assert.ok(statSync(sidecar).mode & 0o111, "Linux sidecar must be executable"); assert.ok(existsSync(join(resources, "web", "index.html")), "Linux app bundle is missing the Web renderer"); assert.ok(existsSync(join(resources, "migrations")), "Linux app bundle is missing migrations"); verifyElfX64(executable, "Electron executable"); verifyElfX64(sidecar, "Rust sidecar"); verifyGlibcBaseline(sidecar); const asarPath = join(resources, "app.asar"); assert.ok(existsSync(asarPath), `Linux app bundle is missing app.asar: ${asarPath}`); const asarFiles = listAsarFiles(asarPath, executable); assert.ok(asarFiles.has("/src/preload/index.cjs"), "Linux app bundle must contain the sandbox-compatible CommonJS preload"); assert.ok(!asarFiles.has("/src/preload/index.mjs"), "Linux app bundle must not contain the unsupported ESM preload"); } else { throw new Error(`Unsupported packaging platform: ${requestedPlatform}`); } console.log(JSON.stringify({ ok: true, platform: requestedPlatform, artifacts: files.filter((path) => /\.(dmg|exe|AppImage)$/.test(path)) }));