docs: add architecture, roadmap, releasing, contributing + docs index
All checks were successful
CI / ci (push) Successful in 5m34s
All checks were successful
CI / ci (push) Successful in 5m34s
Add a grounded doc set under docs/ plus a root CONTRIBUTING.md: - docs/ARCHITECTURE.md: current code shape (layers, data seam, provider resolution, reminder engine, DI, build/tooling, manifest) - docs/ROADMAP.md: status view (M0/M1 done, M2 in progress, open decisions) - docs/RELEASING.md: tag-driven release flow, CI jobs, F-Droid repo, secrets (was referenced by build.gradle.kts and CHANGELOG but missing) - docs/README.md: docs index and how the docs relate - CONTRIBUTING.md: build/test/lint, layer rules, style, PR conventions Also refresh the stale "M0 — skeleton" status note in README. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
101
docs/RELEASING.md
Normal file
101
docs/RELEASING.md
Normal file
@@ -0,0 +1,101 @@
|
||||
# Floret — releasing
|
||||
|
||||
Floret is distributed through a **self-hosted F-Droid repo** (on Hetzner) with a
|
||||
human-readable **Gitea release** per tag. Both are produced automatically by
|
||||
`.gitea/workflows/release.yaml` when you push a tag. There are no APK assets on
|
||||
the Gitea release itself — distribution lives in the F-Droid repo; the release is
|
||||
the changelog of record.
|
||||
|
||||
---
|
||||
|
||||
## The one source of truth: the git tag
|
||||
|
||||
The git tag drives the version. You do **not** hand-edit version numbers for a
|
||||
release — CI substitutes them from the tag:
|
||||
|
||||
- `versionName` = the tag without a leading `v` (e.g. `v0.2.0` → `0.2.0`).
|
||||
- `versionCode` = `MAJOR*10000 + MINOR*100 + PATCH` (e.g. `0.2.0` → `200`,
|
||||
`1.3.4` → `10304`).
|
||||
|
||||
The values committed in `app/build.gradle.kts` are just the local/dev default;
|
||||
keep them roughly matching the latest released tag, but the tag wins at release
|
||||
time.
|
||||
|
||||
---
|
||||
|
||||
## Cutting a release
|
||||
|
||||
1. **Update `CHANGELOG.md`.** Move items out of `[Unreleased]` into a new
|
||||
`## [X.Y.Z]` section. The release pipeline extracts everything between
|
||||
`## [X.Y.Z]` and the next `## [` heading and uses it verbatim as both the
|
||||
Gitea release notes and the F-Droid per-version "What's New"
|
||||
(`changelogs/<versionCode>.txt`). If no matching section exists, a fallback
|
||||
line is used — so the heading **must** match the tag's version exactly.
|
||||
2. **Commit** the changelog on `main`.
|
||||
3. **Tag and push:**
|
||||
```sh
|
||||
git tag v0.2.0
|
||||
git push origin v0.2.0
|
||||
```
|
||||
4. CI takes over (see below). Watch the run in Gitea Actions.
|
||||
|
||||
---
|
||||
|
||||
## What CI does on a tag
|
||||
|
||||
`release.yaml` runs three jobs:
|
||||
|
||||
| Job | Purpose |
|
||||
|---|---|
|
||||
| `ci` | Sanity gate: unit tests + a debug build (catches version-substitution drift). The other jobs depend on this. |
|
||||
| `build-and-deploy` | Substitute version from the tag → build signed release APK → fetch the existing F-Droid repo from Hetzner → add the new APK + per-version changelog → `fdroid update -c` → upload `repo/` + `metadata/` back. Also attaches the R8 `mapping.txt.gz` to the Gitea release (best-effort) so crash stacktraces stay deobfuscatable. |
|
||||
| `gitea-release` | Create/update the Gitea release for the tag, body = the extracted CHANGELOG section. Gated on `ci` only (not deploy), so notes still publish if the F-Droid upload hiccups. |
|
||||
|
||||
Both deploy and release steps are **re-run safe** (idempotent upserts), so a
|
||||
failed run can be retried.
|
||||
|
||||
---
|
||||
|
||||
## Required CI secrets
|
||||
|
||||
Configured in the Gitea repo settings; the workflow fails loudly if the F-Droid
|
||||
ones are missing (it will **never** auto-generate a repo key — that would rotate
|
||||
the repo fingerprint and break every user's pinned repo).
|
||||
|
||||
| Secret | Used for |
|
||||
|---|---|
|
||||
| `KEYSTORE_BASE64`, `KEY_PASSWORD`, `KEY_ALIAS` | App signing keystore (the APK). |
|
||||
| `FDROID_KEYSTORE_BASE64`, `FDROID_CONFIG_BASE64` | The F-Droid **repo** signing key + `config.yml`. Never uploaded to the server. |
|
||||
| `HETZNER_HOST`, `HETZNER_USER`, `HETZNER_PASS` | SFTP target for the published `repo/` + `metadata/`. |
|
||||
| `GITHUB_TOKEN` | Gitea API (release create/patch, asset upload). |
|
||||
|
||||
The repo signing key and `config.yml` come from secrets at build time and are
|
||||
**never** pulled from or pushed back to the server, so they can't leak into the
|
||||
web-served tree (nginx serves only `repo/`).
|
||||
|
||||
---
|
||||
|
||||
## Key rotation / repo recovery
|
||||
|
||||
A manual `workflow_dispatch` run (ref = a branch, not a tag) skips all the
|
||||
tag-only build steps: it just re-signs the existing index with the configured
|
||||
repo key and re-uploads. Use this to recover the repo or rotate infrastructure
|
||||
**without** publishing a new APK.
|
||||
|
||||
---
|
||||
|
||||
## Push CI (non-tag)
|
||||
|
||||
Every push to any branch runs `.gitea/workflows/ci.yaml`: `lintDebug` →
|
||||
`testDebugUnitTest` → `assembleDebug`, plus a Trivy filesystem scan on `main`.
|
||||
Keep this green before tagging.
|
||||
|
||||
---
|
||||
|
||||
## F-Droid metadata
|
||||
|
||||
App store listing lives in `fdroid-metadata/` (`de.jeanlucmakiola.floret.yml`
|
||||
plus `en-US/` summary/description). Per-version changelogs are generated into the
|
||||
repo's `metadata/.../en-US/changelogs/<versionCode>.txt` from `CHANGELOG.md` at
|
||||
release time; metadata is uploaded alongside `repo/` so changelog history
|
||||
survives across releases.
|
||||
Reference in New Issue
Block a user