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

220 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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:
```bash
# 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`](../.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](#manual-smoke-test-before-shipping)
**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`](../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`](../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:
```bash
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):
```properties
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)
```bash
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:
```bash
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`](../apps/app_seeds/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`](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](https://gitlab.com/fdroid/fdroiddata/-/merge_requests/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`](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 `versionCode`s
(`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:
```bash
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`](intro.md); icon and splash are generated by
`flutter_launcher_icons` / `flutter_native_splash` (see `pubspec.yaml`).
## Legal & Play compliance (before any store submission)
- The privacy policy must be live at `https://tane.comunes.org/legal/privacy`
(sources in [`legal/`](legal/README.md)) — 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`](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).