跳轉到

ADR 0003 — Velopack 發布流程與版本號管理

  • 日期:2026-04-20
  • 狀態:Accepted
  • 決策者:使用者

Context

TsERP 之前用 ClickOnce 發布(autodeploy.ps1 / darbtestDeploy.ps1),分別上傳到 Azure Blob 的 cy-erp / darb-test container。程式同時已經引入 Velopack 0.0.1298TsERP.csproj),App.xaml.cs:60VelopackApp.Build().Run()App.xaml.cs:72https://tworkerpdeploy.blob.core.windows.net/darb-vpk 抓更新,但沒有配套的發布腳本,僅有 TsERP/打包.txt 一行 vpk pack 範例。

手動組指令會踩的坑:

  1. 版本號:csproj 內只有 ClickOnce 用的 <ApplicationVersion>1.0.1.%2a</ApplicationVersion>(4 段帶萬用字元),不符合 Velopack 需要的 3 段式 SemVer
  2. Delta update:若沒先 vpk download 既有 releases,vpk pack 會當作首次發布,使用者端拿不到 delta 只能整包下載
  3. 簽章參數:csproj ManifestCertificateThumbprint = 66AE9BC6... 只用在 ClickOnce manifest,Velopack EXE 簽章要另外傳 --signParams
  4. Azure 上傳:既有 ClickOnce 腳本上傳路徑結構(Application Files/<版本資料夾>/)與 Velopack(*.nupkg + RELEASES 放 container 根)完全不同,不能直接套用

Decision

  1. csproj 新增獨立標籤 <VelopackVersion>X.Y.Z</VelopackVersion>
  2. 與 ClickOnce 的 <ApplicationVersion> / <ApplicationRevision> 完全解耦,各走各的
  3. 由發布腳本讀取 / 遞增 / 寫回,用 regex 替換避免 XmlDocument 重寫整份檔案打亂 git diff

  4. 發布腳本放 TsERP/release-velopack.ps1,單一環境(darb-vpk container,無 multi-channel 參數)

  5. 版號策略:每次發布前自動把 patch 段 +1(1.0.1 -> 1.0.2),不提供手動指定

  6. Major / Minor 需要時使用者直接編輯 csproj

  7. 流程順序固定

  8. 檢查 csproj 無未提交變更(避免 auto-commit 掃入無關 diff)
  9. 版號 +1 寫回 csproj
  10. dotnet restore -> Framework MSBuild.exe /t:Publish(見下點 7)輸出到 publish\
  11. vpk download http --url <feed>(抓既有 releases 產 delta;-SkipDownload 首次發布用)
  12. vpk pack--signParams "/fd SHA256 /td SHA256 /tr http://timestamp.sectigo.com /sha1 66AE9BC6..."
  13. az login --tenant 75ef... + az storage blob upload-batchdarb-vpk
  14. git commit 只 add TsERP/TsERP.csproj,訊息 release: v{newVersion}

  15. Publish 必須用 .NET Framework 版 MSBuildTsERP.csprojViewModel.csproj 都有 <COMReference>(Office.Core、VBIDE)。dotnet publish 底層走 .NET Core 版 MSBuild,不支援 ResolveComReference 任務,build 會報 MSB4803 直接失敗。腳本透過 vswhere.exe 定位 VS 2022 / Build Tools 的 Framework MSBuild 後再呼叫 /t:Publish

  16. 簽章:沿用 ClickOnce 同一張 EV 憑證(USB 硬體 token,signtool 會彈 PIN 視窗)。--signParams 透過 /sha1 <thumbprint> 對應,無需匯出 PFX

  17. 逃生閘:4 個開關 -SkipDownload-SkipSign-SkipUpload-SkipCommit,給首次發布、本地驗證、USB 未插時使用

Consequences

正面

  • vpk pack / upload 流程腳本化,減少手動輸入出錯
  • 版號單向遞增 + 自動 commit,git log 可追每一版發布時點
  • ClickOnce 舊腳本完全不動,兩條線並存直到決定淘汰 ClickOnce

負面 / 風險

  • 首次發布必須記得加 -SkipDownloaddarb-vpk container 沒有 RELEASES 檔時 vpk download http 會失敗並中止流程(版號此時已寫回,重跑前需手動 revert csproj 或繼續用新版號)
  • 發布過程中任何階段失敗,csproj 版號已寫回但還沒 commit:重跑前需要 git checkout -- TsERP/TsERP.csproj 還原,否則第二次執行會因 pre-check(csproj 不乾淨)被拒
  • 版號強制 +1:若 pack 或 upload 失敗、但前面 csproj 已 commit,需要手動 revert commit 才能重試同版號。目前實作 commit 放在最後一步降低機率,但不是 100% 安全
  • csproj auto-commit 會合併到 release commit:若 csproj 同時有其他無關修改沒 commit,腳本會拒絕執行,強迫使用者先處理(預期行為)
  • EV 簽章的 USB PIN 每次都要人工輸入:不能 fully unattended,CI/CD 化需換 HSM / 雲端 signing
  • Build 機器必須裝 Visual Studio 2022 或 Build Tools:腳本依賴 vswhere + Framework MSBuild;若只有 .NET SDK 會找不到 MSBuild.exe 直接中止

Migration

  • 既有安裝的 ClickOnce 使用者無法自動升級到 Velopack 安裝包,兩者是不同的安裝方式。切換時需規劃使用者端手動遷移(移除 ClickOnce 版、安裝 Velopack setup.exe)
  • 本 ADR 範圍內不處理 migration,僅建立 Velopack 發布管線

Alternatives Considered

  • (拒絕) 複用 <ApplicationVersion>1.0.1.%2a</ApplicationVersion> 解析出 1.0.1:看似省一個標籤,但兩條發布線(ClickOnce、Velopack)的版本推進節奏不一樣,硬綁會互相干擾。獨立標籤清楚、未來淘汰 ClickOnce 時刪掉對應標籤即可
  • (拒絕) 用 [xml]$csproj 物件修改後 Save:會重寫整份 XML,縮排、屬性順序、換行全會被 .NET XmlWriter 重排,製造巨大 git diff。regex replace 只動單一標籤,diff 乾淨
  • (拒絕) 腳本自動 git push:push 是跨機器可見的破壞性操作,照 CLAUDE.md 規範不自動做。使用者驗證 release commit 後手動 push
  • (拒絕) 支援多 channel(darb-vpk / darb-test / cy-erp):本次需求明確只做單一環境,多 channel 等確有需求再擴。避免過早抽象
  • (拒絕) 自動 git tag v{version}:使用者未要求,且 tag 推上 remote 後不易回收,保守不做