From 7742e411e89ae53069d26c590db232ef736a58ee Mon Sep 17 00:00:00 2001 From: BarbossHack Date: Fri, 6 Mar 2026 20:57:59 +0100 Subject: [PATCH] Add reproducible builds --- .github/workflows/build.yml | 48 ++++++ .github/workflows/ci.yml | 96 ----------- .github/workflows/release.yml | 200 +++++------------------ .gitignore | 7 + man/Makefile | 4 +- reproducible-builds/README.md | 34 ++++ reproducible-builds/build.Containerfile | 11 ++ reproducible-builds/build.sh | 45 +++++ reproducible-builds/client.Containerfile | 7 + reproducible-builds/entrypoint.sh | 68 ++++++++ reproducible-builds/native.Containerfile | 7 + reproducible-builds/verify.sh | 41 +++++ 12 files changed, 307 insertions(+), 261 deletions(-) create mode 100644 .github/workflows/build.yml delete mode 100644 .github/workflows/ci.yml create mode 100644 reproducible-builds/README.md create mode 100644 reproducible-builds/build.Containerfile create mode 100755 reproducible-builds/build.sh create mode 100644 reproducible-builds/client.Containerfile create mode 100644 reproducible-builds/entrypoint.sh create mode 100644 reproducible-builds/native.Containerfile create mode 100755 reproducible-builds/verify.sh diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 00000000..78013b91 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,48 @@ +name: build + +on: + push: + branches: + - "**" + pull_request: + workflow_call: + +permissions: {} + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - name: Build + run: ./reproducible-builds/build.sh + - name: Upload build artifacts + uses: actions/upload-artifact@v7 + with: + name: signal-cli-build + path: dist/* + + build-client: + strategy: + matrix: + os: + - ubuntu + - macos + - windows + runs-on: ${{ matrix.os }}-latest + defaults: + run: + working-directory: ./client + steps: + - uses: actions/checkout@v6 + - name: Install rust + run: rustup default stable + - name: Build client + run: cargo build --release --verbose + - name: Archive production artifacts + uses: actions/upload-artifact@v7 + with: + name: signal-cli-client-${{ matrix.os }} + path: | + client/target/release/signal-cli-client + client/target/release/signal-cli-client.exe diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml deleted file mode 100644 index da2122f1..00000000 --- a/.github/workflows/ci.yml +++ /dev/null @@ -1,96 +0,0 @@ -name: signal-cli CI - -on: - push: - branches: - - '**' - pull_request: - workflow_call: - -permissions: - contents: write # to fetch code (actions/checkout) and submit dependency graph (gradle/gradle-build-action) - -jobs: - build: - - runs-on: ubuntu-latest - strategy: - matrix: - java: [ '25' ] - - steps: - - uses: actions/checkout@v6 - - name: Set up JDK - uses: actions/setup-java@v5 - with: - distribution: 'zulu' - java-version: ${{ matrix.java }} - - name: Setup Gradle - uses: gradle/actions/setup-gradle@v5 - with: - dependency-graph: generate-and-submit - - name: Install asciidoc - run: sudo apt update && sudo apt --no-install-recommends install -y asciidoc-base - - name: Build with Gradle - run: ./gradlew --no-daemon build - - name: Build man page - run: | - cd man - make install - - name: Add man page to archive - run: | - version=$(tar tf build/distributions/signal-cli-*.tar | head -n1 | sed 's|signal-cli-\([^/]*\)/.*|\1|') - echo $version - tar --transform="flags=r;s|man|signal-cli-${version}/man|" -rf build/distributions/signal-cli-${version}.tar man/man{1,5} - - name: Compress archive - run: gzip -n -9 build/distributions/signal-cli-*.tar - - name: Archive production artifacts - uses: actions/upload-artifact@v7 - with: - name: signal-cli-archive-${{ matrix.java }} - path: build/distributions/signal-cli-*.tar.gz - - build-graalvm: - - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v6 - - uses: graalvm/setup-graalvm@v1 - with: - distribution: 'graalvm' - java-version: '25' - cache: 'gradle' - github-token: ${{ secrets.GITHUB_TOKEN }} - - name: Build with Gradle - run: ./gradlew --no-daemon nativeCompile - - name: Archive production artifacts - uses: actions/upload-artifact@v7 - with: - name: signal-cli-native - path: build/native/nativeCompile/signal-cli - - build-client: - strategy: - matrix: - os: - - ubuntu - - macos - - windows - runs-on: ${{ matrix.os }}-latest - defaults: - run: - working-directory: ./client - steps: - - uses: actions/checkout@v6 - - name: Install rust - run: rustup default stable - - name: Build client - run: cargo build --release --verbose - - name: Archive production artifacts - uses: actions/upload-artifact@v7 - with: - name: signal-cli-client-${{ matrix.os }} - path: | - client/target/release/signal-cli-client - client/target/release/signal-cli-client.exe diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 73797e9a..c45091c9 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -5,8 +5,7 @@ on: tags: - v* -permissions: - contents: write # to fetch code (actions/checkout) and create release +permissions: {} env: IMAGE_NAME: signal-cli @@ -15,96 +14,25 @@ env: REGISTRY_PASSWORD: ${{ github.token }} jobs: + build: + uses: AsamK/signal-cli/.github/workflows/build.yml@master - ci_wf: - permissions: - contents: write - uses: AsamK/signal-cli/.github/workflows/ci.yml@master - # ${{ github.repository }} not accepted here - - lib_to_jar: - needs: ci_wf + release: + needs: build runs-on: ubuntu-latest permissions: contents: write - outputs: - signal_cli_version: ${{ steps.cli_ver.outputs.version }} - release_id: ${{ steps.create_release.outputs.id }} - + version: ${{ steps.version.outputs.version }} steps: - - name: Download signal-cli build from CI workflow uses: actions/download-artifact@v8 - name: Get signal-cli version - id: cli_ver + id: version run: | - ver="${GITHUB_REF_NAME#v}" - echo "version=${ver}" >> $GITHUB_OUTPUT - - - name: Extract archive - run: | - tree . - ARCHIVE_DIR=$(ls signal-cli-archive-*/ -d | tail -n1) - tar -xzf ./"${ARCHIVE_DIR}"/*.tar.gz - mv ./"${ARCHIVE_DIR}"/*.tar.gz signal-cli-${{ steps.cli_ver.outputs.version }}.tar.gz - rm -rf signal-cli-archive-*/ - -# - name: Get signal-client jar version -# id: lib_ver -# run: | -# JAR_PREFIX=libsignal-client- -# jar_file=$(find ./signal-cli-*/lib/ -name "$JAR_PREFIX*.jar") -# jar_version=$(echo "$jar_file" | xargs basename | sed "s/$JAR_PREFIX//; s/.jar//") -# echo "$jar_version" -# echo "signal_client_version=${jar_version}" >> $GITHUB_OUTPUT -# -# - name: Download signal-client builds -# env: -# RELEASES_URL: https://github.com/signalapp/libsignal/releases/download/ -# FILE_NAMES: signal_jni.dll libsignal_jni.dylib -# SIGNAL_CLIENT_VER: ${{ steps.lib_ver.outputs.signal_client_version }} -# run: | -# for file_name in $FILE_NAMES; do -# curl -sOL "${RELEASES_URL}/v${SIGNAL_CLIENT_VER}/${file_name}" # note: added v -# done -# tree . - - - name: Compress native app - env: - SIGNAL_CLI_VER: ${{ steps.cli_ver.outputs.version }} - run: | - chmod +x signal-cli-native/signal-cli - tar -czf signal-cli-${SIGNAL_CLI_VER}-Linux-native.tar.gz -C signal-cli-native signal-cli - rm -rf signal-cli-native/ - - - name: Compress client app - env: - SIGNAL_CLI_VER: ${{ steps.cli_ver.outputs.version }} - run: | - chmod +x signal-cli-client-ubuntu/signal-cli-client - tar -czf signal-cli-${SIGNAL_CLI_VER}-Linux-client.tar.gz -C signal-cli-client-ubuntu signal-cli-client - rm -rf signal-cli-client-ubuntu/ - -# - name: Replace Windows lib -# env: -# SIGNAL_CLI_VER: ${{ steps.cli_ver.outputs.version }} -# SIGNAL_CLIENT_VER: ${{ steps.lib_ver.outputs.signal_client_version }} -# run: | -# mv signal_jni.dll libsignal_jni.so -# zip -u ./signal-cli-*/lib/libsignal-client-${SIGNAL_CLIENT_VER}.jar ./libsignal_jni.so -# tar -czf signal-cli-${SIGNAL_CLI_VER}-Windows.tar.gz signal-cli-*/ -# -# - name: Replace macOS lib -# env: -# SIGNAL_CLI_VER: ${{ steps.cli_ver.outputs.version }} -# SIGNAL_CLIENT_VER: ${{ steps.lib_ver.outputs.signal_client_version }} -# run: | -# jar_file=./signal-cli-*/lib/libsignal-client-${SIGNAL_CLIENT_VER}.jar -# zip -d $jar_file libsignal_jni.so -# zip $jar_file libsignal_jni.dylib -# tar -czf signal-cli-${SIGNAL_CLI_VER}-macOS.tar.gz signal-cli-*/ + mv ./signal-cli-build/* . + echo "version=$(cat VERSION)" >> $GITHUB_OUTPUT - name: Create release id: create_release @@ -112,8 +40,8 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: - tag_name: v${{ steps.cli_ver.outputs.version }} # note: added `v` - release_name: v${{ steps.cli_ver.outputs.version }} # note: added `v` + tag_name: v${{ steps.version.outputs.version }} # note: added `v` + release_name: v${{ steps.version.outputs.version }} # note: added `v` draft: true - name: Upload archive @@ -122,19 +50,9 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: signal-cli-${{ steps.cli_ver.outputs.version }}.tar.gz - asset_name: signal-cli-${{ steps.cli_ver.outputs.version }}.tar.gz - asset_content_type: application/x-compressed-tar # .tar.gz - -# - name: Upload Linux archive -# uses: actions/upload-release-asset@v1 -# env: -# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} -# with: -# upload_url: ${{ steps.create_release.outputs.upload_url }} -# asset_path: signal-cli-${{ steps.cli_ver.outputs.version }}-Linux.tar.gz -# asset_name: signal-cli-${{ steps.cli_ver.outputs.version }}-Linux.tar.gz -# asset_content_type: application/x-compressed-tar # .tar.gz + asset_path: signal-cli-${{ steps.version.outputs.version }}.tar.gz + asset_name: signal-cli-${{ steps.version.outputs.version }}.tar.gz + asset_content_type: application/x-compressed-tar # .tar.gz - name: Upload Linux native archive uses: actions/upload-release-asset@v1 @@ -142,9 +60,9 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: signal-cli-${{ steps.cli_ver.outputs.version }}-Linux-native.tar.gz - asset_name: signal-cli-${{ steps.cli_ver.outputs.version }}-Linux-native.tar.gz - asset_content_type: application/x-compressed-tar # .tar.gz + asset_path: signal-cli-${{ steps.version.outputs.version }}-Linux-native.tar.gz + asset_name: signal-cli-${{ steps.version.outputs.version }}-Linux-native.tar.gz + asset_content_type: application/x-compressed-tar # .tar.gz - name: Upload Linux client archive uses: actions/upload-release-asset@v1 @@ -152,35 +70,14 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: signal-cli-${{ steps.cli_ver.outputs.version }}-Linux-client.tar.gz - asset_name: signal-cli-${{ steps.cli_ver.outputs.version }}-Linux-client.tar.gz - asset_content_type: application/x-compressed-tar # .tar.gz - -# - name: Upload windows archive -# uses: actions/upload-release-asset@v1 -# env: -# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} -# with: -# upload_url: ${{ steps.create_release.outputs.upload_url }} -# asset_path: signal-cli-${{ steps.cli_ver.outputs.version }}-Windows.tar.gz -# asset_name: signal-cli-${{ steps.cli_ver.outputs.version }}-Windows.tar.gz -# asset_content_type: application/x-compressed-tar # .tar.gz -# -# - name: Upload macos archive -# uses: actions/upload-release-asset@v1 -# env: -# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} -# with: -# upload_url: ${{ steps.create_release.outputs.upload_url }} -# asset_path: signal-cli-${{ steps.cli_ver.outputs.version }}-macOS.tar.gz -# asset_name: signal-cli-${{ steps.cli_ver.outputs.version }}-macOS.tar.gz -# asset_content_type: application/x-compressed-tar # .tar.gz + asset_path: signal-cli-${{ steps.version.outputs.version }}-Linux-client.tar.gz + asset_name: signal-cli-${{ steps.version.outputs.version }}-Linux-client.tar.gz + asset_content_type: application/x-compressed-tar # .tar.gz build-container: - needs: ci_wf + needs: release runs-on: ubuntu-latest permissions: - contents: read packages: write steps: @@ -188,28 +85,19 @@ jobs: - name: Download signal-cli build from CI workflow uses: actions/download-artifact@v8 - - name: Get signal-cli version - id: cli_ver - run: | - ver="${GITHUB_REF_NAME#v}" - echo "version=${ver}" >> $GITHUB_OUTPUT - - name: Move archive file run: | - ARCHIVE_DIR=$(ls signal-cli-archive-*/ -d | tail -n1) - tar xf ./"${ARCHIVE_DIR}"/*.tar.gz - rm -r signal-cli-archive-* signal-cli-native + tar xf ./signal-cli-build/signal-cli-${{ needs.release.outputs.version }}.tar.gz mkdir -p build/install/ - mv ./signal-cli-"${GITHUB_REF_NAME#v}"/ build/install/signal-cli + mv ./signal-cli-"${{ needs.release.outputs.version }}"/ build/install/signal-cli - name: Build Image id: build_image uses: redhat-actions/buildah-build@v2 with: image: ${{ env.IMAGE_NAME }} - tags: latest ${{ github.sha }} ${{ steps.cli_ver.outputs.version }} - containerfiles: - ./Containerfile + tags: latest ${{ github.sha }} ${{ needs.release.outputs.version }} + containerfiles: ./Containerfile oci: true - name: Push To GHCR @@ -227,10 +115,9 @@ jobs: echo "${{ toJSON(steps.push.outputs) }}" build-container-native: - needs: ci_wf + needs: release runs-on: ubuntu-latest permissions: - contents: read packages: write steps: @@ -238,26 +125,20 @@ jobs: - name: Download signal-cli build from CI workflow uses: actions/download-artifact@v8 - - name: Get signal-cli version - id: cli_ver - run: | - ver="${GITHUB_REF_NAME#v}" - echo "version=${ver}" >> $GITHUB_OUTPUT - - name: Move archive file run: | + tar xf ./signal-cli-build/signal-cli-${{ needs.release.outputs.version }}-Linux-native.tar.gz mkdir -p build/native/nativeCompile/ - chmod +x ./signal-cli-native/signal-cli - mv ./signal-cli-native/signal-cli build/native/nativeCompile/ + mv signal-cli build/native/nativeCompile/ + chmod +x build/native/nativeCompile/signal-cli - name: Build Image id: build_image uses: redhat-actions/buildah-build@v2 with: image: ${{ env.IMAGE_NAME }} - tags: latest-native ${{ github.sha }}-native ${{ steps.cli_ver.outputs.version }}-native - containerfiles: - ./native.Containerfile + tags: latest-native ${{ github.sha }}-native ${{ needs.release.outputs.version }}-native + containerfiles: ./native.Containerfile oci: true - name: Push To GHCR @@ -275,10 +156,9 @@ jobs: echo "${{ toJSON(steps.push.outputs) }}" build-container-client: - needs: ci_wf + needs: release runs-on: ubuntu-latest permissions: - contents: read packages: write steps: @@ -286,26 +166,20 @@ jobs: - name: Download signal-cli build from CI workflow uses: actions/download-artifact@v8 - - name: Get signal-cli version - id: cli_ver - run: | - ver="${GITHUB_REF_NAME#v}" - echo "version=${ver}" >> $GITHUB_OUTPUT - - name: Move archive file run: | + tar xf ./signal-cli-build/signal-cli-${{ needs.release.outputs.version }}-Linux-client.tar.gz mkdir -p client/target/release/ - chmod +x ./signal-cli-client-ubuntu/signal-cli-client - mv ./signal-cli-client-ubuntu/signal-cli-client client/target/release/ + mv signal-cli-client client/target/release/ + chmod +x client/target/release/signal-cli-client - name: Build Image id: build_image uses: redhat-actions/buildah-build@v2 with: image: ${{ env.IMAGE_NAME }} - tags: latest-client ${{ github.sha }}-client ${{ steps.cli_ver.outputs.version }}-client - containerfiles: - ./client.Containerfile + tags: latest-client ${{ github.sha }}-client ${{ needs.release.outputs.version }}-client + containerfiles: ./client.Containerfile oci: true - name: Push To GHCR diff --git a/.gitignore b/.gitignore index 2b6774c1..1e880c6d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ .gradle/ +.kotlin/ .idea/* !.idea/codeStyles/ build/ @@ -13,3 +14,9 @@ out/ .DS_Store /bin/ /test-config/ +/dist/ +/github/ +man/*.1 +man/*.5 +man/man1 +man/man5 diff --git a/man/Makefile b/man/Makefile index e3aca110..e2c94725 100644 --- a/man/Makefile +++ b/man/Makefile @@ -14,8 +14,8 @@ all: $(MANPAGESRC) .PHONY: install install: all $(MKDIR) -p man1 man5 - for f in *.1; do $(GZIP) < "$$f" > man1/"$$f".gz ; done - for f in *.5; do $(GZIP) < "$$f" > man5/"$$f".gz ; done + for f in *.1; do $(GZIP) -n < "$$f" > man1/"$$f".gz ; done + for f in *.5; do $(GZIP) -n < "$$f" > man5/"$$f".gz ; done .PHONY: clean clean: diff --git a/reproducible-builds/README.md b/reproducible-builds/README.md new file mode 100644 index 00000000..f5bbd61d --- /dev/null +++ b/reproducible-builds/README.md @@ -0,0 +1,34 @@ +# Reproducible builds + +This process lets you verify that the version of the app that was downloaded from the Githud Releases matches the source code in our public repository. + +This is achieved by replicating the build environment as Docker images. + +Currently, only the following binaries are reproducible: + +- [x] JAR package (`signal-cli-XXX.tar.gz`) +- [ ] Native binary (`signal-cli-XXX-Linux-native.tar.gz`) +- [x] Rust client binary (`signal-cli-XXX-Linux-client.tar.gz`) + +In the following section, we will use Signal version 0.14.0 as the reference example. Simply replace all occurrences of 0.14.0 with the version number you are about to verify. + +## Step-by-step instructions + +### 0. Prerequisites + +Before you begin, ensure you have the following installed: + +- git +- docker (or podman) + +### 1. Verifying reproducibility + +```bash +git clone --depth 1 --branch v0.14.0 https://github.com/AsamK/signal-cli +cd ./signal-cli +./reproducible-builds/verify.sh +``` + +If each one ends with `... matches!` for every binary (except the native one for now), you're good to go! You've successfully verified that the Github Release binaries were built from exactly the same code as is in the signal-cli git repository. + +If you get `... doesn't match!`, it means something went wrong (except for the native one for now). Please [open an issue](https://github.com/AsamK/signal-cli/issues/new/choose). diff --git a/reproducible-builds/build.Containerfile b/reproducible-builds/build.Containerfile new file mode 100644 index 00000000..f3786423 --- /dev/null +++ b/reproducible-builds/build.Containerfile @@ -0,0 +1,11 @@ +FROM docker.io/azul/zulu-openjdk:25.0.2-jdk@sha256:0349494e05c22fe439e65be99771581b2bc428d89f07363b539389a11103fa5f +ENV SOURCE_DATE_EPOCH=1767225600 +ENV LANG=C.UTF-8 +ENV LC_CTYPE=en_US.UTF-8 +ARG SNAPSHOT=20260101T000000Z +RUN echo "deb http://snapshot.ubuntu.com/ubuntu/${SNAPSHOT}/ jammy main" > /etc/apt/sources.list \ + && echo "deb http://snapshot.ubuntu.com/ubuntu/${SNAPSHOT}/ jammy universe" >> /etc/apt/sources.list +RUN apt update && apt install -y make asciidoc-base +COPY --chmod=0700 reproducible-builds/entrypoint.sh /usr/local/bin/entrypoint.sh +WORKDIR /signal-cli +ENTRYPOINT [ "/usr/local/bin/entrypoint.sh", "build" ] diff --git a/reproducible-builds/build.sh b/reproducible-builds/build.sh new file mode 100755 index 00000000..09a4322c --- /dev/null +++ b/reproducible-builds/build.sh @@ -0,0 +1,45 @@ +#!/bin/bash + +set -eu + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/../" +cd "$ROOT_DIR" +rm -rf "$ROOT_DIR/dist" +mkdir -p "$ROOT_DIR/dist" + +if command -v podman >/dev/null; then + ENGINE=podman + USER= +else + ENGINE=docker + USER="--user $(id -u):$(id -g)" +fi + +VERSION=$(sed -n 's/\s*version\s*=\s*"\(.*\)".*/\1/p' build.gradle.kts | tail -n1) +echo "$VERSION" >dist/VERSION + +$ENGINE build -t signal-cli:build -f reproducible-builds/build.Containerfile . +$ENGINE build -t signal-cli:native -f reproducible-builds/native.Containerfile . +$ENGINE build -t signal-cli:client -f reproducible-builds/client.Containerfile . + +# Build jar +git clean -Xfd -e '!/dist/' -e '!/dist/**' -e '!/github/' -e '!/github/**' +# shellcheck disable=SC2086 +$ENGINE run --pull=never --rm -v "$(pwd)":/signal-cli:Z -e VERSION="$VERSION" $USER signal-cli:build +mv build/distributions/signal-cli-*.tar.gz dist/ + +# Build native-image +git clean -Xfd -e '!/dist/' -e '!/dist/**' -e '!/github/' -e '!/github/**' +# shellcheck disable=SC2086 +$ENGINE run --pull=never --rm -v "$(pwd)":/signal-cli:Z -e VERSION="$VERSION" $USER signal-cli:native +mv build/signal-cli-*-Linux-native.tar.gz dist/ + +# Build rust client +git clean -Xfd -e '!/dist/' -e '!/dist/**' -e '!/github/' -e '!/github/**' +# shellcheck disable=SC2086 +$ENGINE run --pull=never --rm -v "$(pwd)":/signal-cli:Z -e VERSION="$VERSION" $USER signal-cli:client +mv build/signal-cli-*-Linux-client.tar.gz dist/ + +ls -lsh dist/ + +echo -e "\e[32mBuild successful!\e[0m" diff --git a/reproducible-builds/client.Containerfile b/reproducible-builds/client.Containerfile new file mode 100644 index 00000000..2d6321f2 --- /dev/null +++ b/reproducible-builds/client.Containerfile @@ -0,0 +1,7 @@ +FROM docker.io/rust:1.93.1-slim-trixie@sha256:7f2c9f2f0dad8f4afa6faf5efa971e7e566398a36e54fb7684061407ea067058 +ENV SOURCE_DATE_EPOCH=1767225600 +ENV LANG=C.UTF-8 +ENV LC_CTYPE=en_US.UTF-8 +COPY --chmod=0700 reproducible-builds/entrypoint.sh /usr/local/bin/entrypoint.sh +WORKDIR /signal-cli +ENTRYPOINT [ "/usr/local/bin/entrypoint.sh", "client" ] diff --git a/reproducible-builds/entrypoint.sh b/reproducible-builds/entrypoint.sh new file mode 100644 index 00000000..55a655f4 --- /dev/null +++ b/reproducible-builds/entrypoint.sh @@ -0,0 +1,68 @@ +#!/bin/bash + +set -eu + +echo "Build '$1' variant $VERSION ..." + +function reset_file_dates() { + find . -exec touch -m -d "@$SOURCE_DATE_EPOCH" {} \; +} + +reset_file_dates + +if [ "$1" == "build" ]; then + + ./gradlew build \ + --no-daemon \ + --max-workers=1 \ + -Dkotlin.compiler.execution.strategy=in-process \ + --no-build-cache \ + -Dorg.gradle.caching=false \ + -Porg.gradle.java.installations.auto-download=false \ + -Porg.gradle.java.installations.auto-detect=false + cd man + make install + cd .. + reset_file_dates + tar tf build/distributions/signal-cli-*.tar | head -n1 | sed 's|signal-cli-\([^/]*\)/.*|\1|' + tar --mtime="@$SOURCE_DATE_EPOCH" --transform="flags=r;s|man|signal-cli-${VERSION}/man|" -rf "build/distributions/signal-cli-${VERSION}.tar" man/man{1,5} + gzip -n -9 build/distributions/signal-cli-*.tar + +elif [ "$1" == "native" ]; then + + ./gradlew nativeCompile \ + --no-daemon \ + --max-workers=1 \ + -Dkotlin.compiler.execution.strategy=in-process \ + --no-build-cache \ + -Dorg.gradle.caching=false \ + -Dgraalvm.native-image.build-time=2026-01-01T00:00:00Z \ + -Porg.gradle.java.installations.auto-download=false \ + -Porg.gradle.java.installations.auto-detect=false + + strip --strip-all \ + --remove-section=.note.gnu.build-id \ + --remove-section=.comment \ + --remove-section=.gnu_debuglink \ + --remove-section=.annobin.notes \ + --remove-section=.gnu.build.attributes \ + --remove-section=.note.ABI-tag \ + build/native/nativeCompile/signal-cli + + chmod +x build/native/nativeCompile/signal-cli + reset_file_dates + tar --mtime="@$SOURCE_DATE_EPOCH" -czf "build/signal-cli-${VERSION}-Linux-native.tar.gz" -C build/native/nativeCompile signal-cli + +elif [ "$1" == "client" ]; then + + cd client + cargo build --release --locked + cd .. + chmod +x client/target/release/signal-cli-client + mkdir -p build + tar --mtime="@$SOURCE_DATE_EPOCH" -czf "build/signal-cli-${VERSION}-Linux-client.tar.gz" -C client/target/release signal-cli-client + +else + echo "Unknown build variant '$1'" + exit 1 +fi diff --git a/reproducible-builds/native.Containerfile b/reproducible-builds/native.Containerfile new file mode 100644 index 00000000..f93bc1e8 --- /dev/null +++ b/reproducible-builds/native.Containerfile @@ -0,0 +1,7 @@ +FROM container-registry.oracle.com/graalvm/native-image:25.0.2@sha256:e8c5ec4f256bf958f327aea060e1424aa87f63114aeb4a4318a0ac169bbdb9a1 +ENV SOURCE_DATE_EPOCH=1767225600 +ENV LANG=C.UTF-8 +ENV LC_CTYPE=en_US.UTF-8 +COPY --chmod=0700 reproducible-builds/entrypoint.sh /usr/local/bin/entrypoint.sh +WORKDIR /signal-cli +ENTRYPOINT [ "/usr/local/bin/entrypoint.sh", "native" ] diff --git a/reproducible-builds/verify.sh b/reproducible-builds/verify.sh new file mode 100755 index 00000000..310005e5 --- /dev/null +++ b/reproducible-builds/verify.sh @@ -0,0 +1,41 @@ +#!/bin/bash + +set -eu + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/../" +cd "$ROOT_DIR" +rm -rf "$ROOT_DIR/github" +mkdir -p "$ROOT_DIR/github" + +VERSION=$(sed -n 's/\s*version\s*=\s*"\(.*\)".*/\1/p' build.gradle.kts | tail -n1) + +echo "Download latest release from GitHub..." + +curl -L --fail "https://github.com/AsamK/signal-cli/releases/download/v${VERSION}/signal-cli-${VERSION}.tar.gz" -o "github/signal-cli-${VERSION}.tar.gz" +curl -L --fail "https://github.com/AsamK/signal-cli/releases/download/v${VERSION}/signal-cli-${VERSION}-Linux-native.tar.gz" -o "github/signal-cli-${VERSION}-Linux-native.tar.gz" +curl -L --fail "https://github.com/AsamK/signal-cli/releases/download/v${VERSION}/signal-cli-${VERSION}-Linux-client.tar.gz" -o "github/signal-cli-${VERSION}-Linux-client.tar.gz" + +./reproducible-builds/build.sh + +rm -f {github,dist}/VERSION + +echo "commit: $(git rev-parse HEAD)" + +echo "sha256 hashes of GitHub release:" +sha256sum github/* +echo "sha256 hashes of locally built files:" +sha256sum dist/* + +reproducible=true +for file in $(cd github && find . -type f); do + if diff "github/$file" "dist/$file" >/dev/null 2>&1; then + echo -e "\e[32m[+] '$(basename "$file")' matches!\e[0m" + else + echo -e "\e[31m[-] '$(basename "$file")' doesn't match!\e[0m" + reproducible=false + fi +done + +if [ "$reproducible" = false ]; then + exit 1 +fi