This document describes the complete automated release workflow for WhatsApp Backup Reader, including architecture, best practices, and troubleshooting.
The release process follows a draft-first workflow ensuring complete releases:
dev → main → Semantic Release (draft) → Parallel Builds → Publish OR Cleanup
Key Feature: Releases are never visible to users until all platform binaries are successfully built and uploaded.
┌─────────────────────────────────────────────────────────────┐
│ 1. Developer pushes commit to main │
└──────────────────┬──────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 2. Semantic Release analyzes commit messages │
│ - feat: → Minor version (1.0.0 → 1.1.0) │
│ - fix: → Patch version (1.0.0 → 1.0.1) │
│ - BREAKING CHANGE: → Major version (1.0.0 → 2.0.0) │
│ - chore/docs/etc: → No release │
└──────────────────┬──────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 3. Creates DRAFT release (invisible to users) │
│ - Updates CHANGELOG.md │
│ - Commits version bump │
│ - Creates git tag │
│ - Triggers build workflow via workflow_dispatch │
└──────────────────┬──────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 4. Build Workflow starts │
│ │
│ ┌──────────────────────────────────────────────┐ │
│ │ Prebuild Job (runs once) │ │
│ │ - Builds SvelteKit app │ │
│ │ - Uploads as artifact │ │
│ └──────────┬───────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────
10BC0
───────────────────────────────────────┐ │
│ │ Matrix Build Jobs (parallel, ~15 min) │ │
│ │ ┌────────────┐ ┌────────────┐ ┌────────────┐ │ │
│ │ │ macOS │ │ Windows │ │ Linux │ │ │
│ │ │ - x64 │ │ - x64 │ │ - x64 │ │ │
│ │ │ - arm64 │ │ - arm64 │ │ - arm64 │ │ │
│ │ │ → DMG, ZIP │ │ → EXE, ZIP │ │ → DEB, RPM, AppImage │ │ │
│ │ └─────┬──────┘ └─────┬──────┘ └─────┬──────┘ │ │
│ └────────┼──────────────┼──────────────┼─────────────┘ │
│ │ │ │ │
│ └──────────────┴──────────────┘ │
│ │ │
└──────────────────────────┼──────────────────────────────────┘
│
┌───────┴────────┐
│ │
▼ ▼
┌──────────────────┐ ┌──────────────────┐
│ All Succeeded? │ │ Any Failed? │
│ │ │ │
│ publish-release │ │ cleanup-failed │
│ (if: success()) │ │ (if: failure()) │
│ │ │ │
│ Makes draft │ │ Deletes draft │
│ PUBLIC ✅ │ │ release 🗑️ │
└──────────────────┘ └──────────────────┘
- Draft created → Invisible to users
- All builds succeed → Assets uploaded to draft
publish-releasejob runs → Draft becomes public- Users see complete release with all assets
- Draft created → Invisible to users
- Any build fails → Some assets may be uploaded
cleanup-failed-releasejob runs → Draft deleted- Users never see the incomplete release
Trigger: Push to dev branch or Pull Request to dev/main
Workflow: .github/workflows/ci.yml
Jobs:
- ✅ Lint: Code quality checks with Biome
- 🔍 Type Check: TypeScript validation
- 🏗️ Build: Verify SvelteKit build succeeds
Purpose: Quality gates before merging to main
Trigger: Push to main branch (usually via merge from dev)
Workflow: .github/workflows/release.yml
Steps:
- Analyze commit messages since last release
- Determine next version based on commit types (see Versioning)
- Generate
CHANGELOG.md - Update
package.jsonversion - Create Git tag
- Create DRAFT GitHub release (invisible to users)
- Trigger Build Workflow with new version
Configuration: .releaserc.json with "draftRelease": true
Key Benefit: Users never see incomplete releases
Trigger:
- Automatically triggered by Release workflow
- Manual dispatch with version input
- Tag pushes matching
v* - Release published events
Workflow: .github/workflows/build.yml
Prebuild Job (runs once):
- Builds SvelteKit app
- Shares build artifact with all platforms
- Prevents redundant builds
Matrix Strategy (parallel builds, ~15 minutes):
- 🍎 macOS:
macos-latest - 🪟 Windows:
windows-latest - 🐧 Linux:
ubuntu-latest
Reliability Features:
fail-fast: false- One platform failure doesn't stop others- Retry logic: 3 attempts for npm install
- Artifact caching: Electron binaries cached between runs
Build Outputs:
| Platform | Architectures | Outputs | Count |
|---|---|---|---|
| macOS | x64, arm64 | .dmg, .zip |
4 files |
| Windows | x64, arm64 | .exe (NSIS installer), portable .zip |
4 files |
| Linux | x64, arm64 | .deb, .rpm, .AppImage |
6 files |
| Auto-updater | all | .yml, .blockmap |
5 files |
Total: ~19 files per release
Upload targets:
- ✅ GitHub Release (attached to draft)
- ✅ Workflow Artifacts (7-day retention for debugging)
Conditional Jobs (only one runs):
- Trigger: All matrix builds succeed
- Action: Makes draft release public
- Result: Users see complete release with all binaries
- Trigger: Any matrix build fails
- Action: Deletes draft release
- Result: Failed release never visible to users
Trigger: Push to main branch (runs in parallel with release)
Workflow: .github/workflows/deploy.yml
Steps:
- Build SvelteKit static site
- Upload to GitHub Pages
- Deploy to production
Live URL: Configured in repository settings
Follows Semantic Versioning (MAJOR.MINOR.PATCH) with Angular commit conventions:
| Commit Type | Example | Version Bump | Appears in Changelog |
|---|---|---|---|
feat: |
feat: add dark mode |
MINOR (1.0.0 → 1.1.0) | ✅ ✨ Features |
fix: |
fix: resolve crash on startup |
PATCH (1.0.0 → 1.0.1) | ✅ 🐛 Bug Fixes |
perf: |
perf: optimize image loading |
PATCH | ✅ ⚡ Performance |
refactor: |
refactor: simplify parser logic |
PATCH | ✅ ♻️ Refactoring |
docs: |
docs: update README |
No release | ❌ |
style: |
style: format code |
No release | ❌ |
chore: |
chore: update dependencies |
No release | ❌ |
test: |
test: add unit tests |
No release | ❌ |
ci: |
ci: update workflow |
No release | ❌ |
Use BREAKING CHANGE: in commit footer to trigger MAJOR version bump:
feat: redesign UI
BREAKING CHANGE: Old themes are no longer compatible
This changes 1.0.0 → 2.0.0
-
Develop on
devbranchgit checkout dev git pull origin dev
-
Create feature branch
git checkout -b feat/my-feature
-
Commit with conventional format
git commit -m "feat: add awesome feature" git commit -m "fix: resolve bug in parser"
-
Push and create PR to
devgit push origin feat/my-feature
- CI workflow validates code
- Merge after approval
-
Merge
devtomaingit checkout main git pull origin main git merge dev git push origin main
-
Automated process starts:
- ✅ Semantic Release analyzes commits
- ✅ New version determined
- ✅ CHANGELOG updated
- ✅ GitHub release created
- ✅ Build workflow triggered
- ✅ Binaries built for all platforms
- ✅ Assets uploaded to release
- ✅ GitHub Pages deployed
-
Verify release
gh release view v1.x.x
For critical fixes that can't wait for normal development cycle:
git checkout main
git pull origin main
git checkout -b hotfix/critical-bug
git commit -m "fix: critical security issue"
git push origin hotfix/critical-bugCreate PR directly to main → merge → automatic release
If automatic build fails or you need to rebuild:
gh workflow run build.yml --ref main -f version=1.10.0Replace 1.10.0 with the version to build.
# List recent runs
gh run list --limit 10
# Check specific workflow
gh run list --workflow=build.yml --limit 5
# View run details
gh run view <run-id>
# Download logs
gh run download <run-id># View release
gh release view v1.10.0
# List assets
gh release view v1.10.0 --json assets --jq '.assets[] | .name'
# Download release
gh release download v1.10.0Symptoms: Push to main, but no release appears
Causes:
- ✋ No releasable commits (only
docs:,chore:,style:,test:,ci:) - ✋ Commit message doesn't follow convention
- ✋ Already released (no new commits since last release)
Solution:
# Check recent commits
git log --oneline -5
# If needed, add a proper commit
git commit --allow-empty -m "fix: trigger release"
git push origin mainSymptoms: GitHub release exists but has no .dmg, .exe, etc.
Causes:
- ❌ Build workflow not triggered
- ❌ Build jobs failed
- ❌ Draft release cleaned up (builds failed)
Solution:
# Check if release is a draft
gh release list | grep Draft
# Check build runs
gh run list --workflow=build.yml --limit 3
# If no recent runs, trigger manually
gh workflow run build.yml --ref main -f version=<version>
# If runs failed, check logs
gh run view <run-id> --log-failedSymptoms: Release visible as "Draft" in releases list
Causes:
- ❌ Cleanup job failed to execute
- ❌ Workflow permission issues
Solution:
# Check cleanup job logs
gh run view <run-id> --job=<cleanup-job-id>
# Manually delete draft
gh release delete v<version> --yes
# Note: Tag still exists, delete if needed
git push origin :refs/tags/v<version>Symptoms: Only some platforms have binaries in release
Causes:
- ❌ One or more matrix jobs failed
- ❌ Platform-specific build error
Solution:
# View specific run
gh run view <run-id>
# Check which jobs failed
gh run view <run-id> --log-failed
# Common fixes:
# - Check node_modules compatibility
# - Verify icon files exist (static/icon.{icns,ico,png})
# - Ensure package.json has correct build config
# Note: If ANY build fails, entire release is cleaned up
# Fix the issue and the workflow will retry automaticallySymptoms: Build workflow completes, but release has no files
Causes:
- ❌ Release doesn't exist yet (draft not created)
- ❌ Permission issues
- ❌ Wrong tag name in upload step
Solution:
# Verify release exists (including drafts)
gh release view v<version>
# If not, semantic-release didn't run
# Check release workflow logs
gh run list --workflow=release.yml --limit 3
# Check permissions in .github/workflows/build.yml
# Should have: permissions: contents: writeSymptoms: Build fails during dependency installation
Causes:
- ❌ Electron CDN temporarily unavailable
- ❌ electron-builder CDN issues
Solution:
- ✅ Automatic: Retry logic attempts 3 times with
--prefer-offline - ✅ Manual: Re-run the workflow from GitHub Actions UI
- ✅ Wait: CDN usually recovers within minutes
Prevention: Workflow already implements retry logic, failures should be rare
Before merging to main:
- All commits follow conventional format (
feat:,fix:, etc.) - CI passes on
devbranch - Breaking changes documented in commit footer
- README translations updated (if applicable)
After automated release (verify):
- GitHub release created (check:
gh release list) - Release is NOT a draft (cleanup job would have run if builds failed)
- CHANGELOG.md updated in repository
- All platform binaries present (~19 files):
- 4 macOS files (DMG, ZIP for x64 and arm64)
- 4 Windows files (EXE, portable ZIP for x64 and arm64)
- 6 Linux files (DEB, RPM, AppImage for x64 and arm64)
- 5 Auto-updater files (YML, blockmap)
- GitHub Pages deployed successfully
- Test download and installation on at least one platform
Quick verification:
# Check latest release
gh release view --json name,isDraft,assets
# Should show: "isDraft": false and ~17 assetsThe workflows use GITHUB_TOKEN automatically provided by GitHub Actions:
- Release workflow:
contents: write,issues: write,pull-requests: write,actions: write - Build workflow:
contents: write - Deploy workflow:
contents: read,pages: write,id-token: write
No manual token configuration needed.
All conditional jobs inherit permissions from the workflow:
- publish-release: Requires
contents: writeto update draft status - cleanup-failed-release: Requires
contents: writeto delete releases
| Phase | Duration | Notes |
|---|---|---|
| Prebuild | ~1 minute | SvelteKit build (shared) |
| macOS build | ~14 minutes | DMG signing is slowest |
| Windows build | ~5 minutes | Fastest platform |
| Linux build | ~5 minutes | Fast with DEB/RPM only |
| Total | ~15 minutes | Parallel execution |
Comparison:
- Sequential (old): 45+ minutes
- Parallel (current): ~15 minutes
- Speedup: 3x faster
- Shared Prebuild: SvelteKit built once, reused by all platforms
- Parallel Matrix: All platforms build simultaneously
- Artifact Caching: Electron binaries cached between runs
- Retry Logic: Automatic recovery from transient failures
Electron Builder settings in package.json:
{
"build": {
"appId": "com.whatsapp.backup.reader",
"productName": "WhatsApp Backup Reader",
"directories": {
"output": "dist-electron"
},
"mac": {
"icon": "static/icon.icns",
"category": "public.app-category.utilities",
"target": ["dmg", "zip"],
"arch": ["x64", "arm64"]
},
"win": {
"icon": "static/icon.ico",
"target": ["nsis", "portable"],
"arch": ["x64", "arm64"]
},
"linux": {
"icon": "static/icon.png",
"category": "Utility",
"target": ["deb", "rpm", "AppImage"],
"arch": ["x64", "arm64"]
}
}
}Check releases:
# List all releases (including drafts)
gh release list
# View specific version with details
gh release view v1.13.4 --json name,isDraft,publishedAt,assets
# Compare versions
git log v1.13.3...v1.13.4 --oneline
# Check if any drafts exist (should be empty)
gh release list | grep DraftReleases can be in three states:
- Draft (invisible): Build in progress or failed
- Published (visible): Build succeeded, users can download
- Deleted: Build failed, cleanup ran
Example:
$ gh release list
TITLE TYPE TAG NAME PUBLISHED
v1.13.4 Latest v1.13.4 1 hour ago # ✅ Published successfully
v1.13.3 v1.13.3 2 hours ago # ❌ Was draft, cleaned up
v1.13.2 v1.13.2 3 hours ago # ❌ Was draft, cleaned up
v1.13.1 v1.13.1 4 hours ago # ✅ Published successfullyNote: v1.13.2 and v1.13.3 appear in history but were never visible to users (were drafts, then deleted).
# Watch active builds in real-time
gh run watch
# List recent workflow runs
gh run list --limit 10
# Check specific workflow
gh run list --workflow=build.yml --limit 5
# View detailed run information
gh run view <run-id> --log
# Download logs for offline analysis
gh run download <run-id>- ✅ Release workflow completes (creates draft)
- ✅ Build workflow completes (all 3 platforms)
- ✅ Publish job runs (makes draft public)
- ✅ Release shows in
gh release list(NOT as Draft) - ✅ All ~17 assets present
- ❌ Any matrix job fails
- ❌ Cleanup job runs (check logs)
- ❌ Draft release exists after workflow completes
- ❌ Release has fewer than 17 assets
When contributing, ensure commits follow the Conventional Commits specification:
<type>(<scope>): <description>
[optional body]
[optional footer]
Examples:
feat: add bookmark export featurefix: resolve search crash with special charactersdocs: update installation instructionschore: upgrade dependencies
This ensures proper versioning and changelog generation.
- Semantic Versioning
- Conventional Commits
- Semantic Release
- Electron Builder
- GitHub Actions Workflow Syntax
- GitHub Actions Matrix Strategy
| File | Purpose | Trigger |
|---|---|---|
.releaserc.json |
Semantic release config | N/A |
.github/workflows/ci.yml |
Quality checks | Push/PR to dev/main |
.github/workflows/release.yml |
Create draft release | Push to main |
.github/workflows/build.yml |
Build binaries, publish/cleanup | Triggered by release workflow |
.github/workflows/deploy.yml |
Deploy to GitHub Pages | Push to main |
Last Updated: 2025-12-11
Workflow Version: 2.0 (Draft-First Architecture)