Skip to content

Latest commit

 

History

History
205 lines (146 loc) · 7.32 KB

File metadata and controls

205 lines (146 loc) · 7.32 KB

Releasing a New Version

How to cut and ship a new UNIUN release for iOS and Android. Follow the steps in order. Everything user-facing is driven from one place — the version line in pubspec.yaml.


Overview

  1. Bump the version in pubspec.yaml.
  2. Update the changelog in CHANGELOG.md.
  3. Make sure signing is set up (keystore + key.properties for Android, certs for iOS).
  4. Build the release artifact (.ipa for iOS, .aab for Android).
  5. Upload & tag.

1. Bump the version in pubspec.yaml

The version lives on a single line:

version: 1.1.0+2

It has two parts:

1.1.0      +2
└─ name ─┘ └ build ┘
  • Version name (1.1.0) — the human-facing MAJOR.MINOR.PATCH shown in stores. Bump it according to Semantic Versioning:

    Change type Example What moves
    Bug fix (patch) 1.2.0+15 → 1.2.1+16 PATCH
    New feature (backwards-compatible) 1.2.0+15 → 1.3.0+16 MINOR (PATCH resets to 0)
    Major redesign / breaking change 1.2.0+15 → 2.0.0+16 MAJOR (MINOR + PATCH reset to 0)
  • Build number (+2) — the integer after the +. This becomes Android versionCode and the iOS build number (android/app/build.gradle.kts reads flutter.versionCode / flutter.versionName from this line). It must strictly increase on every single upload to a store, even for a re-upload of the same version name. Both Google Play and App Store Connect reject a build whose number was already used. When in doubt, bump it.

⚠️ A version with no +build suffix (e.g. just 1.1.0) makes Flutter default the build number to 1. Always include an explicit, increasing build number before a store upload.


2. Update the changelog

Add a new section at the top of CHANGELOG.md, above the previous release. Follow the existing format (Keep a Changelog): group entries under Added / Changed / Fixed.

## [1.2.0] — 2026-07-01

### Added
- Short, user-facing description of the new feature.

### Changed
- What behaviour or UI changed.

### Fixed
- The bug, in one line.

1.0.0 was the initial release; every release since is appended here. Keep entries user-facing and concise — this is the same text that feeds store "What's New" notes.


3. Signing & keys

A release build must be signed. Debug builds are signed automatically; release builds are not. Set this up once per machine and keep the secrets safe.

Android — upload keystore + key.properties

The release signingConfig in android/app/build.gradle.kts reads four values from a file at android/key.properties (kept out of git — see android/.gitignore, which ignores key.properties, **/*.jks, and **/*.keystore).

a) Generate the keystore (once, ever). Pick a location outside the repo:

keytool -genkey -v \
  -keystore ~/uniun-upload-keystore.jks \
  -keyalg RSA -keysize 2048 -validity 10000 \
  -alias upload

You'll be prompted for a store password, a key password, and your name/org. Remember these.

🔐 Back this file up forever. If you lose the keystore (or its passwords) you can no longer publish updates to the same Google Play listing — you'd have to ship a brand-new app. Store it in a password manager / secure vault, not on a single laptop. Never commit it.

b) Create android/key.properties (this file is gitignored — never commit it):

storePassword=<the store password you chose>
keyPassword=<the key password you chose>
keyAlias=upload
storeFile=/Users/you/uniun-upload-keystore.jks

storeFile is resolved relative to the android/ folder, so an absolute path is safest. If you place the keystore inside android/, a relative path like storeFile=upload-keystore.jks also works (it's gitignored).

If key.properties is missing, the build falls back to no release signing and the resulting .aab will not be accepted by the Play Store.

iOS — Apple signing

iOS signing is managed by Xcode / Apple, not by a file in the repo. You need:

  • An Apple Developer Program membership (the team that owns the in.uniun.app bundle ID).
  • A Distribution certificate and an App Store provisioning profile for the bundle ID.
  • The signing Team selected in Xcode: open ios/Runner.xcworkspaceRunner target → Signing & Capabilities. "Automatically manage signing" is the simplest path; Xcode then fetches/creates the cert + profile for you.
  • An App Store Connect record for the app (created once, under the same bundle ID) before the first upload.

Building an .ipa for the store requires running on macOS with Xcode installed.


4. Build the release artifacts

Clean first if you changed dependencies or native config:

flutter clean && flutter pub get
flutter pub run build_runner build --delete-conflicting-outputs   # if models/freezed changed

iOS

flutter build ipa --release

Output: build/ios/ipa/*.ipa.

Upload the .ipa to App Store Connect with Transporter, Xcode's Organizer, or xcrun altool from the command line:

xcrun altool --upload-app --type ios \
  -f build/ios/ipa/*.ipa \
  --apiKey <API_KEY_ID> \
  --apiIssuer <ISSUER_ID>

Add your own App Store Connect API credentials — never commit them.

  • <API_KEY_ID> and <ISSUER_ID> come from App Store Connect → Users and Access → Integrations → App Store Connect API: the key's Key ID and the team's Issuer ID.
  • Download that key's AuthKey_<API_KEY_ID>.p8 once and place it in ~/.appstoreconnect/private_keys/ (or ~/private_keys/) so altool can find it.
  • Keep the key ID, issuer ID, and the .p8 file out of the repository (e.g. export them as shell env vars or pass them on the command line at upload time).

Android

flutter build appbundle --release

Output: build/app/outputs/bundle/release/app-release.aab. Upload the .aab (App Bundle) to the Google Play Console — Play prefers App Bundles over APKs. (For local sideloading/testing only, flutter build apk --release produces a signed .apk.)


5. Upload & tag

  1. Android → Google Play Console → create a new release on the target track (internal / closed / production), upload the .aab, paste the changelog into "What's New".

  2. iOS → App Store Connect → select the uploaded build, fill release notes, submit for review.

  3. Tag the commit so the release is reproducible:

    git tag -a v1.2.0 -m "Release 1.2.0"
    git push origin v1.2.0

Quick checklist

  • version: bumped in pubspec.yaml (name and +build, build number increased).
  • New section added to CHANGELOG.md.
  • android/key.properties present and points to a valid keystore (Android).
  • Xcode signing/team + App Store Connect record ready (iOS).
  • flutter build ipa --release succeeds.
  • flutter build appbundle --release succeeds.
  • Artifacts uploaded; release notes match the changelog.
  • Commit tagged vX.Y.Z and pushed.