feat(release): automate store publishing (fastlane supply + GitLab CI) and F-Droid recipe
- fastlane: Appfile/Fastfile/Gemfile with deploy_play lane (AAB -> Play internal track) - CI: tag-gated build:android (signed AAB/APK from CI secrets) + deploy:play jobs - F-Droid: fdroiddata build recipe at docs/fdroid/org.comunes.tane.yml - Play compliance: Data Safety / content-rating answer sheet (docs/legal/internal) - docs/release.md: automated tag-triggered flow, dedicated tane-upload keystore - pubspec: description now mentions the market, not just the inventory
This commit is contained in:
parent
dc0d18562f
commit
fd6b9d5f0d
9 changed files with 361 additions and 28 deletions
|
|
@ -10,6 +10,7 @@ stages:
|
||||||
- analyze
|
- analyze
|
||||||
- test
|
- test
|
||||||
- build
|
- build
|
||||||
|
- deploy
|
||||||
|
|
||||||
variables:
|
variables:
|
||||||
PUB_CACHE: "$CI_PROJECT_DIR/.pub-cache"
|
PUB_CACHE: "$CI_PROJECT_DIR/.pub-cache"
|
||||||
|
|
@ -82,3 +83,63 @@ test:app_seeds:
|
||||||
coverage_report:
|
coverage_report:
|
||||||
coverage_format: cobertura
|
coverage_format: cobertura
|
||||||
path: apps/app_seeds/coverage/cobertura.xml
|
path: apps/app_seeds/coverage/cobertura.xml
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Release automation. Runs ONLY on tags, so it never touches the per-push gate.
|
||||||
|
# Password-free: signing + Play credentials come from masked+protected CI vars.
|
||||||
|
# Required CI variables (Settings > CI/CD > Variables), set once:
|
||||||
|
# TANE_KEYSTORE_BASE64 base64 of the dedicated Tane upload keystore (.jks)
|
||||||
|
# TANE_KEYSTORE_PASSWORD store password
|
||||||
|
# TANE_KEY_ALIAS key alias (tane-upload)
|
||||||
|
# TANE_KEY_PASSWORD key password
|
||||||
|
# SUPPLY_JSON_KEY_DATA Google Play service-account JSON (raw file contents)
|
||||||
|
# Tag a commit (e.g. `git tag v0.1.0 && git push --tags`) to build + publish.
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# Build the signed release artifacts (AAB for Play, APK for sideload/QA).
|
||||||
|
build:android:
|
||||||
|
stage: build
|
||||||
|
rules:
|
||||||
|
- if: '$CI_COMMIT_TAG'
|
||||||
|
script:
|
||||||
|
- cd apps/app_seeds
|
||||||
|
- dart run slang
|
||||||
|
- dart run build_runner build --delete-conflicting-outputs
|
||||||
|
# Materialize the signing material from CI secrets (never in the repo).
|
||||||
|
- echo "$TANE_KEYSTORE_BASE64" | base64 -d > "$CI_PROJECT_DIR/tane-upload.jks"
|
||||||
|
- |
|
||||||
|
cat > android/key.properties <<EOF
|
||||||
|
storeFile=$CI_PROJECT_DIR/tane-upload.jks
|
||||||
|
storePassword=$TANE_KEYSTORE_PASSWORD
|
||||||
|
keyAlias=$TANE_KEY_ALIAS
|
||||||
|
keyPassword=$TANE_KEY_PASSWORD
|
||||||
|
EOF
|
||||||
|
- flutter build appbundle --release
|
||||||
|
- flutter build apk --release
|
||||||
|
# Don't leave signing material in the workspace/cache.
|
||||||
|
- rm -f android/key.properties "$CI_PROJECT_DIR/tane-upload.jks"
|
||||||
|
artifacts:
|
||||||
|
paths:
|
||||||
|
- apps/app_seeds/build/app/outputs/bundle/release/app-release.aab
|
||||||
|
- apps/app_seeds/build/app/outputs/flutter-apk/app-release.apk
|
||||||
|
expire_in: 90 days
|
||||||
|
|
||||||
|
# Upload the AAB + store listing to Google Play (internal track) via fastlane.
|
||||||
|
# Needs a Ruby image, not the Flutter one, so it overrides the global setup.
|
||||||
|
deploy:play:
|
||||||
|
stage: deploy
|
||||||
|
image: ruby:3.2
|
||||||
|
needs:
|
||||||
|
- job: build:android
|
||||||
|
artifacts: true
|
||||||
|
rules:
|
||||||
|
- if: '$CI_COMMIT_TAG'
|
||||||
|
before_script:
|
||||||
|
- cd apps/app_seeds
|
||||||
|
- bundle install --path vendor/bundle
|
||||||
|
script:
|
||||||
|
- bundle exec fastlane deploy_play
|
||||||
|
cache:
|
||||||
|
key: "fastlane-gems"
|
||||||
|
paths:
|
||||||
|
- apps/app_seeds/vendor/bundle/
|
||||||
|
|
|
||||||
7
apps/app_seeds/.gitignore
vendored
7
apps/app_seeds/.gitignore
vendored
|
|
@ -43,3 +43,10 @@ app.*.map.json
|
||||||
/android/app/debug
|
/android/app/debug
|
||||||
/android/app/profile
|
/android/app/profile
|
||||||
/android/app/release
|
/android/app/release
|
||||||
|
|
||||||
|
# Release automation (fastlane supply). Credentials come from CI secrets or a
|
||||||
|
# gitignored local file; generated reports are not tracked.
|
||||||
|
/play-service-account.json
|
||||||
|
/fastlane/report.xml
|
||||||
|
/fastlane/Preview.html
|
||||||
|
/Gemfile.lock
|
||||||
|
|
|
||||||
5
apps/app_seeds/Gemfile
Normal file
5
apps/app_seeds/Gemfile
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
# Ruby toolchain for release automation (fastlane supply -> Google Play).
|
||||||
|
# Run from apps/app_seeds/: bundle install && bundle exec fastlane deploy_play
|
||||||
|
source "https://rubygems.org"
|
||||||
|
|
||||||
|
gem "fastlane"
|
||||||
9
apps/app_seeds/fastlane/Appfile
Normal file
9
apps/app_seeds/fastlane/Appfile
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
# Google Play identity + credentials for `fastlane supply`.
|
||||||
|
#
|
||||||
|
# The service-account JSON is injected from CI as the SUPPLY_JSON_KEY_DATA
|
||||||
|
# secret (raw file contents) and is NEVER committed. Locally you can instead
|
||||||
|
# export SUPPLY_JSON_KEY_DATA, or drop a gitignored play-service-account.json
|
||||||
|
# and point json_key_file at it.
|
||||||
|
package_name("org.comunes.tane")
|
||||||
|
|
||||||
|
json_key_data_raw(ENV["SUPPLY_JSON_KEY_DATA"]) if ENV["SUPPLY_JSON_KEY_DATA"]
|
||||||
37
apps/app_seeds/fastlane/Fastfile
Normal file
37
apps/app_seeds/fastlane/Fastfile
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
# Fastlane lanes for Tane (org.comunes.tane).
|
||||||
|
#
|
||||||
|
# Publishing is automated and password-free: a git tag triggers CI, which builds
|
||||||
|
# a signed AAB and runs `deploy_play`. Credentials come from CI secrets, never
|
||||||
|
# typed by hand:
|
||||||
|
# SUPPLY_JSON_KEY_DATA Google Play service-account JSON (raw contents)
|
||||||
|
#
|
||||||
|
# The store listing (titles, descriptions, changelogs, screenshots) is read
|
||||||
|
# straight from fastlane/metadata/android/<locale>/ — the tree that already
|
||||||
|
# feeds the landing site and F-Droid. See docs/release.md and .gitlab-ci.yml.
|
||||||
|
|
||||||
|
default_platform(:android)
|
||||||
|
|
||||||
|
# Release bundle produced by `flutter build appbundle --release`, relative to
|
||||||
|
# the app dir (apps/app_seeds), which is where fastlane runs.
|
||||||
|
AAB_PATH = "build/app/outputs/bundle/release/app-release.aab".freeze
|
||||||
|
|
||||||
|
platform :android do
|
||||||
|
desc "Upload the signed AAB + store listing to Google Play (internal track)"
|
||||||
|
lane :deploy_play do
|
||||||
|
supply(
|
||||||
|
track: "internal",
|
||||||
|
aab: AAB_PATH,
|
||||||
|
release_status: "completed", # internal testing has no review delay
|
||||||
|
skip_upload_apk: true, # we ship the AAB, not a raw APK
|
||||||
|
skip_upload_changelogs: false,
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
desc "Push only the store listing (text + images), no binary"
|
||||||
|
lane :deploy_metadata do
|
||||||
|
supply(
|
||||||
|
skip_upload_aab: true,
|
||||||
|
skip_upload_apk: true,
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
name: tane
|
name: tane
|
||||||
description: "Tane — local-first, encrypted, decentralized traditional-seed inventory."
|
description: "Tane — local-first, encrypted, decentralized traditional-seed inventory and market."
|
||||||
publish_to: 'none'
|
publish_to: 'none'
|
||||||
version: 0.1.0+1
|
version: 0.1.0+1
|
||||||
|
|
||||||
|
|
|
||||||
62
docs/fdroid/org.comunes.tane.yml
Normal file
62
docs/fdroid/org.comunes.tane.yml
Normal file
|
|
@ -0,0 +1,62 @@
|
||||||
|
# F-Droid metadata for Tane (org.comunes.tane).
|
||||||
|
#
|
||||||
|
# This is the source of truth for the fdroiddata merge request. To submit:
|
||||||
|
# 1. Fork https://gitlab.com/fdroid/fdroiddata
|
||||||
|
# 2. Copy this file to metadata/org.comunes.tane.yml
|
||||||
|
# 3. Validate locally: fdroid lint org.comunes.tane
|
||||||
|
# fdroid build -v -l org.comunes.tane
|
||||||
|
# 4. Open the MR.
|
||||||
|
#
|
||||||
|
# Prerequisites (see docs/release.md):
|
||||||
|
# - The source repo below must be publicly clonable without auth.
|
||||||
|
# - Each release is a signed git tag `v<version>` (UpdateCheckMode: Tags).
|
||||||
|
# - F-Droid builds and signs the APK with ITS OWN key, so the F-Droid build
|
||||||
|
# and the Google Play build have different signatures and cannot cross-update.
|
||||||
|
#
|
||||||
|
# NOTE: Flutter-on-F-Droid builds are finicky. Pin the Flutter version to the
|
||||||
|
# developer toolchain (3.41.9) and re-run `fdroid build` after every bump. This
|
||||||
|
# recipe accounts for the monorepo layout (pub workspace root at repo root, app
|
||||||
|
# at apps/app_seeds) and the generated code (slang i18n + Drift via build_runner).
|
||||||
|
|
||||||
|
Categories:
|
||||||
|
- Science & Education
|
||||||
|
License: AGPL-3.0-or-later
|
||||||
|
AuthorName: Comunes
|
||||||
|
AuthorWebSite: https://comunes.org
|
||||||
|
WebSite: https://tane.comunes.org
|
||||||
|
SourceCode: https://git.comunes.org/comunes/tane
|
||||||
|
IssueTracker: https://git.comunes.org/comunes/tane/issues
|
||||||
|
Translation: https://translate.comunes.org/projects/tane
|
||||||
|
|
||||||
|
AutoName: Tane
|
||||||
|
|
||||||
|
RepoType: git
|
||||||
|
Repo: https://git.comunes.org/comunes/tane.git
|
||||||
|
|
||||||
|
Builds:
|
||||||
|
- versionName: 0.1.0
|
||||||
|
versionCode: 1
|
||||||
|
commit: v0.1.0
|
||||||
|
subdir: apps/app_seeds
|
||||||
|
sudo:
|
||||||
|
- apt-get update
|
||||||
|
- apt-get install -y git unzip xz-utils
|
||||||
|
# Pin Flutter to the developer toolchain; keep in sync with .gitlab-ci.yml.
|
||||||
|
- git clone --depth 1 -b 3.41.9 https://github.com/flutter/flutter.git /opt/flutter
|
||||||
|
- git config --global --add safe.directory /opt/flutter
|
||||||
|
output: build/app/outputs/flutter-apk/app-release.apk
|
||||||
|
prebuild:
|
||||||
|
- export PATH="/opt/flutter/bin:$PATH"
|
||||||
|
- flutter config --no-analytics
|
||||||
|
- flutter pub get
|
||||||
|
# Generated sources: i18n (slang) then Drift/other codegen (build_runner).
|
||||||
|
- dart run slang
|
||||||
|
- dart run build_runner build --delete-conflicting-outputs
|
||||||
|
build:
|
||||||
|
- export PATH="/opt/flutter/bin:$PATH"
|
||||||
|
- flutter build apk --release
|
||||||
|
|
||||||
|
AutoUpdateMode: Version v%v
|
||||||
|
UpdateCheckMode: Tags
|
||||||
|
CurrentVersion: 0.1.0
|
||||||
|
CurrentVersionCode: 1
|
||||||
84
docs/legal/internal/play-compliance.md
Normal file
84
docs/legal/internal/play-compliance.md
Normal file
|
|
@ -0,0 +1,84 @@
|
||||||
|
# Google Play compliance answer sheet (internal)
|
||||||
|
|
||||||
|
Draft answers for the Play Console questionnaires. Transcribe into the console;
|
||||||
|
this file is the single source of truth so the answers stay consistent across
|
||||||
|
releases. Grounded in [`../privacy-policy.md`](../privacy-policy.md) and
|
||||||
|
[`../../design/security-privacy.md`](../../design/security-privacy.md).
|
||||||
|
|
||||||
|
Package: `org.comunes.tane` · Publisher: Asociación Comunes · Contact: info@comunes.org
|
||||||
|
Privacy policy URL: https://tane.comunes.org/legal/privacy
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Data Safety form
|
||||||
|
|
||||||
|
**Core principle:** Tane has **no accounts and no Tane server**. Anything a user
|
||||||
|
records stays on-device, encrypted (SQLCipher). Data only leaves the device by an
|
||||||
|
explicit user action (publishing an offer, filling a public profile, sending a
|
||||||
|
message, or syncing to the user's own second device), and then it travels through
|
||||||
|
community-run relays, not through us. So under Play's definitions we *collect*
|
||||||
|
nothing for ourselves; we *share* only the specific things the user chooses to
|
||||||
|
publish, all for **App functionality** — never for analytics, ads, or sale.
|
||||||
|
|
||||||
|
### Does your app collect or share any user data? → **Yes**
|
||||||
|
(Because user-initiated publishing sends data to third-party relays.)
|
||||||
|
|
||||||
|
### Data types — declare as **Shared**, purpose **App functionality**, **optional** (user-initiated)
|
||||||
|
|
||||||
|
| Play data type | Declare? | Notes |
|
||||||
|
|---|---|---|
|
||||||
|
| Location → Approximate location | Shared | Only a coarse ~2–80 km area, only if the user sets a sharing zone for an offer. Never precise. Never collected for us. |
|
||||||
|
| Personal info → Name | Shared | Only the display name the user chooses to put in a public profile. Optional. |
|
||||||
|
| Personal info → Other info (bio) | Shared | Optional public profile bio. |
|
||||||
|
| Photos and videos → Photos | Shared | Only photos the user attaches to a public offer or profile. |
|
||||||
|
| Messages → Other in-app messages | Shared | Direct messages, **end-to-end encrypted**; relays carry only encrypted envelopes. Declare as shared for transparency; mark encrypted in transit. |
|
||||||
|
| App activity / App info & performance | No | No analytics, no crash reporting, no diagnostics sent anywhere. |
|
||||||
|
| Financial, Health, Contacts (device), Calendar, Browsing history, Files, Device IDs | No | Not accessed or transmitted. |
|
||||||
|
|
||||||
|
Note on "Collected vs Shared": Play defines *collection* as leaving the device to
|
||||||
|
the developer. We receive nothing, so nothing is "Collected by us." The items above
|
||||||
|
are *Shared* (to third-party relays / other users). If the console forces a
|
||||||
|
"Collected" tick for a shared type, pair it with the encryption + optional answers below.
|
||||||
|
|
||||||
|
### Security practices
|
||||||
|
- **Is all user data encrypted in transit?** → **Yes.** Relay connections use TLS;
|
||||||
|
direct messages are additionally end-to-end encrypted; device-to-device sync is encrypted.
|
||||||
|
- **Data stored on device** is encrypted at rest (SQLCipher) — mention in the listing, though Play's form focuses on transit.
|
||||||
|
- **Can users request data deletion?** → **Yes.** Everything local can be deleted in-app
|
||||||
|
instantly; published offers can be withdrawn, which asks relays to delete. Relays we
|
||||||
|
don't operate may retain copies (stated honestly in the privacy policy).
|
||||||
|
- **Data is NOT sold.** **Data is NOT used for advertising or marketing.**
|
||||||
|
- **Independent security review?** → No (self-declared; source is public/AGPL and auditable).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Content rating (IARC questionnaire)
|
||||||
|
|
||||||
|
- Category: **Utility / Reference / Productivity** (not a game).
|
||||||
|
- Violence, sexual content, gambling, drugs: **None.**
|
||||||
|
- **User-generated content / communication:** **Yes** — the market has public offers,
|
||||||
|
user profiles, and direct messaging. Declare in-app UGC and user-to-user communication.
|
||||||
|
- Expected outcome: **Everyone / PEGI 3–style**, with the UGC/communication flags set.
|
||||||
|
|
||||||
|
## 3. Target audience & content
|
||||||
|
|
||||||
|
- Target age: **not directed at children.** Age band spans adults; the phrase "10–99"
|
||||||
|
is a usability aim (simple enough for a 10-year-old, legible for a 99-year-old), **not**
|
||||||
|
a claim of child audience. In the console pick the **adult / 18+** or "not for children"
|
||||||
|
target so Families policy does not apply to the market/messaging features.
|
||||||
|
- The seed **inventory** alone is harmless for any age, but the **market and messaging**
|
||||||
|
are social features and are declared not directed at children (see privacy policy §8).
|
||||||
|
|
||||||
|
## 4. UGC policy (Play's user-generated content requirements)
|
||||||
|
|
||||||
|
The market ships the mitigations Play looks for; keep them working:
|
||||||
|
- **Community rules acceptance** gate on entering the market.
|
||||||
|
- **Report** content/users and **block** users, in-app.
|
||||||
|
- A stated content policy: [`../community-rules.md`](../community-rules.md).
|
||||||
|
|
||||||
|
## 5. Ads & privacy
|
||||||
|
|
||||||
|
- **Contains ads?** → **No.**
|
||||||
|
- **Data deletion URL / contact:** info@comunes.org (and in-app deletion).
|
||||||
|
- **Government app?** → No. **Financial features?** → No (optional Ğ1 price is a plain
|
||||||
|
number shown in a listing, no in-app payments, no Play Billing).
|
||||||
110
docs/release.md
110
docs/release.md
|
|
@ -1,7 +1,21 @@
|
||||||
# Releasing Tane (manual, no CI yet)
|
# Releasing Tane
|
||||||
|
|
||||||
Block 1 ships as a self-contained Android app (desktop builds also work). There
|
Tane ships as a self-contained Android app (desktop builds also work). Releases
|
||||||
is no release pipeline yet — this page is the by-hand checklist.
|
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
|
||||||
|
```
|
||||||
|
|
||||||
|
CI (`build:android` → `deploy:play` in [`../.gitlab-ci.yml`](../.gitlab-ci.yml)) builds
|
||||||
|
the signed AAB/APK and uploads to Play's **internal** track. No passwords are typed.
|
||||||
|
|
||||||
## Versioning
|
## Versioning
|
||||||
|
|
||||||
|
|
@ -12,38 +26,92 @@ is no release pipeline yet — this page is the by-hand checklist.
|
||||||
add a matching per-locale note under
|
add a matching per-locale note under
|
||||||
`apps/app_seeds/fastlane/metadata/android/<locale>/changelogs/<versionCode>.txt`.
|
`apps/app_seeds/fastlane/metadata/android/<locale>/changelogs/<versionCode>.txt`.
|
||||||
|
|
||||||
## Android signing (one-time)
|
## One-time setup
|
||||||
|
|
||||||
Release builds sign with your own keystore when a **gitignored**
|
### Android signing keystore (dedicated to Tane)
|
||||||
`apps/app_seeds/android/key.properties` exists; without it they fall back to
|
|
||||||
debug keys (so contributors can still `flutter run --release`).
|
Tane uses its **own** upload keystore, separate from other Comunes apps (Ğ1nkgo,
|
||||||
|
Fuegos), kept in the shared Comunes signing directory:
|
||||||
|
|
||||||
1. Create a keystore (keep it and its passwords safe — losing it means you can
|
|
||||||
never update the app under the same identity):
|
|
||||||
```bash
|
```bash
|
||||||
keytool -genkey -v -keystore ~/tane-release.jks \
|
keytool -genkey -v \
|
||||||
-keyalg RSA -keysize 4096 -validity 10000 -alias tane
|
-keystore /home/vjrj/proyectos/sync/comunes/shared-l2/apkSigning/tane-upload.jks \
|
||||||
|
-keyalg RSA -keysize 4096 -validity 10000 -alias tane-upload
|
||||||
```
|
```
|
||||||
2. Create `apps/app_seeds/android/key.properties` (never commit it):
|
|
||||||
|
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
|
```properties
|
||||||
storeFile=/home/you/tane-release.jks
|
storeFile=/home/vjrj/proyectos/sync/comunes/shared-l2/apkSigning/tane-upload.jks
|
||||||
storePassword=…
|
storePassword=…
|
||||||
keyAlias=tane
|
keyAlias=tane-upload
|
||||||
keyPassword=…
|
keyPassword=…
|
||||||
```
|
```
|
||||||
|
|
||||||
## Build the beta by hand
|
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, GitLab → Settings → CI/CD → Variables)
|
||||||
|
|
||||||
|
Set these as **masked + protected** so releases never prompt for a password:
|
||||||
|
|
||||||
|
| Variable | 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 created in Google Cloud and granted release access in
|
||||||
|
Play Console; it is what lets `fastlane supply` upload without a human. The first
|
||||||
|
upload of a brand-new app must still be done by hand in the console (Play requires
|
||||||
|
the app to exist before the API accepts uploads).
|
||||||
|
|
||||||
|
## Build by hand (fallback / first Play upload)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd apps/app_seeds
|
cd apps/app_seeds
|
||||||
dart run slang # i18n (if strings changed)
|
dart run slang # i18n (if strings changed)
|
||||||
dart run build_runner build --delete-conflicting-outputs # Drift (if schema changed)
|
dart run build_runner build --delete-conflicting-outputs # Drift (if schema changed)
|
||||||
flutter test # must be green
|
flutter test # must be green
|
||||||
flutter build apk --release # or: flutter build appbundle --release
|
flutter build appbundle --release # AAB for Play
|
||||||
|
flutter build apk --release # APK for sideload / QA
|
||||||
```
|
```
|
||||||
|
|
||||||
The APK lands at
|
Artifacts:
|
||||||
`apps/app_seeds/build/app/outputs/flutter-apk/app-release.apk`.
|
- 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).
|
||||||
|
|
||||||
|
## Publish to F-Droid (official repo)
|
||||||
|
|
||||||
|
F-Droid builds and signs 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. F-Droid signs
|
||||||
|
with its own key, so the F-Droid and Play builds have different signatures.
|
||||||
|
|
||||||
## Store metadata
|
## Store metadata
|
||||||
|
|
||||||
|
|
@ -75,7 +143,7 @@ artifact, it does not replace tests):
|
||||||
second install).
|
second install).
|
||||||
5. Switch language (es / en / pt); check an RTL locale if the device offers one.
|
5. Switch language (es / en / pt); check an RTL locale if the device offers one.
|
||||||
|
|
||||||
## Not in this release
|
## Not automated
|
||||||
|
|
||||||
CI, Play Store automation, web builds (SQLCipher is unavailable on web), and
|
Web builds (SQLCipher is unavailable on web) and iOS (no Apple developer setup
|
||||||
the whole social layer (Block 2).
|
yet). The F-Droid MR is opened by hand (F-Droid builds on its own infra, not ours).
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue