Release Workflow

Step-by-step guide for shipping a new kiosk APK to the fleet.

Last updated: April 27, 2026

Prerequisites

  • Access to the familypocket-kiosk Git repo
  • GitHub Actions secrets configured (see Secrets section below)
  • Access to the technician dashboard (or auth service API)

Pre-Release Checklist

Before tagging a release, verify:

  • All tests pass on CI (./gradlew test)
  • APK builds successfully with the production keystore
  • Manual smoke test on at least one staging device
  • Database migration (if any) tested on staging
  • Release notes drafted
  • versionCode is strictly greater than previous release

Step 1: Bump Version

Edit app/build.gradle.kts:

app/build.gradle.kts
kotlin
defaultConfig {
    versionCode = 2    // increment by 1 (monotonically, NEVER decrease)
    versionName = "2.1.0"
}

Rules:

  • versionCode must be strictly greater than the previous release
  • versionName follows semver: MAJOR.MINOR.PATCH
  • Tag must match versionName: tag v2.1.0 for versionName "2.1.0"

Step 2: Commit and Tag

Terminal
bash
git add app/build.gradle.kts
git commit -m "chore(kiosk): bump version to 2.1.0"
git tag v2.1.0
git push origin main --tags

Step 3: CI Takes Over (Automatic)

The tag push triggers .github/workflows/release.yml:

  1. Builds signed release APK (using secrets keystore)
  2. Computes SHA-256 hex checksum
  3. Renames APK to familypocket-kiosk.apk
  4. Creates GitHub Release with the APK attached
  5. POSTs { version, checksum } to auth service webhook
  6. Auth service stores checksum in kiosk_rollout_config.checksums
i
You don't need to do anything here. Wait for the GitHub Actions workflow to complete (check the Actions tab).

Step 4: Configure Rollout

After CI completes, update the rollout config via the technician dashboard OTA screen or the API:

Via API (curl):

curl
bash
curl -X PUT https://auth.familypocket.co.ke/api/auth/kiosk/rollout-config \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer <your-token>" \
  -d '{
    "targetVersion": "2.1.0",
    "previousStableVersion": "2.0.0",
    "rolloutPercentage": 5,
    "active": true
  }'

Via Technician Dashboard:

  1. Open the OTA Rollout screen
  2. Set target version to 2.1.0
  3. Set previous stable to 2.0.0
  4. Set rollout percentage to 5%
  5. Toggle active ON
  6. Save

Step 5: Phased Rollout (7 days)

DayPercentageAction
05%Release to ~500 devices. Monitor telemetry for crashes.
225%If no issues, expand.
460%Continue expanding.
7100%Full fleet rollout.
!
If you see crash spikes, drop rollout to 0% immediately.
curl -X PUT .../rollout-config \
  -H "Authorization: Bearer <token>" \
  -d '{ "rolloutPercentage": 0 }'

How Devices Receive Updates

Update Flow
text
Device boots -> KioskEngineService runs update check every 4 hours
  -> GET cdn.familypocket.io/update/latest
     Headers: X-Device-Id, X-Current-Version
  <- { targetVersion: "2.1.0", apkUrl: "/update/v2.1.0/apk", checksum: {...} }

  -> GET cdn.familypocket.io/update/v2.1.0/apk
  <- 302 redirect -> GitHub Releases download URL

  -> Downloads familypocket-kiosk.apk, verifies SHA-256
  -> Waits for safe window (22:00-03:00, charging, no active trip)
  -> Silent install via PackageInstaller (Device Owner privilege)
  -> POST cdn.familypocket.io/update/confirm

Force updates (forceUpdate=true) bypass the safe window entirely.

Versioning Rules

  • Filename is always familypocket-kiosk.apk, version lives in the Git tag path
  • URL pattern: github.com/.../releases/download/v{VERSION}/familypocket-kiosk.apk
  • Never reuse a tag. If v2.1.0 has a bug, ship v2.1.1
  • Never re-sign with a different keystore
  • Android refuses to install a lower versionCode, so always fix forward

Rollback

!
Android cannot downgrade APKs. You cannot roll back to a previous version.

If a release is bad:

  1. Drop rolloutPercentage to 0 (stops new devices from getting it)
  2. Fix the bug
  3. Release v2.1.1 with the fix
  4. Resume rollout with the fixed version

Post-Rollout Cleanup

After a full 100% rollout with no issues:

  1. Update previous_stable_version to the new version (e.g., 2.1.0), preparing for the next release cycle
  2. Confirm update.completed events from at least 95% of recently-seen devices
  3. Archive release notes in engineering wiki
i
Forgetting to update previous_stable_version means the next phased rollout will serve an outdated fallback to devices outside the rollout percentage.

GitHub Actions Secrets Required

SecretDescription
KIOSK_KEYSTORE_BASE64Base64-encoded release keystore file
KIOSK_KEYSTORE_PASSWORDKeystore password
KIOSK_KEY_ALIASSigning key alias (e.g., kiosk)
KIOSK_KEY_PASSWORDSigning key password
BACKEND_DEPLOY_HOOK_URLhttps://auth.familypocket.co.ke/api/auth/kiosk/release-webhook
SERVICE_TOKENService-to-service auth token (must match auth service env)