CH-J Server Managerserverhantering över SSH
Menyn
Publicerad källa

CH-J Server Manager

Bläddra i kataloger och filer för en specifik programutgåva.

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

CH-J Proprietary Software License 1.14 · Källan finns under CH-J Proprietary Software License 1.14. Dess tillgänglighet ändrar inte licensvillkoren eller ger ytterligare rättigheter.

Arkiv SHA-2563df08cf5e899262a6ce92d7ec2caf5c42b683f1f2b57055662b845ff7dde67a8