tane/docs/fdroid/reproducible-post-merge.md
vjrj 85a75ac3b7
All checks were successful
ci / test-commons-core (push) Successful in 51s
ci / analyze (push) Successful in 1m35s
site / deploy (push) Successful in 34s
ci / test-app-seeds (push) Successful in 6m40s
docs(fdroid): post-merge runbook for reproducible developer-signed builds
2026-07-19 11:36:15 +02:00

75 lines
3.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# F-Droid reproducible, developer-signed builds — post-merge follow-up
The initial F-Droid submission is **F-Droid-signed** (F-Droid builds from source and
signs with its own key). Making F-Droid publish *our* tane-uploadsigned APK
(same signature as Google Play, so users move between stores) is a **post-merge**
step, because F-Droid derives `SOURCE_DATE_EPOCH` from the **commit timestamp of
our metadata file inside fdroiddata** — a value that only exists once the MR is
merged. So a byte-matching reference cannot be produced beforehand.
## Why our own CI couldn't do this pre-merge
- Deps are NOT the problem: F-Droid uses the **standard public repos**
(`mavenCentral()` + Google's maven) — no private mirror. `jsr305` and the old
jcenter artifacts are on Maven Central. The build environment that "just works"
is the public image `registry.gitlab.com/fdroid/fdroidserver:buildserver-trixie`
(bundles the Android SDK + NDK); we verified a full build succeeds in it.
- The real blocker is `SOURCE_DATE_EPOCH` (fdroidserver `get_source_date_epoch`
falls back to the fdroiddata metadata commit, since the app isn't checked out
yet when it's read). Unknown until F-Droid commits our metadata.
## Post-merge procedure
Run this **after** `org.comunes.tane` is merged into fdroiddata, from a clone of
the **merged** fdroiddata (so the metadata commit — and thus SOURCE_DATE_EPOCH —
matches what F-Droid used). Requires Docker and the tane-upload keystore.
```bash
#!/bin/bash
set -eu
IMG=registry.gitlab.com/fdroid/fdroidserver:buildserver-trixie
FDROIDDATA=/path/to/merged/fdroiddata # git clone of gitlab.com/fdroid/fdroiddata (or fork synced past merge)
KS=/home/vjrj/proyectos/sync/comunes/shared-l2/apkSigning/tane-upload.jks
VC_BASE=10 # pubspec versionCode base (0.1.x+N -> N)
docker run --rm -v "$FDROIDDATA":/build -v "$KS":/tmp/ks.jks:ro \
-e KSPASS -e KEYPASS -w /build "$IMG" bash -c '
set -e
sh /opt/buildserver/setup-env-vars /opt/android-sdk
. /etc/profile.d/bsenv.sh
fds=/tmp/fds; mkdir -p "$fds"
curl -sL https://gitlab.com/fdroid/fdroidserver/-/archive/master/fdroidserver-master.tar.gz \
| tar -xz -C "$fds" --strip-components=1
export PATH="$fds:$PATH" PYTHONPATH="$fds:$fds/examples"
git config --global --add safe.directory "*"
# Build the three per-ABI splits exactly as F-Droid does (SOURCE_DATE_EPOCH
# comes from THIS merged fdroiddata metadata commit -> matches F-Droid).
fdroid build --verbose --no-tarball \
org.comunes.tane:$((VC_BASE*10+1)) \
org.comunes.tane:$((VC_BASE*10+2)) \
org.comunes.tane:$((VC_BASE*10+3))
apksigner=$(find "$ANDROID_HOME" -name apksigner -type f | sort -V | tail -1)
for f in unsigned/org.comunes.tane_*.apk; do
vc=${f##*_}; vc=${vc%.apk}
case $((vc%10)) in 1) abi=armeabi-v7a;; 2) abi=arm64-v8a;; 3) abi=x86_64;; esac
"$apksigner" sign --ks /tmp/ks.jks --ks-key-alias tane-upload \
--ks-pass "env:KSPASS" --key-pass "env:KEYPASS" \
--out "/build/app-${abi}-release.apk" "$f"
"$apksigner" verify --print-certs "/build/app-${abi}-release.apk" | grep -i SHA-256
done
'
```
Set `KSPASS`/`KEYPASS` in the environment before running (never echo them).
Expected: `Signer #1 certificate SHA-256 digest:` =
`ddfae432091b8248a8a4a1b353487fa626301f4357ef835e94ec312f69418e38`
(the tane-upload cert = the future `AllowedAPKSigningKeys` value).
Then:
1. Upload the three `app-<abi>-release.apk` to a git.comunes.org release for the tag.
2. Add to the recipe: `AllowedAPKSigningKeys: ddfae432…` and per-build
`binary: https://git.comunes.org/comunes/tane/releases/download/v%v/app-<abi>-release.apk`.
3. Open a follow-up MR. F-Droid re-runs `fdroid build`, gets byte-identical output,
verifies our signature, and switches to publishing our signed APK.
4. If `fdroid verify` still differs, keep it F-Droid-signed — no harm.