build.ps1 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. # DMS 一体化构建脚本
  2. # 用法:在项目根目录执行 .\build\build.ps1
  3. [CmdletBinding()]
  4. param(
  5. [switch]$SkipFrontend,
  6. [switch]$SkipPyInstallerInstall
  7. )
  8. $ErrorActionPreference = "Stop"
  9. $ProjectRoot = Split-Path $PSScriptRoot -Parent
  10. $Backend = Join-Path $ProjectRoot "backend"
  11. $Frontend = Join-Path $ProjectRoot "frontend"
  12. $Dist = Join-Path $ProjectRoot "dist-package"
  13. $BuildCache = Join-Path $PSScriptRoot "__pycache_build"
  14. Write-Host "========== DMS 一体化构建 ==========" -ForegroundColor Cyan
  15. Write-Host "项目根目录: $ProjectRoot"
  16. # Python 后端已废弃(2026-09-16):不再更新、不再运行测试,详见 backend\DEPRECATED.md
  17. Write-Warning "Python 版后端已废弃(DEPRECATED):backend\ 不再更新,也不运行测试。现役版本请使用 .\build\build-java.ps1 构建 Java 版发行包。"
  18. # ---------- 1. 前端构建 ----------
  19. if (-not $SkipFrontend) {
  20. Write-Host "`n[1/5] 构建前端..." -ForegroundColor Yellow
  21. Push-Location $Frontend
  22. try {
  23. if (-not (Test-Path "node_modules")) {
  24. Write-Host " npm install..." -ForegroundColor DarkGray
  25. npm install
  26. if ($LASTEXITCODE -ne 0) { throw "npm install 失败" }
  27. }
  28. npm run build
  29. if ($LASTEXITCODE -ne 0) { throw "前端构建失败" }
  30. } finally { Pop-Location }
  31. } else {
  32. Write-Host "`n[1/5] 跳过前端构建 (-SkipFrontend)" -ForegroundColor DarkGray
  33. }
  34. $indexHtml = Join-Path $Frontend "dist\index.html"
  35. if (-not (Test-Path $indexHtml)) {
  36. throw "前端产物不存在: $indexHtml - 请先执行 npm run build"
  37. }
  38. # ---------- 2. 检查 PyInstaller ----------
  39. Write-Host "`n[2/5] 检查 PyInstaller..." -ForegroundColor Yellow
  40. Push-Location $Backend
  41. try {
  42. $pyinstallerVersion = & python -m PyInstaller --version 2>$null
  43. if ($LASTEXITCODE -ne 0 -or -not $pyinstallerVersion) {
  44. if ($SkipPyInstallerInstall) {
  45. throw "PyInstaller 未安装且 -SkipPyInstallerInstall 已指定"
  46. }
  47. Write-Host " 安装 PyInstaller..." -ForegroundColor DarkGray
  48. python -m pip install pyinstaller
  49. if ($LASTEXITCODE -ne 0) { throw "PyInstaller 安装失败" }
  50. } else {
  51. Write-Host " PyInstaller 版本: $pyinstallerVersion" -ForegroundColor DarkGray
  52. }
  53. } finally { Pop-Location }
  54. # ---------- 3. PyInstaller 打包 ----------
  55. Write-Host "`n[3/5] PyInstaller 打包..." -ForegroundColor Yellow
  56. $ReleaseDir = Join-Path $Dist "dms-server"
  57. # 暂存现有 .env 与 dms-storage:删除发行目录前移出,重打包后自动还原,
  58. # 避免用户配置与运行数据每次丢失(dms-storage 用移动而非复制,同卷瞬时完成)
  59. $KeepEnv = Join-Path $Dist ".env.keep"
  60. $KeepStorage = Join-Path $Dist "dms-storage.keep"
  61. if (Test-Path (Join-Path $ReleaseDir ".env")) {
  62. Copy-Item (Join-Path $ReleaseDir ".env") $KeepEnv -Force
  63. }
  64. # 备份管理配置(界面保存的 rsync 地址等)同样暂存,避免重打包丢失
  65. $KeepBackupCfg = Join-Path $Dist "dms-backup-config.keep"
  66. if (Test-Path (Join-Path $ReleaseDir "dms-backup-config.json")) {
  67. Copy-Item (Join-Path $ReleaseDir "dms-backup-config.json") $KeepBackupCfg -Force
  68. }
  69. if (Test-Path (Join-Path $ReleaseDir "dms-storage")) {
  70. if (Test-Path $KeepStorage) { Remove-Item $KeepStorage -Recurse -Force }
  71. try {
  72. Move-Item (Join-Path $ReleaseDir "dms-storage") $KeepStorage -ErrorAction Stop
  73. } catch {
  74. # dms-storage 被运行中的服务占用(文件句柄未释放):中止打包而非删数据
  75. throw "dms-storage 暂存失败(可能被运行中的服务占用):$_ - 请先停止 dms-server 再打包"
  76. }
  77. }
  78. if (Test-Path $BuildCache) { Remove-Item $BuildCache -Recurse -Force }
  79. if (Test-Path $ReleaseDir) {
  80. try {
  81. Remove-Item $ReleaseDir -Recurse -Force -ErrorAction Stop
  82. } catch {
  83. # 目录被其他进程占用(如命令行窗口停留在该目录内):保留目录节点,清空内容
  84. Write-Warning "发行目录被其他进程占用,改为清空目录内容后继续"
  85. Get-ChildItem $ReleaseDir -Force | Remove-Item -Recurse -Force
  86. }
  87. }
  88. Push-Location $Backend
  89. try {
  90. python -m PyInstaller `
  91. --noconfirm `
  92. --clean `
  93. --distpath $Dist `
  94. --workpath $BuildCache `
  95. (Join-Path $PSScriptRoot "dms-server.spec")
  96. if ($LASTEXITCODE -ne 0) { throw "PyInstaller 打包失败" }
  97. } finally { Pop-Location }
  98. # ---------- 4. 整理发行包 ----------
  99. Write-Host "`n[4/5] 整理发行包..." -ForegroundColor Yellow
  100. if (-not (Test-Path $ReleaseDir)) { throw "发行目录未找到: $ReleaseDir" }
  101. # 复制配置模板(不含真实 .env,保护凭据)
  102. Copy-Item (Join-Path $Backend ".env.example") $ReleaseDir -Force
  103. # 尝试调用项目自带的导出工具,把示例数据库导出为 SQL 文件
  104. # 工具三参数均必填:--env-file 提供库连接、--storage 提供文件根、--output 为输出目录
  105. # (产物含 full_demo_data.sql 与 storage 文件副本),仅取 SQL 进发行包,临时目录用完即删
  106. $ExportTool = Join-Path $ProjectRoot "tools\export_full_demo_data.py"
  107. $ExportedSql = Join-Path $ReleaseDir "database-init.sql"
  108. if (Test-Path $ExportTool) {
  109. # 运行时 .env 不在 backend 目录:按优先级取最新的现有配置(暂存 > 备份 > backend)
  110. $ExportEnv = @(
  111. $KeepEnv,
  112. (Join-Path $ProjectRoot "dist-package-bak\dms-server\.env"),
  113. (Join-Path $Backend ".env")
  114. ) | Where-Object { Test-Path $_ } | Select-Object -First 1
  115. $ExportStorage = Join-Path $Backend "dms-storage"
  116. if (-not $ExportEnv) {
  117. Write-Warning "未找到可用的 .env(暂存 .env.keep / 备份目录 / backend\.env),跳过自动导出"
  118. } elseif (-not (Test-Path $ExportStorage)) {
  119. Write-Warning "未找到存储目录 $ExportStorage,跳过自动导出"
  120. } else {
  121. Write-Host " 导出示例数据库 SQL..." -ForegroundColor DarkGray
  122. $ExportTmp = Join-Path ([System.IO.Path]::GetTempPath()) "dms-demo-export"
  123. if (Test-Path $ExportTmp) { Remove-Item $ExportTmp -Recurse -Force }
  124. try {
  125. # 通过当前 Python 环境运行导出工具
  126. python $ExportTool --env-file $ExportEnv --storage $ExportStorage --output $ExportTmp 2>&1 | Out-Host
  127. $ExportedSource = Join-Path $ExportTmp "full_demo_data.sql"
  128. if (Test-Path $ExportedSource) {
  129. Copy-Item $ExportedSource $ExportedSql -Force
  130. Write-Host " 已生成 database-init.sql" -ForegroundColor DarkGray
  131. } else {
  132. Write-Warning "导出工具未生成 full_demo_data.sql,请手动准备 SQL 文件"
  133. }
  134. } catch {
  135. Write-Warning "导出工具执行失败:$_"
  136. } finally {
  137. if (Test-Path $ExportTmp) { Remove-Item $ExportTmp -Recurse -Force }
  138. }
  139. }
  140. } else {
  141. Write-Warning "未找到 tools\export_full_demo_data.py,跳过自动导出"
  142. }
  143. # 复制说明文档
  144. Copy-Item (Join-Path $PSScriptRoot "README.txt") $ReleaseDir -Force
  145. # DB upgrade script for existing deployments (dms-server.exe upgrade-db)
  146. Copy-Item (Join-Path $PSScriptRoot "upgrade-db.bat") $ReleaseDir -Force
  147. # rsync 备份工具:Windows 发行包随包携带 cwRsync(rsync.exe + cygwin dll),
  148. # RSYNC 备份模式开箱即用;Linux 用系统 rsync 无需二进制(探测顺序见
  149. # backup_service.locate_executable:DMS_RSYNC_EXECUTABLE → PATH → tools/rsync)
  150. $RsyncTools = Join-Path $PSScriptRoot "tools\rsync"
  151. if (Test-Path $RsyncTools) {
  152. Copy-Item $RsyncTools (Join-Path $ReleaseDir "tools\rsync") -Recurse -Force
  153. Write-Host " 已内置 rsync 备份工具(tools\rsync)" -ForegroundColor DarkGray
  154. } else {
  155. Write-Warning "未找到 $RsyncTools,发行包不含内置 rsync(RSYNC 备份模式需目标机自装或配置 DMS_RSYNC_EXECUTABLE)"
  156. }
  157. # ---------- 5. 恢复 .env 和 dms-storage ----------
  158. # 优先级:打包前暂存(原地数据,Move 回) > 备份目录;发行目录已有的 dms-storage 绝不覆盖
  159. Write-Host "`n[5/5] 恢复 .env 和 dms-storage..." -ForegroundColor Yellow
  160. $BackupDir = Join-Path $ProjectRoot "dist-package-bak\dms-server"
  161. if (-not (Test-Path $BackupDir)) {
  162. Write-Host " 备份目录不存在: $BackupDir - 跳过备份恢复" -ForegroundColor DarkGray
  163. } else {
  164. $BackupEnv = Join-Path $BackupDir ".env"
  165. if (Test-Path $BackupEnv) {
  166. Copy-Item $BackupEnv $ReleaseDir -Force
  167. Write-Host " 已恢复 .env(备份)" -ForegroundColor DarkGray
  168. } else {
  169. Write-Warning "备份中没有 .env,跳过"
  170. }
  171. }
  172. if (Test-Path $KeepStorage) {
  173. Move-Item $KeepStorage (Join-Path $ReleaseDir "dms-storage") -Force
  174. Write-Host " 已保留重打包前的 dms-storage" -ForegroundColor DarkGray
  175. } elseif ((Test-Path $BackupDir) -and (Test-Path (Join-Path $BackupDir "dms-storage"))) {
  176. Copy-Item (Join-Path $BackupDir "dms-storage") (Join-Path $ReleaseDir "dms-storage") -Recurse -Force
  177. Write-Host " 已恢复 dms-storage(备份)" -ForegroundColor DarkGray
  178. } else {
  179. Write-Host " 无 dms-storage 可恢复(暂存与备份均无),跳过" -ForegroundColor DarkGray
  180. }
  181. # 还原打包前暂存的 .env(优先级高于备份目录,始终保留用户最新配置)
  182. if (Test-Path $KeepEnv) {
  183. Copy-Item $KeepEnv (Join-Path $ReleaseDir ".env") -Force
  184. Remove-Item $KeepEnv -Force
  185. Write-Host " 已保留重打包前的 .env" -ForegroundColor DarkGray
  186. }
  187. # 还原打包前暂存的备份管理配置
  188. if (Test-Path $KeepBackupCfg) {
  189. Copy-Item $KeepBackupCfg (Join-Path $ReleaseDir "dms-backup-config.json") -Force
  190. Remove-Item $KeepBackupCfg -Force
  191. Write-Host " 已保留重打包前的 dms-backup-config.json" -ForegroundColor DarkGray
  192. }
  193. Write-Host "`n========== 构建完成 ==========" -ForegroundColor Green
  194. Write-Host "发行包目录: $ReleaseDir" -ForegroundColor Green
  195. $sizeMB = [math]::Round((Get-ChildItem $ReleaseDir -Recurse | Measure-Object -Property Length -Sum).Sum / 1MB, 1)
  196. Write-Host "包大小: ${sizeMB} MB" -ForegroundColor Green
  197. Write-Host "将该目录整体压缩即可分发给最终用户" -ForegroundColor Green