From b93ffdd79dd022d483af2a91fb94694bec617c76 Mon Sep 17 00:00:00 2001 From: f321x Date: Thu, 10 Jul 2025 15:55:34 +0200 Subject: [PATCH] appimage: bump appimagetool to new version/repo Updates the appimage build scripts to use the newer https://github.com/AppImage/appimagetool tool to bundle the appimage instead of the discontinued https://github.com/AppImage/AppImageKit. To prevent the new appimagetool from downloading a random "latest" appimage runtime (`type2-runtime`) binary this PR also adds functionality to clone and build https://github.com/AppImage/type2-runtime from source. This is done using the build scripts provided in the `type2-runtime` repository, however the Dockerfile they use for building is replaced by a copy with pinned package versions to prevent issues with reproducibility. This should fix the issue of missing libfuse2 which users of the appimage have on "modern" distributions. The new `type2-runtime` is statically linked and includes the required dependencies now instead of relying on the host to provide it. --- contrib/build-linux/appimage/Dockerfile | 1 + contrib/build-linux/appimage/build.sh | 30 ++++ contrib/build-linux/appimage/make_appimage.sh | 20 +-- .../type2-runtime-reproducible-build.patch | 149 ++++++++++++++++++ 4 files changed, 190 insertions(+), 10 deletions(-) create mode 100644 contrib/build-linux/appimage/patches/type2-runtime-reproducible-build.patch diff --git a/contrib/build-linux/appimage/Dockerfile b/contrib/build-linux/appimage/Dockerfile index cfedc1782..2b5148c64 100644 --- a/contrib/build-linux/appimage/Dockerfile +++ b/contrib/build-linux/appimage/Dockerfile @@ -67,6 +67,7 @@ RUN apt-get update -q && \ libv4l-dev \ libjpeg62-turbo-dev \ libx11-dev \ + desktop-file-utils \ && \ rm -rf /var/lib/apt/lists/* && \ apt-get autoremove -y && \ diff --git a/contrib/build-linux/appimage/build.sh b/contrib/build-linux/appimage/build.sh index a6f5cabeb..d986befb2 100755 --- a/contrib/build-linux/appimage/build.sh +++ b/contrib/build-linux/appimage/build.sh @@ -13,6 +13,10 @@ CONTRIB_APPIMAGE="$CONTRIB/build-linux/appimage" DISTDIR="$PROJECT_ROOT/dist" BUILD_UID=$(/usr/bin/stat -c %u "$PROJECT_ROOT") +# when bumping the runtime commit also check if the `type2-runtime-reproducible-build.patch` still works +TYPE2_RUNTIME_COMMIT="5e7217b7cfeecee1491c2d251e355c3cf8ba6e4d" +TYPE2_RUNTIME_REPO="https://github.com/AppImage/type2-runtime.git" + . "$CONTRIB"/build_tools_util.sh @@ -46,6 +50,32 @@ else info "not doing fresh clone." fi +# build the type2-runtime binary, this build step uses a separate docker container +# defined in the type2-runtime repo (patched with type2-runtime-reproducible-build.patch) +TYPE2_RUNTIME_REPO_DIR="$PROJECT_ROOT_OR_FRESHCLONE_ROOT/contrib/build-linux/appimage/.cache/appimage/type2-runtime" +( + if [ -f "$TYPE2_RUNTIME_REPO_DIR/runtime-x86_64" ]; then + info "type2-runtime already built, skipping" + exit 0 + fi + clone_or_update_repo "$TYPE2_RUNTIME_REPO" "$TYPE2_RUNTIME_COMMIT" "$TYPE2_RUNTIME_REPO_DIR" + + # Apply patch to make runtime build reproducible + info "Applying type2-runtime patch..." + cd "$TYPE2_RUNTIME_REPO_DIR" + git apply "$CONTRIB_APPIMAGE/patches/type2-runtime-reproducible-build.patch" || fail "Failed to apply runtime repo patch" + + info "building type2-runtime in build container..." + cd "$TYPE2_RUNTIME_REPO_DIR/scripts/docker" + env ARCH=x86_64 ./build-with-docker.sh + mv "./runtime-x86_64" "$TYPE2_RUNTIME_REPO_DIR/" + + # clean up the empty created 'out' dir to prevent permission issues + rm -rf "$TYPE2_RUNTIME_REPO_DIR/out" + + info "runtime build successful: $(sha256sum "$TYPE2_RUNTIME_REPO_DIR/runtime-x86_64")" +) || fail "Failed to build type2-runtime" + info "building binary..." # check uid and maybe chown. see #8261 if [ ! -z "$ELECBUILD_COMMIT" ] ; then # fresh clone (reproducible build) diff --git a/contrib/build-linux/appimage/make_appimage.sh b/contrib/build-linux/appimage/make_appimage.sh index 43b8ba295..637039111 100755 --- a/contrib/build-linux/appimage/make_appimage.sh +++ b/contrib/build-linux/appimage/make_appimage.sh @@ -9,6 +9,7 @@ DISTDIR="$PROJECT_ROOT/dist" BUILDDIR="$CONTRIB_APPIMAGE/build/appimage" APPDIR="$BUILDDIR/electrum.AppDir" CACHEDIR="$CONTRIB_APPIMAGE/.cache/appimage" +TYPE2_RUNTIME_REPO_DIR="$CACHEDIR/type2-runtime" export DLL_TARGET_DIR="$CACHEDIR/dlls" PIP_CACHE_DIR="$CONTRIB_APPIMAGE/.cache/pip_cache" @@ -37,11 +38,10 @@ info "downloading some dependencies." download_if_not_exist "$CACHEDIR/functions.sh" "https://raw.githubusercontent.com/AppImage/pkg2appimage/$PKG2APPIMAGE_COMMIT/functions.sh" verify_hash "$CACHEDIR/functions.sh" "8f67711a28635b07ce539a9b083b8c12d5488c00003d6d726c7b134e553220ed" -download_if_not_exist "$CACHEDIR/appimagetool" "https://github.com/AppImage/AppImageKit/releases/download/13/obsolete-appimagetool-x86_64.AppImage" -verify_hash "$CACHEDIR/appimagetool" "df3baf5ca5facbecfc2f3fa6713c29ab9cefa8fd8c1eac5d283b79cab33e4acb" -# TODO migrate to https://github.com/AppImage/appimagetool/releases -# note: we will also have to build AppImage/type2-runtime, as otherwise new appimagetool just downloads "latest" from the internet. -# see https://github.com/AppImage/appimagetool/blob/7cfafc45e5a8e64ad0755870b1001e5d7ffb4e85/README.md#L61 +download_if_not_exist "$CACHEDIR/appimagetool" "https://github.com/AppImage/appimagetool/releases/download/1.9.0/appimagetool-x86_64.AppImage" +verify_hash "$CACHEDIR/appimagetool" "46fdd785094c7f6e545b61afcfb0f3d98d8eab243f644b4b17698c01d06083d1" +# note: desktop-file-utils in the docker image is needed to run desktop-file-validate for appimagetool <= 1.9.0, so it can be removed once +# appimagetool tags a new release (see https://github.com/AppImage/appimagetool/pull/47) download_if_not_exist "$CACHEDIR/Python-$PYTHON_VERSION.tar.xz" "https://www.python.org/ftp/python/$PYTHON_VERSION/Python-$PYTHON_VERSION.tar.xz" verify_hash "$CACHEDIR/Python-$PYTHON_VERSION.tar.xz" "c30bb24b7f1e9a19b11b55a546434f74e739bb4c271a3e3a80ff4380d49f7adb" @@ -264,14 +264,14 @@ info "creating the AppImage." "$CACHEDIR/appimagetool_copy" --appimage-extract # We build a small wrapper for mksquashfs that removes the -mkfs-time option # as it conflicts with SOURCE_DATE_EPOCH. - mv "$BUILDDIR/squashfs-root/usr/lib/appimagekit/mksquashfs" "$BUILDDIR/squashfs-root/usr/lib/appimagekit/mksquashfs_orig" - cat > "$BUILDDIR/squashfs-root/usr/lib/appimagekit/mksquashfs" << EOF + mv "$BUILDDIR/squashfs-root/usr/bin/mksquashfs" "$BUILDDIR/squashfs-root/usr/bin/mksquashfs_orig" + cat > "$BUILDDIR/squashfs-root/usr/bin/mksquashfs" << EOF #!/bin/sh args=\$(echo "\$@" | sed -e 's/-mkfs-time 0//') -"$BUILDDIR/squashfs-root/usr/lib/appimagekit/mksquashfs_orig" \$args +"$BUILDDIR/squashfs-root/usr/bin/mksquashfs_orig" \$args EOF - chmod +x "$BUILDDIR/squashfs-root/usr/lib/appimagekit/mksquashfs" - env VERSION="$VERSION" ARCH=x86_64 ./squashfs-root/AppRun --no-appstream --verbose "$APPDIR" "$APPIMAGE" + chmod +x "$BUILDDIR/squashfs-root/usr/bin/mksquashfs" + env VERSION="$VERSION" ARCH=x86_64 ./squashfs-root/AppRun --runtime-file "$TYPE2_RUNTIME_REPO_DIR/runtime-x86_64" --no-appstream --verbose "$APPDIR" "$APPIMAGE" ) diff --git a/contrib/build-linux/appimage/patches/type2-runtime-reproducible-build.patch b/contrib/build-linux/appimage/patches/type2-runtime-reproducible-build.patch new file mode 100644 index 000000000..0895d2a29 --- /dev/null +++ b/contrib/build-linux/appimage/patches/type2-runtime-reproducible-build.patch @@ -0,0 +1,149 @@ +From 0c54d91dd1d33235ae97566600e692edfb613642 Mon Sep 17 00:00:00 2001 +From: f321x +Date: Thu, 10 Jul 2025 17:45:20 +0200 +Subject: [PATCH] make docker build reproducible + +attempts to make the docker build more reproducible by: +* pinning the docker image (alpine:3.21) to a hash +* version pinning the apk packages in the dockerfile +* setting TZ, LC_ALL and SOURCE_DATE_EPOCH in the container +* only building single threaded (make -j1) +* use a fixed build directory in `build-runtime.sh` instead of mktemp +* prevent linker from adding build id (-Wl,--build-id=none) +* replace absolute build paths in debug info with relative paths + (-fdebug-prefix-map=$(PWD)=.) +* replace absolute paths in all compiler output with relative paths + (-ffile-prefix-map=$(PWD)=.) +* stop adding gnu-debuglink to runtime binary +--- + scripts/build-runtime.sh | 18 +++++++++++---- + scripts/common/install-dependencies.sh | 2 +- + scripts/docker/Dockerfile | 32 ++++++++++++++++++++++---- + src/runtime/Makefile | 2 +- + 4 files changed, 42 insertions(+), 12 deletions(-) + +diff --git a/scripts/build-runtime.sh b/scripts/build-runtime.sh +index 3ce3b91..e11f082 100755 +--- a/scripts/build-runtime.sh ++++ b/scripts/build-runtime.sh +@@ -8,8 +8,10 @@ set -euo pipefail + out_dir="$(readlink -f "$(pwd)")"/out + mkdir -p "$out_dir" + +-# we create a temporary build directory +-build_dir="$(mktemp -d -t type2-runtime-build-XXXXXX)" ++# we create a temporary build directory with a fixed name for reproducibility ++build_dir="$(readlink -f "$(pwd)")"/build-runtime-temp ++rm -rf "$build_dir" ++mkdir -p "$build_dir" + + # since the plain ol' Makefile doesn't support out-of-source builds at all, we need to copy all the files + cp -R src "$build_dir"/ +@@ -17,13 +19,14 @@ cp -R src "$build_dir"/ + pushd "$build_dir" + + pushd src/runtime/ +-make -j"$(nproc)" runtime ++make -j1 runtime + + file runtime + + objcopy --only-keep-debug runtime runtime.debug + +-strip --strip-debug --strip-unneeded runtime ++# strip --strip-debug --strip-unneeded runtime ++strip --strip-all runtime + + ls -lh runtime runtime.debug + +@@ -50,7 +53,7 @@ fi + mv runtime runtime-"$architecture" + mv runtime.debug runtime-"$architecture".debug + +-objcopy --add-gnu-debuglink runtime-"$architecture".debug runtime-"$architecture" ++# objcopy --add-gnu-debuglink runtime-"$architecture".debug runtime-"$architecture" + + # "classic" magic bytes which cannot be embedded with compiler magic, always do AFTER strip + # needs to be done after calls to objcopy, strip etc. +@@ -61,3 +64,8 @@ cp runtime-"$architecture" "$out_dir"/ + cp runtime-"$architecture".debug "$out_dir"/ + + ls -al "$out_dir" ++ ++# cleanup ++popd # return to build_dir ++popd # return to original working directory ++rm -rf "$build_dir" +diff --git a/scripts/common/install-dependencies.sh b/scripts/common/install-dependencies.sh +index 0e21cdb..5237079 100755 +--- a/scripts/common/install-dependencies.sh ++++ b/scripts/common/install-dependencies.sh +@@ -39,7 +39,7 @@ tar xf 0.5.2.tar.gz + pushd squashfuse-*/ + ./autogen.sh + ./configure LDFLAGS="-static" +-make -j"$(nproc)" ++make -j1 + make install + /usr/bin/install -c -m 644 ./*.h '/usr/local/include/squashfuse' + popd +diff --git a/scripts/docker/Dockerfile b/scripts/docker/Dockerfile +index 07b6533..fba9c6e 100644 +--- a/scripts/docker/Dockerfile ++++ b/scripts/docker/Dockerfile +@@ -1,13 +1,35 @@ +-FROM alpine:3.21 ++FROM alpine:3.21@sha256:a8560b36e8b8210634f77d9f7f9efd7ffa463e380b75e2e74aff4511df3ef88c + + # includes dependencies from https://git.alpinelinux.org/aports/tree/main/fuse3/APKBUILD + RUN apk add --no-cache \ +- bash alpine-sdk util-linux strace file autoconf automake libtool xz \ +- eudev-dev gettext-dev linux-headers meson \ +- zstd-dev zstd-static zlib-dev zlib-static clang musl-dev mimalloc-dev ++ bash=5.2.37-r0 \ ++ alpine-sdk=1.1-r0 \ ++ util-linux=2.40.4-r1 \ ++ strace=6.12-r0 \ ++ file=5.46-r2 \ ++ autoconf=2.72-r0 \ ++ automake=1.17-r0 \ ++ libtool=2.4.7-r3 \ ++ xz=5.6.3-r1 \ ++ eudev-dev=3.2.14-r5 \ ++ gettext-dev=0.22.5-r0 \ ++ linux-headers=6.6-r1 \ ++ meson=1.6.1-r0 \ ++ zstd-dev=1.5.6-r2 \ ++ zstd-static=1.5.6-r2 \ ++ zlib-dev=1.3.1-r2 \ ++ zlib-static=1.3.1-r2 \ ++ clang19=19.1.4-r0 \ ++ musl-dev=1.2.5-r9 \ ++ mimalloc2-dev=2.1.7-r0 + + COPY scripts/common/install-dependencies.sh /tmp/scripts/common/install-dependencies.sh + COPY patches/ /tmp/patches/ + ++# Set environment variables for reproducible build ++ENV SOURCE_DATE_EPOCH=1640995200 ++ENV TZ=UTC ++ENV LC_ALL=C ++ + WORKDIR /tmp +-RUN bash scripts/common/install-dependencies.sh ++RUN bash scripts/common/install-dependencies.sh +\ No newline at end of file +diff --git a/src/runtime/Makefile b/src/runtime/Makefile +index 9fd4165..3a3cbaa 100644 +--- a/src/runtime/Makefile ++++ b/src/runtime/Makefile +@@ -1,6 +1,6 @@ + GIT_COMMIT := $(shell cat version) + CC = clang +-CFLAGS = -std=gnu99 -Os -D_FILE_OFFSET_BITS=64 -DGIT_COMMIT=\"$(GIT_COMMIT)\" -T data_sections.ld -ffunction-sections -fdata-sections -Wl,--gc-sections -static -Wall -Werror -static-pie ++CFLAGS = -std=gnu99 -Os -D_FILE_OFFSET_BITS=64 -DGIT_COMMIT=\"$(GIT_COMMIT)\" -T data_sections.ld -ffunction-sections -fdata-sections -Wl,--gc-sections -Wl,--build-id=none -static -Wall -Werror -static-pie -fdebug-prefix-map=$(PWD)=. -ffile-prefix-map=$(PWD)=. + LIBS = -lsquashfuse -lsquashfuse_ll -lzstd -lz -lfuse3 -lmimalloc + + all: runtime +-- +2.50.0