From 1296607e03c914eab36a15da000744acae1eba40 Mon Sep 17 00:00:00 2001 From: Ovi Trif Date: Thu, 21 May 2026 16:53:20 +0200 Subject: [PATCH 01/12] ci: add release workflows --- .github/workflows/release-internal.yml | 89 ++++++++++++++++++++++++++ .github/workflows/release-store.yml | 89 ++++++++++++++++++++++++++ settings.gradle.kts | 6 +- 3 files changed, 182 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/release-internal.yml create mode 100644 .github/workflows/release-store.yml diff --git a/.github/workflows/release-internal.yml b/.github/workflows/release-internal.yml new file mode 100644 index 0000000000..8a9c3b5149 --- /dev/null +++ b/.github/workflows/release-internal.yml @@ -0,0 +1,89 @@ +name: Release Internal + +on: + workflow_dispatch: + push: + tags: + - 'v*' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: false + +env: + TERM: xterm-256color + FORCE_COLOR: 1 + +jobs: + build-internal: + if: github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/heads/release-') || startsWith(github.ref, 'refs/tags/v') + runs-on: ubuntu-latest + timeout-minutes: 45 + environment: release-internal + + permissions: + contents: read + packages: read + + steps: + - name: Checkout + uses: actions/checkout@v6 + + - name: Setup Java + uses: actions/setup-java@v5 + with: + java-version: '17' + distribution: 'adopt' + + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v5 + + - name: Decode mainnet release google-services.json + env: + MAINNET_RELEASE_GOOGLE_SERVICES_JSON_BASE64: ${{ secrets.MAINNET_RELEASE_GOOGLE_SERVICES_JSON_BASE64 }} + run: | + set -euo pipefail + test -n "$MAINNET_RELEASE_GOOGLE_SERVICES_JSON_BASE64" + mkdir -p app/src/mainnetRelease + printf '%s' "$MAINNET_RELEASE_GOOGLE_SERVICES_JSON_BASE64" | base64 --decode > app/src/mainnetRelease/google-services.json + + - name: Decode internal keystore + env: + INTERNAL_KEYSTORE_BASE64: ${{ secrets.INTERNAL_KEYSTORE_BASE64 }} + run: | + set -euo pipefail + test -n "$INTERNAL_KEYSTORE_BASE64" + umask 077 + keystore_path="$RUNNER_TEMP/internal.keystore" + printf '%s' "$INTERNAL_KEYSTORE_BASE64" | base64 --decode > "$keystore_path" + echo "KEYSTORE_FILE=$keystore_path" >> "$GITHUB_ENV" + + - name: Build internal release artifacts + env: + GPR_USER: ${{ secrets.GPR_USER || github.actor }} + GPR_TOKEN: ${{ secrets.GPR_TOKEN || github.token }} + GITHUB_TOKEN: ${{ secrets.GPR_TOKEN || github.token }} + KEYSTORE_PASSWORD: ${{ secrets.INTERNAL_KEYSTORE_PASSWORD }} + KEY_ALIAS: ${{ secrets.INTERNAL_KEY_ALIAS }} + KEY_PASSWORD: ${{ secrets.INTERNAL_KEY_PASSWORD }} + run: ./gradlew assembleMainnetRelease bundleMainnetRelease --no-daemon --stacktrace + + - name: Collect internal artifacts + id: artifacts + run: | + set -euo pipefail + artifact_dir="$RUNNER_TEMP/internal-release" + mkdir -p "$artifact_dir" + find app/build/outputs/bundle/mainnetRelease -name 'bitkit-mainnet-release-*.aab' -print0 | + xargs -0 -I {} cp {} "$artifact_dir/" + find app/build/outputs/apk/mainnet/release -name 'bitkit-mainnet-release-*.apk' -print0 | + xargs -0 -I {} cp {} "$artifact_dir/" + (cd "$artifact_dir" && sha256sum *.aab *.apk > SHA256SUMS.txt) + echo "artifact_dir=$artifact_dir" >> "$GITHUB_OUTPUT" + + - name: Upload internal artifacts + uses: actions/upload-artifact@v6 + with: + name: bitkit-internal-release-${{ github.run_number }} + path: ${{ steps.artifacts.outputs.artifact_dir }} + retention-days: 30 diff --git a/.github/workflows/release-store.yml b/.github/workflows/release-store.yml new file mode 100644 index 0000000000..3938decd21 --- /dev/null +++ b/.github/workflows/release-store.yml @@ -0,0 +1,89 @@ +name: Release Store + +on: + workflow_dispatch: + push: + tags: + - 'v*' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: false + +env: + TERM: xterm-256color + FORCE_COLOR: 1 + +jobs: + build-store: + if: github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/heads/release-') || startsWith(github.ref, 'refs/tags/v') + runs-on: ubuntu-latest + timeout-minutes: 45 + environment: release-store + + permissions: + contents: read + packages: read + + steps: + - name: Checkout + uses: actions/checkout@v6 + + - name: Setup Java + uses: actions/setup-java@v5 + with: + java-version: '17' + distribution: 'adopt' + + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v5 + + - name: Decode mainnet release google-services.json + env: + MAINNET_RELEASE_GOOGLE_SERVICES_JSON_BASE64: ${{ secrets.MAINNET_RELEASE_GOOGLE_SERVICES_JSON_BASE64 }} + run: | + set -euo pipefail + test -n "$MAINNET_RELEASE_GOOGLE_SERVICES_JSON_BASE64" + mkdir -p app/src/mainnetRelease + printf '%s' "$MAINNET_RELEASE_GOOGLE_SERVICES_JSON_BASE64" | base64 --decode > app/src/mainnetRelease/google-services.json + + - name: Decode store keystore + env: + BITKIT_KEYSTORE_BASE64: ${{ secrets.BITKIT_KEYSTORE_BASE64 }} + run: | + set -euo pipefail + test -n "$BITKIT_KEYSTORE_BASE64" + umask 077 + keystore_path="$RUNNER_TEMP/bitkit.keystore" + printf '%s' "$BITKIT_KEYSTORE_BASE64" | base64 --decode > "$keystore_path" + echo "KEYSTORE_FILE=$keystore_path" >> "$GITHUB_ENV" + + - name: Build store release artifacts + env: + GPR_USER: ${{ secrets.GPR_USER || github.actor }} + GPR_TOKEN: ${{ secrets.GPR_TOKEN || github.token }} + GITHUB_TOKEN: ${{ secrets.GPR_TOKEN || github.token }} + KEYSTORE_PASSWORD: ${{ secrets.BITKIT_KEYSTORE_PASSWORD }} + KEY_ALIAS: ${{ secrets.BITKIT_KEY_ALIAS }} + KEY_PASSWORD: ${{ secrets.BITKIT_KEY_PASSWORD }} + run: ./gradlew assembleMainnetRelease bundleMainnetRelease --no-daemon --stacktrace + + - name: Collect store artifacts + id: artifacts + run: | + set -euo pipefail + artifact_dir="$RUNNER_TEMP/store-release" + mkdir -p "$artifact_dir" + find app/build/outputs/bundle/mainnetRelease -name 'bitkit-mainnet-release-*.aab' -print0 | + xargs -0 -I {} cp {} "$artifact_dir/" + find app/build/outputs/apk/mainnet/release -name 'bitkit-mainnet-release-*.apk' -print0 | + xargs -0 -I {} cp {} "$artifact_dir/" + (cd "$artifact_dir" && sha256sum *.aab *.apk > SHA256SUMS.txt) + echo "artifact_dir=$artifact_dir" >> "$GITHUB_OUTPUT" + + - name: Upload store artifacts + uses: actions/upload-artifact@v6 + with: + name: bitkit-store-release-${{ github.run_number }} + path: ${{ steps.artifacts.outputs.artifact_dir }} + retention-days: 30 diff --git a/settings.gradle.kts b/settings.gradle.kts index e3a1da778c..9790eeb9eb 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -12,10 +12,12 @@ fun getGithubCredentials( passKey: String = "gpr.key", userKey: String = "gpr.user", ): Pair { - val user = System.getenv("GITHUB_ACTOR") + val user = System.getenv("GPR_USER") + ?: System.getenv("GITHUB_ACTOR") ?: providers.gradleProperty(userKey).orNull ?: localProperties.getProperty(userKey) - val key = System.getenv("GITHUB_TOKEN") + val key = System.getenv("GPR_TOKEN") + ?: System.getenv("GITHUB_TOKEN") ?: providers.gradleProperty(passKey).orNull ?: localProperties.getProperty(passKey) return user to key From 4c1b820c33a28fff185e5c47b00f8edbe73b74ca Mon Sep 17 00:00:00 2001 From: Ovi Trif Date: Thu, 21 May 2026 16:53:27 +0200 Subject: [PATCH 02/12] chore: add dummy google-services.json --- .gitignore | 3 +++ README.md | 6 +++-- app/src/devDebug/google-services.json | 29 +++++++++++++++++++++++ app/src/mainnetDebug/google-services.json | 29 +++++++++++++++++++++++ app/src/tnetDebug/google-services.json | 29 +++++++++++++++++++++++ 5 files changed, 94 insertions(+), 2 deletions(-) create mode 100644 app/src/devDebug/google-services.json create mode 100644 app/src/mainnetDebug/google-services.json create mode 100644 app/src/tnetDebug/google-services.json diff --git a/.gitignore b/.gitignore index 1700753e3f..68274ce506 100644 --- a/.gitignore +++ b/.gitignore @@ -17,6 +17,9 @@ local.properties !*.local.template* # Secrets google-services.json +!app/src/devDebug/google-services.json +!app/src/mainnetDebug/google-services.json +!app/src/tnetDebug/google-services.json .env *.keystore !debug.keystore diff --git a/README.md b/README.md index 29c01e91d9..c5c00085e3 100644 --- a/README.md +++ b/README.md @@ -10,11 +10,13 @@ This repository contains the **native Android app** for Bitkit. #### 1. Firebase Configuration -Download `google-services.json` from the Firebase Console for each of the following build flavor groups,: +Debug builds include placeholder Firebase configuration so a fresh clone can compile without a private `google-services.json` files. + +Download `google-services.json` from the Firebase Console when you need real Firebase integration for push notifications testing: - dev/tnet/mainnetDebug: Place in `app/google-services.json` - mainnetRelease: Place in `app/src/mainnetRelease/google-services.json` -> **Note**: Each flavor requires its own Firebase project configuration. The mainnet flavor will fail to build without its dedicated `google-services.json` file. +> **Note**: Placeholder debug configs are only for local builds. FCM token registration and push notifications require real Firebase configuration. The mainnet release flavor will fail to build without the `mainnetRelease/google-services.json` file. #### 2. GitHub Packages setup diff --git a/app/src/devDebug/google-services.json b/app/src/devDebug/google-services.json new file mode 100644 index 0000000000..bb4b217cff --- /dev/null +++ b/app/src/devDebug/google-services.json @@ -0,0 +1,29 @@ +{ + "project_info": { + "project_number": "000000000000", + "project_id": "bitkit-debug-placeholder", + "storage_bucket": "bitkit-debug-placeholder.appspot.com" + }, + "client": [ + { + "client_info": { + "mobilesdk_app_id": "1:000000000000:android:0000000000000000000000", + "android_client_info": { + "package_name": "to.bitkit.dev" + } + }, + "oauth_client": [], + "api_key": [ + { + "current_key": "AIzaSyDebugPlaceholderKeyForLocalBuilds000" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [] + } + } + } + ], + "configuration_version": "1" +} diff --git a/app/src/mainnetDebug/google-services.json b/app/src/mainnetDebug/google-services.json new file mode 100644 index 0000000000..9ab09a6fb3 --- /dev/null +++ b/app/src/mainnetDebug/google-services.json @@ -0,0 +1,29 @@ +{ + "project_info": { + "project_number": "000000000000", + "project_id": "bitkit-debug-placeholder", + "storage_bucket": "bitkit-debug-placeholder.appspot.com" + }, + "client": [ + { + "client_info": { + "mobilesdk_app_id": "1:000000000000:android:0000000000000000000001", + "android_client_info": { + "package_name": "to.bitkit" + } + }, + "oauth_client": [], + "api_key": [ + { + "current_key": "AIzaSyDebugPlaceholderKeyForLocalBuilds001" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [] + } + } + } + ], + "configuration_version": "1" +} diff --git a/app/src/tnetDebug/google-services.json b/app/src/tnetDebug/google-services.json new file mode 100644 index 0000000000..63fecea25a --- /dev/null +++ b/app/src/tnetDebug/google-services.json @@ -0,0 +1,29 @@ +{ + "project_info": { + "project_number": "000000000000", + "project_id": "bitkit-debug-placeholder", + "storage_bucket": "bitkit-debug-placeholder.appspot.com" + }, + "client": [ + { + "client_info": { + "mobilesdk_app_id": "1:000000000000:android:0000000000000000000002", + "android_client_info": { + "package_name": "to.bitkit.tnet" + } + }, + "oauth_client": [], + "api_key": [ + { + "current_key": "AIzaSyDebugPlaceholderKeyForLocalBuilds002" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [] + } + } + } + ], + "configuration_version": "1" +} From e3e8a7a2049d702c7dd3debe8b7a2739db6e9686 Mon Sep 17 00:00:00 2001 From: Ovi Trif Date: Thu, 21 May 2026 18:06:53 +0200 Subject: [PATCH 03/12] ci: add reproducible release workflow --- .github/workflows/release-internal.yml | 8 +- .github/workflows/reproducible-release.yml | 102 + README.md | 2 + docs/reproducible-builds.md | 106 + gradle/verification-metadata.xml | 7274 ++++++++++++++++++++ scripts/reproduce-release.sh | 156 + 6 files changed, 7643 insertions(+), 5 deletions(-) create mode 100644 .github/workflows/reproducible-release.yml create mode 100644 docs/reproducible-builds.md create mode 100644 gradle/verification-metadata.xml create mode 100755 scripts/reproduce-release.sh diff --git a/.github/workflows/release-internal.yml b/.github/workflows/release-internal.yml index 8a9c3b5149..5d20590b6d 100644 --- a/.github/workflows/release-internal.yml +++ b/.github/workflows/release-internal.yml @@ -58,7 +58,7 @@ jobs: printf '%s' "$INTERNAL_KEYSTORE_BASE64" | base64 --decode > "$keystore_path" echo "KEYSTORE_FILE=$keystore_path" >> "$GITHUB_ENV" - - name: Build internal release artifacts + - name: Build internal release APK env: GPR_USER: ${{ secrets.GPR_USER || github.actor }} GPR_TOKEN: ${{ secrets.GPR_TOKEN || github.token }} @@ -66,7 +66,7 @@ jobs: KEYSTORE_PASSWORD: ${{ secrets.INTERNAL_KEYSTORE_PASSWORD }} KEY_ALIAS: ${{ secrets.INTERNAL_KEY_ALIAS }} KEY_PASSWORD: ${{ secrets.INTERNAL_KEY_PASSWORD }} - run: ./gradlew assembleMainnetRelease bundleMainnetRelease --no-daemon --stacktrace + run: ./gradlew assembleMainnetRelease --no-daemon --stacktrace - name: Collect internal artifacts id: artifacts @@ -74,11 +74,9 @@ jobs: set -euo pipefail artifact_dir="$RUNNER_TEMP/internal-release" mkdir -p "$artifact_dir" - find app/build/outputs/bundle/mainnetRelease -name 'bitkit-mainnet-release-*.aab' -print0 | - xargs -0 -I {} cp {} "$artifact_dir/" find app/build/outputs/apk/mainnet/release -name 'bitkit-mainnet-release-*.apk' -print0 | xargs -0 -I {} cp {} "$artifact_dir/" - (cd "$artifact_dir" && sha256sum *.aab *.apk > SHA256SUMS.txt) + (cd "$artifact_dir" && sha256sum *.apk > SHA256SUMS.txt) echo "artifact_dir=$artifact_dir" >> "$GITHUB_OUTPUT" - name: Upload internal artifacts diff --git a/.github/workflows/reproducible-release.yml b/.github/workflows/reproducible-release.yml new file mode 100644 index 0000000000..ec581c6496 --- /dev/null +++ b/.github/workflows/reproducible-release.yml @@ -0,0 +1,102 @@ +name: Reproducible Release + +on: + workflow_dispatch: + inputs: + comparison_artifact_name: + description: Optional artifact name to compare against with diffoscope + required: false + default: '' + push: + tags: + - 'v*' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: false + +env: + TERM: xterm-256color + FORCE_COLOR: 1 + +jobs: + reproduce-mainnet: + if: github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/heads/release-') || startsWith(github.ref, 'refs/tags/v') + runs-on: ubuntu-latest + timeout-minutes: 60 + environment: release-store + + permissions: + contents: read + packages: read + + steps: + - name: Checkout + uses: actions/checkout@v6 + + - name: Setup Java + uses: actions/setup-java@v5 + with: + java-version: '17' + distribution: 'adopt' + + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v5 + + - name: Decode mainnet release google-services.json + env: + MAINNET_RELEASE_GOOGLE_SERVICES_JSON_BASE64: ${{ secrets.MAINNET_RELEASE_GOOGLE_SERVICES_JSON_BASE64 }} + run: | + set -euo pipefail + test -n "$MAINNET_RELEASE_GOOGLE_SERVICES_JSON_BASE64" + mkdir -p app/src/mainnetRelease + printf '%s' "$MAINNET_RELEASE_GOOGLE_SERVICES_JSON_BASE64" | base64 --decode > app/src/mainnetRelease/google-services.json + + - name: Decode store keystore + env: + BITKIT_KEYSTORE_BASE64: ${{ secrets.BITKIT_KEYSTORE_BASE64 }} + run: | + set -euo pipefail + test -n "$BITKIT_KEYSTORE_BASE64" + umask 077 + keystore_path="$RUNNER_TEMP/bitkit.keystore" + printf '%s' "$BITKIT_KEYSTORE_BASE64" | base64 --decode > "$keystore_path" + echo "KEYSTORE_FILE=$keystore_path" >> "$GITHUB_ENV" + + - name: Download comparison artifact + if: ${{ github.event_name == 'workflow_dispatch' && inputs.comparison_artifact_name != '' }} + uses: actions/download-artifact@v6 + with: + name: ${{ inputs.comparison_artifact_name }} + path: ${{ runner.temp }}/comparison + + - name: Install diffoscope + if: ${{ github.event_name == 'workflow_dispatch' && inputs.comparison_artifact_name != '' }} + run: | + set -euo pipefail + sudo apt-get update + sudo apt-get install -y diffoscope + + - name: Build reproducibility artifacts + env: + GPR_USER: ${{ secrets.GPR_USER || github.actor }} + GPR_TOKEN: ${{ secrets.GPR_TOKEN || github.token }} + GITHUB_TOKEN: ${{ secrets.GPR_TOKEN || github.token }} + KEYSTORE_PASSWORD: ${{ secrets.BITKIT_KEYSTORE_PASSWORD }} + KEY_ALIAS: ${{ secrets.BITKIT_KEY_ALIAS }} + KEY_PASSWORD: ${{ secrets.BITKIT_KEY_PASSWORD }} + OUTPUT_DIR: ${{ runner.temp }}/reproducible-release + run: | + set -euo pipefail + if [ -d "$RUNNER_TEMP/comparison" ]; then + export DIFFOSCOPE_COMPARE_DIR="$RUNNER_TEMP/comparison" + fi + scripts/reproduce-release.sh + + - name: Upload reproducibility artifacts + uses: actions/upload-artifact@v6 + with: + name: bitkit-reproducible-release-${{ github.run_number }} + path: ${{ runner.temp }}/reproducible-release + retention-days: 30 + diff --git a/README.md b/README.md index c5c00085e3..3d3e6af71e 100644 --- a/README.md +++ b/README.md @@ -170,6 +170,8 @@ For Play Store submission, build an AAB instead of APK: AAB is generated in `app/build/outputs/bundle/mainnetRelease/`. +See [reproducible builds](docs/reproducible-builds.md) for the WalletScrutiny-oriented release reproduction flow. + ### Build for E2E Testing Pass `E2E=true` and build any flavor. By default, E2E uses a local Electrum override. diff --git a/docs/reproducible-builds.md b/docs/reproducible-builds.md new file mode 100644 index 0000000000..f770446107 --- /dev/null +++ b/docs/reproducible-builds.md @@ -0,0 +1,106 @@ +# Reproducible builds + +This document captures the current Bitkit Android release reproduction flow for WalletScrutiny-style review. + +## Current release target + +- Flavor/build type: `mainnetRelease` +- Gradle wrapper: `8.13` +- Android Gradle Plugin: `8.13.2` +- Java: `17` +- Compile SDK: `36` +- Bundletool: `1.18.1` +- AAB output: `app/build/outputs/bundle/mainnetRelease/` +- APK output: `app/build/outputs/apk/mainnet/release/` + +The release build needs the private mainnet Firebase config at `app/src/mainnetRelease/google-services.json` and release signing material. CI writes those files from protected GitHub environment secrets; external verifiers need an equivalent file from the project or an agreed public/non-secret release config strategy. + +## Local reproduction + +Configure GitHub Packages credentials without committing secrets: + +```sh +export GITHUB_ACTOR=YOUR_GITHUB_USERNAME +export GITHUB_TOKEN=YOUR_READ_PACKAGES_TOKEN +``` + +Configure release signing without `keystore.properties`: + +```sh +export KEYSTORE_FILE=/absolute/path/to/bitkit.keystore +export KEYSTORE_PASSWORD=... +export KEY_ALIAS=... +export KEY_PASSWORD=... +``` + +Build the mainnet release bundle and recreate APK splits: + +```sh +scripts/reproduce-release.sh +``` + +The script writes artifacts under `.ai/reproducible-release/` by default: + +- `artifacts/*.aab` +- `artifacts/*.apks` +- `extracted-apks/` +- `checksums/release-artifacts.sha256` +- `checksums/extracted-apks.sha256` +- `checksums/arm64-native-libs.sha256` +- `arm64-apks.txt` +- `arm64-native-libs.txt` + +To reuse an existing AAB: + +```sh +SKIP_GRADLE_BUILD=true AAB_PATH=/path/to/bitkit-mainnet-release-181.aab scripts/reproduce-release.sh +``` + +## GitHub workflow + +The `Reproducible Release` workflow builds `bundleMainnetRelease`, recreates APK splits with bundletool, extracts the `arm64-v8a` native libraries, and uploads checksums plus reproduction artifacts. Workflow behavior can only be fully verified after merge because GitHub Actions workflow changes are only active for PRs opened after the workflow change is merged. + +If a comparison artifact is available in GitHub Actions, pass its artifact name to the manual workflow input. The workflow installs `diffoscope` and writes `diffoscope.html` and `diffoscope.txt` when possible. + +## Manual diffoscope checks + +Compare generated APK splits against a downloaded or previously generated APK set: + +```sh +diffoscope path/to/reference-apks .ai/reproducible-release/extracted-apks \ + --html .ai/reproducible-release/diffoscope.html +``` + +Compare only the arm64 native libraries: + +```sh +diffoscope path/to/reference-native-libs .ai/reproducible-release/native-libs \ + --html .ai/reproducible-release/native-libs-diffoscope.html +``` + +## Known WalletScrutiny issue + +WalletScrutiny issue `synonymdev/bitkit-android#953` previously reported that most release APK contents reproduced, with remaining differences in native libraries inside the `arm64-v8a` split. + +Known mappings: + +- `libbitkitcore.so` and `libpubky_app_specs...so` come from `com.synonym:bitkit-core-android:0.1.58` +- `libdatastore_shared_counter.so` comes from `androidx.datastore:datastore-core:1.2.0` +- `libjnidispatch.so` comes from `net.java.dev.jna:jna:5.18.1` + +The app repository can provide a stable release recipe, dependency verification, and artifact checksums. The remaining native reproducibility work is upstream artifact provenance, especially for Rust-produced Android libraries. + +## Upstream native follow-ups + +For Rust/native Android artifacts, the upstream repositories should publish reproducible AAR/native library builds with: + +- pinned Rust toolchain +- pinned Android NDK +- committed `Cargo.lock` +- stable build paths +- `SOURCE_DATE_EPOCH` +- `codegen-units = 1` +- path remapping +- deterministic stripping +- published AAR and native `.so` checksums + diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml new file mode 100644 index 0000000000..373281fb3f --- /dev/null +++ b/gradle/verification-metadata.xml @@ -0,0 +1,7274 @@ + + + + true + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/scripts/reproduce-release.sh b/scripts/reproduce-release.sh new file mode 100755 index 0000000000..b600c61c53 --- /dev/null +++ b/scripts/reproduce-release.sh @@ -0,0 +1,156 @@ +#!/usr/bin/env bash +set -euo pipefail + +repo_root=$(git rev-parse --show-toplevel 2>/dev/null || pwd) +cd "$repo_root" + +output_dir=${OUTPUT_DIR:-.ai/reproducible-release} +bundletool_version=${BUNDLETOOL_VERSION:-1.18.1} +bundletool_sha256=${BUNDLETOOL_SHA256:-a73341a7945abcb0e6b8971c7b1b2801bd765006447ca0d2437a4260d572ceac} +bundletool_url=${BUNDLETOOL_URL:-https://repo1.maven.org/maven2/com/android/tools/build/bundletool/${bundletool_version}/bundletool-${bundletool_version}.jar} +bundletool_jar=${BUNDLETOOL_JAR:-${output_dir}/tools/bundletool-${bundletool_version}.jar} + +artifacts_dir="$output_dir/artifacts" +checksums_dir="$output_dir/checksums" +extracted_dir="$output_dir/extracted-apks" +native_dir="$output_dir/native-libs" + +sha256_file() { + if command -v sha256sum >/dev/null 2>&1; then + sha256sum "$@" + else + shasum -a 256 "$@" + fi +} + +sha256_value() { + sha256_file "$1" | awk '{ print $1 }' +} + +checksum_tree() { + local root=$1 + local output=$2 + + mkdir -p "$(dirname "$output")" + if [[ ! -d "$root" ]]; then + : > "$output" + return + fi + + ( + cd "$root" + find . -type f | LC_ALL=C sort | while IFS= read -r file; do + file=${file#./} + sha256_file "$file" + done + ) > "$output" +} + +download_bundletool() { + if [[ -f "$bundletool_jar" ]]; then + local actual + actual=$(sha256_value "$bundletool_jar") + if [[ "$actual" == "$bundletool_sha256" ]]; then + return + fi + rm -f "$bundletool_jar" + fi + + mkdir -p "$(dirname "$bundletool_jar")" + local tmp + tmp=$(mktemp) + curl --fail --location --silent --show-error "$bundletool_url" --output "$tmp" + + local actual + actual=$(sha256_value "$tmp") + if [[ "$actual" != "$bundletool_sha256" ]]; then + echo "bundletool checksum mismatch: expected '$bundletool_sha256', got '$actual'" >&2 + rm -f "$tmp" + exit 1 + fi + + mv "$tmp" "$bundletool_jar" +} + +signing_args=() +if [[ -n "${KEYSTORE_FILE:-}" ]]; then + : "${KEYSTORE_PASSWORD:?KEYSTORE_PASSWORD is required when KEYSTORE_FILE is set}" + : "${KEY_ALIAS:?KEY_ALIAS is required when KEYSTORE_FILE is set}" + signing_args+=( + "--ks=$KEYSTORE_FILE" + "--ks-pass=pass:$KEYSTORE_PASSWORD" + "--ks-key-alias=$KEY_ALIAS" + "--key-pass=pass:${KEY_PASSWORD:-$KEYSTORE_PASSWORD}" + ) +fi + +rm -rf "$artifacts_dir" "$checksums_dir" "$extracted_dir" "$native_dir" +mkdir -p "$artifacts_dir" "$checksums_dir" "$extracted_dir" "$native_dir" + +if [[ "${SKIP_GRADLE_BUILD:-false}" != "true" ]]; then + ./gradlew bundleMainnetRelease --no-daemon --stacktrace +fi + +aab_path=${AAB_PATH:-} +if [[ -z "$aab_path" ]]; then + aab_path=$(find app/build/outputs/bundle/mainnetRelease -name 'bitkit-mainnet-release-*.aab' | LC_ALL=C sort | tail -n 1) +fi +if [[ ! -f "$aab_path" ]]; then + echo "AAB not found. Set AAB_PATH or run bundleMainnetRelease first." >&2 + exit 1 +fi + +download_bundletool + +aab_name=$(basename "$aab_path") +apks_path="$artifacts_dir/${aab_name%.aab}.apks" +cp "$aab_path" "$artifacts_dir/$aab_name" + +java -jar "$bundletool_jar" build-apks \ + --bundle="$aab_path" \ + --output="$apks_path" \ + --mode=default \ + --overwrite \ + "${signing_args[@]}" + +unzip -q "$apks_path" -d "$extracted_dir" + +find "$extracted_dir" -type f -name '*.apk' | LC_ALL=C sort > "$output_dir/apks.txt" +grep -E 'arm64[-_]v8a|arm64-v8a|arm64_v8a' "$output_dir/apks.txt" > "$output_dir/arm64-apks.txt" || true + +while IFS= read -r apk; do + apk_name=$(basename "$apk" .apk) + mkdir -p "$native_dir/$apk_name" + unzip -q -o "$apk" 'lib/arm64-v8a/*.so' -d "$native_dir/$apk_name" 2>/dev/null || true +done < "$output_dir/apks.txt" + +find "$native_dir" -type f -name '*.so' | LC_ALL=C sort > "$output_dir/arm64-native-libs.txt" + +checksum_tree "$artifacts_dir" "$checksums_dir/release-artifacts.sha256" +checksum_tree "$extracted_dir" "$checksums_dir/extracted-apks.sha256" +checksum_tree "$native_dir" "$checksums_dir/arm64-native-libs.sha256" + +if [[ -n "${DIFFOSCOPE_COMPARE_DIR:-}" && -d "$DIFFOSCOPE_COMPARE_DIR" ]]; then + if command -v diffoscope >/dev/null 2>&1; then + diffoscope "$DIFFOSCOPE_COMPARE_DIR" "$extracted_dir" \ + --html "$output_dir/diffoscope.html" \ + > "$output_dir/diffoscope.txt" || true + else + echo "diffoscope is not installed; skipping comparison." > "$output_dir/diffoscope.txt" + fi +fi + +cat > "$output_dir/README.txt" < Date: Thu, 21 May 2026 18:24:47 +0200 Subject: [PATCH 04/12] ci: fix reproducible workflow checks --- .github/workflows/reproducible-release.yml | 21 ++++++++- docs/reproducible-builds.md | 3 +- gradle/verification-metadata.xml | 52 ++++++++++++++++++++++ 3 files changed, 73 insertions(+), 3 deletions(-) diff --git a/.github/workflows/reproducible-release.yml b/.github/workflows/reproducible-release.yml index ec581c6496..2c2060c14f 100644 --- a/.github/workflows/reproducible-release.yml +++ b/.github/workflows/reproducible-release.yml @@ -7,6 +7,14 @@ on: description: Optional artifact name to compare against with diffoscope required: false default: '' + comparison_run_id: + description: Workflow run id that produced the comparison artifact + required: false + default: '' + comparison_repository: + description: Optional owner/repo for the comparison artifact + required: false + default: '' push: tags: - 'v*' @@ -27,6 +35,7 @@ jobs: environment: release-store permissions: + actions: read contents: read packages: read @@ -63,12 +72,23 @@ jobs: printf '%s' "$BITKIT_KEYSTORE_BASE64" | base64 --decode > "$keystore_path" echo "KEYSTORE_FILE=$keystore_path" >> "$GITHUB_ENV" + - name: Validate comparison inputs + if: ${{ github.event_name == 'workflow_dispatch' && inputs.comparison_artifact_name != '' }} + env: + COMPARISON_RUN_ID: ${{ inputs.comparison_run_id }} + run: | + set -euo pipefail + test -n "$COMPARISON_RUN_ID" + - name: Download comparison artifact if: ${{ github.event_name == 'workflow_dispatch' && inputs.comparison_artifact_name != '' }} uses: actions/download-artifact@v6 with: name: ${{ inputs.comparison_artifact_name }} path: ${{ runner.temp }}/comparison + github-token: ${{ github.token }} + repository: ${{ inputs.comparison_repository || github.repository }} + run-id: ${{ inputs.comparison_run_id }} - name: Install diffoscope if: ${{ github.event_name == 'workflow_dispatch' && inputs.comparison_artifact_name != '' }} @@ -99,4 +119,3 @@ jobs: name: bitkit-reproducible-release-${{ github.run_number }} path: ${{ runner.temp }}/reproducible-release retention-days: 30 - diff --git a/docs/reproducible-builds.md b/docs/reproducible-builds.md index f770446107..7b6ad9c40b 100644 --- a/docs/reproducible-builds.md +++ b/docs/reproducible-builds.md @@ -60,7 +60,7 @@ SKIP_GRADLE_BUILD=true AAB_PATH=/path/to/bitkit-mainnet-release-181.aab scripts/ The `Reproducible Release` workflow builds `bundleMainnetRelease`, recreates APK splits with bundletool, extracts the `arm64-v8a` native libraries, and uploads checksums plus reproduction artifacts. Workflow behavior can only be fully verified after merge because GitHub Actions workflow changes are only active for PRs opened after the workflow change is merged. -If a comparison artifact is available in GitHub Actions, pass its artifact name to the manual workflow input. The workflow installs `diffoscope` and writes `diffoscope.html` and `diffoscope.txt` when possible. +If a comparison artifact is available in GitHub Actions, pass its artifact name and source workflow run id to the manual workflow inputs. The optional repository input can point at a different `owner/repo` when the artifact is not in the current repository. The workflow installs `diffoscope` and writes `diffoscope.html` and `diffoscope.txt` when possible. ## Manual diffoscope checks @@ -103,4 +103,3 @@ For Rust/native Android artifacts, the upstream repositories should publish repr - path remapping - deterministic stripping - published AAR and native `.so` checksums - diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index 373281fb3f..98b62cae3d 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -3397,6 +3397,11 @@ + + + + + @@ -3882,6 +3887,14 @@ + + + + + + + + @@ -6515,6 +6528,11 @@ + + + + + @@ -6531,6 +6549,14 @@ + + + + + + + + @@ -6796,6 +6822,16 @@ + + + + + + + + + + @@ -6879,6 +6915,11 @@ + + + + + @@ -6919,6 +6960,11 @@ + + + + + @@ -6968,11 +7014,17 @@ + + + + + + From 602ee4c0caaa142c99e3770fec8818d3aba8a927 Mon Sep 17 00:00:00 2001 From: Ovi Trif Date: Thu, 21 May 2026 18:35:12 +0200 Subject: [PATCH 05/12] ci: add aapt2 verification metadata --- gradle/verification-metadata.xml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index 98b62cae3d..f5185db0a3 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -2190,6 +2190,14 @@ + + + + + + + + From bf45354dea3ecb8842a9904ec20c9bbb1ee02afc Mon Sep 17 00:00:00 2001 From: Ovi Trif Date: Thu, 21 May 2026 18:35:15 +0200 Subject: [PATCH 06/12] ci: rename release workflow --- .../{release-store.yml => release.yml} | 18 +++++++++--------- .github/workflows/reproducible-release.yml | 4 ++-- 2 files changed, 11 insertions(+), 11 deletions(-) rename .github/workflows/{release-store.yml => release.yml} (89%) diff --git a/.github/workflows/release-store.yml b/.github/workflows/release.yml similarity index 89% rename from .github/workflows/release-store.yml rename to .github/workflows/release.yml index 3938decd21..c938dc84d7 100644 --- a/.github/workflows/release-store.yml +++ b/.github/workflows/release.yml @@ -1,4 +1,4 @@ -name: Release Store +name: Release on: workflow_dispatch: @@ -15,11 +15,11 @@ env: FORCE_COLOR: 1 jobs: - build-store: + build-release: if: github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/heads/release-') || startsWith(github.ref, 'refs/tags/v') runs-on: ubuntu-latest timeout-minutes: 45 - environment: release-store + environment: release permissions: contents: read @@ -47,7 +47,7 @@ jobs: mkdir -p app/src/mainnetRelease printf '%s' "$MAINNET_RELEASE_GOOGLE_SERVICES_JSON_BASE64" | base64 --decode > app/src/mainnetRelease/google-services.json - - name: Decode store keystore + - name: Decode release keystore env: BITKIT_KEYSTORE_BASE64: ${{ secrets.BITKIT_KEYSTORE_BASE64 }} run: | @@ -58,7 +58,7 @@ jobs: printf '%s' "$BITKIT_KEYSTORE_BASE64" | base64 --decode > "$keystore_path" echo "KEYSTORE_FILE=$keystore_path" >> "$GITHUB_ENV" - - name: Build store release artifacts + - name: Build release artifacts env: GPR_USER: ${{ secrets.GPR_USER || github.actor }} GPR_TOKEN: ${{ secrets.GPR_TOKEN || github.token }} @@ -68,11 +68,11 @@ jobs: KEY_PASSWORD: ${{ secrets.BITKIT_KEY_PASSWORD }} run: ./gradlew assembleMainnetRelease bundleMainnetRelease --no-daemon --stacktrace - - name: Collect store artifacts + - name: Collect release artifacts id: artifacts run: | set -euo pipefail - artifact_dir="$RUNNER_TEMP/store-release" + artifact_dir="$RUNNER_TEMP/release" mkdir -p "$artifact_dir" find app/build/outputs/bundle/mainnetRelease -name 'bitkit-mainnet-release-*.aab' -print0 | xargs -0 -I {} cp {} "$artifact_dir/" @@ -81,9 +81,9 @@ jobs: (cd "$artifact_dir" && sha256sum *.aab *.apk > SHA256SUMS.txt) echo "artifact_dir=$artifact_dir" >> "$GITHUB_OUTPUT" - - name: Upload store artifacts + - name: Upload release artifacts uses: actions/upload-artifact@v6 with: - name: bitkit-store-release-${{ github.run_number }} + name: bitkit-release-${{ github.run_number }} path: ${{ steps.artifacts.outputs.artifact_dir }} retention-days: 30 diff --git a/.github/workflows/reproducible-release.yml b/.github/workflows/reproducible-release.yml index 2c2060c14f..f526563a1b 100644 --- a/.github/workflows/reproducible-release.yml +++ b/.github/workflows/reproducible-release.yml @@ -32,7 +32,7 @@ jobs: if: github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/heads/release-') || startsWith(github.ref, 'refs/tags/v') runs-on: ubuntu-latest timeout-minutes: 60 - environment: release-store + environment: release permissions: actions: read @@ -61,7 +61,7 @@ jobs: mkdir -p app/src/mainnetRelease printf '%s' "$MAINNET_RELEASE_GOOGLE_SERVICES_JSON_BASE64" | base64 --decode > app/src/mainnetRelease/google-services.json - - name: Decode store keystore + - name: Decode release keystore env: BITKIT_KEYSTORE_BASE64: ${{ secrets.BITKIT_KEYSTORE_BASE64 }} run: | From 3b24fd14ca94cfe3048153cfcbb327cd0cce9824 Mon Sep 17 00:00:00 2001 From: Ovi Trif Date: Thu, 21 May 2026 18:37:49 +0200 Subject: [PATCH 07/12] ci: select latest release bundle --- scripts/reproduce-release.sh | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/scripts/reproduce-release.sh b/scripts/reproduce-release.sh index b600c61c53..aded7ff232 100755 --- a/scripts/reproduce-release.sh +++ b/scripts/reproduce-release.sh @@ -46,6 +46,33 @@ checksum_tree() { ) > "$output" } +file_mtime() { + if stat -f %m "$1" >/dev/null 2>&1; then + stat -f %m "$1" + else + stat -c %Y "$1" + fi +} + +latest_file() { + local root=$1 + local pattern=$2 + local latest= + local latest_mtime=0 + local candidate + local candidate_mtime + + while IFS= read -r -d '' candidate; do + candidate_mtime=$(file_mtime "$candidate") + if [[ -z "$latest" || "$candidate_mtime" -gt "$latest_mtime" ]]; then + latest=$candidate + latest_mtime=$candidate_mtime + fi + done < <(find "$root" -type f -name "$pattern" -print0) + + printf '%s\n' "$latest" +} + download_bundletool() { if [[ -f "$bundletool_jar" ]]; then local actual @@ -93,7 +120,7 @@ fi aab_path=${AAB_PATH:-} if [[ -z "$aab_path" ]]; then - aab_path=$(find app/build/outputs/bundle/mainnetRelease -name 'bitkit-mainnet-release-*.aab' | LC_ALL=C sort | tail -n 1) + aab_path=$(latest_file app/build/outputs/bundle/mainnetRelease 'bitkit-mainnet-release-*.aab') fi if [[ ! -f "$aab_path" ]]; then echo "AAB not found. Set AAB_PATH or run bundleMainnetRelease first." >&2 @@ -153,4 +180,3 @@ Bundletool: $bundletool_version EOF echo "Wrote reproducibility artifacts to '$output_dir'." - From c02a77f2169775b2936c4d895d5c2309d9a9f0dd Mon Sep 17 00:00:00 2001 From: Ovi Trif Date: Thu, 21 May 2026 18:43:42 +0200 Subject: [PATCH 08/12] ci: add ksp verification metadata --- gradle/verification-metadata.xml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index f5185db0a3..81ae237cf2 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -3000,6 +3000,14 @@ + + + + + + + + @@ -6326,6 +6334,14 @@ + + + + + + + + From 26f4f24db0e4bfb410da78f004a82d1712211791 Mon Sep 17 00:00:00 2001 From: Ovi Trif Date: Thu, 21 May 2026 18:54:12 +0200 Subject: [PATCH 09/12] ci: fix comparison artifact flow --- .github/workflows/reproducible-release.yml | 10 ++++------ docs/reproducible-builds.md | 2 +- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/.github/workflows/reproducible-release.yml b/.github/workflows/reproducible-release.yml index f526563a1b..122a29aa49 100644 --- a/.github/workflows/reproducible-release.yml +++ b/.github/workflows/reproducible-release.yml @@ -11,10 +11,6 @@ on: description: Workflow run id that produced the comparison artifact required: false default: '' - comparison_repository: - description: Optional owner/repo for the comparison artifact - required: false - default: '' push: tags: - 'v*' @@ -87,7 +83,7 @@ jobs: name: ${{ inputs.comparison_artifact_name }} path: ${{ runner.temp }}/comparison github-token: ${{ github.token }} - repository: ${{ inputs.comparison_repository || github.repository }} + repository: ${{ github.repository }} run-id: ${{ inputs.comparison_run_id }} - name: Install diffoscope @@ -108,7 +104,9 @@ jobs: OUTPUT_DIR: ${{ runner.temp }}/reproducible-release run: | set -euo pipefail - if [ -d "$RUNNER_TEMP/comparison" ]; then + if [ -d "$RUNNER_TEMP/comparison/extracted-apks" ]; then + export DIFFOSCOPE_COMPARE_DIR="$RUNNER_TEMP/comparison/extracted-apks" + elif [ -d "$RUNNER_TEMP/comparison" ]; then export DIFFOSCOPE_COMPARE_DIR="$RUNNER_TEMP/comparison" fi scripts/reproduce-release.sh diff --git a/docs/reproducible-builds.md b/docs/reproducible-builds.md index 7b6ad9c40b..8aa8662d58 100644 --- a/docs/reproducible-builds.md +++ b/docs/reproducible-builds.md @@ -60,7 +60,7 @@ SKIP_GRADLE_BUILD=true AAB_PATH=/path/to/bitkit-mainnet-release-181.aab scripts/ The `Reproducible Release` workflow builds `bundleMainnetRelease`, recreates APK splits with bundletool, extracts the `arm64-v8a` native libraries, and uploads checksums plus reproduction artifacts. Workflow behavior can only be fully verified after merge because GitHub Actions workflow changes are only active for PRs opened after the workflow change is merged. -If a comparison artifact is available in GitHub Actions, pass its artifact name and source workflow run id to the manual workflow inputs. The optional repository input can point at a different `owner/repo` when the artifact is not in the current repository. The workflow installs `diffoscope` and writes `diffoscope.html` and `diffoscope.txt` when possible. +If a comparison artifact from this repository is available in GitHub Actions, pass its artifact name and source workflow run id to the manual workflow inputs. The workflow installs `diffoscope` and writes `diffoscope.html` and `diffoscope.txt` when possible. ## Manual diffoscope checks From 56cc5b35b18154a07da3d2f6196ce941a66118d4 Mon Sep 17 00:00:00 2001 From: Ovi Trif Date: Thu, 21 May 2026 19:05:17 +0200 Subject: [PATCH 10/12] ci: use debug firebase config paths --- .github/workflows/ci.yml | 4 +++- .github/workflows/e2e.yml | 8 ++++++-- .github/workflows/e2e_migration.yml | 4 +++- .github/workflows/ui-tests.yml | 4 +++- README.md | 6 ++++-- 5 files changed, 19 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 54d33373cd..713ad9c547 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -35,7 +35,9 @@ jobs: uses: gradle/actions/setup-gradle@v5 - name: Decode google-services.json - run: echo "$GOOGLE_SERVICES_JSON_BASE64" | base64 -d > app/google-services.json + run: | + mkdir -p app/src/devDebug + echo "$GOOGLE_SERVICES_JSON_BASE64" | base64 -d > app/src/devDebug/google-services.json env: GOOGLE_SERVICES_JSON_BASE64: ${{ secrets.GOOGLE_SERVICES_JSON_BASE64 }} diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index be8f9b14d2..6bf0e90067 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -57,7 +57,9 @@ jobs: uses: gradle/actions/setup-gradle@v5 - name: Decode google-services.json - run: echo "$GOOGLE_SERVICES_JSON_BASE64" | base64 -d > app/google-services.json + run: | + mkdir -p app/src/devDebug + echo "$GOOGLE_SERVICES_JSON_BASE64" | base64 -d > app/src/devDebug/google-services.json env: GOOGLE_SERVICES_JSON_BASE64: ${{ secrets.GOOGLE_SERVICES_JSON_BASE64 }} @@ -100,7 +102,9 @@ jobs: uses: gradle/actions/setup-gradle@v5 - name: Decode google-services.json - run: echo "$GOOGLE_SERVICES_JSON_BASE64" | base64 -d > app/google-services.json + run: | + mkdir -p app/src/devDebug + echo "$GOOGLE_SERVICES_JSON_BASE64" | base64 -d > app/src/devDebug/google-services.json env: GOOGLE_SERVICES_JSON_BASE64: ${{ secrets.GOOGLE_SERVICES_JSON_BASE64 }} diff --git a/.github/workflows/e2e_migration.yml b/.github/workflows/e2e_migration.yml index 373f3236e1..cceeed3181 100644 --- a/.github/workflows/e2e_migration.yml +++ b/.github/workflows/e2e_migration.yml @@ -37,7 +37,9 @@ jobs: uses: gradle/actions/setup-gradle@v5 - name: Decode google-services.json - run: echo "$GOOGLE_SERVICES_JSON_BASE64" | base64 -d > app/google-services.json + run: | + mkdir -p app/src/devDebug + echo "$GOOGLE_SERVICES_JSON_BASE64" | base64 -d > app/src/devDebug/google-services.json env: GOOGLE_SERVICES_JSON_BASE64: ${{ secrets.GOOGLE_SERVICES_JSON_BASE64 }} diff --git a/.github/workflows/ui-tests.yml b/.github/workflows/ui-tests.yml index c6da4ed2ea..2b5c19bd76 100644 --- a/.github/workflows/ui-tests.yml +++ b/.github/workflows/ui-tests.yml @@ -41,7 +41,9 @@ jobs: gradle-${{ runner.os }}- - name: Decode google-services.json - run: echo "$GOOGLE_SERVICES_JSON_BASE64" | base64 -d > app/google-services.json + run: | + mkdir -p app/src/devDebug + echo "$GOOGLE_SERVICES_JSON_BASE64" | base64 -d > app/src/devDebug/google-services.json env: GOOGLE_SERVICES_JSON_BASE64: ${{ secrets.GOOGLE_SERVICES_JSON_BASE64 }} diff --git a/README.md b/README.md index 3d3e6af71e..2a14b08c95 100644 --- a/README.md +++ b/README.md @@ -10,10 +10,12 @@ This repository contains the **native Android app** for Bitkit. #### 1. Firebase Configuration -Debug builds include placeholder Firebase configuration so a fresh clone can compile without a private `google-services.json` files. +Debug builds include placeholder Firebase configuration so a fresh clone can compile without private `google-services.json` files. Download `google-services.json` from the Firebase Console when you need real Firebase integration for push notifications testing: -- dev/tnet/mainnetDebug: Place in `app/google-services.json` +- devDebug: Place in `app/src/devDebug/google-services.json` +- tnetDebug: Place in `app/src/tnetDebug/google-services.json` +- mainnetDebug: Place in `app/src/mainnetDebug/google-services.json` - mainnetRelease: Place in `app/src/mainnetRelease/google-services.json` > **Note**: Placeholder debug configs are only for local builds. FCM token registration and push notifications require real Firebase configuration. The mainnet release flavor will fail to build without the `mainnetRelease/google-services.json` file. From a12664bb7541e9b544bd561bc7deb938ffb4c980 Mon Sep 17 00:00:00 2001 From: Ovi Trif Date: Thu, 21 May 2026 23:13:35 +0200 Subject: [PATCH 11/12] ci: remove verification metadata --- docs/reproducible-builds.md | 2 +- gradle/verification-metadata.xml | 7350 ------------------------------ 2 files changed, 1 insertion(+), 7351 deletions(-) delete mode 100644 gradle/verification-metadata.xml diff --git a/docs/reproducible-builds.md b/docs/reproducible-builds.md index 8aa8662d58..549d8a6bdf 100644 --- a/docs/reproducible-builds.md +++ b/docs/reproducible-builds.md @@ -88,7 +88,7 @@ Known mappings: - `libdatastore_shared_counter.so` comes from `androidx.datastore:datastore-core:1.2.0` - `libjnidispatch.so` comes from `net.java.dev.jna:jna:5.18.1` -The app repository can provide a stable release recipe, dependency verification, and artifact checksums. The remaining native reproducibility work is upstream artifact provenance, especially for Rust-produced Android libraries. +The app repository can provide a stable release recipe and artifact checksums. The remaining native reproducibility work is upstream artifact provenance, especially for Rust-produced Android libraries. ## Upstream native follow-ups diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml deleted file mode 100644 index 81ae237cf2..0000000000 --- a/gradle/verification-metadata.xml +++ /dev/null @@ -1,7350 +0,0 @@ - - - - true - false - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From 02bd93f7ab8b287414dd80ae04af2d283baa86ed Mon Sep 17 00:00:00 2001 From: Ovi Trif Date: Sat, 23 May 2026 04:58:57 +0200 Subject: [PATCH 12/12] ci: address release review comments --- README.md | 16 ++++++++++++++++ scripts/reproduce-release.sh | 2 +- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 2a14b08c95..c8effde6b4 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,22 @@ Download `google-services.json` from the Firebase Console when you need real Fir - mainnetDebug: Place in `app/src/mainnetDebug/google-services.json` - mainnetRelease: Place in `app/src/mainnetRelease/google-services.json` +The debug files above are tracked placeholders. Before replacing a debug placeholder with a real Firebase config, hide that local-only change from Git: + +```sh +git update-index --skip-worktree app/src/devDebug/google-services.json +cp /secure/path/google-services.json app/src/devDebug/google-services.json +``` + +Repeat with the `tnetDebug` or `mainnetDebug` path when testing those debug variants. + +When you are done testing, restore the tracked placeholder: + +```sh +git update-index --no-skip-worktree app/src/devDebug/google-services.json +git restore app/src/devDebug/google-services.json +``` + > **Note**: Placeholder debug configs are only for local builds. FCM token registration and push notifications require real Firebase configuration. The mainnet release flavor will fail to build without the `mainnetRelease/google-services.json` file. #### 2. GitHub Packages setup diff --git a/scripts/reproduce-release.sh b/scripts/reproduce-release.sh index aded7ff232..4a1f35ac6d 100755 --- a/scripts/reproduce-release.sh +++ b/scripts/reproduce-release.sh @@ -7,7 +7,7 @@ cd "$repo_root" output_dir=${OUTPUT_DIR:-.ai/reproducible-release} bundletool_version=${BUNDLETOOL_VERSION:-1.18.1} bundletool_sha256=${BUNDLETOOL_SHA256:-a73341a7945abcb0e6b8971c7b1b2801bd765006447ca0d2437a4260d572ceac} -bundletool_url=${BUNDLETOOL_URL:-https://repo1.maven.org/maven2/com/android/tools/build/bundletool/${bundletool_version}/bundletool-${bundletool_version}.jar} +bundletool_url=${BUNDLETOOL_URL:-https://dl.google.com/dl/android/maven2/com/android/tools/build/bundletool/${bundletool_version}/bundletool-${bundletool_version}.jar} bundletool_jar=${BUNDLETOOL_JAR:-${output_dir}/tools/bundletool-${bundletool_version}.jar} artifacts_dir="$output_dir/artifacts"