Release notes explain what people can do and what works better. Write in Divine’s
voice: direct, human, slightly playful. Follow the brand quick reference and tone
of voice in brand-guidelines. Describe benefits, not commit subjects or internal
implementation details.
-
Find the actual source commit of the previous shipped build and the next candidate. Verify historical tags against build provenance: the original
1.0.23tag predates the binaries uploaded on October 6, 2026. Never move that tag or use it blindly as the comparison baseline. -
From the repository root, generate an editing inventory:
python3 mobile/scripts/prepare_release_notes.py \ --base <previous-shipped-source-commit> \ --head <candidate-source-commit> \ --output release-notes/<version>.md
This uses first-parent history and groups changes into features, fixes, and other work to review. It refuses to overwrite an existing file. The output is a working draft, not automatically publishable copy.
-
Rewrite the inventory into concise, user-facing sections. Check the actual diff and feature flags; exclude reverted changes, unavailable features, maintenance-only work, and sensitive details. Mention platform restrictions. Ask for review through the release-notes PR. Remove
<!-- DRAFT -->only when the copy is ready. Commit the notes before building the production candidate. -
Keep the filename aligned with the marketing version in
mobile/pubspec.yaml: for example,release-notes/1.0.24.md. The publisher adds the exact build SHA.
1.0.23.md preserves the reviewed notes already published for that release. It
is not permission to rebuild or replace its existing downloads.
PUBLISH_TO_GITHUB=YES enables GitHub publishing. It remains off by default.
The iOS, Android, and macOS build workflows expose RELEASE_CHANNEL:
| Channel | GitHub | Zapstore |
|---|---|---|
BETA (default) |
Prerelease, never Latest; tag <version>-beta.<backend>.<full-source-sha> |
Not published |
PRODUCTION |
Stable; GitHub selects Latest automatically by date/version; tag <version>; requires finished notes and production backend |
Android publishes the stable release |
DEFAULT_ENV selects the backend, independently of release readiness. A build
using the production backend can still be a beta. Different beta backends and
source commits get separate tags. iOS and Android from the same source/backend
share the same beta release. Record one full candidate commit SHA before starting
any platform job, and start every platform build from that exact commit. Check
each job’s checked-out SHA before publication; a moving branch name alone does
not identify a candidate. If the build launcher cannot select that commit, stop
and use a launcher that can; do not rebuild from a newer main under the same tag.
GitHub direct downloads, Zapstore, App Store and Google Play are independent release channels. A tested production-backend APK can ship as a stable GitHub release with finished release notes before either store approves or completes its rollout. Store review, internal testing and staged rollout do not block GitHub Production publication, promotion or retries. The same independence applies to macOS direct downloads.
Use RELEASE_CHANNEL=BETA while a candidate is still being tested. Once the
release owner accepts that candidate for direct distribution and its notes are
reviewed, promote its original production-backend artifacts on GitHub without
rebuilding. Publish to Zapstore through the signed manual command when that
channel is ready. Independently promote the original store artifacts through
App Store Connect and Play Console and verify each store's rollout there.
GitHub publication does not establish or change store availability.
Record the candidate source SHA, available downloads, and each channel's verified availability in the release handoff. If a store or platform is still pending, say so plainly in the release notes; do not describe a direct release as available in every store. An Android release need not wait for an iOS or macOS artifact. Add later platform artifacts only from the same candidate, using the publisher's digest checks.
Current update-prompt limitation: stable GitHub publication updates Latest. The shipped update checker reads that version for every install source, then opens the install source's own download URL. It does not check that store's available version. Store users may therefore see a prompt before their store has the update. Record that limitation when channels differ; it is not a veto on direct downloads. Update availability should ultimately be checked per installation channel. This policy change does not change shipped clients.
Do not start another iOS or Android build merely to promote a tested beta:
iOS preflight rejects a version already live in the store, and Android would
create and upload another build rather than promote the tested artifacts.
Use --promote-from below. Publication retries reuse the original files;
they are not new store builds. A new macOS Production build can publish its
own ready artifact independently of the stores.
Production publishing uses GitHub’s automatic Latest selection instead of forcing its own release over a concurrently published newer version. Latest is verified to be this version or a higher version.
Production publishing stops if the version’s tag points to another commit, notes are missing or marked DRAFT, or a newer stable version is already Latest. Existing downloads are never clobbered: identical bytes are skipped; different bytes or an unavailable digest cause a failure. A new release stays a draft until its artifact upload succeeds. Retry a failed upload using the original files; rebuilding signed binaries may produce different bytes.
All published metadata, source tags, and artifact digests are read back and verified. Concurrent first uploads can cause one platform job to fail safely; retry publication with its original artifacts after the other job completes.
This does not change store submission: iOS still uploads without submitting for review, and Play still receives the internal-testing AAB. App Store and Play promotion remain explicit store operations.
Download and preserve the failed job's original signed artifacts from Codemagic. Do not run the build again. For a production retry, verify the direct-release readiness requirements above. In an isolated checkout of that job's exact source commit, with its reviewed notes and matching pubspec version, run:
python3 mobile/scripts/publish_github_release.py \
--repo divinevideo/divine-mobile \
--channel PRODUCTION --backend PRODUCTION \
--artifacts-dir /absolute/path/to/original-artifactsThe directory may contain nested downloads. The same files as a normal publish
are selected: arm64/armeabi APKs, IPAs and DMGs. Other APK variants are excluded;
include only this candidate's original files. The publisher checks
existing asset digests, skips byte-identical assets, uploads missing files, and
refuses to replace anything. It also refuses a tag pointing to another commit.
Use --channel BETA and the original backend for a beta retry. Recovery still
applies channel readiness checks; it does not coordinate store rollout.
If GitHub publication succeeded and only Zapstore failed, do not rebuild or
republish GitHub. Retry the signed Zapstore command in the repository's Zapstore
publishing instructions against that verified stable release, including its
existing signing environment and --skip-certificate-linking option.
Once the beta has passed testing for the platforms being published, the release owner accepts it for direct distribution, and its release notes have been reviewed and committed, run this from a checkout containing the publisher and those notes. Store approval and rollout are independent:
python3 mobile/scripts/publish_github_release.py \
--repo divinevideo/divine-mobile \
--promote-from <version>-beta.production.<full-source-sha>The command verifies the beta source tag, downloads its existing assets, checks all digests, and publishes those exact files under the stable version tag. The notes must describe that beta’s source, even if this checkout is newer. Verify the artifacts for the platforms being published are present before promotion. Other platforms need not delay them. Later platforms can be promoted again from the same beta: identical assets are skipped.
This command promotes GitHub only; it does not require or perform store promotion. For Zapstore, publish the resulting stable GitHub release through the existing signed Zapstore tooling; do not rebuild Android merely to trigger a store publication. Never promote a staging or POC beta to production.
No script changes or repairs old tags automatically. If a production tag has already shipped from a different source, use the next maintainer-approved version rather than rewriting release history.