Problem
The current release process is split across two workflows with different triggers, making it confusing and error-prone:
| Workflow |
Trigger |
What it builds |
main.yml |
Push to main + version change in pubspec.yaml |
Android APK + AAB + GitHub Release |
desktop.yml |
Tag push v*.*.* |
Linux, macOS, Windows desktop builds |
What goes wrong
To create a full release today, you need three manual steps:
- Edit
pubspec.yaml with the new version
- Push to
main
- Create and push a tag
v*.*.*
If you forget step 1 (version bump), the Android build is skipped silently. If you only push a tag (like happened with v1.2.1), only the desktop builds run — no APK, no GitHub Release with Android artifacts.
This is exactly what happened: tag v1.2.1 was pushed, desktop builds completed successfully, but no Android APK was generated because pubspec.yaml wasn't updated.
Proposed Solution
Unify everything into a single tag-based workflow (like Choke does). One trigger, one manual step:
git tag v1.2.1 && git push --tags
The workflow handles everything else automatically.
New release.yml workflow
Trigger: push tags: v*
Steps (in order):
- Extract version from tag — parse
v1.2.1 → 1.2.1
- Generate changelog — categorize commits between previous and current tag (features, fixes, docs, other)
- Update
pubspec.yaml — set version: 1.2.1+{commit_count} automatically
- Commit changelog + version bump to main — so main always reflects the latest release
- Verify signing secrets — fail fast with clear error if any secret is missing
- Setup Android keystore — decode
ANDROID_KEYSTORE_FILE from secrets, create key.properties
- Build Android —
flutter build apk --split-per-abi + flutter build appbundle
- Verify APK signing — existing jarsigner/apksigner checks
- Create GitHub Release — with APKs, AAB, and generated release notes
- Trigger desktop builds —
repository_dispatch to desktop.yml (keeps desktop builds parallel on different OS runners)
- Cleanup — remove keystore and key.properties
What changes
| File |
Change |
.github/workflows/release.yml |
New — unified tag-based workflow (combines current main.yml Android build + changelog + version management) |
.github/workflows/main.yml |
Remove or repurpose as CI-only (no release logic) |
.github/workflows/desktop.yml |
Keep — still triggered by repository_dispatch from the new release workflow, plus manual workflow_dispatch and direct tag push |
.github/workflows/flutter.yml |
No change — CI on PRs stays the same |
What stays the same
- Android keystore signing (same secrets:
ANDROID_KEYSTORE_FILE, ANDROID_KEYSTORE_PASSWORD, ANDROID_KEY_PASSWORD, ANDROID_KEY_ALIAS)
- Split APKs per ABI (armeabi-v7a, arm64-v8a)
- AAB for Play Store
- Desktop builds via
desktop.yml (Linux, macOS, Windows)
- APK signing verification
flutter.yml CI on PRs
Release flow comparison
Before (3 manual steps, easy to mess up):
1. Edit pubspec.yaml → version: 1.2.1+42
2. git commit && git push origin main
3. git tag v1.2.1 && git push --tags
↳ If you forget step 1: no Android build
↳ If you forget step 3: no desktop builds
After (1 manual step):
1. git tag v1.2.1 && git push --tags
↳ Everything happens automatically
Benefits
- Single source of truth — the tag IS the version, no manual
pubspec.yaml edits
- Impossible to forget a step — one trigger builds everything
- Automatic changelog — generated from commits between tags
- Version consistency —
pubspec.yaml is always in sync with the tag
- Existing secrets work as-is — no new secrets needed
- Desktop builds unchanged —
desktop.yml keeps working via repository_dispatch
Migration
- No breaking changes — existing secrets and signing config work as-is
main.yml can be kept temporarily as a CI build (without release logic) or removed entirely since flutter.yml already handles CI on PRs
- First release with the new workflow can be the next version after merging
Problem
The current release process is split across two workflows with different triggers, making it confusing and error-prone:
main.ymlmain+ version change inpubspec.yamldesktop.ymlv*.*.*What goes wrong
To create a full release today, you need three manual steps:
pubspec.yamlwith the new versionmainv*.*.*If you forget step 1 (version bump), the Android build is skipped silently. If you only push a tag (like happened with
v1.2.1), only the desktop builds run — no APK, no GitHub Release with Android artifacts.This is exactly what happened: tag
v1.2.1was pushed, desktop builds completed successfully, but no Android APK was generated becausepubspec.yamlwasn't updated.Proposed Solution
Unify everything into a single tag-based workflow (like Choke does). One trigger, one manual step:
The workflow handles everything else automatically.
New
release.ymlworkflowTrigger:
push tags: v*Steps (in order):
v1.2.1→1.2.1pubspec.yaml— setversion: 1.2.1+{commit_count}automaticallyANDROID_KEYSTORE_FILEfrom secrets, createkey.propertiesflutter build apk --split-per-abi+flutter build appbundlerepository_dispatchtodesktop.yml(keeps desktop builds parallel on different OS runners)What changes
.github/workflows/release.ymlmain.ymlAndroid build + changelog + version management).github/workflows/main.yml.github/workflows/desktop.ymlrepository_dispatchfrom the new release workflow, plus manualworkflow_dispatchand direct tag push.github/workflows/flutter.ymlWhat stays the same
ANDROID_KEYSTORE_FILE,ANDROID_KEYSTORE_PASSWORD,ANDROID_KEY_PASSWORD,ANDROID_KEY_ALIAS)desktop.yml(Linux, macOS, Windows)flutter.ymlCI on PRsRelease flow comparison
Before (3 manual steps, easy to mess up):
After (1 manual step):
Benefits
pubspec.yamleditspubspec.yamlis always in sync with the tagdesktop.ymlkeeps working viarepository_dispatchMigration
main.ymlcan be kept temporarily as a CI build (without release logic) or removed entirely sinceflutter.ymlalready handles CI on PRs