Skip to content

app and feature release process

synd3x edited this page Aug 29, 2026 · 1 revision

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.


Table of Contents

  1. Prerequisites & Roles
  2. How Version Numbers Work
  3. Binary Release Lifecycle
  4. Automated Processes
  5. Feature Flag Lifecycle
  6. Diagrams
  7. Workflow Quick-Access Table
  8. How to Manually Trigger a Workflow

1. Prerequisites & Roles

Release Coordinator

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-env environment 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.


2. How Version Numbers Work

The app's version is maintained in version.bzl at the repository root:

MAJOR_VERSION = 0
MINOR_VERSION = 18
  • MINOR_VERSION is incremented for each release (e.g. 0.17 → 0.18).
  • MAJOR_VERSION changes only for breaking platform changes or major product milestones.
  • The resulting versionName is "{MAJOR}.{MINOR}" (e.g. "0.18").
  • Bumping MINOR_VERSION on develop automatically triggers changelog generation (see §3.2).

3. Binary Release Lifecycle

3.1 Bump the version

Trigger: Manual — the coordinator commits a MINOR_VERSION increment to version.bzl on develop to start the release cycle.

Coordinator action:

  1. Edit version.bzl and increment MINOR_VERSION (e.g. 1718).
  2. 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).


3.2 Generate the changelog

Trigger:

  • Automatic — on every push to develop that modifies version.bzl (triggered by §3.1)
  • Manual — via workflow_dispatch of 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:

  1. Identifies all commits on develop since the previous version tag.
  2. Passes them to GenerateChangelogs.kt, which uses Vertex AI to produce user-facing release notes.
  3. Opens a PR to develop adding 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.


3.3 Create the release branch

Trigger: Manual — after the version bump and changelog PR have landed on develop.

Coordinator action:

  1. Cut a release branch from develop HEAD:
    git checkout -b release-0.18 upstream/develop
    git push upstream release-0.18
    
  2. Do not commit directly to the release branch after the cut. If a critical fix is needed, merge it to develop first and then cherry-pick the commit onto the release branch.

3.4 Build and sign the release

Trigger: Manual (workflow_dispatch) — run this after the release branch is ready.

Note: This workflow runs in the oppia-android-release-env GitHub 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:

  1. Checks out the specified source_ref.
  2. Builds the release AAB using Bazel.
  3. Signs the AAB using Cloud KMS (HSM-backed — the private key never leaves KMS).
  4. Archives the signed AAB to GCS:
    gs://oppia-android-{flavor}-releases/{version}/RC{n}/oppia-android-{version}-rc{n}-{flavor}-{sha}.aab
    
  5. 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.


3.5 Validate via Firebase App Distribution

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:

  1. Downloads the signed AAB from GCS.
  2. Distributes it to Firebase App Distribution.
  3. Associates the release with the {flavor}-qa-testers group (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.


3.6 Deploy to Play Console

Trigger: Manual (workflow_dispatch) — run this after QA sign-off.

Note: This workflow runs in the oppia-android-release-env environment, 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:

  1. Downloads the signed AAB from GCS.
  2. 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.
  3. 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.


3.7 Manage the staged rollout

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%).


3.8 Update the changelog post-release

Trigger:

  • Automatic — on every push to develop that modifies any file matching config/changelogs/**.md
  • Manual — via workflow_dispatch with version and optional flavor inputs

What it does:

  1. Identifies the changelog file for the specified version (and flavor, if provided).
  2. 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.


4. Automated Processes

These workflows run on a schedule without manual coordinator intervention. The coordinator's only role is to review and merge any PRs they open.

4.1 Run automated alpha releases

Trigger:

  • Automatic — weekly cron every Tuesday at 03:30 UTC
  • Manual — via workflow_dispatch of the Auto Release Alpha workflow (useful for off-schedule alpha cuts)

What it does:

  1. 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.
  2. Force-pushes the latest-alpha tag to that commit SHA.
  3. Dispatches build_and_sign.yml with flavor=alpha and source_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.


4.2 Update pinned lesson versions

Trigger:

  • Automatic — weekly cron every Monday at 02:30 UTC
  • Manual — via workflow_dispatch of the Pull Latest Lesson Versions workflow

What it does:

  1. Runs the download_lesson_list Bazel script against the live Oppia production server for both alpha and prod flavors.
  2. Updates the pinned lesson version files:
    • config/lessons/alpha_pinned_lesson_versions.textproto
    • config/lessons/prod_pinned_lesson_versions.textproto
  3. Opens a PR to develop on the dedicated automated/lesson-versions branch containing only the updated textproto diff (idempotent — if files are already current, no commit or PR is created).

Note: The workflow uses BOT_TOKEN rather than GITHUB_TOKEN for 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.


5. Feature Flag Lifecycle

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.


6. Diagrams

6.1 End-to-end binary release flow

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)"]
Loading

6.2 Automated alpha release flow

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 ✓"]
Loading

6.3 Feature flag lifecycle

See the Platform Parameters & Feature Flags wiki page for the definitive guide on the feature flag progression model.


7. Workflow Quick-Access Table

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

8. How to Manually Trigger a Workflow

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 1: Select the workflow and click Run workflow

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 2: Fill in the workflow inputs

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.

Step 3: Approve the run in the release environment


See also: Interpreting CI Results · Platform Parameters & Feature Flags · Instructions for Making a Code Change

Clone this wiki locally