CH-J Server Managerserverhåndtering over SSH
Menu
Offentliggjort kilde

CH-J Server Manager

Gennemse mapper og filer til en specifik applikationsudgivelse.

Download kilde ZIP
CH-J / Server Manager 193 filer
core-20261011T184039Z-4148710795d0 Tilføjet: Dato ikke registreret · Bygget:
Filer
packageMacInstaller.js Tilføjet: Dato ikke registreret 7,4 KB · 162 linjer
Download fil
1"use strict";
3const fs = require("node:fs");
4const os = require("node:os");
5const path = require("node:path");
6const { execFileSync, spawn, spawnSync } = require("node:child_process");
7const { signAsync } = require("@electron/osx-sign");
9const projectRoot = path.resolve(__dirname, "..");
10const packageJson = require(path.join(projectRoot, "package.json"));
11const productName = packageJson.build?.productName || packageJson.productName;
12const distRoot = path.resolve(projectRoot, process.env.CHJ_DIST_DIR || "dist");
13const appPath = path.join(distRoot, "mac-arm64", `${productName}.app`);
14const outputPath = path.join(distRoot, `${productName}-${packageJson.version}-arm64.dmg`);
16function run(command, args, options = {}) {
17 return execFileSync(command, args, { encoding: "utf8", stdio: options.capture ? "pipe" : "inherit" });
20function signatureIsValid(bundlePath) {
21 try {
22 run("codesign", ["--verify", "--deep", "--strict", "--verbose=2", bundlePath], { capture: true });
23 return true;
24 } catch (_) {
25 return false;
26 }
29function signatureDescription(bundlePath) {
30 const result = spawnSync("codesign", ["-dv", "--verbose=4", bundlePath], { encoding: "utf8" });
31 return `${result.stdout || ""}\n${result.stderr || ""}`;
34async function ensureValidSignature() {
35 if (signatureIsValid(appPath)) {
36 const description = signatureDescription(appPath);
37 if (description.includes("Authority=Developer ID Application")) return "developer-id";
38 if (description.includes("Signature=adhoc") && !description.includes("runtime)")) return "adhoc";
39 }
41 const identities = run("security", ["find-identity", "-v", "-p", "codesigning"], { capture: true });
42 const hasDeveloperId = identities.includes("Developer ID Application:");
43 const options = {
44 app: appPath,
45 platform: "darwin",
46 // Preserve the link-signed, hash-pinned native client. The outer app seal covers it.
47 ignore: (filePath) => /native-runtime\/.*\/curl$/.test(filePath),
48 preAutoEntitlements: false,
49 preEmbedProvisioningProfile: false,
50 };
51 if (!hasDeveloperId) {
52 options.identity = "-";
53 options.identityValidation = false;
54 options.optionsForFile = () => ({
55 hardenedRuntime: false,
56 signatureFlags: [],
57 timestamp: "none",
58 });
59 }
60 await signAsync(options);
61 if (!signatureIsValid(appPath)) throw new Error("Kontrola codesign po podepsání .app selhala.");
62 return hasDeveloperId ? "developer-id" : "adhoc";
65async function smokeTestApp(bundlePath) {
66 const executable = path.join(bundlePath, "Contents", "MacOS", productName);
67 const userDataDir = fs.mkdtempSync(path.join(os.tmpdir(), "chj-mac-smoke-data-"));
68 try {
69 // Fixture consent for this isolated packaging smoke test only. Production
70 // startup always checks the record; the environment does not bypass it.
71 const { LicenseAcceptance } = require("../src/main/legal/licenseAcceptance");
72 const acceptance = new LicenseAcceptance({ storageRoot: userDataDir,
73 licensePath: path.join(bundlePath, "Contents", "Resources", "licenses", "APPLICATION_LICENSE.txt"),
74 docsRoot: path.join(bundlePath, "Contents", "Resources", "docs"),
75 appVersion: packageJson.version, buildId: require("../src/shared/buildInfo.json").buildId });
76 acceptance.accept({ accepted: true, sha256: acceptance.identity.sha256, guidanceShown: true, guidanceSha256: acceptance.identity.guidanceSha256 });
77 await new Promise((resolve, reject) => {
78 let output = "";
79 const smokeEnvironment = {
80 ...process.env,
81 CHJ_BUILD_SMOKE_USER_DATA_DIR: userDataDir,
82 ELECTRON_ENABLE_LOGGING: "1",
83 };
84 delete smokeEnvironment.ELECTRON_RUN_AS_NODE;
85 delete smokeEnvironment.NODE_OPTIONS;
86 const child = spawn(executable, [], {
87 env: smokeEnvironment,
88 stdio: ["ignore", "pipe", "pipe"],
89 });
90 child.stdout.on("data", (chunk) => { output += chunk; });
91 child.stderr.on("data", (chunk) => { output += chunk; });
92 const timeout = setTimeout(() => {
93 child.kill("SIGTERM");
94 reject(new Error(`Aplikace nedokončila smoke test do 12 sekund.\n${output.trim()}`));
95 }, 12000);
96 child.once("error", (error) => {
97 clearTimeout(timeout);
98 reject(error);
99 });
100 child.once("exit", (code, signal) => {
101 clearTimeout(timeout);
102 if (code === 0 && output.includes("CHJ_BUILD_SMOKE_READY")) resolve();
103 else reject(new Error(`Aplikace nedokončila smoke test (code=${code}, signal=${signal}).\n${output.trim()}`));
104 });
105 });
106 } finally {
107 fs.rmSync(userDataDir, { recursive: true, force: true });
108 }
111async function main() {
112 if (process.platform !== "darwin") throw new Error("macOS instalační DMG lze vytvářet pouze na macOS.");
113 if (!productName || !fs.statSync(appPath, { throwIfNoEntry: false })?.isDirectory()) {
114 throw new Error(`Chybí sestavená aplikace ${appPath}. Nejdřív spusťte macOS build.`);
115 }
117 const { validateRuntime, runtimeEnvironment } = require("../src/main/diagnostics/httpRuntime");
118 const originalRuntime = validateRuntime({ resourcesPath: path.join(projectRoot, "build") });
119 const resourcesPath = path.join(appPath, "Contents", "Resources");
120 const packagedClient = path.join(resourcesPath, "native-runtime", "darwin-arm64", "curl");
121 // A packager may re-sign nested Mach-O files. Restore the verified client before
122 // creating the app's final resource seal; never edit a finished/signed DMG.
123 fs.copyFileSync(originalRuntime.executable, packagedClient);
124 fs.chmodSync(packagedClient, 0o755);
125 const signature = await ensureValidSignature();
126 const finalRuntime = validateRuntime({ resourcesPath });
127 const runtimeVersion = execFileSync(finalRuntime.executable, ["--disable", "--version"], { encoding: "utf8", env: runtimeEnvironment() });
128 if (!/Features:.*HTTP3/.test(runtimeVersion)) throw new Error("Signed app lacks its working HTTP/3 runtime");
129 fs.rmSync(outputPath, { force: true });
130 const staging = fs.mkdtempSync(path.join(os.tmpdir(), "chj-dmg-stage-"));
131 try {
132 run("ditto", [appPath, path.join(staging, `${productName}.app`)]);
133 fs.symlinkSync("/Applications", path.join(staging, "Applications"));
134 run("hdiutil", ["create", "-volname", productName, "-srcfolder", staging, "-format", "UDZO", "-ov", outputPath]);
135 } finally { fs.rmSync(staging, { recursive: true, force: true }); }
136 run("hdiutil", ["verify", outputPath]);
138 const verifyRoot = fs.mkdtempSync(path.join(os.tmpdir(), "chj-mac-installer-"));
139 try {
140 run("hdiutil", ["attach", "-readonly", "-nobrowse", "-mountpoint", verifyRoot, outputPath]);
141 if (fs.readlinkSync(path.join(verifyRoot, "Applications")) !== "/Applications") throw new Error("DMG lacks Applications shortcut");
142 const extractedApp = path.join(verifyRoot, `${productName}.app`);
143 if (!fs.statSync(extractedApp, { throwIfNoEntry: false })?.isDirectory() || !signatureIsValid(extractedApp)) {
144 throw new Error("Výsledný DMG neobsahuje kompletní aplikaci s platným podpisem bundle.");
145 }
146 await smokeTestApp(extractedApp);
147 } finally {
148 run("hdiutil", ["detach", verifyRoot]);
149 fs.rmSync(verifyRoot, { recursive: true, force: true });
150 }
152 console.log(`Instalační balíček: ${outputPath}`);
153 console.log(`Podpis: ${signature}`);
154 if (signature !== "developer-id") {
155 console.warn("VAROVÁNÍ: Jde o testovací ad-hoc sestavení. Pro bezobslužnou instalaci z webu je nutný Developer ID podpis a Apple notarizace.");
156 }
159main().catch((error) => {
160 console.error(error.message || error);
161 process.exitCode = 1;
162});
SHA-2562a73fa890b1ac95cb8ce3cf6e358280f6522a87b7b21aef04b05091d8318ebc3

CH-J Proprietary Software License 1.14 · Kilden leveres under CH-J Proprietary Software License 1.14. Dens tilgængelighed ændrer ikke licensbetingelserne eller giver yderligere rettigheder.

Arkiv SHA-2565a8ea02d06a5643cf2330fe9f54b8767a06c1c59d36527705e536fa9dbca7614