refactor: reproducible builds from devcontainers - #2017
Merged
Conversation
- Remove sudo entirely; all privileged setup done as root before USER switch - Consolidate all apt installs into a single RUN block including openjdk-21-jdk - Remove unused packages: zip, software-properties-common - Replace wget with curl throughout - Rename USER to APP_USER to avoid shadowing the standard Linux variable - Move all ENV declarations to the top; derive HOME from APP_USER - Fix RUSTFLAGS hardcoded /home/docker path to use $HOME - Merge Rust install, verification, and rustup target adds into one RUN - Merge Android SDK setup and sdkmanager calls into one RUN - Copy directly into /app instead of staging directory - Remove redundant fvm flutter clean on fresh clone - Add ENV_SOURCE arg to control .env origin (template or local) - Add FAKE_KEYSTORE arg with secret mount support for production signing - Update makefile docker-build to expose all build args as overridable variables
…ator and add .rustup remap
…ld timestamp OpenSSL embeds a wall-clock build timestamp in compiled binaries, causing .so differences between Docker builds run at different times. Setting SOURCE_DATE_EPOCH to the latest git commit timestamp fixes this in both the Dockerfile and the makefile build target.
… Dockerfile Root Dockerfile - Remove SOURCE/VERSION args; always build from local source - Fix ENV_SOURCE=local branch which was a no-op (cp file to itself) reproducibility/Dockerfile - Add --platform=linux/amd64; upgrade Java 17 -> 21 - Replace wget with curl; drop wget from apt installs - Remove apksigner and Android SDK setup — only used for informational output that never affected the verdict - Merge apktool and bundletool RUN layers reproducibility/verify_build.sh - Move all validation (args, git tag check, required tools) before any work - Add --yes flag to skip interactive prompts for CI/automation - Replace wget with curl; replace grep -c "^" with wc -l - Use container_name consistently; get commit hash from local git instead of spinning up a container - Write RESULTS.md to workspace with verdict, metadata, and full diff
…en --apk is provided, skip tag check for local APK verification
Resolve conflicts in Dockerfile and makefile: - Dockerfile: keep develop's toolchain-only image (debian:trixie, pinned versions) - Dockerfile.apk: new file for the APK build stage (FROM bull-mobile) - makefile: combine both branches' targets, add 'make apk [release|debug]' and 'make verify' - verify_build.sh: update to use two-stage Docker build
Member
Author
|
Running this test: #!/bin/bash
# ==============================================================================
# Bull Bitcoin Mobile - Reproducibility Test
# Builds the APK twice and compares pre-signature contents.
# If the diff is empty, the build is reproducible.
# ==============================================================================
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
RED='\033[1;31m'
GREEN='\033[1;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
# Detect container runtime
if command -v podman &> /dev/null; then
CTR="podman"
elif command -v docker &> /dev/null; then
CTR="docker"
else
echo -e "${RED}Error: Neither podman nor docker found.${NC}"
exit 1
fi
echo "Using $CTR"
# Parse arguments
MODE="${1:-debug}"
if [[ "$MODE" != "debug" && "$MODE" != "release" ]]; then
echo "Usage: $0 [debug|release]"
exit 1
fi
WORK_DIR="$SCRIPT_DIR/reproducibility_test_$(date +%s)"
mkdir -p "$WORK_DIR"
echo "Workspace: $WORK_DIR"
APK_PATH="build/app/outputs/flutter-apk/app-${MODE}.apk"
VERIFY_TOOLS_IMAGE="bullbitcoin-verify-tools:latest"
# Build verification tools container
echo "Building verification tools container..."
$CTR build -q -t "$VERIFY_TOOLS_IMAGE" "$SCRIPT_DIR" > /dev/null
# --- Build 1 ---
echo ""
echo -e "${YELLOW}=== Build 1 ===${NC}"
cd "$REPO_ROOT"
make apk "$MODE"
$CTR create --name repro_build1_$$ bull-mobile-apk > /dev/null
$CTR cp "repro_build1_$$:/app/$APK_PATH" "$WORK_DIR/build1.apk"
$CTR rm repro_build1_$$ > /dev/null
echo "Saved: $WORK_DIR/build1.apk"
sha256sum "$WORK_DIR/build1.apk"
# --- Build 2 (no cache) ---
echo ""
echo -e "${YELLOW}=== Build 2 (no cache) ===${NC}"
$CTR rmi bull-mobile-apk > /dev/null 2>&1 || true
make apk "$MODE"
$CTR create --name repro_build2_$$ bull-mobile-apk > /dev/null
$CTR cp "repro_build2_$$:/app/$APK_PATH" "$WORK_DIR/build2.apk"
$CTR rm repro_build2_$$ > /dev/null
echo "Saved: $WORK_DIR/build2.apk"
sha256sum "$WORK_DIR/build2.apk"
# --- Decode both APKs ---
echo ""
echo -e "${YELLOW}=== Decoding APKs ===${NC}"
$CTR run --rm \
-v "$WORK_DIR":/work \
"$VERIFY_TOOLS_IMAGE" \
sh -c "apktool d -f -o /work/decoded1 /work/build1.apk && \
apktool d -f -o /work/decoded2 /work/build2.apk"
# --- Compare ---
echo ""
echo "=== Comparing pre-signature contents ==="
diff_output=$(diff -r "$WORK_DIR/decoded1" "$WORK_DIR/decoded2" | grep -v META-INF || true)
if [[ -z "$diff_output" ]]; then
echo -e "${GREEN}BUILD IS REPRODUCIBLE${NC}"
echo "Both builds are identical (excluding META-INF signatures)."
exit 0
else
echo -e "${RED}DIFFERENCES FOUND${NC}"
echo "$diff_output" | tee "$WORK_DIR/diff.txt"
echo ""
echo "Full diff saved to: $WORK_DIR/diff.txt"
exit 1
fiThere is one last remaining fix which has to be found for native assets since they are the only two diffs between two builds |
ethicnology
force-pushed
the
repro-from-devcontainers
branch
from
April 13, 2026 08:30
4c03a15 to
29c7172
Compare
ethicnology
force-pushed
the
repro-from-devcontainers
branch
from
April 14, 2026 03:45
78fe546 to
dbc6d2a
Compare
- Remove flutter_dotenv dependency and .env asset bundling - Hardcode API URLs, auth credentials, and Sentry DSN in constants.dart - Remove fake keystore generation from Dockerfile.apk - Build unsigned APK when no key.properties is present - Extract APK to host at end of make apk - Use Platform.environment in integration test instead of dotenv
ethicnology
force-pushed
the
repro-from-devcontainers
branch
from
April 14, 2026 07:32
5e0505b to
de44c1b
Compare
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Dockerfile(toolchain only mydevcontainersworks)Dockerfile.apk(crafted from @basantagoswami 'sreproducibilitybranch)reproducibility/verify_build.shdownloads an official APK (GitHub or local), rebuilds from source, decodes both with apktool, and diffs pre-signature contents (from @basantagoswami 'sreproducibilitybranch)make apk [release|debug],make verify VERSION=x.y.z [APK=path]