Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
153 changes: 153 additions & 0 deletions wiki/Release-Playbook.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
# Release Playbook

This page is the step-by-step coordinator guide for Oppia Android releases using the automated
release pipeline. For conceptual background on any step, see the
[App and Feature Release Process](app-and-feature-release-process.md) wiki page. For manual
fallback procedures when automation fails, see the
[In-Depth Release Reference](In-Depth-Release-Reference.md).

---

## Table of Contents

1. [Weekly alpha release](#1-weekly-alpha-release)
2. [Changelog review](#2-changelog-review)
3. [Production release](#3-production-release)
4. [Staged rollout](#4-staged-rollout)
5. [Weekly coordinator checklist](#5-weekly-coordinator-checklist)

---

## 1. Weekly Alpha Release

The **Auto Release Alpha** workflow, defined by
[`auto_release_alpha.yml`](../.github/workflows/auto_release_alpha.yml), fires automatically every
**Tuesday at 03:30 UTC**. It finds the latest passing commit on `develop`, tags it as
`latest-alpha`, and dispatches **Build and Sign**. The coordinator's job starts at the approval
gate.

### Steps

- [ ] **Approve the build** — Navigate to Actions → **Build and Sign** run → approve in the
`oppia-android-release-env` gate.
- [ ] **Copy the GCS path** — After the build completes, copy the signed AAB path from the job
Comment thread
adhiamboperes marked this conversation as resolved.
summary (format: `gs://oppia-android-alpha-releases/…/*.aab`), which will be used for the
deployment steps.
- [ ] **Distribute to QA** — Trigger **Deploy to Firebase** (`deploy_to_firebase.yml`) with the
GCS path above and approve the run in the `oppia-android-release-env` gate.
- [ ] **Await QA sign-off** — Notify the alpha tester group and wait for their confirmation that
the alpha build is stable and ready for Play Store deployment.
- [ ] **Deploy to Play Console** — After QA sign-off, trigger **Deploy to Play Console**
(`deploy_to_play_console.yml`) with `track=alpha` and `rollout_fraction=1000` (100% of the
alpha track). The Firebase and Play Console deployments are sequential: QA on Firebase is
completed first, then the approved build is pushed to the alpha track:

![Deploy to Play Console dispatch dialog](https://github.com/user-attachments/assets/76b3dbbd-eede-4a1c-a359-8751d08b304e)

> If **Auto Release Alpha** exits with an error or finds no passing commit, see the
> [In-Depth Release Reference](In-Depth-Release-Reference.md) for manual fallback steps.

---

## 2. Changelog Review

The **Generate Changelogs** workflow

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand that this is supposed to trigger when version.bzl is updated, but when does that latter event happen? Can you explain?

Also, is the release coordinator ever supposed to run this manually? If so, in what circumstances? Please explain.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

version.bzl is bumped by the RC at the start of each release cycle as part of the pre-release step in §3 that push triggers Generate Changelogs automatically. The manual workflow_dispatch option exists for cases where the auto-trigger didn't fire or the changelog needs to be regenerated after corrections. Updated the §2 intro to explain both triggers.

([`generate_changelog.yml`](../.github/workflows/generate_changelog.yml)) opens a PR to
`develop` automatically when `version.bzl` is updated with a new `MINOR_VERSION`. This update
happens at the start of each release cycle when the RC bumps `MINOR_VERSION` as part of the
pre-release step in §3. The workflow can also be triggered manually via `workflow_dispatch` if
the auto-trigger did not fire (e.g. the version bump happened without triggering CI) or if the
changelog needs to be regenerated after corrections.

### Steps

- [ ] Review the AI-generated release notes in `config/changelogs/{version}.md` (and any
flavor-specific overrides such as `{version}_alpha.md` or `{version}_beta.md`).
- [ ] Edit the release notes if the AI summary needs adjustment before merging.
- [ ] Merge the changelog PR into `develop`. Merging automatically triggers **Deploy Updated
Changelog** (`deploy_updated_changelog.yml`), which uploads the release notes to Play Console.

---

## 3. Production Release

Follow this checklist for a full beta or production (GA) release.

### Pre-release

- [ ] Bump `MINOR_VERSION` in `version.bzl`. This push triggers [**Generate Changelogs**](#2-changelog-review)
automatically.
- [ ] Review and merge the changelog PR opened by [**Generate Changelogs**](#2-changelog-review). **Do not cut
the release branch until this PR is merged** — the release branch must include the correct
version number and changelog.
- [ ] Cut the release branch from `develop` HEAD:
```
git checkout -b release-0.X upstream/develop
git push upstream release-0.X
```

### Build & sign

- [ ] Trigger **Build and Sign** (`build_and_sign.yml`) with `flavor=beta` or `flavor=ga` and
`source_ref=release-0.X`:

![Build and Sign dispatch dialog](https://github.com/user-attachments/assets/c9119df5-5d3a-459e-bb31-ba3b7683b98b)

- [ ] Approve the run in the `oppia-android-release-env` gate.
- [ ] Copy the signed AAB GCS path from the job summary.

### QA

- [ ] Trigger **Deploy to Firebase** (`deploy_to_firebase.yml`) with the GCS path and approve
the run in the `oppia-android-release-env` gate.
- [ ] Notify QA testers and await sign-off.
- [ ] If critical issues are found during QA: land the fix on `develop`, cherry-pick to
`release-0.X`, then rebuild from Build & sign above.

### Deploy

- [ ] Trigger **Deploy to Play Console** (`deploy_to_play_console.yml`) with `track=beta` or
`track=production` and `rollout_fraction=100` (10% initial rollout — see §4):

![Deploy to Play Console dispatch dialog](https://github.com/user-attachments/assets/a8c27c48-7fe6-48c1-aea6-3c5393b6db9d)

---

## 4. Staged Rollout

After the initial deployment, progressively increase the rollout using `update_rollout.yml`.
Monitor Firebase Crashlytics between each step; halt and investigate if crash rates spike.

| Day | `rollout_fraction` | Actual rollout | Action |

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These look like integers rather than "fractions"?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They are in permille (thousandths out of 1000), so 100 = 10%, 1000 = 100%. Added a note below the table to clarify the unit.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But then shouldn't the name be something like "rollout_permille", not "rollout_fraction"? The integer here is still not a fraction.

@Sandesh282 Sandesh282 Aug 31, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rollout_fraction is already the established parameter name in update_rollout.yml (the workflow input, job name, and Bazel target are all named accordingly). Keeping the same name in the playbook table makes it easy for release coordinators to directly map the doc to the actual workflow input when triggering a run.

PS: The note directly below the table already clarifies the permille semantics: "rollout_fraction is specified in permille (thousandths out of 1000) as required by the Play Console API — 100 means 10%, 1000 means 100%."

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I am actually suggesting that that name is misleading throughout and should be changed, not just here, but in the workflows. This should probably have been caught during earlier review.

If a variable does not actually represent a fraction, it should not be called a fraction.

Could you please look into this? Is there an external constraint that forces us to keep the incorrect naming?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Sandesh282, it may be useful to explain why we user permille instead of percent

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Play Console API's userFraction field accepts a decimal from 0 to 1 with sub-1% precision (e.g. 0.005 = 0.5%). Using percent integers [0, 100] would limit steps to whole percentages and couldn't represent values like 0.5%. Permille [0, 1000] covers the same sub-1% granularity while keeping the workflow input as a plain integer; the script converts it to the API decimal via rolloutFraction / 1000.0. The name rollout_fraction mirrors the API's own userFraction field name, which is why we kept it consistent throughout.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @Sandesh282, that is not an adequate reason.

The userFraction field is correctly named in the Play Console API because the value it stores is a fraction (in this case, a decimal from 0 to 1).

Our rolloutFraction, however, is named misleadingly, because it does not store a fraction, and the value that it stores is instead an integer from 0 to 1000 that is supposed to be treated as a permille. Developers reading this code will naturally assume a _fraction is a float between 0.0 and 1.0 (matching the API's behavior).

Could we update the workflow/variable name to rollout_permille or similar to reflect the actual unit being used?

Note that I am not suggesting that we use percent instead. I am talking about naming the field in a way that is not misleading.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Screenshot 2026-09-02 at 15 54 01

I understand. I had already added a sentence with an example explaining what the number represents. I think this makes values like 1000 or 100 less ambiguous, since readers can refer to the example to understand what the value means.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Sandesh282 We are talking about two different things here. The message for the users of the feature is good, but the problem I am discussing is the name of the variable in the code, which I still think should be fixed to reflect the type of value that it stores.

|---|---|---|---|
| 0 | `100` | 10% | Initial deploy via **Deploy to Play Console** |
| 1 | `250` | 25% | Trigger **Update Rollout** after monitoring |
| 3 | `500` | 50% | Trigger **Update Rollout** |
| 7+ | `1000` | 100% | Trigger **Update Rollout** for full rollout |

> `rollout_fraction` is specified in **permille** (thousandths out of 1000) as required by the
> Play Console API — `100` means 10%, `1000` means 100%.

**Inputs for `update_rollout.yml`:**

| Input | Value |
|---|---|
| `track` | `beta` or `production` |
| `version` | e.g. `0.18` |
| `rollout_fraction` | New fraction from the table above |

---

## 5. Weekly Coordinator Checklist

Run through this checklist each week:

- [ ] Check whether **Auto Release Alpha** completed successfully on Tuesday. If not,
consult the [In-Depth Release Reference](In-Depth-Release-Reference.md).
- [ ] Check whether **Pull Latest Lesson Versions** opened a PR on Monday. If so, review
and merge the lesson-versions PR.
- [ ] Check for any pending changelog PRs opened by **Generate Changelogs** and merge them.
- [ ] Review crash rates in Firebase Crashlytics and advance the rollout fraction if stable
(see §4).

---
Loading