Aplikasi Wails dengan Self-Update
Dalam tutorial ini Anda akan menambahkan updater dalam aplikasi ke aplikasi Wails v3 baru. Di akhir, aplikasi akan:
- Mengecek GitHub Releases sesuai permintaan (dan opsional dengan timer).
- Mengunduh asset yang tepat untuk OS + arsitektur yang sedang berjalan.
- Memverifikasi digest SHA-256 (dan opsional tanda tangan Ed25519) terhadap byte yang diunduh.
- Menampilkan release notes di jendela update default framework.
- Menukar biner yang sedang berjalan dan meluncurkan ulang — semua tanpa mengirim executable helper terpisah.
Kita akan menggunakan GitHub Releases sebagai sumber update karena gratis dan tidak memerlukan infrastruktur. Pola yang sama bekerja dengan keygen.sh dan Sparkle AppCast — lihat panduan Updater setelah selesai.
Mulai dengan aplikasi Wails baru
Buat proyek baru dengan template vanilla:
wails3 init --template vanilla --name updater-tutorial
cd updater-tutorial
Anda sekarang seharusnya memiliki direktori dengan main.go, frontend/, dan Taskfile.yml. Konfirmasi dapat di-build dan diluncurkan:
wails3 task dev
Jendela Wails kosong seharusnya terbuka. Keluar dan lanjutkan.
Tambahkan import updater
Buka main.go dan tambahkan dua paket updater ke import Anda:
package main
import (
_ "embed"
"github.com/wailsapp/wails/v3/pkg/application"
"github.com/wailsapp/wails/v3/pkg/updater"
"github.com/wailsapp/wails/v3/pkg/updater/providers/github"
)Ini menarik Updater itu sendiri dan provider GitHub Releases.
Konfigurasi Updater
app.Updater sudah terhubung ke setiap *application.App — Anda hanya perlu memanggil Init:
const currentVersion = "1.0.0"
gh, err := github.New(github.Config{
Repository: "yourorg/your-repo", // ← change this
ChecksumAsset: "SHA256SUMS", // sibling file with sha256 digests
})
if err != nil {
log.Fatalf("github.New: %v", err)
}
if err := app.Updater.Init(updater.Config{
CurrentVersion: currentVersion,
Providers: []updater.Provider{gh},
}); err != nil {
log.Fatalf("Updater.Init: %v", err)
}Tempatkan ini setelah application.New dan sebelum app.Run().
Tambahkan item menu yang memicu update
Di main.go yang sama, tambahkan entri menu “Check for Updates…”:
menu := app.Menu.New()
app.Menu.SetApplicationMenu(menu)
appMenu := menu.AddSubmenu("App")
appMenu.Add("Check for Updates…").OnClick(func(*application.Context) {
go func() {
if err := app.Updater.CheckAndInstall(context.Background()); err != nil {
app.Logger.Error("update", "error", err)
}
}()
})CheckAndInstall membuka jendela update framework, menjalankan Check, dan jika rilis ditemukan, menjalankan DownloadAndInstall otomatis. Jendela tetap terbuka dalam state “Up to Date” saat tidak ada yang baru — pengguna menutupnya dengan tombol Close.
Jalankan sekali tanpa rilis
wails3 task dev
Klik App → Check for Updates…. Anda seharusnya melihat jendela update terbuka sebentar, mengakses API GitHub, tidak menemukan rilis lebih baru dari 1.0.0, dan menetap pada state Up to Date dengan ✓ hijau.
Jika Anda mendapat error di sini, biasanya salah satu dari:
| Gejala | Perbaikan |
|---|---|
404 Not Found |
Field Repository salah — harus owner/repo |
403 rate-limited |
Tambahkan Token: "ghp_…" ke github.Config (gunakan PAT dengan scope public_repo) |
| Network errors | Konfirmasi aplikasi yang berjalan dapat mengakses api.github.com |
Terbitkan rilis uji
Naikkan currentVersion di main.go ke 1.0.0 (atau biarkan). Build untuk satu platform untuk mendapatkan biner yang dapat dilampirkan ke rilis:
wails3 task build:darwin
# produces bin/updater-tutorial.app
# zip it for the release asset:
cd bin && zip -r updater-tutorial-darwin-arm64.zip updater-tutorial.app && cd ..
wails3 task build:linux
# produces bin/updater-tutorial
mv bin/updater-tutorial bin/updater-tutorial-linux-amd64
wails3 task build:windows
# produces bin/updater-tutorial.exe
mv bin/updater-tutorial.exe bin/updater-tutorial-windows-amd64.exe
Buat file SHA256SUMS di samping biner:
cd bin
shasum -a 256 updater-tutorial-* > SHA256SUMS
cat SHA256SUMS
Anda seharusnya melihat satu atau lebih baris seperti:
abc123… updater-tutorial-darwin-arm64.zipSekarang terbitkan ini sebagai v2.0.0 di repositori GitHub Anda:
gh release create v2.0.0 \
--title "v2.0.0" \
--notes "First update for the self-update tutorial.
- **Bold** Markdown renders in the update window
- \`Code spans\` too
- Lists work
- GFM tables work" \
bin/SHA256SUMS bin/updater-tutorial-*
Jalankan aplikasi dan verifikasi update
Dengan currentVersion masih 1.0.0, jalankan aplikasi lagi:
wails3 task dev
Klik App → Check for Updates…. Kali ini Anda seharusnya melihat sesuatu seperti:

- Ikon hero berubah dari biru ↓ (“Update Available”) ke hijau ✓ (“Update Ready”).
- Subtitle menampilkan
v1.0.0 → v2.0.0 · <size>. - Panel release notes merender Markdown Anda dengan bold, code span, dan tabel.
- Progress bar terisi selama unduhan (cepat — biner kecil).
Updater men-stage biner baru di direktori temp. Untuk menyelesaikan update:
- Klik Restart & Apply.
- Aplikasi Anda keluar, helper menukar biner, biner baru diluncurkan ulang.
- Aplikasi yang diluncurkan ulang melaporkan
currentVersion = "1.0.0"(karena kita hardcode), tetapi byte di disk sesuai build v2.0.0.
Di aplikasi nyata, currentVersion akan diset saat build via -ldflags agar biner baru tahu sekarang v2.0.0 dan pengecekan berikutnya tidak menemukan update.
Hubungkan `currentVersion` ke build
Ganti konstanta dengan variabel build-time:
var (
currentVersion = "dev" // overridden by -ldflags at release time
)Lalu di perintah build Anda:
wails3 task build:darwin -- -ldflags "-X main.currentVersion=2.0.0"
Atau tambahkan -ldflags ke Taskfile.yml agar mengambil dari git describe --tags.
Tambahkan penandatanganan kriptografis (disarankan untuk produksi)
Path SHA256SUMS memverifikasi integritas (byte sesuai yang disimpan GitHub) tetapi bukan autentisitas (byte tersebut diproduksi pipeline rilis Anda, bukan akun maintainer yang dikompromikan). Untuk ketahanan terhadap perubahan, tandatangani setiap rilis dengan kunci Ed25519:
# One-time: generate the keypair
ssh-keygen -t ed25519 -f updater-key -N "" -C "wails-updater"
# updater-key — keep secret (build server, HSM, password manager)
# updater-key.pub — bundle in your app
Untuk setiap rilis, tandatangani digest SHA-256 setiap asset dengan kunci privat Anda. Helper Go kecil:
package main
import (
"crypto/ed25519"
"crypto/sha256"
"encoding/base64"
"fmt"
"io"
"os"
)
func main() {
priv, _ := os.ReadFile("updater-key")
key := ed25519.PrivateKey(priv) // raw 64-byte private key
f, _ := os.Open(os.Args[1])
defer f.Close()
h := sha256.New()
_, _ = io.Copy(h, f)
sig := ed25519.Sign(key, h.Sum(nil))
fmt.Println(base64.StdEncoding.EncodeToString(sig))
}Provider GitHub default saat ini tidak mengambil file tanda tangan terpisah — Anda dapat menulis provider kustom yang melakukannya, atau beralih ke keygen.sh yang menandatangani setiap artifact di sisi server dan mengekspos digest dan tanda tangan via API-nya.
Embed kunci publik di aplikasi Anda:
//go:embed updater-key.pub
var updaterPublicKey []byte
app.Updater.Init(updater.Config{
CurrentVersion: currentVersion,
PublicKey: updaterPublicKey,
Providers: []updater.Provider{gh},
})Dengan PublicKey diset, rilis apa pun yang mengirim Signature harus diverifikasi terhadap kunci ini. Sumber rilis tidak dapat mengganti kuncinya sendiri — itulah tujuan pinning out-of-band saat build time.
Sesuaikan jendela
Jendela default mencakup kasus umum. Tiga jalan keluar jika Anda butuh kontrol lebih — pilih satu berdasarkan seberapa banyak yang ingin disesuaikan:
app.Updater.Init(updater.Config{
// …
Window: &updater.BuiltinWindow{
CSS: `:root { --accent: #ff6f00; --radius: 16px; }`,
},
})Lihat bagian Tema via variabel CSS untuk daftar variabel lengkap.
//go:embed updater-window.html
var updaterHTML string
app.Updater.Init(updater.Config{
// …
Window: &updater.BuiltinWindow{HTML: updaterHTML},
})HTML Anda harus subscribe ke event updater:* dan emit aksi updater:user:* via channel event Wails. Lihat Ganti template untuk shim JS.
myWin := app.Window.NewWithOptions(application.WebviewWindowOptions{
Title: "My App Updater",
Width: 520, Height: 460,
HTML: updaterHTML,
AllowSimpleEventEmit: true, // required — see security note
})
app.Updater.Init(updater.Config{
// …
Window: updater.BYOWindow(myWin.AsUpdaterWindow()),
})Berguna saat Anda sudah punya infrastruktur jendela sendiri dan ingin updater mengendalikannya alih-alih membuka jendela lain. Template HTML sepenuhnya kustom — didorong oleh event updater yang sama dengan default — terlihat seperti ini:

Jalankan pengecekan otomatis di latar belakang
Untuk mengecek dengan timer alih-alih (atau selain) klik menu:
app.Updater.Init(updater.Config{
CurrentVersion: currentVersion,
Providers: []updater.Provider{gh},
PublicKey: updaterPublicKey,
CheckInterval: 6 * time.Hour,
})Setiap tick menjalankan alur CheckAndInstall yang sama seperti klik manual. Set Window: updater.WindowNone jika Anda ingin pengecekan berkala diam sampai sesuatu benar-benar ditemukan — lalu subscribe ke EventUpdateAvailable sendiri untuk memutuskan UX apa yang ditampilkan.
Selesai
Anda sekarang memiliki aplikasi Wails yang:
- Mengecek GitHub Releases untuk update sesuai permintaan dan dengan timer.
- Merender release notes sebagai Markdown di jendela default yang rapi.
- Memverifikasi unduhan terhadap digest SHA-256 yang Anda terbitkan.
- Opsional memverifikasi tanda tangan Ed25519 terhadap kunci publik yang Anda embed saat build time.
- Menukar biner yang sedang berjalan in-place dan meluncurkan ulang otomatis.
Langkah selanjutnya
- Panduan Updater memiliki referensi API lengkap, setiap event, setiap opsi konfigurasi, dan mekanisme swap mode helper.
- Lihat
v3/examples/updateruntuk contoh lengkap yang dapat Anda clone. - Repo target uji
wailsapp/updater-demomenunjukkan tata letak release-asset yang disarankan.
Hal yang perlu diperhatikan di produksi
- Code signing di macOS — Gatekeeper memerlukan biner yang ditukar ditandatangani dan dinotarisasi. Tandatangani bundle
.appAnda sebelum zip untuk rilis. Updater mempertahankan byte verbatim; tidak menandatangani ulang apa pun. - Antivirus di Windows — file
.exeunsigned yang diunduh dari internet dapat memicu peringatan SmartScreen. Tandatangani biner dengan sertifikat Authenticode, atau terima bahwa pengguna di mesin terkunci mungkin perlu whitelist aplikasi Anda. - Rilis atomik — terbitkan
SHA256SUMS(dan biner Anda) bersama, bukan di commit terpisah. Updater mengambil sidecar terpisah dari biner; jika keduanya drift, pemeriksaan digest gagal closed. - Versi yang dilewati — tombol “Skip This Version” jendela default mencatat skip secara lokal. Jika Anda mengirim update keamanan kritis, beri nomor versi baru agar tidak otomatis dilewati pengguna yang menolak rilis sebelumnya.