1
0

prepare-github-release.mjs 3.0 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980
  1. import { cpSync, existsSync, mkdirSync, readdirSync, readFileSync, rmSync, statSync, writeFileSync } from "node:fs"
  2. import { spawnSync } from "node:child_process"
  3. import { dirname, extname, resolve } from "node:path"
  4. import { fileURLToPath } from "node:url"
  5. import { buildCurrentReleaseNotes } from "./release-notes.mjs"
  6. const root = resolve(dirname(fileURLToPath(import.meta.url)), "..")
  7. const pkg = JSON.parse(readFileSync(resolve(root, "package.json"), "utf8"))
  8. const releaseNotes = await buildCurrentReleaseNotes(root)
  9. const bundleDir = resolve(root, "src-tauri/target/release/bundle/nsis")
  10. const outDir = resolve(root, "release-github")
  11. const portableExe = resolve(root, "release-portable/QMaiWrite.exe")
  12. if (!existsSync(bundleDir)) {
  13. throw new Error(`未找到安装包目录:${bundleDir}`)
  14. }
  15. const bundleFiles = readdirSync(bundleDir)
  16. const updaterAssetName = bundleFiles
  17. .filter((name) => {
  18. if (name.endsWith(".sig")) return false
  19. return [".exe", ".msi", ".zip"].includes(extname(name).toLowerCase())
  20. })
  21. .sort((left, right) => statSync(resolve(bundleDir, right)).mtimeMs - statSync(resolve(bundleDir, left)).mtimeMs)[0]
  22. if (!updaterAssetName) {
  23. throw new Error("未找到 updater 安装包,请先执行 npm run build:github-release")
  24. }
  25. const updaterAssetPath = resolve(bundleDir, updaterAssetName)
  26. const releaseAssetName = `QMaiWrite_${pkg.version}_windows_X64${extname(updaterAssetName)}`
  27. const releaseAssetPath = resolve(outDir, releaseAssetName)
  28. const releaseSignaturePath = `${releaseAssetPath}.sig`
  29. rmSync(outDir, { recursive: true, force: true })
  30. mkdirSync(outDir, { recursive: true })
  31. cpSync(updaterAssetPath, releaseAssetPath)
  32. const privateKeyPath = process.env.TAURI_SIGNING_PRIVATE_KEY_PATH || resolve(process.env.USERPROFILE ?? "", ".tauri/qmai-updater.key")
  33. if (!existsSync(privateKeyPath)) {
  34. throw new Error(`未找到 updater 签名私钥:${privateKeyPath}`)
  35. }
  36. const tauriCli = resolve(root, "node_modules/@tauri-apps/cli/tauri.js")
  37. const result = spawnSync(process.execPath, [
  38. tauriCli,
  39. "signer",
  40. "sign",
  41. "--private-key-path",
  42. privateKeyPath,
  43. `--password=${process.env.TAURI_SIGNING_PRIVATE_KEY_PASSWORD ?? ""}`,
  44. releaseAssetPath,
  45. ], { stdio: "inherit" })
  46. if (result.status !== 0) {
  47. throw new Error("生成 updater 签名文件失败")
  48. }
  49. if (existsSync(portableExe)) {
  50. cpSync(portableExe, resolve(outDir, "QMaiWrite-portable.exe"))
  51. }
  52. const signature = readFileSync(releaseSignaturePath, "utf8").trim()
  53. const latest = {
  54. version: pkg.version,
  55. notes: `QMAI ${pkg.version} 发布版本`,
  56. pub_date: new Date().toISOString(),
  57. platforms: {
  58. "windows-x86_64": {
  59. signature,
  60. url: `https://github.com/Mochocyang/QMAI/releases/latest/download/${releaseAssetName}`,
  61. },
  62. },
  63. }
  64. latest.notes = releaseNotes
  65. writeFileSync(resolve(outDir, "latest.json"), JSON.stringify(latest, null, 2), "utf8")
  66. writeFileSync(resolve(outDir, "release-notes.txt"), releaseNotes, "utf8")
  67. console.log(`GitHub Release 产物已生成:${outDir}`)
  68. console.log(`更新包:${releaseAssetName}`)
  69. console.log(`签名文件:${releaseAssetName}.sig`)