fix: 提升Windows分发包启动兼容性
This commit is contained in:
parent
5de47ee420
commit
cb68af5a0f
@ -13,6 +13,8 @@ const releaseRoot = path.resolve(root, "release");
|
||||
const productFolderName = `RPA-Studio-${version}-win-x64`;
|
||||
const productFolder = path.join(releaseRoot, productFolderName);
|
||||
const zipPath = path.join(releaseRoot, `${productFolderName}.zip`);
|
||||
const setupPath = path.join(releaseRoot, `RPA-Studio-Setup-${version}-win-x64.exe`);
|
||||
const setupArchive = path.join(releaseRoot, `${productFolderName}.7z`);
|
||||
|
||||
function assertInsideRelease(target) {
|
||||
const relative = path.relative(releaseRoot, path.resolve(target));
|
||||
@ -27,7 +29,7 @@ function run(command, args) {
|
||||
}
|
||||
|
||||
fs.mkdirSync(releaseRoot, { recursive: true });
|
||||
for (const target of [productFolder, zipPath]) {
|
||||
for (const target of [productFolder, zipPath, setupPath, setupArchive]) {
|
||||
assertInsideRelease(target);
|
||||
fs.rmSync(target, { recursive: true, force: true });
|
||||
}
|
||||
@ -97,17 +99,61 @@ fs.writeFileSync(path.join(productFolder, "使用说明.txt"), [
|
||||
"3. 任务、流程和运行记录默认保存在当前 Windows 用户的 AppData\\Roaming\\rpa-studio。",
|
||||
"4. Word 自动化需要本机安装 Microsoft Word;其他文件级能力无需安装 Office。",
|
||||
"5. 首次启动如被 Windows SmartScreen 提示,请核对同目录 SHA256SUMS.txt;当前构建未进行商业代码签名。",
|
||||
"6. 如果双击没有窗口,请运行 启动诊断.cmd,并把自动打开的 startup.log 发给开发人员。",
|
||||
"",
|
||||
`版本:${version}`,
|
||||
`源码提交:${revision}`,
|
||||
`构建时间:${builtAt}`
|
||||
].join("\r\n"), "utf8");
|
||||
fs.writeFileSync(path.join(productFolder, "系统要求.txt"), [
|
||||
"RPA Studio 系统要求",
|
||||
"",
|
||||
"- Windows 10 64 位或 Windows 11 64 位",
|
||||
"- 建议至少 4 GB 内存和 500 MB 可用磁盘空间",
|
||||
"- 必须先完整解压,不能直接在 ZIP 压缩包中双击 exe",
|
||||
"- Word 自动化需要本机安装 Microsoft Word",
|
||||
"- 当前版本未购买商业代码签名证书,SmartScreen 可能显示未知发布者",
|
||||
"- 软件默认关闭 GPU 硬件加速,以兼容远程桌面、虚拟机和旧显卡驱动"
|
||||
].join("\r\n"), "utf8");
|
||||
fs.writeFileSync(path.join(productFolder, "启动诊断.cmd"), [
|
||||
"@echo off",
|
||||
"chcp 65001 >nul",
|
||||
"set ELECTRON_ENABLE_LOGGING=1",
|
||||
"set LOG=%APPDATA%\\rpa-studio\\logs\\startup.log",
|
||||
"start \"RPA Studio\" \"%~dp0RPA Studio.exe\" --enable-logging --v=1",
|
||||
"timeout /t 8 /nobreak >nul",
|
||||
"if exist \"%LOG%\" (start notepad.exe \"%LOG%\") else (echo 未生成启动日志,请检查安全软件拦截记录。 & pause)"
|
||||
].join("\r\n"), "utf8");
|
||||
fs.writeFileSync(path.join(productFolder, "安装到本机.cmd"), [
|
||||
"@echo off",
|
||||
"chcp 65001 >nul",
|
||||
"set \"TARGET=%LOCALAPPDATA%\\Programs\\RPA Studio\"",
|
||||
"if not exist \"%TARGET%\" mkdir \"%TARGET%\"",
|
||||
"robocopy \"%~dp0\" \"%TARGET%\" /E /R:2 /W:1 /NFL /NDL /NJH /NJS /NP >nul",
|
||||
"powershell -NoProfile -ExecutionPolicy Bypass -Command \"$w=New-Object -ComObject WScript.Shell;$s=$w.CreateShortcut([IO.Path]::Combine([Environment]::GetFolderPath('Desktop'),'RPA Studio.lnk'));$s.TargetPath=[IO.Path]::Combine($env:LOCALAPPDATA,'Programs','RPA Studio','RPA Studio.exe');$s.WorkingDirectory=[IO.Path]::Combine($env:LOCALAPPDATA,'Programs','RPA Studio');$s.Save()\"",
|
||||
"start \"RPA Studio\" \"%TARGET%\\RPA Studio.exe\"",
|
||||
"exit /b 0"
|
||||
].join("\r\n"), "utf8");
|
||||
|
||||
console.log("[package] zip");
|
||||
const zip = new AdmZip();
|
||||
zip.addLocalFolder(productFolder, productFolderName);
|
||||
zip.writeZip(zipPath);
|
||||
const sha256 = createHash("sha256").update(fs.readFileSync(zipPath)).digest("hex");
|
||||
fs.writeFileSync(path.join(releaseRoot, "SHA256SUMS.txt"), `${sha256} ${path.basename(zipPath)}\r\n`, "utf8");
|
||||
const zipSha256 = createHash("sha256").update(fs.readFileSync(zipPath)).digest("hex");
|
||||
|
||||
console.log(JSON.stringify({ productFolder, zipPath, sha256 }, null, 2));
|
||||
let setupSha256 = "";
|
||||
const sevenZip = "C:/Program Files/7-Zip/7z.exe";
|
||||
const sfxModule = "C:/Program Files/7-Zip/7z.sfx";
|
||||
if (fs.existsSync(sevenZip) && fs.existsSync(sfxModule)) {
|
||||
console.log("[package] setup sfx");
|
||||
const archived = spawnSync(sevenZip, ["a", "-t7z", setupArchive, ".", "-mx=9"], { cwd: productFolder, stdio: "inherit" });
|
||||
if (archived.status !== 0) throw new Error(`7-Zip 打包失败,退出码 ${archived.status}`);
|
||||
const config = Buffer.from(`;!@Install@!UTF-8!\nTitle=\"RPA Studio ${version}\"\nBeginPrompt=\"将 RPA Studio 安装到当前用户并创建桌面快捷方式?\"\nRunProgram=\"安装到本机.cmd\"\nGUIMode=\"1\"\n;!@InstallEnd@!\n`, "utf8");
|
||||
fs.writeFileSync(setupPath, Buffer.concat([fs.readFileSync(sfxModule), config, fs.readFileSync(setupArchive)]));
|
||||
setupSha256 = createHash("sha256").update(fs.readFileSync(setupPath)).digest("hex");
|
||||
}
|
||||
const sums = [`${zipSha256} ${path.basename(zipPath)}`];
|
||||
if (setupSha256) sums.push(`${setupSha256} ${path.basename(setupPath)}`);
|
||||
fs.writeFileSync(path.join(releaseRoot, "SHA256SUMS.txt"), `${sums.join("\r\n")}\r\n`, "utf8");
|
||||
|
||||
console.log(JSON.stringify({ productFolder, zipPath, setupPath: setupSha256 ? setupPath : null, zipSha256, setupSha256: setupSha256 || null }, null, 2));
|
||||
|
||||
@ -7,11 +7,17 @@ import { createDemoFlow, createExampleFlows, createFlow, deleteFlow, getFlow, li
|
||||
import { capabilityCount, capabilityIds } from "./engine/registry";
|
||||
import { closeBrowserSession, startRun, stopRun } from "./run/manager";
|
||||
import { fixtureUrl, startFixtureServer, stopFixtureServer } from "./fixture/server";
|
||||
import { logMain } from "./logger";
|
||||
|
||||
let mainWindow: BrowserWindow | null = null;
|
||||
|
||||
if (process.env.RPA_ENABLE_GPU !== "1") app.disableHardwareAcceleration();
|
||||
process.on("uncaughtException", (error) => logMain("uncaughtException", error.stack || error.message));
|
||||
process.on("unhandledRejection", (reason) => logMain("unhandledRejection", reason instanceof Error ? reason.stack || reason.message : String(reason)));
|
||||
|
||||
function createWindow(): void {
|
||||
const e2e = process.env.RPA_E2E === "1";
|
||||
logMain("createWindow", { packaged: app.isPackaged, appPath: app.getAppPath(), userData: app.getPath("userData") });
|
||||
mainWindow = new BrowserWindow({
|
||||
width: 1440,
|
||||
height: 900,
|
||||
@ -34,7 +40,18 @@ function createWindow(): void {
|
||||
}
|
||||
});
|
||||
|
||||
mainWindow.on("ready-to-show", () => { if (!e2e) mainWindow?.show(); });
|
||||
let shown = false;
|
||||
const showWindow = (source: string) => {
|
||||
if (e2e || shown || !mainWindow || mainWindow.isDestroyed()) return;
|
||||
shown = true;
|
||||
logMain("showWindow", source);
|
||||
mainWindow.show();
|
||||
mainWindow.focus();
|
||||
};
|
||||
const showFallback = setTimeout(() => showWindow("fallback-timeout"), 5000);
|
||||
mainWindow.on("ready-to-show", () => { clearTimeout(showFallback); showWindow("ready-to-show"); });
|
||||
mainWindow.on("unresponsive", () => logMain("window-unresponsive"));
|
||||
mainWindow.on("responsive", () => logMain("window-responsive"));
|
||||
mainWindow.on("closed", () => {
|
||||
mainWindow = null;
|
||||
});
|
||||
@ -42,6 +59,14 @@ function createWindow(): void {
|
||||
if (/^https?:/i.test(url)) void shell.openExternal(url);
|
||||
return { action: "deny" };
|
||||
});
|
||||
mainWindow.webContents.on("did-fail-load", (_event, code, description, url) => {
|
||||
logMain("did-fail-load", { code, description, url });
|
||||
showWindow("did-fail-load");
|
||||
});
|
||||
mainWindow.webContents.on("render-process-gone", (_event, details) => logMain("render-process-gone", details));
|
||||
mainWindow.webContents.on("console-message", (_event, level, message, line, sourceId) => {
|
||||
if (level >= 2) logMain("renderer-console", { level, message, line, sourceId });
|
||||
});
|
||||
|
||||
const devUrl = process.env.ELECTRON_RENDERER_URL;
|
||||
if (devUrl) {
|
||||
@ -121,10 +146,12 @@ if (!gotLock) {
|
||||
});
|
||||
|
||||
app.whenReady().then(async () => {
|
||||
logMain("app-ready", { electron: process.versions.electron, chrome: process.versions.chrome, node: process.versions.node });
|
||||
try {
|
||||
await startFixtureServer(Number(process.env.RPA_FIXTURE_PORT || 17863));
|
||||
} catch (error) {
|
||||
console.warn("local fixture server unavailable", error);
|
||||
logMain("fixture-server-failed", error instanceof Error ? error.stack || error.message : String(error));
|
||||
}
|
||||
registerIpc();
|
||||
createWindow();
|
||||
|
||||
14
src/main/logger.ts
Normal file
14
src/main/logger.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import { app } from "electron";
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
|
||||
export function logMain(event: string, detail: unknown = ""): void {
|
||||
try {
|
||||
const directory = path.join(app.getPath("userData"), "logs");
|
||||
fs.mkdirSync(directory, { recursive: true });
|
||||
const content = typeof detail === "string" ? detail : JSON.stringify(detail);
|
||||
fs.appendFileSync(path.join(directory, "startup.log"), `${new Date().toISOString()} [${event}] ${content}\r\n`, "utf8");
|
||||
} catch {
|
||||
// Logging must never prevent the app from starting.
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user