Files
agendula/docs/RELEASING.md
Jean-Luc Makiola 76a0139fda
All checks were successful
CI / ci (push) Successful in 8m33s
Rename app Floret → Agendula
Floret is promoted to the family / design-language (shared-kit) name; the
tasks app itself becomes Agendula (de.jeanlucmakiola.agendula) — agenda
('things to be done') + Calendula's -ula, a twin of the Calendula name.

Renames the package, namespace, applicationId, rootProject.name, app_name,
FloretApp/FloretNavHost/FloretTransitions classes, theme, F-Droid metadata
dir, CI artifact name, and docs. The botanical word 'florets' is preserved in
the name-origin prose, which is rewritten to Agendula's etymology. Clean
build + unit tests green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-27 23:10:24 +02:00

102 lines
4.1 KiB
Markdown

# Agendula — releasing
Agendula 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.agendula.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.