build-portable.mjs 3.0 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091
  1. import { cpSync, existsSync, mkdirSync, rmSync, statSync, writeFileSync, renameSync } from "node:fs"
  2. import { readFile } from "node:fs/promises"
  3. import { dirname, resolve } from "node:path"
  4. import { fileURLToPath } from "node:url"
  5. const root = resolve(dirname(fileURLToPath(import.meta.url)), "..")
  6. const pkg = JSON.parse(await readFile(resolve(root, "package.json"), "utf8"))
  7. const releaseExe = resolve(root, "src-tauri/target/release/qmai.exe")
  8. const portableDevExe = resolve(root, "src-tauri/target/portable-dev/qmai.exe")
  9. const sourceExe = existsSync(portableDevExe) ? portableDevExe : releaseExe
  10. const releasePdfium = resolve(root, "src-tauri/target/release/pdfium/pdfium.dll")
  11. const portableDevPdfium = resolve(root, "src-tauri/target/portable-dev/pdfium/pdfium.dll")
  12. const sourcePdfium = existsSync(portableDevPdfium) ? portableDevPdfium : releasePdfium
  13. const outDir = resolve(root, "release-portable")
  14. const outExe = resolve(outDir, "QMaiWrite.exe")
  15. const outPdfium = resolve(outDir, "pdfium/pdfium.dll")
  16. const outSkillDir = resolve(outDir, "skills")
  17. const manifest = resolve(outDir, "version-info.json")
  18. const backupDir = resolve(root, "release-portable-backup")
  19. if (!existsSync(sourceExe)) {
  20. throw new Error(`未找到最新 EXE,请先运行 npm run tauri build:${sourceExe}`)
  21. }
  22. // 尝试安全清理输出目录
  23. try {
  24. if (existsSync(backupDir)) {
  25. try {
  26. rmSync(backupDir, { recursive: true, force: true })
  27. } catch {}
  28. }
  29. if (existsSync(outDir)) {
  30. // 先尝试重命名而不是直接删除
  31. renameSync(outDir, backupDir)
  32. try {
  33. rmSync(backupDir, { recursive: true, force: true })
  34. } catch {}
  35. }
  36. } catch {}
  37. mkdirSync(outDir, { recursive: true })
  38. // 复制 exe,处理被占用的情况
  39. try {
  40. if (existsSync(outExe)) {
  41. const backupExe = resolve(outDir, "QMaiWrite-old.exe")
  42. try {
  43. if (existsSync(backupExe)) {
  44. rmSync(backupExe, { force: true })
  45. }
  46. renameSync(outExe, backupExe)
  47. } catch {}
  48. }
  49. cpSync(sourceExe, outExe)
  50. } catch (e) {
  51. console.warn("警告:无法完全替换正在运行的 exe,保留旧版本,但已更新其他资源")
  52. }
  53. if (existsSync(sourcePdfium)) {
  54. mkdirSync(dirname(outPdfium), { recursive: true })
  55. try {
  56. cpSync(sourcePdfium, outPdfium)
  57. } catch {}
  58. }
  59. // 复制 skills 文件夹到便携版目录
  60. const sourceSkillDir = resolve(root, "skills")
  61. if (existsSync(sourceSkillDir)) {
  62. try {
  63. rmSync(outSkillDir, { recursive: true, force: true })
  64. } catch {}
  65. mkdirSync(outSkillDir, { recursive: true })
  66. cpSync(sourceSkillDir, outSkillDir, { recursive: true })
  67. }
  68. const exeStat = statSync(outExe)
  69. writeFileSync(manifest, JSON.stringify({
  70. productName: "青幕AI写作",
  71. version: pkg.version,
  72. builtAt: new Date().toISOString(),
  73. sourceExe,
  74. portableExe: outExe,
  75. exeBytes: exeStat.size,
  76. includesPdfium: existsSync(outPdfium),
  77. includesSkills: existsSync(outSkillDir),
  78. }, null, 2), "utf8")
  79. console.log(`便携版已生成:${outExe}`)
  80. console.log(`版本信息:${manifest}`)