chore(fdroid): single-source fastlane metadata + reproducible-build prep
Some checks failed
CI / ci (pull_request) Failing after 8s
Some checks failed
CI / ci (pull_request) Failing after 8s
Make fastlane/metadata/android/ the single source of truth for store
listing metadata, consumed directly by the official F-Droid repo and
transformed into the self-hosted repo's localized layout at release time.
- Move fdroid-metadata/<appid>/<locale>/ -> fastlane/metadata/android/<locale>/
(git-tracked renames: summary->short_description, description->full_description,
+ title.txt, images/icon.png, images/phoneScreenshots/); keep the app-level
.yml control file for the self-hosted `fdroid update`.
- Add scripts/fastlane_to_fdroid_localized.sh (fastlane -> F-Droid localized,
incl. changelogs) and scripts/sync_changelog_to_fastlane.sh (CHANGELOG.md ->
fastlane changelog); verified byte-identical to the previous metadata.
- release.yaml: build self-hosted metadata from the fastlane tree and sync the
per-version changelog before the transform (one changelog source for both
channels).
- Disable AGP VCS-info embedding on release builds (vcsInfo { include = false })
so builds reproduce byte-for-byte vs the distributed APK — the only file that
otherwise differed (META-INF/version-control-info.textproto). Effective from
the next release.
- Add docs/fdroid-official/ (draft fdroiddata recipe: reproducible build +
AllowedAPKSigningKeys + Binaries + notes).
- Repoint README screenshots/icon, update docs/README + RELEASING, and skip the
Android build on fastlane-only changes (ci.yaml).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -12,7 +12,9 @@ Where to look for what:
|
||||
| [`../.planning/STATE.md`](../.planning/STATE.md) | Snapshot of where development currently stands |
|
||||
| [`superpowers/specs/`](superpowers/specs/) | The original design spec (2026-06-08) — historical record, not updated |
|
||||
| [`superpowers/plans/`](superpowers/plans/) | Per-milestone implementation plans with task checklists — historical record of how each slice was built, including provider lessons learned |
|
||||
| [`../fdroid-metadata/`](../fdroid-metadata/) | F-Droid/fastlane store metadata: descriptions, icon, screenshots (DE + EN) |
|
||||
| [`../fastlane/metadata/android/`](../fastlane/metadata/android/) | Store metadata (single source of truth): descriptions, title, icon, screenshots (DE + EN). Harvested directly by the official F-Droid repo; transformed into the self-hosted repo layout at release time by [`../scripts/fastlane_to_fdroid_localized.sh`](../scripts/fastlane_to_fdroid_localized.sh) |
|
||||
| [`../fdroid-metadata/`](../fdroid-metadata/) | App-level F-Droid control file (`*.yml`: Categories, License, links) for the self-hosted repo's `fdroid update` |
|
||||
| [`fdroid-official/`](fdroid-official/) | Draft recipe + notes for publishing to the **official** F-Droid repo (reproducible build + developer-signed binary) |
|
||||
|
||||
Conventions: plans and specs under `superpowers/` are point-in-time
|
||||
artifacts of the agentic workflow that built each milestone — they get
|
||||
|
||||
@@ -33,7 +33,15 @@ Published version codes so far: `v0.1.0`→100 … `v1.0.0`→10000 … `v2.0.0`
|
||||
the F-Droid per-version changelog.
|
||||
3. Bump the committed `versionName` (and `versionCode`) in
|
||||
`app/build.gradle.kts` to the new version. **This bump is what triggers the
|
||||
release** when the branch merges to `main`.
|
||||
release** when the branch merges to `main`. Then run
|
||||
```bash
|
||||
scripts/sync_changelog_to_fastlane.sh
|
||||
```
|
||||
and commit the generated
|
||||
`fastlane/metadata/android/en-US/changelogs/<versionCode>.txt`. This is what
|
||||
makes the **official** F-Droid repo show this version's changelog (it reads
|
||||
the changelog from the tagged source tree). The self-hosted pipeline
|
||||
regenerates it regardless, so forgetting only affects the official listing.
|
||||
4. **Verify the release build on a real device** — the mandatory gate. The
|
||||
shipped APK is R8-shrunk/obfuscated, and bugs that only appear there, or
|
||||
only on first run, never show up in the debug build or on a device that
|
||||
|
||||
94
docs/fdroid-official/README.md
Normal file
94
docs/fdroid-official/README.md
Normal file
@@ -0,0 +1,94 @@
|
||||
# Official F-Droid submission (draft)
|
||||
|
||||
Goal: publish Calendula to the **official F-Droid repo** *alongside* the
|
||||
self-hosted repo, as **one app** — same signed binary in both channels, so
|
||||
existing self-hosted users migrate with no reinstall and no data loss.
|
||||
|
||||
This folder holds the **draft** fdroiddata recipe. Nothing here is submitted
|
||||
yet. The self-hosted pipeline (`../../fdroid-metadata/`, `.gitea/workflows/release.yaml`)
|
||||
is unaffected by these files.
|
||||
|
||||
## Publishing model: reproducible build + developer-signed binary
|
||||
|
||||
F-Droid builds Calendula from source on its buildserver, then verifies the
|
||||
output is byte-for-byte identical to **our** signed APK (fetched via the
|
||||
`Binaries` URL). On a match it publishes **our** binary, signed with **our**
|
||||
key — identified by `AllowedAPKSigningKeys`. Result: official and self-hosted
|
||||
both carry the same signature. If a build ever fails to match, F-Droid simply
|
||||
skips that version (fails safe — no bad publish).
|
||||
|
||||
## Verification status (2026-06-21)
|
||||
|
||||
- ✅ **Run-to-run reproducible** — `v2.7.0` built twice from a clean worktree
|
||||
(R8 minify + resource-shrink, build cache off) → byte-for-byte identical
|
||||
(`sha256 568c944a…`).
|
||||
- ✅ **Cross-JDK reproducible** — rebuilt with JDK 17 and JDK 21 → identical
|
||||
APK. JDK version is not a sensitivity, so it need not be pinned.
|
||||
- ✅ **Tag self-consistent** — committed `versionCode`/`versionName` already
|
||||
equal the tag-derived values, so F-Droid building the tag as-is matches the
|
||||
CI-published binary (CI's `sed` substitution is a no-op).
|
||||
- ✅ **App signing cert SHA-256 extracted** from published APKs (identical
|
||||
across v1.0.0 / v2.0.0 / v2.4.0): `5cdaee8e…`. No keystore needed.
|
||||
- ✅ **Eligibility** — MIT, 100% FOSS deps (no Play Services / Firebase /
|
||||
analytics / billing), no `INTERNET` permission, no native code.
|
||||
- ✅ **End-to-end reproducible vs the DISTRIBUTED binary** — built tag `v2.7.1`
|
||||
from source and compared against the published `calendula_v2.7.1.apk`: every
|
||||
app entry (dex / resources / assets / manifest) byte-identical. The only
|
||||
difference was `META-INF/version-control-info.textproto` (AGP's git metadata,
|
||||
env-dependent). Fixed with `vcsInfo { include = false }` on the release build;
|
||||
re-validated → 0 non-signature differences. **Takes effect from the first
|
||||
release built after that change — submit the recipe starting at that version**
|
||||
(v2.7.1 and earlier still embed the textproto and won't verify).
|
||||
- ⚠️ **Not yet proven**: cross-host / fixed-build-path reproducibility on
|
||||
F-Droid's buildserver (low risk for a no-NDK pure-JVM app; with vcsInfo
|
||||
disabled, the env-dependent field is gone; F-Droid confirms at review).
|
||||
- ✅ **Buildserver toolchain supported** (checked 2026-06-21):
|
||||
- **Gradle 9.5.1** is in F-Droid's gradle-transparency-log (`checksums.json`)
|
||||
with a verified sha256, so `gradlew.py` will download + verify + run it.
|
||||
- **AGP 9.2** is in `gradlew.py`'s `MIN_GRADLE_VERSION` map (`9.2 -> 9.4.1`).
|
||||
- **JDK 17** is standard on the buildserver (AGP 9.2 requires exactly 17).
|
||||
- **build-tools 36.0.0 / android-37**: not statically preinstalled (baseline
|
||||
stops at 33), but `provision-android-sdk` makes `$ANDROID_HOME/build-tools`
|
||||
and `/platforms` group-writable so AGP/Gradle install newer ones on demand.
|
||||
|
||||
## Before submitting — checklist
|
||||
|
||||
1. Confirm the `Binaries` URL is publicly reachable and stable long-term, e.g.
|
||||
`https://apps.dev.jeanlucmakiola.de/dev/fdroid/repo/calendula_v2.7.0.apk`
|
||||
resolves to the dev-signed APK. F-Droid re-fetches it on every build.
|
||||
2. Confirm F-Droid's buildserver supports the toolchain (see timing risk).
|
||||
3. (Recommended) Reproduce one published release end-to-end: build the tag from
|
||||
source, strip signatures, and diff against the downloaded `calendula_v<ver>.apk`
|
||||
to confirm the from-source build matches the *distributed* binary — not just
|
||||
another local build.
|
||||
|
||||
## Submission steps (fdroiddata, GitLab)
|
||||
|
||||
1. Fork `https://gitlab.com/fdroid/fdroiddata`.
|
||||
2. Copy `de.jeanlucmakiola.calendula.yml` to `metadata/` in the fork.
|
||||
3. Test locally with `fdroid build -v de.jeanlucmakiola.calendula` and
|
||||
`fdroid lint de.jeanlucmakiola.calendula` (and `fdroid readmeta`).
|
||||
4. Open a Merge Request. Initial review can take weeks to months; the
|
||||
self-hosted repo keeps serving in the meantime, so there's no rush.
|
||||
|
||||
## Listing metadata (descriptions, screenshots, icon)
|
||||
|
||||
These are **not** in the recipe `.yml`. F-Droid's `fdroid update` harvests them
|
||||
automatically from the fastlane tree in the app's source repo:
|
||||
|
||||
fastlane/metadata/android/<locale>/
|
||||
short_description.txt full_description.txt title.txt
|
||||
images/icon.png images/phoneScreenshots/*.png
|
||||
changelogs/<versionCode>.txt (optional, per release)
|
||||
|
||||
This is the single source of truth: en-US + de-DE already exist there, and the
|
||||
self-hosted repo is generated from the same tree at release time via
|
||||
`scripts/fastlane_to_fdroid_localized.sh`. Nothing extra to add to fdroiddata —
|
||||
F-Droid picks the listing up from source. (Screenshots may not be committed to
|
||||
the fdroiddata repo anyway, so the source-tree fastlane layout is required.)
|
||||
|
||||
Per-version changelogs: the self-hosted release workflow generates
|
||||
`changelogs/<versionCode>.txt` at release time. For the official repo to show a
|
||||
changelog, that file must be committed into `fastlane/metadata/android/<locale>/changelogs/`
|
||||
at the tagged commit — wire this into the release/version-bump step, or accept
|
||||
no in-client changelog for the official listing initially.
|
||||
58
docs/fdroid-official/de.jeanlucmakiola.calendula.yml
Normal file
58
docs/fdroid-official/de.jeanlucmakiola.calendula.yml
Normal file
@@ -0,0 +1,58 @@
|
||||
# ---------------------------------------------------------------------------
|
||||
# DRAFT fdroiddata metadata for the OFFICIAL F-Droid repository.
|
||||
#
|
||||
# This is NOT the self-hosted metadata (that lives in ../../fdroid-metadata/).
|
||||
# When submitting, this file's contents go to fdroiddata on GitLab as
|
||||
# metadata/de.jeanlucmakiola.calendula.yml
|
||||
# See README.md in this folder for the verification status and submission steps.
|
||||
#
|
||||
# Publishing model: REPRODUCIBLE BUILD + developer-signed binary.
|
||||
# F-Droid builds from source on its buildserver, then verifies the result is
|
||||
# identical to our own signed APK (fetched via `Binaries`). If it matches,
|
||||
# F-Droid publishes OUR binary signed with OUR key (`AllowedAPKSigningKeys`),
|
||||
# so the official and self-hosted channels carry the SAME signature and users
|
||||
# migrate between them with no reinstall / no data loss.
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
Categories:
|
||||
- Time
|
||||
License: MIT
|
||||
AuthorName: Jean-Luc Makiola
|
||||
SourceCode: https://gitea.jeanlucmakiola.de/makiolaj/calendula
|
||||
IssueTracker: https://gitea.jeanlucmakiola.de/makiolaj/calendula/issues
|
||||
Changelog: https://gitea.jeanlucmakiola.de/makiolaj/calendula/src/branch/main/CHANGELOG.md
|
||||
|
||||
AutoName: Calendula
|
||||
|
||||
RepoType: git
|
||||
Repo: https://gitea.jeanlucmakiola.de/makiolaj/calendula.git
|
||||
|
||||
# IMPORTANT: the first build entry MUST target the first release that contains
|
||||
# `vcsInfo { include = false }` (added on branch chore/release-verification-process).
|
||||
# v2.7.1 and earlier embed META-INF/version-control-info.textproto, whose
|
||||
# env-dependent git metadata breaks reproducibility — they will NOT verify.
|
||||
# Update the versionName/versionCode/commit/Binaries below once that release is
|
||||
# tagged (e.g. v2.7.2 -> versionCode 20702). Values below are placeholders.
|
||||
Builds:
|
||||
- versionName: 2.7.2
|
||||
versionCode: 20702
|
||||
commit: v2.7.2
|
||||
subdir: app
|
||||
gradle:
|
||||
- yes
|
||||
# No NDK / no flavors. The release buildType applies a signingConfig only
|
||||
# when key.properties exists; on the buildserver it does not, so this
|
||||
# produces the unsigned APK F-Droid compares against our binary.
|
||||
|
||||
# SHA-256 of our app signing certificate (public; embedded in every published
|
||||
# APK). Locks F-Droid to publish only binaries signed with our key.
|
||||
AllowedAPKSigningKeys: 5cdaee8eb31cb0df9157c646ae3ec8b3dc43b5bb72624e088c9ddea0c4eb5ec1
|
||||
|
||||
# Our own developer-signed APK for reproducible-build verification. %v -> the
|
||||
# versionName, so v2.7.2 resolves to calendula_v2.7.2.apk on the self-hosted repo.
|
||||
Binaries: https://apps.dev.jeanlucmakiola.de/dev/fdroid/repo/calendula_v%v.apk
|
||||
|
||||
AutoUpdateMode: Version v%v
|
||||
UpdateCheckMode: Tags
|
||||
CurrentVersion: 2.7.2
|
||||
CurrentVersionCode: 20702
|
||||
Reference in New Issue
Block a user