From ec36ae7f26f7df202d19aaacc1d3a1f3359b12a8 Mon Sep 17 00:00:00 2001 From: ayumi-signal Date: Mon, 5 Aug 2024 13:37:53 -0700 Subject: [PATCH] Update reproducible build script to reflect build process --- reproducible-builds/Dockerfile | 35 ++++++++++++-- reproducible-builds/README.md | 37 ++++++++++----- reproducible-builds/build.sh | 35 +++++++++++++- reproducible-builds/docker-entrypoint.sh | 60 ++++++++++++++++++++++++ ts/scripts/generate-dns-fallback.ts | 2 +- 5 files changed, 148 insertions(+), 21 deletions(-) create mode 100644 reproducible-builds/docker-entrypoint.sh diff --git a/reproducible-builds/Dockerfile b/reproducible-builds/Dockerfile index 95f51c9e5753..180dab23e91c 100644 --- a/reproducible-builds/Dockerfile +++ b/reproducible-builds/Dockerfile @@ -5,12 +5,19 @@ FROM ubuntu:jammy-20230624@sha256:b060fffe8e1561c9c3e6dea6db487b900100fc26830b9e # UNIX timestamp will be generated at the time of the build, and is non-deterministic. # # Read https://reproducible-builds.org/specs/source-date-epoch/ for more info -ENV SOURCE_DATE_EPOCH=1 +ENV SOURCE_DATE_EPOCH=0 # Due to some issues with NVM reading .nvmrc, we define the version -# as an environment variable and use that instead. +# as an environment variable and use that instead. ARG NODE_VERSION +# User for building. electron-builder works better when not running as root. +ARG USERNAME=signaluser +ARG USER_UID=1000 +ARG USER_GID=$USER_UID + +ENV SIGNAL_ENV=production + # --- # This portion of the code is identical to the Signal Android's # reproducible build system. https://github.com/signalapp/Signal-Android/blob/main/reproducible-builds/Dockerfile @@ -19,18 +26,21 @@ ARG NODE_VERSION COPY docker/ docker/ COPY docker/apt.conf docker/sources.list /etc/apt/ +# Ubuntu needs the ca-certificates package before it'll trust our mirror. +# But we can't install it because it doesn't trust our mirror! # Temporarily disables APT's certificate signature checking -# to download the certificates. See +# to download the certificates. RUN apt update -oAcquire::https::Verify-Peer=false RUN apt install -oAcquire::https::Verify-Peer=false -y ca-certificates +# Back to normal, verification back on RUN apt update RUN apt install -y git curl g++ gcc make python3 tar # --- # Install nvm +ENV NVM_VERSION=0.40.0 ENV NVM_DIR=/usr/local/nvm -ENV NVM_VERSION=0.39.7 RUN mkdir $NVM_DIR RUN curl -o- "https://raw.githubusercontent.com/nvm-sh/nvm/v${NVM_VERSION}/install.sh" | bash \ @@ -42,4 +52,19 @@ RUN curl -o- "https://raw.githubusercontent.com/nvm-sh/nvm/v${NVM_VERSION}/insta ENV NODE_PATH=$NVM_DIR/v$NODE_VERSION/lib/node_modules ENV PATH=$NVM_DIR/versions/node/v$NODE_VERSION/bin:$PATH -RUN git config --global --add safe.directory /project \ No newline at end of file +RUN git config --global --add safe.directory /project + +COPY docker-entrypoint.sh /usr/local/bin/ +RUN chmod +x /usr/local/bin/docker-entrypoint.sh + +# Use non-root user for build. +RUN groupadd --gid $USER_GID $USERNAME \ + && useradd --uid $USER_UID --gid $USER_GID -m $USERNAME + +USER $USERNAME + +ENTRYPOINT ["docker-entrypoint.sh"] + +# Specify build type using CMD, which affects the app version and name of the resulting package. +# For more information see build.sh and docker-entrypoint.sh +CMD ["dev"] diff --git a/reproducible-builds/README.md b/reproducible-builds/README.md index 3e73e86ea1f8..57c06dcaf3bf 100644 --- a/reproducible-builds/README.md +++ b/reproducible-builds/README.md @@ -1,8 +1,11 @@ + + + # Reproducible builds -In order to verify that Signal's official apps are correctly built from the open source code, we need *reproducible builds*. +In order to verify that Signal's official apps are correctly built from the open source code, we need _reproducible builds_. -Reproducible builds help ensure that anyone, including you, can build Signal Desktop in a way that is completely identical to the official downloads available to all users. +Reproducible builds help ensure that anyone, including you, can build Signal Desktop in a way that is completely identical to the official downloads available to all users. This provides an extra security layer to ensure that the builds aren't tampered with, corrupted, and built with the free open source code. @@ -12,6 +15,12 @@ Reproducible builds for macOS and Windows are not available yet. ## Reproduce and verify the Linux build +### Experimental notice + +We are in the process of rolling out and verifying reproducible builds. As such, reproducibility is still +experimental and may not work on public releases yet. If you notice any inconsistencies then please file an issue [on the Github Issues page](https://github.com/signalapp/Signal-Desktop/issues). +Thanks for your patience while we set it up! + ### Pre-requisites - Docker Engine is installed and running on your computer @@ -26,29 +35,31 @@ First, grab the source code by using `git`: $ git clone https://github.com/signalapp/Signal-Desktop.git ``` -This will download Signal Desktop's source code under the `Signal-Desktop` file. Once the download is complete, go inside the file and make sure you are selecting the branch used in official builds. For instance, if you are trying to build `7.18.0`, then do: +This will download Signal Desktop's source code under the `Signal-Desktop` directory. Once the download is complete, go inside the directory and make sure you are on the branch used in official builds. For instance, if you are trying to build `7.18.0`, then do: ```bash $ cd Signal-Desktop/ -Signal-Desktop$ git checkout tags/7.16.0 +$ git checkout tags/7.18.0 ``` -You are now on the version of the source code used for `7.16.0`. Then, make sure your shell is in the `reproducible-builds` directory first: +You are now on the version of the source code used for `7.18.0`. Then, make sure your shell is in the `reproducible-builds` directory: ```bash -Signal-Desktop$ cd reproducible-builds/ -Signal-Desktop/reproducible-builds$ pwd +$ cd reproducible-builds/ +$ pwd [...]/Signal-Desktop/reproducible-builds ``` -Last step is to run the `./build.sh` script. (If your user is not in Docker's `docker` group, then you may need to run the script as `sudo`). +The last step is to run the `./build.sh` script, passing the "public" arg because you are verifying +a public production or beta build. +(If your user is not in Docker's `docker` group, then you may need to run the script as `sudo`). ```bash -Signal-Desktop/reproducible-builds$ chmod +x ./build.sh -Signal-Desktop/reproducible-builds$ ./build.sh +$ chmod +x ./build.sh public +$ ./build.sh public ``` -This bash script will do two things. First, it will create the Docker container where Signal Desktop will be built. Second, it will build Signal Desktop inside the container. +This bash script will do two things. First, it will create the Docker container where Signal Desktop will be built. Second, it will build Signal Desktop inside the container. When the build is completed, the resulting file will be available at `Signal-Desktop/release/signal-desktop_7.18.0_amd64.deb`. @@ -69,10 +80,10 @@ To verify the official `.deb` package against your build, make sure that your ve ```bash $ sha256sum signal-desktop_7.18.0_amd64-OUR_BUILD.deb signal-desktop_7.18.0_amd64_OFFICIAL_BUILD.deb -0df3d06f74c6855559ef079b368326ca18e144a28ede559fd76648a62ec3eed7 signal-desktop_7.18.0_amd64-OUR_BUILD.deb +0df3d06f74c6855559ef079b368326ca18e144a28ede559fd76648a62ec3eed7 signal-desktop_7.18.0_amd64-OUR_BUILD.deb 0df3d06f74c6855559ef079b368326ca18e144a28ede559fd76648a62ec3eed7 signal-desktop_7.18.0_amd64_OFFICIAL_BUILD.deb ``` ### What to do if the checksums don't match -- File an issue [on the Github Issues page](https://github.com/signalapp/Signal-Desktop/issues). \ No newline at end of file +- File an issue [on the Github Issues page](https://github.com/signalapp/Signal-Desktop/issues). diff --git a/reproducible-builds/build.sh b/reproducible-builds/build.sh index 6044b790c8d2..0cfa882e030e 100755 --- a/reproducible-builds/build.sh +++ b/reproducible-builds/build.sh @@ -1,5 +1,36 @@ #!/bin/sh +# Copyright 2024 Signal Messenger, LLC +# SPDX-License-Identifier: AGPL-3.0-only -docker build -t signal-desktop --build-arg NODE_VERSION=$(cat ../.nvmrc) . +# Usage: +# ./build.sh [ dev (default) | public (prod and beta builds) | alpha | test | staging ] [ Build timestamp override. Defaults to latest git commit or 0. ] + +# First we prepare the docker container in which our build scripts will run. This container includes +# all build dependencies at specific versions. +# We set SOURCE_DATE_EPOCH to make system build timestamps deterministic. +docker build -t signal-desktop --build-arg SOURCE_DATE_EPOCH=0 --build-arg NODE_VERSION=$(cat ../.nvmrc) . + +# Before performing the actual build, go to the project root. cd .. -docker run --rm -v "$(pwd)":/project -w /project --user "$(id -u):$(id -g)" signal-desktop sh -c "npm install; npm run generate; npm run build-release" + +# Prepare the timestamp of the actual build based on the latest git commit. +source_date_epoch=0 +if [ "$2" != "" ]; then + echo "Using override timestamp for SOURCE_DATE_EPOCH." + source_date_epoch=$(($2)) +else + git_timestamp=$(git log -1 --pretty=%ct) + if [ "${git_timestamp}" != "" ]; then + echo "At commit: $(git log -1 --oneline)" + echo "Setting SOURCE_DATE_EPOCH to latest commit's timestamp." + source_date_epoch=$((git_timestamp)) + else + echo "Can't get latest commit timestamp. Defaulting to 0." + source_date_epoch=0 + fi +fi + +# Perform the build by mounting the project into the container and passing in the 1st command line +# arg to select the build type (e.g. "public"). The container runs docker-entrypoint.sh. +# After the process is finished, the resulting package is located in the ./release/ directory. +docker run --rm -v "$(pwd)":/project -w /project --user "$(id -u):$(id -g)" -e SOURCE_DATE_EPOCH=$source_date_epoch signal-desktop $1 diff --git a/reproducible-builds/docker-entrypoint.sh b/reproducible-builds/docker-entrypoint.sh new file mode 100644 index 000000000000..da65ebc85c6e --- /dev/null +++ b/reproducible-builds/docker-entrypoint.sh @@ -0,0 +1,60 @@ +#!/usr/bin/env bash +# Copyright 2024 Signal Messenger, LLC +# SPDX-License-Identifier: AGPL-3.0-only + +trap '[[ $pid ]] && kill $pid; exit' EXIT + +# This is the default entrypoint for the when running the build container. +# Usage: docker-entrypoint.sh [BUILD_TYPE] + +# BUILD_TYPE affects the package name and version. +# dev (default): +# - name: signal-desktop +# - version: package.json version +# public: +# - name: signal-desktop or signal-desktop-beta, depending on package.json version +# - version: package.json version +# alpha: +# - name: signal-desktop-alpha +# - version: package.json version + commit sha +# test: Same as alpha +# staging: +# - name: signal-desktop-staging +# - version: package.json version + commit sha; replaces "alpha" with "staging" +if [ "$1" != "" ]; then + BUILD_TYPE="$1" +fi +echo "BUILD_TYPE: ${BUILD_TYPE}" + +# SOURCE_DATE_EPOCH allows package builders like FPM (used for creating the .deb +# package on linux) to make their build timestamps determistic. Otherwise, a fresh +# UNIX timestamp will be generated at the time of the build, and is non-deterministic. +echo "SOURCE_DATE_EPOCH: ${SOURCE_DATE_EPOCH}" + +npm install +npm run clean-transpile +cd sticker-creator +npm install +npm run build +cd .. +npm run generate + +if [ "${BUILD_TYPE}" = "public" ]; then + npm run prepare-beta-build +elif [ "${BUILD_TYPE}" = "alpha" ]; then + npm run prepare-alpha-version + npm run prepare-alpha-build +elif [ "${BUILD_TYPE}" = "staging" ]; then + npm run prepare-alpha-version + npm run prepare-staging-build +elif [ "${BUILD_TYPE}" = "test" ]; then + npm run prepare-alpha-version + npm run prepare-alpha-build +elif [ "${BUILD_TYPE}" = "dev" ]; then + echo "dev build, using package.json as is" +else + echo "Unknown build type ${BUILD_TYPE}" + exit 1 +fi + +npm run build-linux diff --git a/ts/scripts/generate-dns-fallback.ts b/ts/scripts/generate-dns-fallback.ts index cdbed8c31995..90e7736b9a73 100644 --- a/ts/scripts/generate-dns-fallback.ts +++ b/ts/scripts/generate-dns-fallback.ts @@ -60,7 +60,7 @@ async function main() { const outPath = join(__dirname, '../../build/dns-fallback.json'); - //await writeFile(outPath, `${JSON.stringify(config, null, 2)}\n`); + await writeFile(outPath, `${JSON.stringify(config, null, 2)}\n`); } main().catch(error => {