ci / rust-arm64 (push) Successful in 2m3s
ci / web (push) Successful in 1m15s
ci / docs-site (push) Successful in 1m43s
ci / rust (push) Successful in 5m15s
docker / builders (--build-arg FEDORA_VERSION=44, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm, -f44) (push) Successful in 7s
docker / builders (ci/android-ci.Dockerfile, punktfunk-android-ci) (push) Successful in 7s
docker / builders (ci/arch-ci.Dockerfile, punktfunk-arch-ci) (push) Successful in 8s
docker / builders (ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 8s
docker / builders (ci/rust-ci-noble.Dockerfile, punktfunk-rust-ci-noble) (push) Successful in 9s
docker / builders (ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 7s
android / android (push) Successful in 5m55s
docker / builders-arm64cross (push) Successful in 13s
docker / apps (., web/Dockerfile, punktfunk-web) (push) Successful in 51s
docker / apps (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 1m10s
docker / deploy-docs (push) Successful in 36s
Production access came through on 2026-08-01. Until now a `vX.Y.Z` tag could only reach `alpha` and someone had to promote it by hand in the Console; it now goes to `production` at 100% (`completed`). Canary is unchanged on `internal`, and its run-number versionCodes always outrank production, so testers keep getting the newer build. A tag therefore reaches real users with no further click. What keeps that honest: the tag is only pushed once every platform is green, and Play reviews each production release before it ships. Ramping instead is `--status inProgress --user-fraction 0.2` on the upload step. Play's "What's new" gets its own file, docs/releases/whatsnew/vX.Y.Z.txt — the vX.Y.Z.md body is ~34 KB against a 500-char cap, so it cannot be reused. Only tags have one; canary is a moving target and Play carrying the previous text over is fine for internal testers. Same freeze rule as the notes: once the tag exists, the file describes what that versionCode shipped. android-promote.yml is the lever for everything that is not a fresh tag — promote a tested build, halt a rollout, or roll production back onto an older versionCode. It is separate from android.yml because promotion must not rebuild, and an `if:` on all ten build steps is worse than one small workflow. dry_run defaults to true, so a mis-typed versionCode validates and deletes the edit instead of publishing. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
96 lines
4.0 KiB
YAML
96 lines
4.0 KiB
YAML
# Move a versionCode that is ALREADY on Google Play between tracks — no rebuild.
|
|
#
|
|
# Why this is separate from android.yml: promotion must not rebuild. A rebuild produces a fresh
|
|
# versionCode (github.run_number) from possibly-newer sources, so it ships something nobody tested;
|
|
# promoting assigns the byte-identical artifact the testers already ran. Bolting this onto
|
|
# android.yml would mean an `if:` on all ten of its build steps.
|
|
#
|
|
# What it is for:
|
|
# * promote a tested build up a track (alpha -> production)
|
|
# * roll production back by re-pointing it at an older versionCode (to_track=production,
|
|
# version_code=<the good one>, from_track blank)
|
|
# * halt a rollout (status=halted)
|
|
#
|
|
# Defaults are deliberately the safe ones: dry_run starts TRUE, so a mis-typed versionCode
|
|
# validates and deletes the edit instead of publishing. Flip it to false only when the dry run
|
|
# printed what you meant.
|
|
name: android-promote
|
|
|
|
# Two concurrent promotions would race on the same Play edit; the loser fails with a stale-edit
|
|
# error. One at a time, and never cancel one mid-flight — a half-applied track change is worse
|
|
# than a queued one.
|
|
concurrency:
|
|
group: android-promote
|
|
cancel-in-progress: false
|
|
|
|
on:
|
|
workflow_dispatch:
|
|
inputs:
|
|
version_code:
|
|
description: 'versionCode already on Play (e.g. 10816)'
|
|
required: true
|
|
to_track:
|
|
description: 'destination track'
|
|
required: true
|
|
default: 'production'
|
|
from_track:
|
|
description: 'track to verify it is on, then clear (blank = touch nothing else)'
|
|
required: false
|
|
default: 'alpha'
|
|
notes_tag:
|
|
description: "tag whose docs/releases/whatsnew/<tag>.txt to attach, e.g. v0.23.0 (blank = none)"
|
|
required: false
|
|
default: ''
|
|
status:
|
|
description: 'completed (100%) | inProgress (needs user_fraction) | halted | draft'
|
|
required: true
|
|
default: 'completed'
|
|
user_fraction:
|
|
description: 'staged rollout fraction for inProgress, e.g. 0.2 (blank otherwise)'
|
|
required: false
|
|
default: ''
|
|
dry_run:
|
|
description: 'validate only, publish nothing'
|
|
required: true
|
|
default: 'true'
|
|
|
|
jobs:
|
|
promote:
|
|
runs-on: ubuntu-24.04
|
|
# Same image as android.yml purely for python3 + openssl (play-upload.py's only deps); it is
|
|
# already warm on the runner. Nothing here builds.
|
|
container:
|
|
image: 192.168.1.58:5010/punktfunk-android-ci:latest
|
|
timeout-minutes: 15
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
|
|
- name: Promote
|
|
env:
|
|
SERVICE_ACCOUNT_JSON: ${{ secrets.SERVICE_ACCOUNT_JSON }}
|
|
VERSION_CODE: ${{ inputs.version_code }}
|
|
TO_TRACK: ${{ inputs.to_track }}
|
|
FROM_TRACK: ${{ inputs.from_track }}
|
|
NOTES_TAG: ${{ inputs.notes_tag }}
|
|
STATUS: ${{ inputs.status }}
|
|
USER_FRACTION: ${{ inputs.user_fraction }}
|
|
DRY_RUN: ${{ inputs.dry_run }}
|
|
run: |
|
|
set -- --package io.unom.punktfunk \
|
|
--promote "$VERSION_CODE" \
|
|
--track "$TO_TRACK" --status "$STATUS"
|
|
# Explicit `if`, not `[ ] && …`: under `sh -e` a false AND-OR list that ends up LAST in
|
|
# the script aborts the step, and these get reordered.
|
|
if [ -n "$FROM_TRACK" ]; then set -- "$@" --promote-from "$FROM_TRACK"; fi
|
|
if [ -n "$USER_FRACTION" ]; then set -- "$@" --user-fraction "$USER_FRACTION"; fi
|
|
if [ -n "$NOTES_TAG" ]; then
|
|
NOTES="docs/releases/whatsnew/${NOTES_TAG}.txt"
|
|
# Fail loudly rather than silently publishing with the PREVIOUS release's text still
|
|
# showing on the store listing.
|
|
[ -f "$NOTES" ] || { echo "ERROR: no such notes file: $NOTES"; exit 1; }
|
|
set -- "$@" --release-notes-file "$NOTES"
|
|
fi
|
|
if [ "$DRY_RUN" = "true" ]; then set -- "$@" --no-commit; fi
|
|
echo "promoting versionCode=$VERSION_CODE -> $TO_TRACK (dry_run=$DRY_RUN)"
|
|
python3 clients/android/ci/play-upload.py "$@"
|