### What this changes Adds beta releases. Pushing a `release/*` branch whose committed `versionName` is `X.Y.Z-beta.N` makes the new `.gitea/workflows/beta.yaml` run the unit tests, build and sign the APK with the app key, and publish it as a **Codeberg pre-release** (APK + `.sha256`), plus a Gitea pre-release with the R8 mapping. F-Droid (self-hosted and official) and Play never get a beta; Obtainium only offers it with *Include prereleases* on. - **`scripts/version_info.sh`** is the single source for `versionName` → `versionCode`, used by `release.yaml`, `beta.yaml`, the changelog sync and the store-listing check. From 1.1.0: `X*1000000 + Y*10000 + Z*100 + N` for betas (N = 1–98), `+ 99` for stable, so `1.1.0-beta.1` → `1010001`, `1.1.0` → `1010099`. All 1.0.x versions keep the legacy formula, so `release/v1.0.1` (code `10001`) stays valid. - **Shared publish scripts:** `scripts/publish_codeberg_release.sh`, `scripts/publish_gitea_release.sh` and `scripts/release_notes.sh`, moved out of `release.yaml`. The stable path behaves as before. - **Guards:** - CI fails a PR whose committed `versionCode` doesn't match its `versionName`. - CI fails a PR into `main` that carries a beta version. - `release.yaml`'s `detect` refuses a beta on `main` as a backstop. - `beta.yaml` refuses a beta of a version that has already shipped as stable. - Betas get no store What's New file. - **Docs:** "Cutting a beta" and the versionCode table in `docs/RELEASING.md`; a note on beta tags in `docs/fdroid-official/README.md`; how to opt in to betas in the README. ### Why To ship a test build of an upcoming version (e.g. 1.1.0) to opted-in testers before the stable release, without it reaching F-Droid or Play users. ### How it was tested - `scripts/version_info.sh` against stable, beta, legacy and invalid version names. - Both publish scripts against a mock forge API: create, re-run (PATCH plus asset replacement), Codeberg's 500-then-retry path, and the skip when no token is set. - A scratch copy with `1.1.0-beta.1` committed: the version check passes, the changelog sync and `check_store_listing.py --complete` pass without a What's New, the PR-into-main guard trips, and a wrong `versionCode` is rejected. - `sync_changelog_to_fastlane.sh` and `check_store_listing.py` (with and without `--complete`) still pass on the current `1.0.0`. - All three workflow files parse as YAML. Not run on the real runners yet. The first beta push is the live test of `beta.yaml`, which assumes a mirrored branch push starts a workflow on Gitea, the same way pushes to `main` already do. ### Checklist - [x] No `versionName` / `versionCode` bump - [x] No `values-*/strings.xml` touched - [x] `CHANGELOG.md` not updated: this is release infrastructure, not a user-visible change Co-authored-by: Jean-Luc Makiola <business@jeanlucmakiola.de> Reviewed-on: https://codeberg.org/jlmakiola/agendula/pulls/38
Official F-Droid submission
de.jeanlucmakiola.agendula.yml is the fdroiddata recipe for the official
F-Droid repo. Same model as Calendula: F-Droid rebuilds each tag from source,
checks it is byte-identical to our signed APK (Binaries), and publishes our
binary, so official and self-hosted installs share a signature and update each
other. A version that doesn't reproduce is skipped, never published wrong.
Verified (2026-09-24)
- From-source rebuild matches the distributed APK. v0.4.0 rebuilt from its
tag against the published
agendula_v0.4.0.apk: 136 of 140 entries identical. The other four were datastore'slibdatastore_shared_counter.so, stripped only because the local host had an NDK and CI doesn't. Fixed withjniLibs { keepDebugSymbols += "**/*.so" }; the rebuild now ships them byte-identical to the published APK. 1.0.0 is the first release with it. - Signing block is clean: v2 signature + verity padding, no dependency
metadata.
fdroid scannerfinds no non-free classes and no extra blocks. - All dependencies are FOSS (no Play Services, Firebase or analytics). Crash reports are shown to the user and only sent by hand.
- Committed version equals the tag-derived one, so the pipeline's versionCode pin is a no-op and F-Droid building the tag as-is matches.
- floret-kit is pinned to a tagged commit on its Codeberg
main, so thesubmodules: truecheckout resolves from a clean clone. - App signing cert SHA-256 (
AllowedAPKSigningKeys) read from the published APK:097946b3…af120. Agendula's own key, not Calendula's.
scripts/check_reproducible_release.sh guards all of the above on every PR.
Listing
Nothing listing-related goes in the recipe. F-Droid harvests
fastlane/metadata/android/<locale>/ from the tagged source, the same tree Play
is fed from: title, descriptions, icon, feature graphic, screenshots and
changelogs/<versionCode>.txt. title.txt becomes the app name per locale.
Status
Submitted as fdroiddata!49998 on 2026-09-24. Its CI rebuilt v1.0.0 and verified it against our published APK ("compared built binary to supplied reference binary successfully").
The file here is a copy of the submitted recipe. fdroiddata's copy is the one
that counts; after the merge F-Droid picks up new vX.Y.Z tags on its own
(AutoUpdateMode), so there is no per-release work there. Beta tags
(vX.Y.Z-beta.N) don't match UpdateCheckMode: Tags ^v[0-9.]+$, so betas stay
off the official repo; keep that pattern if the recipe ever changes. Only a change to the
recipe itself (a new submodule, a build flag) needs an MR, pinned to a full
commit hash, never a tag.
The recipe says License: MIT; :dav is vendored MPL-2.0 (dav/PROVENANCE.md).
If the reviewer asks, change it to MIT AND MPL-2.0.