diff --git a/docs/RELEASING.md b/docs/RELEASING.md index 130849a..0a34f12 100644 --- a/docs/RELEASING.md +++ b/docs/RELEASING.md @@ -7,7 +7,7 @@ 5. Test an update from the previous published Harbor release while Harbor remains profile owner. 6. Complete the physical-device release matrix. 7. Create an immutable annotated source tag named `v` and verify it points to the intended release commit. -8. From a clean checkout of that exact tag, build the unsigned release APK with the documented JDK and Android SDK versions. +8. From a fresh `git clone` of the repository, check out that exact tag and build the unsigned release APK with the documented JDK and Android SDK versions. 9. Sign the APK with `sh scripts/sign-release.sh`. The script uses an F-Droid-compatible Android Build Tools 34 `apksigner`, writes the exact asset name `app-release-signed.apk` by default, and verifies the resulting certificate before returning success. 10. Publish the GitHub release with `app-release-signed.apk` attached. 11. Verify that F-Droid detects the tag, independently rebuilds it, and successfully completes its reproducibility check before treating the F-Droid release as complete. @@ -47,7 +47,7 @@ Before signing, provide these environment variables through the release environm The signing script passes password references to `apksigner` using its `env:` input mode; it does not put password values in the command arguments. -From a clean checkout of the release tag: +From a fresh `git clone` of the repository, check out the release tag and run: ```shell ./gradlew --no-daemon clean assembleRelease diff --git a/scripts/verify-deterministic-build.sh b/scripts/verify-deterministic-build.sh index ad48f13..aae475b 100644 --- a/scripts/verify-deterministic-build.sh +++ b/scripts/verify-deterministic-build.sh @@ -6,13 +6,33 @@ second_dir=$(mktemp -d) trap 'rm -rf "$first_dir" "$second_dir"' EXIT gradle_bin=${GRADLE_BIN:-./gradlew} +if ! command -v unzip >/dev/null 2>&1; then + printf 'unzip is required to inspect release APK metadata\n' >&2 + exit 1 +fi + +verify_vcs_metadata() { + apk=$1 + if ! metadata=$(unzip -p "$apk" META-INF/version-control-info.textproto 2>/dev/null); then + printf 'Release APK is missing valid VCS metadata\n' >&2 + exit 1 + fi + + if printf '%s\n' "$metadata" | grep -Fq 'NO_VALID_GIT_FOUND'; then + printf 'Release APK contains invalid Git metadata: NO_VALID_GIT_FOUND\n' >&2 + exit 1 + fi +} + "$gradle_bin" --no-daemon --no-parallel --no-configuration-cache clean "$gradle_bin" --no-daemon --no-parallel --no-configuration-cache assembleRelease cp app/build/outputs/apk/release/app-release-unsigned.apk "$first_dir/harbor.apk" +verify_vcs_metadata "$first_dir/harbor.apk" "$gradle_bin" --no-daemon --no-parallel --no-configuration-cache clean "$gradle_bin" --no-daemon --no-parallel --no-configuration-cache assembleRelease cp app/build/outputs/apk/release/app-release-unsigned.apk "$second_dir/harbor.apk" +verify_vcs_metadata "$second_dir/harbor.apk" hash_file() { if command -v sha256sum >/dev/null 2>&1; then