Compare commits

..

4 Commits

Author SHA1 Message Date
88d770bebe Merge pull request 'ci(fdroid): guard all three reproducible-build invariants' (!26) from chore/harden-repro-guard into main
All checks were successful
Release — F-Droid repo + Gitea release / detect (push) Successful in 4s
Release — F-Droid repo + Gitea release / release (push) Has been skipped
Reviewed-on: #26
2026-06-22 19:58:54 +00:00
b6bee55fc6 ci(fdroid): guard all three reproducible-build invariants
All checks were successful
CI / ci (pull_request) Successful in 2m19s
The reproducible-release CI guard only asserted `vcsInfo { include = false }`.
F-Droid review surfaced two more invariants that, if they regress, silently
stall official publishing (fails safe — stuck on an old version):

- no foojay toolchain resolver in any Gradle script (the offline source
  scanner rejects org.gradle.toolchains.foojay-resolver — it can fetch a JDK
  over the network), and
- `dependenciesInfo { includeInApk = false }` (else AGP embeds a "Dependency
  metadata" block, id 0x504b4453, in the APK Signing Block, which the binary
  scanner rejects as an extra signing block).

The script now checks all three, accumulates failures (reports every broken
invariant in one run, not just the first), and exits non-zero if any fails.
Verified positive + one negative per invariant + all-three-broken.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 11:27:58 +02:00
d0b6823385 Merge pull request 'build(fdroid): drop AGP dependency-metadata block; release v2.7.5' (#25) from release/v2.7.5 into main
All checks were successful
Release — F-Droid repo + Gitea release / detect (push) Successful in 4s
Release — F-Droid repo + Gitea release / release (push) Successful in 4m58s
Renovate / renovate (push) Successful in 1m6s
Reviewed-on: #25
2026-06-21 19:00:43 +00:00
5c80f99179 build(fdroid): drop AGP dependency-metadata block; release v2.7.5
All checks were successful
CI / ci (pull_request) Successful in 5m57s
F-Droid's binary scanner (the fdroiddata `check apk` job) rejects any
"extra signing block". Our release APK carried AGP's dependency-metadata
block (id 0x504b4453, ~8 KB) in the APK Signing Block. Disable it with
`dependenciesInfo { includeInApk = false; includeInBundle = false }`.

The block lives in the signing block, not the zip entries, so removing it
changes no build output — v2.7.5 is functionally identical to 2.7.4 and
still reproduces byte-for-byte. Verified locally: a releaseTest build now
carries only the v2 signature + verity padding (no 0x504b4453 block).

This was the last F-Droid blocker: v2.7.5 now clears vcsInfo (since 2.7.3),
foojay (since 2.7.4) and the dependency-metadata block. Bumps versionName
to 2.7.5 and retargets the official recipe draft at v2.7.5.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 20:48:59 +02:00
5 changed files with 99 additions and 32 deletions

View File

@@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased] ## [Unreleased]
## [2.7.5] — 2026-06-21
### Changed
- Further build cleanup for the official F-Droid repository: stopped embedding
AGP's dependency-metadata block in the APK, which F-Droid's reproducible-build
scanner rejects as an extra signing block. No functional or visible changes —
the same app as 2.7.4, just without that Play-oriented metadata blob.
## [2.7.4] — 2026-06-21 ## [2.7.4] — 2026-06-21
### Changed ### Changed

View File

@@ -28,8 +28,8 @@ android {
// which builds this version and then creates the matching vX.Y.Z tag + // which builds this version and then creates the matching vX.Y.Z tag +
// release itself (versionCode is pinned to MAJOR*10000 + MINOR*100 + // release itself (versionCode is pinned to MAJOR*10000 + MINOR*100 +
// PATCH from versionName, e.g. 2.7.2 -> 20702). See docs/RELEASING.md. // PATCH from versionName, e.g. 2.7.2 -> 20702). See docs/RELEASING.md.
versionCode = 20704 versionCode = 20705
versionName = "2.7.4" versionName = "2.7.5"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
} }
@@ -92,6 +92,16 @@ android {
compose = true compose = true
} }
// Don't embed AGP's dependency-metadata block in the APK signing block. It's
// a Play-oriented blob, and F-Droid's reproducible-build scanner rejects any
// "extra signing block" — so leaving it in blocks publishing to the official
// repo. It lives in the signing block, not the zip entries, so disabling it
// doesn't change the build output (reproducibility is unaffected).
dependenciesInfo {
includeInApk = false
includeInBundle = false
}
packaging { packaging {
resources { resources {
excludes += "/META-INF/{AL2.0,LGPL2.1}" excludes += "/META-INF/{AL2.0,LGPL2.1}"

View File

@@ -27,18 +27,20 @@ AutoName: Calendula
RepoType: git RepoType: git
Repo: https://gitea.jeanlucmakiola.de/makiolaj/calendula.git Repo: https://gitea.jeanlucmakiola.de/makiolaj/calendula.git
# First build entry = v2.7.4, the first release that BOTH disables AGP VCS-info # First build entry = v2.7.5, the first release that clears ALL three F-Droid
# (`vcsInfo { include = false }`, since v2.7.3) AND drops the unused Gradle # blockers: (1) AGP VCS-info disabled (`vcsInfo { include = false }`, since
# foojay toolchain-resolver plugin, which F-Droid's offline build scanner rejects # v2.7.3); (2) the unused Gradle foojay toolchain-resolver plugin removed (since
# (it can fetch a JDK over the network). The plugin was inert here (no toolchain # v2.7.4) — the offline build scanner rejects it as it can fetch a JDK; (3) AGP's
# block invoked it), so v2.7.4 is functionally identical to v2.7.3 and reproduces # dependency-metadata block no longer embedded (`dependenciesInfo { includeInApk
# byte-for-byte from source. v2.7.3 and earlier either embed # = false }`) — the binary scanner rejects it as an "extra signing block". All
# META-INF/version-control-info.textproto or trip the foojay scanner — do not # three live outside the zip entries or are inert, so v2.7.5 is functionally
# target them. Subsequent versions are added automatically (AutoUpdateMode). # identical to v2.7.3 and reproduces byte-for-byte from source. v2.7.4 and
# earlier trip one of these checks — do not target them. Subsequent versions are
# added automatically (AutoUpdateMode).
Builds: Builds:
- versionName: 2.7.4 - versionName: 2.7.5
versionCode: 20704 versionCode: 20705
commit: v2.7.4 commit: v2.7.5
subdir: app subdir: app
gradle: gradle:
- yes - yes
@@ -51,7 +53,7 @@ Builds:
AllowedAPKSigningKeys: 5cdaee8eb31cb0df9157c646ae3ec8b3dc43b5bb72624e088c9ddea0c4eb5ec1 AllowedAPKSigningKeys: 5cdaee8eb31cb0df9157c646ae3ec8b3dc43b5bb72624e088c9ddea0c4eb5ec1
# Our own developer-signed APK for reproducible-build verification. %v -> the # Our own developer-signed APK for reproducible-build verification. %v -> the
# versionName, so v2.7.4 resolves to calendula_v2.7.4.apk on the self-hosted repo. # versionName, so v2.7.5 resolves to calendula_v2.7.5.apk on the self-hosted repo.
Binaries: https://apps.dev.jeanlucmakiola.de/dev/fdroid/repo/calendula_v%v.apk Binaries: https://apps.dev.jeanlucmakiola.de/dev/fdroid/repo/calendula_v%v.apk
# After this one-time submission F-Droid auto-tracks new vX.Y.Z tags and creates # After this one-time submission F-Droid auto-tracks new vX.Y.Z tags and creates
@@ -59,5 +61,5 @@ Binaries: https://apps.dev.jeanlucmakiola.de/dev/fdroid/repo/calendula_v%v.apk
# it to release tags only. # it to release tags only.
AutoUpdateMode: Version AutoUpdateMode: Version
UpdateCheckMode: Tags ^v[0-9.]+$ UpdateCheckMode: Tags ^v[0-9.]+$
CurrentVersion: 2.7.4 CurrentVersion: 2.7.5
CurrentVersionCode: 20704 CurrentVersionCode: 20705

View File

@@ -0,0 +1,6 @@
### Changed
- Further build cleanup for the official F-Droid repository: stopped embedding
AGP's dependency-metadata block in the APK, which F-Droid's reproducible-build
scanner rejects as an extra signing block. No functional or visible changes —
the same app as 2.7.4, just without that Play-oriented metadata blob.

View File

@@ -1,23 +1,64 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# Reproducibility guard for the official F-Droid repo. # Reproducibility guard for the official F-Droid repo (de.jeanlucmakiola.calendula).
# #
# F-Droid only republishes OUR signed binary if a from-source build reproduces # F-Droid only republishes OUR signed binary if a from-source build reproduces it
# it byte-for-byte. AGP's VCS-info injection writes # byte-for-byte and the binary carries no extra signing blocks. If any invariant
# META-INF/version-control-info.textproto with build-environment git metadata # below regresses, the official repo silently stalls on the last good version
# (commit hash / path) — env-dependent, and the one thing that broke # (fails safe — but you'd be stuck on an old release without noticing). So fail
# reproducibility before we disabled it. It MUST stay off on the release build, # loudly here, on every PR.
# or new versions silently stop publishing to the official repo (fails safe, but #
# you'd be stuck on an old version without noticing). Fail loudly if it regresses. # Each invariant was learned from a real fdroiddata CI rejection (see
# docs/fdroid-official/ and the v2.7.3 -> v2.7.5 history):
# 1. vcsInfo { include = false } — else AGP embeds env-dependent git
# metadata (META-INF/version-control-info.textproto) -> not reproducible.
# 2. no foojay toolchain resolver — F-Droid's offline source scanner
# rejects org.gradle.toolchains.foojay-resolver (it can fetch a JDK over
# the network at build time).
# 3. dependenciesInfo { includeInApk = false } — else AGP embeds a "Dependency
# metadata" block (id 0x504b4453) in the APK Signing Block, which F-Droid's
# binary scanner rejects as an extra signing block.
set -euo pipefail set -euo pipefail
F="app/build.gradle.kts"
# Match `vcsInfo { ... include = false ... }` within the block, tolerant of APP="app/build.gradle.kts"
# whitespace/newlines (-z: whole file as one record; [^}] stays inside the block). SETTINGS="settings.gradle.kts"
if grep -Pzoq 'vcsInfo\s*\{[^}]*include\s*=\s*false' "$F"; then fail=0
echo "OK: release build disables AGP VCS-info — stays reproducible for F-Droid."
# 1. AGP VCS-info must be disabled on the release build. -z reads the whole file
# as one record so the match can span newlines; [^}] keeps it inside the block.
if grep -Pzoq 'vcsInfo\s*\{[^}]*include\s*=\s*false' "$APP"; then
echo "OK: vcsInfo { include = false } — no env-dependent VCS metadata embedded."
else else
echo "ERROR: '$F' release build is missing 'vcsInfo { include = false }'." >&2 echo "ERROR: '$APP' release build is missing 'vcsInfo { include = false }'." >&2
echo "AGP would embed env-dependent git metadata (version-control-info.textproto)," >&2 echo " AGP would embed version-control-info.textproto, breaking reproducibility." >&2
echo "breaking F-Droid reproducible builds. Restore it. See docs/fdroid-official/." >&2 fail=1
fi
# 2. The foojay toolchain resolver must not be present in any Gradle script.
gradle_files=("$SETTINGS" "$APP")
[ -f build.gradle.kts ] && gradle_files+=(build.gradle.kts)
if grep -qi 'foojay' "${gradle_files[@]}"; then
echo "ERROR: foojay toolchain resolver found in: $(grep -li foojay "${gradle_files[@]}" | tr '\n' ' ')" >&2
echo " F-Droid's source scanner rejects org.gradle.toolchains.foojay-resolver" >&2
echo " (it can fetch a JDK over the network). Remove the plugin." >&2
fail=1
else
echo "OK: no foojay toolchain resolver — offline build scanner stays happy."
fi
# 3. AGP dependency-metadata block must not be embedded in the APK.
if grep -Pzoq 'dependenciesInfo\s*\{[^}]*includeInApk\s*=\s*false' "$APP"; then
echo "OK: dependenciesInfo { includeInApk = false } — no extra APK signing block."
else
echo "ERROR: '$APP' is missing 'dependenciesInfo { includeInApk = false }'." >&2
echo " AGP would embed a 'Dependency metadata' block (0x504b4453) in the APK" >&2
echo " Signing Block, which F-Droid's binary scanner rejects." >&2
fail=1
fi
if [ "$fail" -ne 0 ]; then
echo >&2
echo "Reproducible-release invariant(s) violated — official F-Droid publishing would" >&2
echo "stall. Fix the above before merging. See docs/fdroid-official/." >&2
exit 1 exit 1
fi fi
echo "All reproducible-release invariants hold."