diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 00000000..2b23ed3c --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,57 @@ +name: build + +on: + push: + branches: + - "**" + pull_request: + workflow_call: + +permissions: {} + +jobs: + build: + strategy: + matrix: + # The "reproducible" entry is used to build the project with the LTS Java version used in reproducible builds script. + # More Java versions can be added to test compatibility, eg. "26". + java: ["reproducible", "26"] + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - name: Build + run: | + if [ "${{ matrix.java }}" != "reproducible" ]; then + export OVERRIDE_JAVA_VERSION="${{ matrix.java }}" + fi + ./reproducible-builds/build.sh + - name: Upload build artifacts + uses: actions/upload-artifact@v7 + with: + name: signal-cli-${{ matrix.java }}-${{ github.job }} + 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 cbbbbd7a..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', '26' ] - - 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..a18b953f 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-reproducible-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-reproducible-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-reproducible-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-reproducible-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..780f8ae4 --- /dev/null +++ b/reproducible-builds/README.md @@ -0,0 +1,34 @@ +# Reproducible builds + +This process lets you verify that the version of signal-cli that was downloaded from the Github Releases matches the source code in the 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-cli version 0.14.2 as the reference example. Simply replace all occurrences of 0.14.2 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.2 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..0e73ddc5 --- /dev/null +++ b/reproducible-builds/build.Containerfile @@ -0,0 +1,13 @@ +ARG ZULU_TAG="25.0.2-jdk@sha256:9582df6c4415d9c770eb5ff8fce426ebba53631149c9eb083ee126568d32fab3" + +FROM docker.io/azul/zulu-openjdk:$ZULU_TAG +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..8a3878ce --- /dev/null +++ b/reproducible-builds/build.sh @@ -0,0 +1,50 @@ +#!/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 ${OVERRIDE_JAVA_VERSION:+--build-arg ZULU_TAG=$OVERRIDE_JAVA_VERSION} -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/ + +if [ -n "${OVERRIDE_JAVA_VERSION:-}" ]; then + echo -e "\e[33mBuild was performed with overridden Java version $OVERRIDE_JAVA_VERSION, native-image and client will not be built.\e[0m" + exit 0 +fi + +# 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..8a8dfa34 --- /dev/null +++ b/reproducible-builds/client.Containerfile @@ -0,0 +1,7 @@ +FROM docker.io/rust:1.94.1-slim-trixie@sha256:c6a474d7164ea2455e09b60a759b1edca38db7373c5689c1dae31780de4e71ac +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..5aa521d2 --- /dev/null +++ b/reproducible-builds/entrypoint.sh @@ -0,0 +1,78 @@ +#!/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 .. + tar_archive="build/distributions/signal-cli-${VERSION}.tar" + tar --transform="flags=r;s|man|signal-cli-${VERSION}/man|" -rf "$tar_archive" man/man{1,5} + + # Remake the tarball to ensure reproducible file order and timestamps + mkdir -p build/extracted + tar -xf "$tar_archive" -C build/extracted/ + reset_file_dates + rm -f "$tar_archive" + tar --sort=name --mtime="@$SOURCE_DATE_EPOCH" --transform='s|^\./||' --owner=0 --group=0 --numeric-owner -cf "$tar_archive" -C build/extracted . + + gzip -n -9 "$tar_archive" + +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 --sort=name --mtime="@$SOURCE_DATE_EPOCH" --owner=0 --group=0 --numeric-owner -cf "build/signal-cli-${VERSION}-Linux-native.tar" -C build/native/nativeCompile signal-cli + gzip -n -9 "build/signal-cli-${VERSION}-Linux-native.tar" + +elif [ "$1" == "client" ]; then + + cd client + cargo build --release --locked + cd .. + chmod +x client/target/release/signal-cli-client + mkdir -p build + reset_file_dates + tar --sort=name --mtime="@$SOURCE_DATE_EPOCH" --owner=0 --group=0 --numeric-owner -cf "build/signal-cli-${VERSION}-Linux-client.tar" -C client/target/release signal-cli-client + gzip -n -9 "build/signal-cli-${VERSION}-Linux-client.tar" + +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..47cd5ce3 --- /dev/null +++ b/reproducible-builds/native.Containerfile @@ -0,0 +1,7 @@ +FROM container-registry.oracle.com/graalvm/native-image:25.0.2@sha256:4c0d5919f6840d89721274eb8cf81962faa2f870b816967e6732e2a151b150d8 +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..938eb7a2 --- /dev/null +++ b/reproducible-builds/verify.sh @@ -0,0 +1,44 @@ +#!/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" + elif [[ "$file" =~ "native" ]]; then + echo -e "\e[33m[-] '$(basename "$file")' doesn't match! (not supported yet)\e[0m" + reproducible=false + else + echo -e "\e[31m[-] '$(basename "$file")' doesn't match!\e[0m" + reproducible=false + fi +done + +if [ "$reproducible" = false ]; then + exit 1 +fi