tane/docs/release.md
vjrj 1f3c7975eb
All checks were successful
ci / analyze (push) Successful in 1m3s
ci / test-commons-core (push) Successful in 36s
ci / test-app-seeds (push) Successful in 6m1s
site / deploy (push) Successful in 34s
feat(site,docs): Tane is in F-Droid — link it next to Play
The inclusion MR was merged on 2026-07-25 and the app page went live on
2026-07-28 (v0.1.16, no antifeatures), so the landing page can finally
offer both stores.

- site: publish the F-Droid link and give each store link a single-colour
  icon, so the two are told apart by shape (play triangle vs. the
  free-software robot) rather than by brand colour. The badge row is a
  flex row now: left-aligned in the hero, centred under "Get Tane".
- README: an Install section with both stores, and a Status that matches
  reality (Block 1 shipped, Block 2 under way) instead of "early design".
- release.md: record that the app is published, that further releases only
  need a versionCode bump in the fdroiddata recipe, and that publication
  lags the green build by a day or two.
- open-decisions.md: log what unblocked inclusion — reproducible
  developer-signed builds, a Google-free APK, and opt-in networking.
2026-07-28 11:55:54 +02:00

10 KiB
Raw Blame History

Releasing Tane

Tane ships as a self-contained Android app (desktop builds also work). Releases are automated and password-free: pushing a signed git tag triggers CI, which builds a signed AAB and publishes it to Google Play. This page covers the one-time setup, the automated flow, and the by-hand fallback.

TL;DR — cut a release

Once the one-time setup below is done:

# bump version in apps/app_seeds/pubspec.yaml, update CHANGELOG + changelogs/<code>.txt
git tag v0.1.0 && git push origin v0.1.0

Forgejo Actions (../.forgejo/workflows/release.yml) builds the signed AAB/APK and uploads to Play's production track at 100% rollout. No passwords are typed. Because a tag now publishes to everyone (subject to Google review), run the manual smoke test before tagging. To stage on the internal track first, run bundle exec fastlane deploy_internal by hand.

Versioning

  • Semantic version in apps/app_seeds/pubspec.yaml as MAJOR.MINOR.PATCH+BUILD (e.g. 0.1.0+1). Flutter maps +BUILD to Android versionCode and the rest to versionName.
  • Record every user-visible change in ../CHANGELOG.md and add a matching per-locale note under apps/app_seeds/fastlane/metadata/android/<locale>/changelogs/<versionCode>.txt.

One-time setup

Android signing keystore (dedicated to Tane)

Tane uses its own upload keystore, separate from other Comunes apps (Ğ1nkgo, Fuegos), kept in the shared Comunes signing directory:

keytool -genkey -v \
  -keystore /home/vjrj/proyectos/sync/comunes/shared-l2/apkSigning/tane-upload.jks \
  -keyalg RSA -keysize 4096 -validity 10000 -alias tane-upload

Release builds sign with this keystore when a gitignored apps/app_seeds/android/key.properties exists; without it they fall back to debug keys (so contributors can still flutter run --release). For local signed builds, create apps/app_seeds/android/key.properties (never commit it):

storeFile=/home/vjrj/proyectos/sync/comunes/shared-l2/apkSigning/tane-upload.jks
storePassword=
keyAlias=tane-upload
keyPassword=

With Play App Signing (recommended, enabled once in the console) this is the upload key; Google holds the app signing key and can help rotate the upload key if it is ever lost.

CI secrets (one-time, Forgejo → repo Settings → Actions → Secrets)

Set these so releases never prompt for a password:

Secret What
TANE_KEYSTORE_BASE64 base64 -w0 tane-upload.jks
TANE_KEYSTORE_PASSWORD store password
TANE_KEY_ALIAS tane-upload
TANE_KEY_PASSWORD key password
SUPPLY_JSON_KEY_DATA Google Play service-account JSON (raw file contents)

The Play service account is reused from Ğ1nkgo (ginkgo-play-uploader@…, /home/vjrj/etc/ginkgo_play_api_key.json) — it belongs to the Comunes Play account, not to one app, so it just needs access granted to org.comunes.tane in Play Console. fastlane supply uses it to upload without a human. The first upload of the brand-new app must still be done by hand in the console (Play requires the app to exist before the API accepts uploads).

CI runner (Forgejo Actions) — action needed

The release + gate workflows (.forgejo/workflows/*.yml) run on Forgejo Actions, which needs a registered runner. As of 2026-07-16 the aaron host runs the forgejo and jenkins containers but no Forgejo Actions runner — so the workflows will queue and never start until one is registered. Two options:

  • Register an act_runner on aaron (a forgejo-runner/act_runner container registered against git.comunes.org with a label matching runs-on: — currently docker). Then tag → build → publish works as designed.
  • Or move CI to Jenkins (already running on aaron) via a Jenkinsfile; the build and fastlane deploy_play steps are the same shell commands.

Also: the keystore's real alias is tane-upload, so the Forgejo secret TANE_KEY_ALIAS and local key.properties must both use tane-upload.

Build by hand (fallback / first Play upload)

cd apps/app_seeds
dart run slang                                    # i18n (if strings changed)
dart run build_runner build --delete-conflicting-outputs   # Drift (if schema changed)
flutter test                                      # must be green
flutter build appbundle --release                 # AAB for Play
flutter build apk --release                       # APK for sideload / QA

Artifacts:

  • AAB → apps/app_seeds/build/app/outputs/bundle/release/app-release.aab
  • APK → apps/app_seeds/build/app/outputs/flutter-apk/app-release.apk

Verify the signature is Tane's key (not another app's): apksigner verify --print-certs app-release.apk → alias tane-upload.

Publish to Google Play (fastlane)

CI does this on tag, but you can run it locally too:

cd apps/app_seeds
bundle install
SUPPLY_JSON_KEY_DATA="$(cat play-service-account.json)" bundle exec fastlane deploy_play

Lanes live in fastlane/Fastfile; the store listing (titles, descriptions, changelogs, screenshots) is read straight from fastlane/metadata/android/. Data Safety and content-rating answers: legal/internal/play-compliance.md.

Lanes:

  • deploy_play — upload the AAB to production at 100% (what CI runs on tag).
  • deploy_internal — same AAB to the internal test track (manual QA safety net).
  • deploy_metadata — push only the store listing (no binary).
  • promote_production — promote an internal release to production without a rebuild.

Country/region availability (Play Console, not this repo)

Which countries the app is offered in is not in the repo and fastlane supply does not manage it — it's a Play Console setting. To add countries: Play Console → (Tane) → Production → Countries / regions → Add countries/regions (under "Availability"). Play asks you to pick countries when you first move a release to production; widen the list there for new markets.

Note: the locales under fastlane/metadata/android/<locale> (en-US, es-ES) are listing languages, not countries — adding a locale improves the store page but does not by itself make the app available in a new country.

Publish to F-Droid (official repo)

Tane is live in F-Droid since 2026-07-28 (v0.1.16, no antifeatures): https://f-droid.org/packages/org.comunes.tane/. The inclusion MR (43144) was merged on 2026-07-25; from now on each release only needs the recipe in fdroiddata master bumped, not a new inclusion request. Note that publication lags the green build by a day or two — check repo/status/build.json before assuming breakage.

F-Droid builds from source after a merge request to fdroiddata. The build recipe is kept in-repo at fdroid/org.comunes.tane.yml. Copy it to metadata/org.comunes.tane.yml in a fork of fdroiddata, validate with fdroid lint / fdroid build -l org.comunes.tane, then open the MR.

Every version bump must advance the recipe's per-ABI versionCodes (build × 10 + ABI offset) together with the pubspec.yaml bump — otherwise the fdroid_reference job fails, and a bad tag cannot be fixed by re-tagging (Play rejects a reused versionCode): cut the next version instead.

Reproducible, developer-signed. The recipe pins AllowedAPKSigningKeys (the SHA-256 of the tane-upload certificate) and a binary: URL per ABI. F-Droid rebuilds each split, and if its output matches our signed reference APK byte-for-byte it publishes our APK — so F-Droid and Play share the same signature and users can move between stores without reinstalling. The reference APKs are the per-ABI splits the release workflow attaches to the Forgejo release on git.comunes.org (.forgejo/workflows/release.yml).

To (re)compute the fingerprint from the keystore:

keytool -list -v -keystore .../tane-upload.jks -alias tane-upload \
  | sed -n 's/.*SHA256: //p' | tr -d ':' | tr 'A-Z' 'a-z'

Before pushing the MR, confirm reproducibility in the fdroiddata fork: fdroid build -l org.comunes.tane then fdroid verify org.comunes.tane must be green against the Forgejo binary:. If a split is not reproducible, fall back to F-Droid-signing that version (drop AllowedAPKSigningKeys/binary:) until fixed.

Store metadata

F-Droid / Fastlane-style metadata lives under apps/app_seeds/fastlane/metadata/android/. Descriptions are derived from intro.md; icon and splash are generated by flutter_launcher_icons / flutter_native_splash (see pubspec.yaml).

  • The privacy policy must be live at https://tane.comunes.org/legal/privacy (sources in legal/) — Play Console requires the URL.
  • Fill the Data Safety form and the UGC/content-rating questionnaires from the answer sheet in legal/internal/play-compliance.md.
  • The in-app requirements (community-rules acceptance on market entry, report and block) ship with the app; keep them working — Play's UGC policy review looks for them.

Manual smoke test before shipping

Automated tests cover behaviour; a release build still gets a human pass on a real device (the one place we allow manual testing — it validates the packaged artifact, it does not replace tests):

  1. Cold start → intro carousel → home.
  2. Quick-add a seed with a photo; it appears in the list.
  3. Mark a lot to share; the "I share" filter and printable catalog appear.
  4. Save a backup, then restore it (and test the recovery sheet + code on a second install).
  5. Switch language (es / en / pt); check an RTL locale if the device offers one.

Not automated

Web builds (SQLCipher is unavailable on web) and iOS (no Apple developer setup yet). The F-Droid MR is opened by hand (F-Droid builds on its own infra, not ours).