diff --git a/.forgejo/workflows/ci.yml b/.forgejo/workflows/ci.yml deleted file mode 100644 index 9331d0a..0000000 --- a/.forgejo/workflows/ci.yml +++ /dev/null @@ -1,51 +0,0 @@ -# Per-push gate for git.comunes.org (Forgejo Actions): analyze + tests. -# Mirrors the local verification gate in docs/design/testing.md. Ported from the -# retired .gitlab-ci.yml. Flutter pinned to the developer toolchain (3.41.9). -# -# NOTE: `runs-on` must match a label your Forgejo runner registered with; adjust -# if the Comunes runner uses something other than `docker`. - -name: ci - -on: - push: - branches: ['**'] - pull_request: - -jobs: - analyze: - runs-on: docker - container: - image: ghcr.io/cirruslabs/flutter:3.41.9 - steps: - - uses: actions/checkout@v4 - - run: flutter pub get - - run: dart format --output=none --set-exit-if-changed . - - run: flutter analyze - - test-commons-core: - runs-on: docker - container: - image: ghcr.io/cirruslabs/flutter:3.41.9 - steps: - - uses: actions/checkout@v4 - - run: flutter pub get - - name: dart test - working-directory: packages/commons_core - run: dart test - - test-app-seeds: - runs-on: docker - container: - image: ghcr.io/cirruslabs/flutter:3.41.9 - steps: - - uses: actions/checkout@v4 - # SQLCipher so the "no plaintext at rest" security test actually runs. - - run: apt-get update -qq && apt-get install -y -qq libsqlcipher-dev - - run: flutter pub get - - name: Generate code + test - working-directory: apps/app_seeds - run: | - dart run slang - dart run build_runner build --delete-conflicting-outputs - flutter test --coverage diff --git a/.forgejo/workflows/release.yml b/.forgejo/workflows/release.yml deleted file mode 100644 index 558a89c..0000000 --- a/.forgejo/workflows/release.yml +++ /dev/null @@ -1,78 +0,0 @@ -# Release automation for git.comunes.org (Forgejo Actions). -# -# Password-free: pushing a tag `v*` builds a signed AAB/APK and uploads it to -# Google Play's internal track via fastlane. All credentials come from repo -# secrets (Settings > Actions > Secrets), never typed: -# TANE_KEYSTORE_BASE64 base64 of the dedicated 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 (reused from Ğ1nkgo) -# -# Build + deploy live in ONE job so the AAB never has to cross a job boundary. -# NOTE: `runs-on` must match a label your Forgejo runner registered with; adjust -# if the Comunes runner uses something other than `docker`. - -name: release - -on: - push: - tags: - - 'v*' - -jobs: - android: - runs-on: docker - container: - image: ghcr.io/cirruslabs/flutter:3.41.9 - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Generate code (i18n + Drift) - working-directory: apps/app_seeds - run: | - flutter pub get - dart run slang - dart run build_runner build --delete-conflicting-outputs - - - name: Materialize signing material from secrets - working-directory: apps/app_seeds - env: - TANE_KEYSTORE_BASE64: ${{ secrets.TANE_KEYSTORE_BASE64 }} - TANE_KEYSTORE_PASSWORD: ${{ secrets.TANE_KEYSTORE_PASSWORD }} - TANE_KEY_ALIAS: ${{ secrets.TANE_KEY_ALIAS }} - TANE_KEY_PASSWORD: ${{ secrets.TANE_KEY_PASSWORD }} - run: | - echo "$TANE_KEYSTORE_BASE64" | base64 -d > "$GITHUB_WORKSPACE/tane-upload.jks" - cat > android/key.properties < Google Play). -# Run from apps/app_seeds/: bundle install && bundle exec fastlane deploy_play -source "https://rubygems.org" - -gem "fastlane" diff --git a/apps/app_seeds/fastlane/Appfile b/apps/app_seeds/fastlane/Appfile deleted file mode 100644 index 590393c..0000000 --- a/apps/app_seeds/fastlane/Appfile +++ /dev/null @@ -1,9 +0,0 @@ -# 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"] diff --git a/apps/app_seeds/fastlane/Fastfile b/apps/app_seeds/fastlane/Fastfile deleted file mode 100644 index 25190cd..0000000 --- a/apps/app_seeds/fastlane/Fastfile +++ /dev/null @@ -1,37 +0,0 @@ -# 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// — 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 diff --git a/apps/app_seeds/fastlane/metadata/android/en-US/images/featureGraphic.png b/apps/app_seeds/fastlane/metadata/android/en-US/images/featureGraphic.png deleted file mode 100644 index f320591..0000000 Binary files a/apps/app_seeds/fastlane/metadata/android/en-US/images/featureGraphic.png and /dev/null differ diff --git a/apps/app_seeds/fastlane/metadata/android/en-US/images/icon.png b/apps/app_seeds/fastlane/metadata/android/en-US/images/icon.png deleted file mode 100644 index 72b550d..0000000 Binary files a/apps/app_seeds/fastlane/metadata/android/en-US/images/icon.png and /dev/null differ diff --git a/apps/app_seeds/fastlane/metadata/android/es-ES/images/featureGraphic.png b/apps/app_seeds/fastlane/metadata/android/es-ES/images/featureGraphic.png deleted file mode 100644 index d12b286..0000000 Binary files a/apps/app_seeds/fastlane/metadata/android/es-ES/images/featureGraphic.png and /dev/null differ diff --git a/apps/app_seeds/fastlane/metadata/android/es-ES/images/icon.png b/apps/app_seeds/fastlane/metadata/android/es-ES/images/icon.png deleted file mode 100644 index 72b550d..0000000 Binary files a/apps/app_seeds/fastlane/metadata/android/es-ES/images/icon.png and /dev/null differ diff --git a/apps/app_seeds/pubspec.yaml b/apps/app_seeds/pubspec.yaml index ce2b6bb..fd47a9a 100644 --- a/apps/app_seeds/pubspec.yaml +++ b/apps/app_seeds/pubspec.yaml @@ -1,5 +1,5 @@ name: tane -description: "Tane — local-first, encrypted, decentralized traditional-seed inventory and market." +description: "Tane — local-first, encrypted, decentralized traditional-seed inventory." publish_to: 'none' version: 0.1.0+1 diff --git a/apps/app_seeds/tool/gen_feature_graphic.py b/apps/app_seeds/tool/gen_feature_graphic.py deleted file mode 100644 index 63ea2bc..0000000 --- a/apps/app_seeds/tool/gen_feature_graphic.py +++ /dev/null @@ -1,101 +0,0 @@ -#!/usr/bin/env python3 -"""Generate Play Store feature graphics (1024x500) + store icon (512) for Tane. - -Brand green #2F7D34, white sprout from the adaptive foreground, 'Tane' wordmark -in Noto Sans (the site's brand typeface), and the official value-proposition -tagline. On-brand and replaceable with a designed asset later. - -Run from apps/app_seeds/: python3 tool/gen_feature_graphic.py -""" -import os -from PIL import Image, ImageDraw, ImageFont - -REPO_APP = "/home/vjrj/proyectos/dev/tane/.claude/worktrees/interesting-cray-6261fd/apps/app_seeds" -NOTO = "/usr/share/fonts/truetype/noto" -F_BOLD = f"{NOTO}/NotoSans-Bold.ttf" -F_MED = f"{NOTO}/NotoSans-Medium.ttf" -F_REG = f"{NOTO}/NotoSans-Regular.ttf" -FG = f"{REPO_APP}/assets/icon_foreground.png" -ICON = f"{REPO_APP}/assets/icon.png" - -GREEN = (47, 125, 52) # #2F7D34 -GREEN_DARK = (34, 82, 30) # #22521E -WHITE = (255, 255, 255) -FAINT = (208, 228, 210) - -W, H = 1024, 500 - -# value line = what the app is (explains it); attrs = the differentiators. -COPY = { - "en-US": ("Keep and share traditional seeds", "Offline · Encrypted · No account"), - "es-ES": ("Guarda y comparte semillas tradicionales", "Sin conexión · Cifrado · Sin cuenta"), -} - - -def sprout(height): - im = Image.open(FG).convert("RGBA") - bb = im.getbbox() - if bb: - im = im.crop(bb) - s = height / im.height - return im.resize((max(1, int(im.width * s)), height), Image.LANCZOS) - - -def bg(): - top = Image.new("RGB", (W, H), GREEN) - bot = Image.new("RGB", (W, H), GREEN_DARK) - mask = Image.new("L", (W, H)) - md = ImageDraw.Draw(mask) - for y in range(H): - md.line([(0, y), (W, y)], fill=int(60 * (y / H))) - return Image.composite(bot, top, mask) - - -def center_line(d, text, font, top, fill): - b = d.textbbox((0, 0), text, font=font) - w = b[2] - b[0] - d.text(((W - w) / 2 - b[0], top - b[1]), text, font=font, fill=fill) - return b[3] - b[1] - - -def feature_graphic(locale, out): - img = bg() - d = ImageDraw.Draw(img) - value, attrs = COPY[locale] - - # Row 1: sprout + 'Tane', centered as a group in the upper half. - spr = sprout(150) - wf = ImageFont.truetype(F_BOLD, 120) - wb = d.textbbox((0, 0), "Tane", font=wf) - ww, wh = wb[2] - wb[0], wb[3] - wb[1] - gap = 26 - gw = spr.width + gap + ww - gx = (W - gw) // 2 - row_top = 92 - cy = row_top + spr.height / 2 - img.paste(spr, (gx, row_top), spr) - d.text((gx + spr.width + gap - wb[0], cy - wh / 2 - wb[1]), "Tane", font=wf, fill=WHITE) - y = row_top + spr.height + 34 - - # Row 2: value proposition (explains the app). - vf = ImageFont.truetype(F_MED, 42) - y += center_line(d, value, vf, y, WHITE) + 18 - - # Row 3: differentiators, quieter. - af = ImageFont.truetype(F_REG, 30) - center_line(d, attrs, af, y, FAINT) - - img.save(out, "PNG") - print("wrote", out, img.size) - - -def store_icon(out): - Image.open(ICON).convert("RGB").resize((512, 512), Image.LANCZOS).save(out, "PNG") - print("wrote", out) - - -for loc in ("en-US", "es-ES"): - d = f"{REPO_APP}/fastlane/metadata/android/{loc}/images" - os.makedirs(d, exist_ok=True) - feature_graphic(loc, f"{d}/featureGraphic.png") - store_icon(f"{d}/icon.png") diff --git a/docs/fdroid/org.comunes.tane.yml b/docs/fdroid/org.comunes.tane.yml deleted file mode 100644 index 1476ada..0000000 --- a/docs/fdroid/org.comunes.tane.yml +++ /dev/null @@ -1,62 +0,0 @@ -# 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` (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 diff --git a/docs/legal/internal/play-compliance.md b/docs/legal/internal/play-compliance.md deleted file mode 100644 index 53b2837..0000000 --- a/docs/legal/internal/play-compliance.md +++ /dev/null @@ -1,84 +0,0 @@ -# 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). diff --git a/docs/release.md b/docs/release.md index b03f092..4d414ae 100644 --- a/docs/release.md +++ b/docs/release.md @@ -1,21 +1,7 @@ -# Releasing Tane +# Releasing Tane (manual, no CI yet) -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/.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 **internal** track. No passwords are typed. +Block 1 ships as a self-contained Android app (desktop builds also work). There +is no release pipeline yet — this page is the by-hand checklist. ## Versioning @@ -26,110 +12,38 @@ builds the signed AAB/APK and uploads to Play's **internal** track. No passwords add a matching per-locale note under `apps/app_seeds/fastlane/metadata/android//changelogs/.txt`. -## One-time setup +## Android signing (one-time) -### Android signing keystore (dedicated to Tane) +Release builds sign with your own 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`). -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 + keytool -genkey -v -keystore ~/tane-release.jks \ + -keyalg RSA -keysize 4096 -validity 10000 -alias tane + ``` +2. Create `apps/app_seeds/android/key.properties` (never commit it): + ```properties + storeFile=/home/you/tane-release.jks + storePassword=… + keyAlias=tane + keyPassword=… + ``` -```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) +## Build the beta by hand ```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 +flutter build apk --release # or: flutter build appbundle --release ``` -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). - -## 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. +The APK lands at +`apps/app_seeds/build/app/outputs/flutter-apk/app-release.apk`. ## Store metadata @@ -161,7 +75,7 @@ artifact, it does not replace tests): second install). 5. Switch language (es / en / pt); check an RTL locale if the device offers one. -## Not automated +## Not in this release -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). +CI, Play Store automation, web builds (SQLCipher is unavailable on web), and +the whole social layer (Block 2).