docs(fdroid): post-merge runbook for reproducible developer-signed builds
This commit is contained in:
parent
4d99e1a840
commit
85a75ac3b7
1 changed files with 75 additions and 0 deletions
75
docs/fdroid/reproducible-post-merge.md
Normal file
75
docs/fdroid/reproducible-post-merge.md
Normal file
|
|
@ -0,0 +1,75 @@
|
||||||
|
# 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-upload–signed 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.
|
||||||
Loading…
Add table
Add a link
Reference in a new issue