15 KiB
Velopack — Knowledge Base
Tài liệu kiến thức tổng quát về Velopack — framework đóng gói & auto-update cross-platform (Windows / macOS / Linux), kế thừa từ Squirrel. Dùng làm tham chiếu áp dụng cho mọi project (không gắn với codebase nào cụ thể).
1. Velopack là gì?
- Installer + Auto-updater all-in-one cho desktop app.
- Hỗ trợ: .NET, native (C/C++/Rust), Go, Python (PyInstaller), Electron, JS… bất cứ app nào ra được file thực thi.
- Output:
Setup.exe,Portable.zip,.nupkg(delta/full), feed JSON. - Cập nhật không cần admin trên Windows (per-user, vào
%LocalAppData%). - CLI:
vpk(cài quadotnet tool install -g vpk).
So với các giải pháp khác
| Velopack | Squirrel.Windows | MSIX | electron-updater | Inno/NSIS | |
|---|---|---|---|---|---|
| Cross-platform | ✅ Win/Mac/Linux | ❌ chỉ Win | ❌ chỉ Win | ✅ (Electron) | ❌ |
| Per-user, no admin | ✅ | ✅ | ⚠️ | ✅ | ❌ |
| Delta update | ✅ | ✅ | ✅ | ✅ | ❌ |
| Code signing tích hợp | ✅ | ⚠️ | ✅ | ✅ | ⚠️ |
| Bundle runtime (.NET) | ✅ | ⚠️ | ✅ | N/A | ⚠️ |
2. Khái niệm cốt lõi
| Khái niệm | Giải thích |
|---|---|
| Package | Một .nupkg chứa toàn bộ app cho một version |
| Channel | Tag phân nhánh (vd win, win-beta, osx); mỗi channel có feed riêng |
| Full / Delta | Full = toàn bộ app; Delta = diff giữa 2 version (nhỏ hơn nhiều) |
| Feed | URL HTTP(S) chứa releases.<channel>.json + các .nupkg |
| Update.exe | Stub binary do Velopack sinh ra, xử lý swap version |
| Main exe | Binary chính được launch sau cài/khởi động (mainExe trong metadata) |
| Lifecycle hooks | App được gọi với args --veloapp-* ở các thời điểm install/update/uninstall |
3. Layout sau cài đặt (Windows)
%LocalAppData%\<AppId>\
├── Update.exe ← Velopack updater (giữ nguyên giữa các version)
├── <MainExe>.exe ← launcher stub (chạy file trong current\)
├── packages\ ← các .nupkg đã tải (full + delta + cũ để rollback)
│ ├── <AppId>-1.0.0-full.nupkg
│ └── <AppId>-1.0.1-full.nupkg
└── current\ ← version đang active (Update.exe swap thư mục này)
├── sq.version ← metadata version hiện tại
└── <toàn bộ file app>
Quy tắc vàng:
- Code app không bao giờ ghi vào
current\(sẽ mất khi update). - Lưu state ở
%AppData%\<AppName>\hoặc%LocalAppData%\<AppName>\Data\. - Đường dẫn binary là động — luôn dùng
os.Executable()/Process.MainModule.
4. Cài đặt CLI
dotnet tool install -g vpk
vpk --version
Yêu cầu .NET 8 SDK trở lên. Trên CI dùng dotnet tool install -g vpk --version <pin>
để build reproducible.
5. Lệnh vpk pack — tham số quan trọng
vpk pack `
--packId MyApp `
--packVersion 1.2.3 `
--packDir .\publish `
--mainExe MyApp.exe `
--packTitle "My App" `
--packAuthors "Acme Inc" `
--channel win `
--shortcuts "Desktop,StartMenuRoot" `
--icon .\assets\icon.ico `
--signParams "/fd SHA256 /a /tr http://timestamp.digicert.com /td SHA256" `
--outputDir .\releases
| Flag | Mô tả |
|---|---|
--packId |
ID duy nhất (PascalCase, không space). Không bao giờ đổi |
--packVersion |
SemVer 3 phần, monotonic tăng |
--packDir |
Thư mục chứa output build (sẽ pack toàn bộ) |
--mainExe |
Tên file exe chính trong --packDir |
--channel |
Phân nhánh release (default win/osx/linux) |
--shortcuts |
Desktop, StartMenuRoot, StartMenu, Startup, None |
--icon |
Icon cho Setup.exe + shortcut |
--signParams |
Tham số truyền vào signtool sign |
--signTemplate |
Lệnh ký custom (vd dùng azuresigntool) |
--delta |
BestSpeed / BestSize / None |
--noPortable |
Bỏ tạo Portable.zip |
--noInst |
Bỏ tạo Setup.exe |
--releaseNotes |
File markdown cho changelog |
Output --outputDir
releases\
├── MyApp-1.2.3-full.nupkg
├── MyApp-1.2.3-delta.nupkg (nếu có bản trước)
├── MyApp-win-Setup.exe
├── MyApp-win-Portable.zip
├── releases.win.json ← feed
├── RELEASES ← Squirrel legacy manifest
└── assets.win.json
6. Delta update
Velopack tự tạo delta nếu output dir đã có bản trước.
Best practice:
- Trước khi pack version N, sync
<outputDir>từ server (tải về full + delta cũ). - Pack version N → sinh
N-full.nupkg+N-delta.nupkgso với N-1. - Push toàn bộ
<outputDir>lên server.
# 1. download existing releases
vpk download github --repoUrl https://github.com/acme/myapp --outputDir .\releases
# hoặc rsync/scp/aws s3 sync
# 2. pack
vpk pack --packId MyApp --packVersion 1.2.3 ...
# 3. upload
vpk upload github --repoUrl https://github.com/acme/myapp --outputDir .\releases
# hoặc tự upload bằng scp/aws/azcopy
Backend vpk upload có sẵn: github, s3, azure, gitea, http.
7. Feed server
7.1. Cấu trúc URL
https://updates.acme.com/myapp/
├── releases.win.json (Content-Type: application/json)
├── RELEASES
├── MyApp-1.2.2-full.nupkg (Content-Type: application/octet-stream)
├── MyApp-1.2.3-full.nupkg
└── MyApp-1.2.3-delta.nupkg
7.2. Headers khuyến nghị
| Header | Giá trị |
|---|---|
Cache-Control (cho releases.*.json) |
no-cache, max-age=60 |
Cache-Control (cho .nupkg) |
public, max-age=31536000, immutable |
Content-Type (json) |
application/json |
Content-Type (nupkg) |
application/octet-stream |
Nupkg là immutable (hash trong tên file → đổi nội dung = đổi tên).
7.3. CDN
OK dùng CloudFront / Cloudflare / Bunny. Bắt buộc: purge releases.*.json
sau mỗi release. Nếu không, client sẽ thấy version cũ hàng giờ.
8. Tích hợp auto-update vào app
Có 2 lựa chọn:
8.1. Velopack SDK (.NET / Rust / JS)
// .NET
VelopackApp.Build().Run(); // gọi đầu Main(), xử lý hooks
var mgr = new UpdateManager("https://updates.acme.com/myapp/");
var info = await mgr.CheckForUpdatesAsync();
if (info != null) {
await mgr.DownloadUpdatesAsync(info);
mgr.ApplyUpdatesAndRestart(info);
}
// Rust crate: velopack
let app = VelopackApp::build().run();
let um = UpdateManager::new("https://updates.acme.com/myapp/", None, None)?;
if let UpdateCheck::UpdateAvailable(u) = um.check_for_updates()? {
um.download_updates(&u, None)?;
um.apply_updates_and_restart(Some(&u))?;
}
8.2. Tự gọi Update.exe (cho Go, Python, C++…)
App tự fetch feed, tải nupkg, rồi exec Update.exe:
// 1. Đọc version đang chạy
version := readSqVersion(filepath.Join(exeDir(), "sq.version"))
// 2. GET https://updates.acme.com/myapp/releases.win.json
// so sánh với version
// 3. Tải nupkg về %LocalAppData%\MyApp\packages\
// 4. Gọi updater
exec.Command(
filepath.Join(rootDir(), "Update.exe"),
"apply", "--silent",
"--waitPid", strconv.Itoa(os.Getpid()),
"--package", pkgPath,
).Start()
os.Exit(0) // Update.exe sẽ swap rồi restart main exe
Update.exe apply flags hữu dụng:
| Flag | Ý nghĩa |
|---|---|
--silent |
Không hiện UI tiến trình |
--package <path> |
Dùng nupkg cụ thể (skip download từ feed) |
--waitPid <pid> |
Chờ pid này thoát mới swap |
--restart (default on) |
Restart main exe sau khi apply |
--norestart |
Không restart |
9. Lifecycle hooks
Velopack invoke main exe với args đặc biệt. App phải xử lý sớm trong main()
và exit(0) ngay sau khi xong (đừng vẽ UI cho hook).
| Arg | Khi nào | Mục đích thường dùng |
|---|---|---|
--veloapp-install <ver> |
Lần đầu cài | Migrate cấu hình, đăng ký service |
--veloapp-updated <ver> |
Sau mỗi update | Migrate schema DB, copy file out-of-bundle |
--veloapp-obsolete <ver> |
Version vừa bị thay | Cleanup tạm (hiếm dùng) |
--veloapp-uninstall <ver> |
Trước khi gỡ | Unregister service, xoá protocol handler |
Mẫu Go:
func handleStartupArgs() (exit bool) {
if len(os.Args) < 2 { return false }
switch os.Args[1] {
case "--veloapp-install", "--veloapp-updated":
runPostInstallSteps()
return true
case "--veloapp-uninstall":
runCleanupSteps()
return true
case "--veloapp-obsolete":
return true
}
return false
}
func main() {
if handleStartupArgs() { return }
// ... app bình thường ...
}
Quan trọng: hooks chạy SONG SONG với instance app khác có thể đang chạy. Đặt trước single-instance mutex.
10. Code signing
10.1. Windows (Authenticode)
vpk pack ... `
--signParams "/sha1 <THUMBPRINT> /fd SHA256 /tr http://timestamp.digicert.com /td SHA256"
Hoặc dùng template để gọi azuresigntool (Azure Key Vault):
vpk pack ... `
--signTemplate "azuresigntool sign -kvu https://kv.vault.azure.net -kvi $env:AZ_ID -kvs $env:AZ_SECRET -kvc cert-name -tr http://timestamp.digicert.com -td sha256 {{file}}"
{{file}} được Velopack thay bằng đường dẫn từng file cần ký.
10.2. macOS (codesign + notarization)
vpk pack \
--packId MyApp --packVersion 1.2.3 --packDir ./publish --mainExe MyApp \
--icon icon.icns \
--signAppIdentity "Developer ID Application: Acme (TEAMID)" \
--notaryProfile "AC_PASSWORD" # đã lưu bằng `xcrun notarytool store-credentials`
10.3. Linux (AppImage)
vpk pack --packId myapp --mainExe myapp --packDir ./out --channel linux
# output: myapp-1.2.3.AppImage + nupkg + releases.linux.json
11. Bundle runtime / prerequisites
vpk pack ... --runtimes "net8,vcredist140-x64"
Velopack tải runtime installer tương ứng nhúng vào Setup.exe, prompt user cài
nếu thiếu. Danh sách: vpk runtime list.
12. Versioning rules
- SemVer 3 phần:
MAJOR.MINOR.PATCH. Không dùng pre-release tag (-beta1) trừ khi tách channel riêng — Velopack so sánh dạng numeric. - Mỗi release version phải lớn hơn version trước trên cùng channel.
- Downgrade không được hỗ trợ trực tiếp; muốn rollback phải:
- Bump version mới
> latestchứa code cũ, hoặc - Apply thủ công nupkg cũ bằng
Update.exe apply --package <old.nupkg>.
- Bump version mới
13. CI/CD pattern (GitHub Actions)
name: release
on:
push:
tags: ['v*']
jobs:
build:
runs-on: windows-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-dotnet@v4
with: { dotnet-version: '8.x' }
- run: dotnet tool install -g vpk
- name: Build app
run: dotnet publish src/MyApp -c Release -r win-x64 --self-contained -o publish
- name: Download previous releases (for delta)
run: vpk download github --repoUrl ${{ github.server_url }}/${{ github.repository }} --outputDir releases
continue-on-error: true
- name: Pack
run: |
$ver = "${{ github.ref_name }}".TrimStart('v')
vpk pack --packId MyApp --packVersion $ver --packDir publish `
--mainExe MyApp.exe --channel win --outputDir releases
- name: Upload
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: vpk upload github --repoUrl ${{ github.server_url }}/${{ github.repository }} --outputDir releases --publish --releaseName "v$ver" --tag "v$ver"
14. Best practices
14.1. App design
- Đặt
VelopackApp.Build().Run()(hoặchandleStartupArgs()) dòng đầu củamain. - Tách state khỏi
current\(sẽ bị wipe khi update). - Single-instance check sau khi xử lý hooks (hooks là one-shot, không xung đột).
- Auto-update check: delay 30–90s sau khởi động (tránh slow start), repeat 4–12h.
14.2. Operations
- Luôn HTTPS cho feed.
- Ký số tất cả
.exetrước khi pack. - Theo dõi telemetry update success rate (tỉ lệ apply / check).
- Giữ ≥ 3 bản gần nhất trên feed để delta có "anchor".
- Bump version ngay cả khi chỉ fix metadata — không bao giờ re-upload nupkg cùng tên với nội dung khác.
14.3. Bảo mật
- Velopack verify SHA256 của nupkg trước khi apply (so với feed JSON).
- Nếu app cần admin (vd service Windows), elevate chỉ phần đó qua một worker
riêng (
ShellExecuteEx "runas"), đừng chạy cả app dưới UAC. - Không bao giờ download nupkg từ HTTP plaintext; tránh mixed-content nếu chạy trong WebView.
15. Troubleshooting
| Triệu chứng | Nguyên nhân | Cách xử lý |
|---|---|---|
| Client không thấy update | Cache releases.<channel>.json |
Purge CDN / set no-cache |
Update.exe apply exit 0, không đổi version |
Cùng version hoặc nupkg hỏng | Bump version, pack lại, verify SHA256 |
| "Hash mismatch" | Nupkg trên server khác hash trong feed | Re-upload đồng bộ cả 2 |
| App không restart sau update | Main exe crash sớm | Bọc panic recovery, xem log Velopack |
| Setup.exe đòi admin | Không phải Velopack issue — do AV / SmartScreen | Ký số EV, build reputation |
| Delta apply fail rồi tự fallback full | Bình thường, log thôi | Không cần fix |
Log paths (Windows)
- App: tự define (vd
%ProgramData%\<App>\Logs\) - Velopack:
%LocalAppData%\<AppId>\SquirrelClowdTemp\*.log - Setup:
%TEMP%\Velopack.*.log
Force update ngay (debug)
& "$env:LOCALAPPDATA\<AppId>\Update.exe" apply --silent `
--package "$env:LOCALAPPDATA\<AppId>\packages\<AppId>-x.y.z-full.nupkg"
Rollback
& "$env:LOCALAPPDATA\<AppId>\Update.exe" apply --silent `
--package "$env:LOCALAPPDATA\<AppId>\packages\<AppId>-<old>-full.nupkg"
16. Checklist release nhanh
- ☐ Bump version (
MAJOR.MINOR.PATCH) trong source + CI tag - ☐ Build release binary, output vào
publish\ - ☐ Ký số các
.exe(hoặc dùng--signParams) - ☐
vpk downloadbản trước vềreleases\ - ☐
vpk pack --packVersion <new> - ☐ Smoke test local:
Update.exe apply --package <new>-full.nupkg - ☐ Upload
releases\*lên feed - ☐ Purge cache
releases.<channel>.json - ☐ Verify từ máy khác: tray/app tự update sau interval
17. Tham khảo
- Trang chủ: https://velopack.io
- Docs: https://docs.velopack.io
- Repo: https://github.com/velopack/velopack
- Samples: https://github.com/velopack/velopack/tree/develop/samples
- Discord cộng đồng: link ở trang chủ