-
Notifications
You must be signed in to change notification settings - Fork 639
app and feature release process
This page describes the end-to-end lifecycle for releasing app binaries and managing feature flags in oppia-android. It is intended for release coordinators and any contributor who wants to understand how a change reaches end users.
- Prerequisites & Roles
- How Version Numbers Work
- Binary Release Lifecycle
- Automated Processes
- Feature Flag Lifecycle
- Diagrams
- Workflow Quick-Access Table
- How to Manually Trigger a Workflow
The release coordinator is responsible for triggering all manual GitHub Actions workflows
described in this page. The release coordinator is either the tech lead, or any other approved
dev-workflow or infrastructure reviewer codeowner.
Access required by the coordinator:
| Access | Purpose |
|---|---|
GitHub: oppia-android-release-env environment |
Approve build/sign/deploy jobs before they run |
GitHub: oppia-android-automation-env environment |
Approve automated PR-creation workflows |
Access pre-configured for the automation (as GitHub environment secrets — no manual setup needed by the coordinator):
| Access | Purpose |
|---|---|
| GCP: Workload Identity Federation service account | Sign binaries via Cloud KMS, read/write GCS archive |
GCP: Cloud KMS — oppia-android-release-key
|
HSM-backed signing key (key never leaves KMS) |
| Google Play Console: "Release to production" permission | Upload AABs and update track releases |
Note: The
oppia-android-release-envenvironment enforces a required reviewer approval gate. No build, signing, or deployment step executes until an authorized reviewer explicitly approves the run in the GitHub Actions UI.
The app's version is maintained in version.bzl at the repository root:
MAJOR_VERSION = 0
MINOR_VERSION = 18-
MINOR_VERSIONis incremented for each release (e.g. 0.17 → 0.18). -
MAJOR_VERSIONchanges only for breaking platform changes or major product milestones. - The resulting
versionNameis"{MAJOR}.{MINOR}"(e.g."0.18"). - Bumping
MINOR_VERSIONondevelopautomatically triggers changelog generation (see §3.2).
Trigger: Manual — the coordinator commits a MINOR_VERSION increment to version.bzl on
develop to start the release cycle.
Coordinator action:
- Edit
version.bzland incrementMINOR_VERSION(e.g.17→18). - Commit and push the change directly to
develop:git commit -m "Bump MINOR_VERSION to 18 for release 0.18" git push upstream develop
Pushing this change automatically triggers changelog generation (§3.2).
Trigger:
-
Automatic — on every push to
developthat modifiesversion.bzl(triggered by §3.1) -
Manual — via
workflow_dispatchof the Generate Changelog workflow
Coordinator action:
- If triggered automatically: No action is needed to start the workflow. Wait for the changelog PR to be opened, then review and merge it (see below).
- If triggered manually: Dispatch the Generate Changelog workflow from the Actions tab (see §8), then wait for the PR to be opened.
- After the workflow completes: Review the opened changelog PR. Edit the AI-generated release notes if they need adjustment, then merge the PR.
What it does:
- Identifies all commits on
developsince the previous version tag. - Passes them to
GenerateChangelogs.kt, which uses Vertex AI to produce user-facing release notes. - Opens a PR to
developadding a new changelog file.
Changelog file paths:
| File | Applies to |
|---|---|
config/changelogs/0.18.md |
All flavors (default) |
config/changelogs/0.18_alpha.md |
Alpha flavor only (overrides default) |
config/changelogs/0.18_beta.md |
Beta flavor only (overrides default) |
A flavor-specific file always takes precedence over the generic one for its corresponding Play Console track.
Trigger: Manual — after the version bump and changelog PR have landed on develop.
Coordinator action:
- Cut a release branch from
developHEAD:git checkout -b release-0.18 upstream/develop git push upstream release-0.18 - Do not commit directly to the release branch after the cut. If a critical fix is needed,
merge it to
developfirst and then cherry-pick the commit onto the release branch.
Trigger: Manual (workflow_dispatch) — run this after the release branch is ready.
Note: This workflow runs in the
oppia-android-release-envGitHub environment, which requires an authorized reviewer to approve the run before any step executes.
Inputs:
| Input | Description | Example |
|---|---|---|
flavor |
App flavor to build |
alpha, beta, ga
|
source_ref |
Branch or tag to build from |
release-0.18, latest-alpha
|
What it does:
- Checks out the specified
source_ref. - Builds the release AAB using Bazel.
- Signs the AAB using Cloud KMS (HSM-backed — the private key never leaves KMS).
- Archives the signed AAB to GCS:
gs://oppia-android-{flavor}-releases/{version}/RC{n}/oppia-android-{version}-rc{n}-{flavor}-{sha}.aab - Prints the full GCS path in the job summary — copy this path for use in the next steps.
Coordinator action: Dispatch the workflow, approve the run in the oppia-android-release-env
environment, and copy the GCS path from the job summary once it completes.
Trigger: Manual (workflow_dispatch) — run this after Build and sign the release succeeds.
Inputs:
| Input | Description |
|---|---|
gcs_aab_path |
Full GCS path from the build_and_sign job output |
release_notes |
Optional QA notes shown to testers in the Firebase console |
What it does:
- Downloads the signed AAB from GCS.
- Distributes it to Firebase App Distribution.
- Associates the release with the
{flavor}-qa-testersgroup (e.g.alpha-qa-testers); Firebase automatically emails all testers in that group. Tester group membership is managed from the Firebase console — no additional coordinator action is needed for the notification to go out.
QA testers can then install the build via the Firebase App Distribution app and validate it before the coordinator proceeds to the Play Console deployment.
The app's Play Store listing is at: https://play.google.com/store/apps/details?id=org.oppia.android
Coordinator action: Dispatch the workflow and wait for QA testers to confirm the build is ready. Proceed to §3.6 only after receiving QA sign-off.
Trigger: Manual (workflow_dispatch) — run this after QA sign-off.
Note: This workflow runs in the
oppia-android-release-envenvironment, which requires an authorized reviewer to approve the run before any step executes.
Inputs:
| Input | Description | Example |
|---|---|---|
gcs_aab_path |
Full GCS path from build_and_sign
|
gs://…/oppia-android-0.18-rc01-alpha-abc1234.aab |
track |
Play Console track to deploy to |
alpha, beta, ga
|
rollout_fraction |
Initial staged rollout as integer [0, 1000] where 1000 = 100% (optional, default: 1000) |
100 for 10% |
What it does:
- Downloads the signed AAB from GCS.
- Runs
UploadBinaryToPlayConsole.kt, which enforces the following preconditions before uploading:- No version inversion — fails if the version being deployed is lower than what's currently live on the target track.
- No duplicate deploy — fails if the commit SHA is already live on that track.
-
Changelog must exist — fails if
config/changelogs/{version}.md(or a flavor override) does not exist.
- Uploads the AAB to the specified Play Console track at the requested rollout fraction.
Note: Start with a low rollout fraction (e.g. 10%) and monitor crash rates in Firebase Crashlytics before expanding.
Coordinator action: Dispatch the workflow with a low initial rollout_fraction (e.g. 100
for 10%), approve the run, and monitor Crashlytics before proceeding to §3.7.
Trigger: Manual (workflow_dispatch) — run this each time you want to increase the rollout
percentage for a live release.
Inputs:
| Input | Description | Example |
|---|---|---|
track |
Play Console track to update |
alpha, beta, production
|
version |
Version in major.minor format — must match a live release on the track |
0.18 |
rollout_fraction |
New rollout as integer [0, 1000] where 1000 = 100% |
500 for 50% |
What it does:
Calls UpdateRolloutFraction.kt, which uses the Play Developer API to update the staged rollout
fraction for the current live release on the target track — without re-uploading the binary.
Typical progression:
10% → (monitor 24h) → 25% → (monitor 48h) → 50% → (monitor) → 100%
A concurrency lock shared with deploy_updated_changelog.yml prevents two simultaneous Play
Console edit sessions (the Play Developer API enforces a single active edit per package at a time).
Coordinator action: Dispatch the workflow after each monitoring window. Repeat until
rollout_fraction=1000 (100%).
Trigger:
-
Automatic — on every push to
developthat modifies any file matchingconfig/changelogs/**.md -
Manual — via
workflow_dispatchwithversionand optionalflavorinputs
What it does:
- Identifies the changelog file for the specified version (and flavor, if provided).
- Runs
UploadChangelogToPlayConsole.kt, which:- Fails if the version is not yet live on Play Console — this prevents a race condition against the initial binary deployment.
- Compares the local release notes against what is currently deployed on Play Console.
- Uploads only the changed or added translations.
This means you can edit config/changelogs/0.18.md directly on develop at any time after a
release goes live, and the changes will automatically sync to the Play Console store listing.
Coordinator action: Edit the changelog file on develop and push — the sync runs
automatically. No additional action is required unless the workflow fails.
These workflows run on a schedule without manual coordinator intervention. The coordinator's only role is to review and merge any PRs they open.
Trigger:
- Automatic — weekly cron every Tuesday at 03:30 UTC
-
Manual — via
workflow_dispatchof the Auto Release Alpha workflow (useful for off-schedule alpha cuts)
What it does:
- Runs
FindAlphaCandidate.kt, which:- Fetches the most recent commits on
develop(up to a configurable limit, default 50). - Walks them newest-first, querying the GitHub Check Runs API for each commit.
- Returns the first (newest) SHA where every check run has completed with a passing, skipped, or neutral conclusion. A neutral conclusion covers check runs that completed without a definitive pass or fail — for example, a check that was skipped because it did not apply to that commit, or one cancelled by a later push to the same branch.
- Fetches the most recent commits on
- Force-pushes the
latest-alphatag to that commit SHA. - Dispatches
build_and_sign.ymlwithflavor=alphaandsource_ref=latest-alpha.
A release coordinator still approves the build step via oppia-android-release-env before
signing runs — the automation removes the manual "find a good commit and tag it" step, not the
human sign-off on the actual binary.
If no passing commit is found within the configured limit, the outcome depends on why:
- Commits exist but none have passing CI — the workflow exits with an error to alert repository maintainers that the alpha channel is blocked on CI flakiness.
- No commits at all within the limit — the workflow logs this and exits cleanly without failing, since there is nothing new to release.
Coordinator action: Approve the dispatched build_and_sign.yml run in the
oppia-android-release-env environment.
Trigger:
- Automatic — weekly cron every Monday at 02:30 UTC
-
Manual — via
workflow_dispatchof the Pull Latest Lesson Versions workflow
What it does:
- Runs the
download_lesson_listBazel script against the live Oppia production server for bothalphaandprodflavors. - Updates the pinned lesson version files:
config/lessons/alpha_pinned_lesson_versions.textprotoconfig/lessons/prod_pinned_lesson_versions.textproto
- Opens a PR to
developon the dedicatedautomated/lesson-versionsbranch containing only the updated textproto diff (idempotent — if files are already current, no commit or PR is created).
Note: The workflow uses
BOT_TOKENrather thanGITHUB_TOKENfor PR creation so that CI is properly triggered on the opened PR. The dedicated branch is force-pushed on every run, keeping the PR diff clean regardless of how many runs have occurred.
Coordinator action: Review and merge the opened lesson-versions PR.
Feature flags (called platform parameters in this codebase) allow features to be developed and deployed incrementally — enabled for one flavor or environment at a time — without gating on a full release.
See the Platform Parameters & Feature Flags wiki page for the complete guide on defining, enabling, graduating, and removing flags, including the full progression model and the exact code constructs involved.
flowchart TD
A["Bump MINOR_VERSION in version.bzl → develop"] --> B["generate_changelog.yml\n(Vertex AI generates release notes)"]
B --> C["Coordinator merges changelog PR"]
C --> D["Cut release-X.Y branch from develop"]
D --> E["build_and_sign.yml\nflavor + source_ref=release-X.Y\n(requires reviewer approval)"]
E --> F["Signed AAB archived in GCS"]
F --> G["deploy_to_firebase.yml\nQA testers install & validate"]
G --> H{"QA pass?"}
H -- "No" --> I["Fix on develop → cherry-pick\n→ rebuild"]
I --> E
H -- "Yes" --> J["deploy_to_play_console.yml\nrollout_fraction=10%"]
J --> K["update_rollout.yml\n25% → 50% → 100%"]
K --> L["Full rollout complete ✓"]
J --> M["deploy_updated_changelog.yml\n(auto on changelog edits)"]
flowchart TD
A["Weekly cron — Tuesday 03:30 UTC\n(or manual dispatch)"] --> B["auto_release_alpha.yml"]
B --> C["FindAlphaCandidate\nwalks develop commits newest-first\nqueries GitHub Check Runs API"]
C --> D{"Commits exist\nwithin limit?"}
D -- "No commits" --> E["Log: nothing to release\nWorkflow exits cleanly (no error)"]
D -- "Commits exist" --> F{"Any with all\nCI checks passing?"}
F -- "No" --> G["Exit with error\n(alpha blocked on CI flakiness)\nMaintainers notified"]
F -- "Yes" --> H["Force-push latest-alpha tag\nto candidate SHA"]
H --> I["Dispatch build_and_sign.yml\nflavor=alpha, source_ref=latest-alpha"]
I --> J["Coordinator approves in\noppia-android-release-env"]
J --> K["Signed alpha AAB archived in GCS ✓"]
See the Platform Parameters & Feature Flags wiki page for the definitive guide on the feature flag progression model.
| Workflow file | Trigger | Key inputs | Purpose |
|---|---|---|---|
generate_changelog.yml |
Push to develop (version.bzl) / manual | version |
Generate AI release notes, open changelog PR |
build_and_sign.yml |
Manual + reviewer approval |
flavor, source_ref
|
Build AAB, sign via KMS, archive to GCS |
deploy_to_firebase.yml |
Manual | gcs_aab_path |
Distribute to QA testers via Firebase |
deploy_to_play_console.yml |
Manual |
gcs_aab_path, track, rollout_fraction [0-1000] |
Upload to Play Console track |
update_rollout.yml |
Manual |
track, version, rollout_fraction [0-1000] |
Increase staged rollout percentage |
deploy_updated_changelog.yml |
Push to develop (changelogs/**) / manual |
version, flavor
|
Sync edited release notes to Play Console |
auto_release_alpha.yml |
Weekly cron (Tue 03:30 UTC) / manual |
branch, commit_limit
|
Automated weekly alpha cut |
pull_latest_lesson_versions.yml |
Weekly cron (Mon 02:30 UTC) / manual | — | Update pinned lesson version textprotos, open PR |
All manual workflows in this release process use GitHub's workflow_dispatch trigger. Here is
how to run one:
Step 1 — Open the Actions tab and select the workflow
Navigate to the Actions tab of the oppia/oppia-android repository and click the workflow
you want to run from the left-hand sidebar. Then click the "Run workflow" button on the
right.

Step 2 — Fill in the inputs
A dropdown appears with a branch selector and the workflow's input fields. Select the correct branch and fill in all required inputs (refer to the relevant section above for the expected values).

Step 3 — Confirm and approve
Click "Run workflow" to queue the run. For workflows that run in the
oppia-android-release-env environment, a reviewer approval gate will appear — an authorized
reviewer must approve the run in the GitHub Actions UI before any step executes.

See also: Interpreting CI Results · Platform Parameters & Feature Flags · Instructions for Making a Code Change
Have an idea for how to improve the wiki? Please help make our documentation better by following our instructions for contributing to the wiki.
Core documentation
Developing Oppia
- Contributing to Oppia Android
- Key Workflows
- Testing
- Developing Skills
- Frequent Errors and Solutions
- RTL Guidelines
- Working on UI
- Writing Design Docs
Developer Reference
- Code style
- Background Processing
- Dark mode
- Buf Guide
- Firebase Console Guide
- Platform Parameters & Feature Flags
- Work Manager
- Dependency Injection with Dagger
- Revert & regression policy
- Upgrading target SDK version
- Spotlight Guide
- Triaging Process
- Bazel
- Internationalization
- Terminology in Oppia
- Past Events