From 326cee54e1915364eefffe201288a1a86e293032 Mon Sep 17 00:00:00 2001 From: Greg Price Date: Tue, 23 Dec 2025 15:09:08 -0800 Subject: [PATCH 1/7] docs/release: Update for small changes in Apple and GitHub web UIs --- docs/release.md | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/docs/release.md b/docs/release.md index 6fd5dab8d6..c434b95d31 100644 --- a/docs/release.md +++ b/docs/release.md @@ -129,7 +129,7 @@ is an open TODO. * Upload both the AAB and the APK. - * Check the box "This is a pre-release". + * Check the box "Set as a pre-release". * iOS via TestFlight: @@ -148,7 +148,7 @@ is an open TODO. * Also submit for App Store review, to save latency in the prod rollout: - * In App Store Connect for the app, [go to the "App Store" + * In App Store Connect for the app, [go to the "Distribution" tab][asc-main], and hit the "+" button next to "iOS App" at the top of the left sidebar. Enter the version number. This creates a new draft listing. @@ -166,10 +166,7 @@ is an open TODO. this version"). * Back at the top, hit "Save" and then "Add for Review", and hit - "Continue" in the resulting dialog box. - - * In the resulting "Confirm Submission" page, hit - "Submit to App Review". + "Submit for Review" in the resulting modal sidebar. * The draft listing should enter state "Waiting for Review". From here, it typically takes a day or so to get a result from the @@ -177,7 +174,7 @@ is an open TODO. button to roll it out. [asc-external]: https://appstoreconnect.apple.com/apps/1203036395/testflight/groups/1bf18c25-da12-4bad-8384-9dd872ce447f -[asc-main]: https://appstoreconnect.apple.com/apps/1203036395/appstore/info +[asc-main]: https://appstoreconnect.apple.com/apps/1203036395/distribution/info ## Announce From 83c2bcd12486080ff624cf831187ecabc103182f Mon Sep 17 00:00:00 2001 From: Greg Price Date: Tue, 23 Dec 2025 16:14:17 -0800 Subject: [PATCH 2/7] docs/release: Finish updating release instructions to reflect main app This documents the release process I've been doing since this app launched as the main Zulip mobile app. --- docs/release.md | 70 ++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 58 insertions(+), 12 deletions(-) diff --git a/docs/release.md b/docs/release.md index c434b95d31..73b44153d3 100644 --- a/docs/release.md +++ b/docs/release.md @@ -1,17 +1,15 @@ # Making releases -## NOTE: This document is out of date. +This doc explains how to make a release of the Zulip mobile app to the +iOS App Store, to the Google Play Store, and as APKs on the web. -Now that this is the main Zulip mobile app, -the actual release process is roughly a hybrid of the steps below -and those from the legacy app's release instructions. +(Some additional information can be found in the [legacy app's release +instructions][]. Incorporating those remaining pieces into this doc +is an open TODO. -The steps below have been updated up through "Promote to beta". -After that, announce the release following a hybrid of the two docs; -and release to production following the other doc. +The main release process, however, is all fully set forth below.) -Revising this further into a single coherent set of instructions -is an open TODO. +[legacy app's release instructions]: https://github.com/zulip/zulip-mobile/blob/main/docs/howto/release.md ## Prepare source tree @@ -177,16 +175,64 @@ is an open TODO. [asc-main]: https://appstoreconnect.apple.com/apps/1203036395/distribution/info +## Release to production + +Historically we would wait a couple of days after sending to beta +before sending to production. More recently (since 2025) we've +been sending a typical release to production promptly after beta. +Discussion thread: [#mobile-team > mobile releases @ 💬](https://chat.zulip.org/#narrow/channel/243-mobile-team/topic/mobile.20releases/near/2218205) + + +* Android via Play Store: + + * In the Play Console, go to [Release > Testing > + Open testing][play-open-testing]. + + * Under the release you want to promote, choose "Promote release > + Production". + + * Under "Staged roll-out", set 100% as the roll-out percentage. + + * Occasionally we start with a smaller percentage. In that case, + remember to come back later to make a 100% rollout. + + * Confirm and send to Google for review. + +[play-open-testing]: https://play.google.com/console/developers/8060868091387311598/app/4976350040864490411/tracks/open-testing + + +* Android via GitHub: + + * Edit the release [on GitHub][gh-releases]. Uncheck + "Set as a pre-release", and check "Set as the latest release". + +[gh-releases]: https://github.com/zulip/zulip-flutter/releases + + +* iOS via App Store: + + * (This assumes the new version was submitted for App Store review + at the time of the beta rollout, and accepted. See beta steps + above for how to submit it.) + + * In App Store Connect for the app, go to [Distribution > iOS App > + (the draft release)][asc-inflight]. + + * Hit the big blue button at top right to release to the App Store. + +[asc-inflight]: https://appstoreconnect.apple.com/apps/1203036395/appstore/ios/version/inflight + + ## Announce * Announce the updated beta in - [#announce > mobile beta][releases-thread]. + [#announce > mobile releases][releases-thread]. For release notes, use `tools/format-changelog czo`. -[releases-thread]: https://chat.zulip.org/#narrow/stream/1-announce/topic/mobile.20beta +[releases-thread]: https://chat.zulip.org/#narrow/stream/1-announce/topic/mobile.20releases -* For any fixed issues that were tagged "beta feedback", or otherwise +* For any fixed issues that were tagged "user feedback", or otherwise had people outside the mobile team specifically interested in them, follow up with the requesters: post on the relevant thread (in GitHub or Zulip) and @-mention the individuals who asked for the From 0713acbe4e99b634479e109b04e412624edd1d14 Mon Sep 17 00:00:00 2001 From: Greg Price Date: Tue, 23 Dec 2025 16:26:59 -0800 Subject: [PATCH 3/7] docs/release: Incorporate instructions on security releases These are lightly adapted from the version that was in the legacy app's doc, with updates where needed. --- docs/release.md | 68 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/docs/release.md b/docs/release.md index 73b44153d3..b855985f17 100644 --- a/docs/release.md +++ b/docs/release.md @@ -274,6 +274,74 @@ Steps specific to this type of release are: (with the correct version number), and push. +## Security releases + +Very occasionally, we find a security vulnerability in the app. +When a release fixes a vulnerability which isn't already public, +we follow a variation of the process above. + +The goal is to get the update onto most users' phones almost before +the issue is disclosed, minimizing the window where the issue is +public and users are still vulnerable. + +### Preparing commit + +* Write the fixes on a private branch, but don't push to the main repo (or + other public repos.) + +* Prepare and QA the commit as usual. We'll be skipping the beta phase, so + be especially diligent with the QA, and choosy in what commits to include. + Definitely make it a stable release, with only hand-picked changes on top + of the last release. + +* Tag the commit, but don't push it yet. + +### Android prep + +* Build and upload to Google Play, but release only to alpha for now. + Repeat manual QA with the alpha. + +* Also send for Google's review to promote to both beta and + production, but adjust settings so that it will wait to roll out + until we later hit a button saying so. + + (The last time we needed this procedure was years ago, before the + Play Store had blocking reviews on updates, so we've not yet + actually done this step in practice.) + +* Don't upload to GitHub yet. + +### iOS prep + +* Build and upload to App Store Connect, but release only to alpha for now. + Repeat manual QA with the alpha. + +* Follow the steps to release to production, with one change: in the draft + listing, find the option for "Manually release this version", and select it. + +### Release + +* Wait for Apple's review; on success, the app will enter state "Pending + Developer Release". (On failure, try to fix the issue, then resubmit.) + Similarly wait for Google's review. + +* Now release the app to both the App Store and the Play Store. + +* Also now submit to TestFlight, for beta users on iOS. + Wait for that to go out before discussing further in public. + +### Followup + +* Wait for the release to be approved for TestFlight. + (On failure, try to fix, then resubmit.) + +* Push the tagged commit, and also push the corresponding changes to main. + +* Upload the APKs to GitHub as usual. + +* Discuss freely. + + ## One-time or annual setup * You'll need the Google Play upload key. The setup is similar to From ecb140ce211e250428e7cf3000d418f3a8cc79a1 Mon Sep 17 00:00:00 2001 From: Greg Price Date: Tue, 23 Dec 2025 16:34:41 -0800 Subject: [PATCH 4/7] docs/release: Incorporate Android signing-key instructions This cuts another dependency on the legacy app's instructions. --- docs/release.md | 42 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/docs/release.md b/docs/release.md index b855985f17..90fc700e3f 100644 --- a/docs/release.md +++ b/docs/release.md @@ -344,8 +344,46 @@ public and users are still vulnerable. ## One-time or annual setup -* You'll need the Google Play upload key. The setup is similar to - what we use for the React Native app, but the key is a fresh one. +### Prepare Android + +You'll need the Google Play upload key. +This key also serves as the [app signing key][] for +the APK and AAB files we publish directly via GitHub releases. +As the linked upstream doc explains, this is a highly sensitive secret +which it's very important to handle securely. + +[app signing key]: https://developer.android.com/studio/publish/app-signing#secure_key + +(This setup is similar to what we used for the legacy mobile app, +but the key is a fresh one.) + +* Get the keystore file, and the keystore properties file. + An existing/previous release manager can send these to you, + encrypted to your PGP key. + + * Never make an unencrypted version visible to the network or to a + cloud service (including Zulip). + +* Put the release-signing keystore, PGP-encrypted to yourself, + at `android/release.keystore.pgp`. + + * Don't leave an unencrypted version on disk, except temporarily. + The script `tools/checkout-keystore` will help manage this; see + `tools/checkout-keystore --help` and release instructions above. + +* Put the keystore properties file at + `android/release-keystore.properties`. + It looks like this (passwords redacted): + +``` +storeFile=release.keystore +keyAlias=zulip-mobile +storePassword=***** +keyPassword=***** +``` + + +### Prepare iOS * You'll need an "Apple Distribution" certificate and its key, and also an iOS "provisioning profile" that refers to that From 7087137c9fffc9476977939435abdc79d0bafcab Mon Sep 17 00:00:00 2001 From: Greg Price Date: Tue, 23 Dec 2025 16:52:11 -0800 Subject: [PATCH 5/7] docs/release: Incorporate terminology section on alpha/beta/prod Adapted from the legacy app's version mostly by updating the discussion of our beta practices. Also putting this section at the end, to avoid cluttering the way to the main instructions that we use on each release. --- docs/release.md | 70 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/docs/release.md b/docs/release.md index 90fc700e3f..3a1f8e9ce3 100644 --- a/docs/release.md +++ b/docs/release.md @@ -402,3 +402,73 @@ keyPassword=***** Install the app, open it, and log in. + + +## Terminology + +This section defines the terms **alpha**, **beta**, and **production** +(or **prod**) as used in this document and in our release process. + +Google and Apple each have different terminology for the various +channels of progressively wider release. We don't use or need the +full complexity of either one, and for sanity's sake we use a common, +simple terminology for the process we follow with both. + +* **Alpha**: A release only to active developers of the app. + See [instructions][join-alpha] for joining. + + * What this means on each platform: + * Google Play: release to "Internal testing" + * iOS: release in TestFlight to "App Store Connect Users" + * GitHub: a Git tag + + * On both Google Play and TestFlight, a new version in this channel + is available for update immediately on devices. We use it for + final manual QA before releasing to beta or production. + + * NB Google Play has its own feature it calls "Alpha" + (aka "Closed testing"), which is sort of intermediate between + "Internal testing" and "Open testing". + We don't use that feature. + +[join-alpha]: https://github.com/zulip/zulip-mobile/blob/main/docs/howto/alpha.md + + +* **Beta**: A release to users who have volunteered to get new versions + early and give us feedback. See + [instructions](https://github.com/zulip/zulip-mobile#using-the-beta) for + joining. + + * What this means on each platform: + * Google Play: release to "Open testing" + * iOS: release to all our TestFlight users (through the + "External Testers" group) + * GitHub: a GitHub release with binaries and description, + marked as pre-release + + * We sometimes use this channel for wider testing of a release + before sending to production: historically about 2-4 days for a + typical new release. More recently we tend to leave a release in + beta for at most 1 day before sending to prod; see discussion + above. + + * NB Google Play also calls this "Beta track" or "Open track", as + well as "Open testing". + + +* **Production** (aka **prod**): A general release to all users. + + * What this means on each platform: + * Google Play: release to "Production" + * iOS: release to the App Store + * GitHub: a GitHub release with binaries and description, + not marked pre-release + + * On iOS there is a gotcha we've occasionally fallen for in the + past: because releasing to the App Store is mostly a separate + process from releasing to TestFlight, it's easy to release a given + version to the App Store without ever interacting with TestFlight. + If we do, our beta users will simply never get that version, and + stay on the (older) last version we gave them. + Naturally this isn't good for our kind beta users, nor for us; so + don't do this. :) From d4615796a5db65b4f9e0a526d455150c7fce6184 Mon Sep 17 00:00:00 2001 From: Greg Price Date: Tue, 23 Dec 2025 16:54:54 -0800 Subject: [PATCH 6/7] docs/release: Add pointers at top to terminology and setup sections Logically these sections come before the main instructions, as they're needed before one can follow those. It's convenient to keep the main instructions handy right at the top, though, because we consult them much more often. So these links help make it comfortable to maintain that inversion. --- docs/release.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/release.md b/docs/release.md index 3a1f8e9ce3..96b6c2f341 100644 --- a/docs/release.md +++ b/docs/release.md @@ -3,6 +3,9 @@ This doc explains how to make a release of the Zulip mobile app to the iOS App Store, to the Google Play Store, and as APKs on the web. +If you're reading this page for the first time, see the sections on +[terminology](#terminology) and [setup](#setup) below. + (Some additional information can be found in the [legacy app's release instructions][]. Incorporating those remaining pieces into this doc is an open TODO. @@ -342,6 +345,8 @@ public and users are still vulnerable. * Discuss freely. +
+ ## One-time or annual setup ### Prepare Android From e7105082c7dd6e984be6222057495fcadb21b512 Mon Sep 17 00:00:00 2001 From: Greg Price Date: Tue, 23 Dec 2025 17:09:43 -0800 Subject: [PATCH 7/7] docs/release: Incorporate alpha/beta instructions; declare updates complete This was the last piece of information where this doc relied on a reference to the legacy app's documentation. --- docs/howto/alpha.md | 48 +++++++++++++++++++++++++++++++++++++++++++++ docs/release.md | 27 ++++++------------------- 2 files changed, 54 insertions(+), 21 deletions(-) create mode 100644 docs/howto/alpha.md diff --git a/docs/howto/alpha.md b/docs/howto/alpha.md new file mode 100644 index 0000000000..db33d0a95e --- /dev/null +++ b/docs/howto/alpha.md @@ -0,0 +1,48 @@ +# Joining the alpha channel + +If you're actively developing the app, you should join the [alpha +channel](../release.md#terminology) so that when we make an alpha +release you get it on your normal devices you use daily. This means +you'll see any regressions we have, so you can help find and fix them +before they go out wider. + +* **Android**: A maintainer will add you to the list, and then give + you a link you'll use to confirm. + + (Maintainer: see [Release > Testing > Internal testing > + Testers][play-internal-testers] in the Play Console.) + +* **iOS**: A maintainer will send you an invite to join App Store Connect. + Then after you join, there's a second step to join the list of users that + get alpha updates. + + (Maintainer: that list is confusingly labeled in the UI as simply + ["App Store Connect Users"][] — don't be fooled. Confirm the + person is on it; if not, see the plus-sign icon next to the + "Testers" heading, which lets you send an invite for that step.) + +[join-beta]: https://github.com/zulip/zulip-mobile#using-the-beta +[play-internal-testers]: https://play.google.com/console/developers/8060868091387311598/app/4976350040864490411/tracks/internal-testing?tab=testers +["App Store Connect Users"]: https://appstoreconnect.apple.com/apps/1203036395/testflight/groups/d246e92d-76a2-4b3e-8293-347a1a6e27ab + + +## Joining the beta channel + +Historically we also used a [beta channel](../release.md#terminology). +Our current practice is that the beta channel is nearly equivalent to +the prod channel, though, so there's little effect to be had from +joining the beta. + +Here are the instructions for joining the beta channel, mostly for the +purpose of internal notes in case we decide to revive the use of it: + +* Android: install the app, then just + [join the testing program](https://play.google.com/apps/testing/com.zulipmobile/) + on Google Play. + * Or if you don't use Google Play, you can [download an + APK](https://github.com/zulip/zulip-flutter/releases/); the latest + release on GitHub (including "pre-releases") is the current beta. + +* iOS: install [TestFlight](https://developer.apple.com/testflight/testers/), + then open [this public invitation link](https://testflight.apple.com/join/ZuzqwXGf) + on your device. diff --git a/docs/release.md b/docs/release.md index 96b6c2f341..24734fd427 100644 --- a/docs/release.md +++ b/docs/release.md @@ -6,14 +6,6 @@ iOS App Store, to the Google Play Store, and as APKs on the web. If you're reading this page for the first time, see the sections on [terminology](#terminology) and [setup](#setup) below. -(Some additional information can be found in the [legacy app's release -instructions][]. Incorporating those remaining pieces into this doc -is an open TODO. - -The main release process, however, is all fully set forth below.) - -[legacy app's release instructions]: https://github.com/zulip/zulip-mobile/blob/main/docs/howto/release.md - ## Prepare source tree @@ -420,7 +412,7 @@ full complexity of either one, and for sanity's sake we use a common, simple terminology for the process we follow with both. * **Alpha**: A release only to active developers of the app. - See [instructions][join-alpha] for joining. + See [instructions](howto/alpha.md) for joining. * What this means on each platform: * Google Play: release to "Internal testing" @@ -436,13 +428,12 @@ simple terminology for the process we follow with both. "Internal testing" and "Open testing". We don't use that feature. -[join-alpha]: https://github.com/zulip/zulip-mobile/blob/main/docs/howto/alpha.md - -* **Beta**: A release to users who have volunteered to get new versions - early and give us feedback. See - [instructions](https://github.com/zulip/zulip-mobile#using-the-beta) for - joining. +* **Beta**: Historically, a release to users who have volunteered to + get new versions early and give us feedback. + More recently, we usually don't make use of this channel; + we send a release here just before sending the same release to prod. + See [discussion above](#release-to-production). * What this means on each platform: * Google Play: release to "Open testing" @@ -451,12 +442,6 @@ simple terminology for the process we follow with both. * GitHub: a GitHub release with binaries and description, marked as pre-release - * We sometimes use this channel for wider testing of a release - before sending to production: historically about 2-4 days for a - typical new release. More recently we tend to leave a release in - beta for at most 1 day before sending to prod; see discussion - above. - * NB Google Play also calls this "Beta track" or "Open track", as well as "Open testing".