Files
nix-files/pkgs/by-name/signal-desktop-from-src/package.nix
Colin 713a85b3d3 fix bad --add-flags invocations
makeBinaryWrapper was updated some months ago to apply shell-style parsing to --add-flags; thats not what you want most of the time
2025-07-28 07:18:32 +00:00

469 lines
20 KiB
Nix
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# arch package:
# - <https://gitlab.archlinux.org/archlinux/packaging/packages/signal-desktop/-/blob/main/PKGBUILD?ref_type=heads>
# - builds with `yarn generate; yarn build`
# alpine package:
# - <https://gitlab.alpinelinux.org/alpine/aports/-/blob/master/testing/signal-desktop/APKBUILD?ref_type=heads>
# - more involved build process, apparently to reuse deps shared with other parts of the OS:
# - manually build signal's webrtc using ninja
# - manually build signal's ringrtc using yarn
# - manually build libsignal with yarn, cargo and cbindgen
# - yarn build:acknowledgments
# - yarn patch-package
# - npm rebuild esbuild # apparently esbuild is to be used later in the build process
# - yarn build:dev
# - yarn install
# - then it `mv`s and `patch`s a bunch of stuff
# - tasje pack
#
#
### TODO: build more components from source:
#
# - the dependencies which alpine builds live over here:
# - <https://github.com/signalapp/libsignal/archive/refs/tags/v$_libsignalver/libsignal-$_libsignalver.tar.gz>
# - <https://github.com/signalapp/ringrtc/archive/refs/tags/v$_ringrtcver/ringrtc-$_ringrtcver.tar.gz>
# - <https://s3.sakamoto.pl/lnl-aports-snapshots/webrtc-$_webrtcver.tar.zst>
# - <https://github.com/signalapp/Signal-FTS5-Extension/archive/refs/tags/v$_stokenizerver/stokenizer-$_stokenizerver.tar.gz>
#
# - nixpkgs packages libsignal-protocol-c, but i think not the actual `libsignal`
# - <https://github.com/signalapp/libsignal-protocol-c>
# - flare-signal references <https://github.com/signalapp/libsignal>
# - same with gurk-rs
# - neither of these do anything special with it.
# - however, they do refer to it as `name = "libsignal-protocol"`, so maybe libsignal-protocol-c is quite related?
# - and also `name = "poksho"`, signal-crypto, zkcredential, zkgroup, etc
#
#### better-sqlite
# try:
# - npm rebuild @signalapp/better-sqlite3 --offline
# - npm rebuild @signalapp/better-sqlite3 --offline --nodedir="${nodeSources}" --build-from-source
# - patchelf --add-needed ${icu}/lib/libicutu.so node_modules/@signalapp/better-sqlite3/build/Release/better_sqlite3.node
# or:
# - npm rebuild \
# @signalapp/better-sqlite3 \
# sharp spellchecker websocket \
# utf-8-validate bufferutil fs-xattr \
# --offline --nodedir="${nodeSources}" --build-from-source
# or:
# - npm_config_nodedir="${nodeSources}" npm_config_node_gyp="${buildPackages.nodejs}/lib/node_modules/npm/node_modules/node-gyp/bin/node-gyp.js" npm rebuild --verbose --sqlite=${sqlite.dev} @signalapp/better-sqlite3
#
# or, with nodeSources = srcOnly nodejs';
# - pushd node_modules/@signalapp/better-sqlite3
# - patch -p1 < ${bettersqlitePatch}
# - npm run build-release --offline
# - npm run build-release --offline --nodedir="${nodeSources}"
# - find build -type f -exec \
# remove-references-to \
# -t "${nodeSources}" {} \;
# - popd
#
#### ringrtc:
# - readme says that it's going to download webrtc components -- that's probably why Alpine builds that first
# - "To build the Node.js module suitable for including in an Electron app, run:"
# make electron PLATFORM=<platform> NODEJS_ARCH=<arch>
# - compiling x64 -> arm64 is supported, but arm64 -> arm64 is *not*
# - the referenced file is a 13 MB ELF: /nix/store/7hb6840x2f10zpm3d5ccj3kr3vaf93n5-signal-desktop-6.36.0/lib/Signal/resources/app.asar.unpacked/node_modules/@signalapp/ringrtc/build/linux/libringrtc-x64.node
# - not in crates.io
#
#### webrtc:
# - nothing much in nixpkgs cites "signalapp", so i probably have to do this manually
# - nothing named `webrtc` linked from the signal-desktop appimage
# - it's like 900MB?? https://s3.sakamoto.pl/lnl-aports-snapshots/webrtc-5845h.tar.zst
# - not in crates.io
#
#
# HOW TO UPDATE
# - `./scripts/update sane.signal-desktop-from-src`
# if using nixpkgs' prebuilds, that's all! else:
# - check signal-desktop's package.json for new ringrtc <repo:signalapp/Signal-Desktop:package.json>
# - if sqlcipher fails then update sqlcipherTarball url/hash (rare)
# build errors which can be safely ignored:
# - "Error: Could not detect abi for version 30.1.1 and runtime electron. Updating "node-abi" might help solve this issue if it is a new release of electron"
# - <https://github.com/signalapp/Signal-Desktop/pull/6889>
{
alsa-lib,
asar,
at-spi2-atk,
at-spi2-core,
atk,
autoPatchelfHook,
cups,
electron_35-bin,
fetchFromGitHub,
fetchurl,
flac,
gdk-pixbuf,
git,
gitUpdater,
gnused,
gtk3,
icu,
lib,
libgbm,
libpulseaudio,
libwebp,
libxslt,
makeShellWrapper,
nix-update-script,
nodejs,
nspr,
nss,
pango,
pkgsBuildHost,
pnpm_10,
python3,
rsync,
signal-desktop-bin,
# sqlite,
# sqlcipher,
stdenv,
wrapGAppsHook,
xdg-utils,
}:
let
ringrtcPrebuild = "${signal-desktop-bin}/lib/signal-desktop/resources/app.asar.unpacked/node_modules/@signalapp/ringrtc";
betterSqlitePrebuild = null;
# betterSqlitePrebuild = "${signal-desktop-bin}/lib/signal-desktop/resources/app.asar.unpacked/node_modules/@signalapp/better-sqlite3";
# ringrtcPrebuild = stdenv.mkDerivation {
# name = "ringrtc-bin";
# src = fetchurl {
# # version is found in signal-desktop's package.json as "@signalapp/ringrtc"
# url = "https://build-artifacts.signal.org/libraries/ringrtc-desktop-build-v2.48.7.tar.gz";
# hash = "sha256-mwCLOIN6frEwVsOCnkAjZAC8YfKK3FZarbZVRTU6SAc=";
# };
# sourceRoot = ".";
# installPhase = ''
# mkdir -p $out
# cp -R build $out/build
# '';
# };
# better-sqlite3 can be built from source, or use the prebuilt version from nixpkgs' signal-desktop.
# just do whatever's easiest (and works) at the time of upgrade; set `null` to use the prebuild
sqlcipherTarball = null;
# sqlcipherTarball = fetchurl {
# # this is a dependency of better-sqlite3.
# # version/url is found in <repo:signalapp/better-sqlite3:deps/download.js>
# # - checkout the better-sqlite3 tag which matches signal-dekstop's package.json "@signalapp/better-sqlite3" key.
# url = let
# BETTER_SQLITE3_VERSION = "9.0.11"; #< from Signal-Desktop/package.json
# HASH = "6253f886c40e49bf892d5cdc92b2eb200b12cd8d80c48ce5b05967cfd01ee8c7";
# SQLCIPHER_VERSION = "4.6.1-signal-patch2";
# EXTENSION_VERSION = "0.2.1-asm2";
# in "https://build-artifacts.signal.org/desktop/sqlcipher-v2-${SQLCIPHER_VERSION}--${EXTENSION_VERSION}-${HASH}.tar.gz";
# hash = "sha256-YlP4hsQOSb+JLVzckrLrIAsSzY2AxIzlsFlnz9Ae6Mc=";
# };
# signal-fts5-extension = callPackage ./fts5-extension { };
# bettersqlitePatch = substituteAll {
# # this patch does more than just use the system sqlcipher.
# # it also tells bettersqlite to link against its needed runtime dependencies,
# # since there's a few statically linked things which aren't called out.
# # it also tells bettersqlite not to download sqlcipher when we `npm rebuild`
# src = ./bettersqlite-use-system-sqlcipher.patch;
# inherit sqlcipher;
# inherit (nodejs') libv8;
# signal_fts5_extension = signal-fts5-extension;
# };
# note that `package.json` locks the electron version, but we seem to not be strictly beholden to that.
# prefer to use the same electron version as everywhere else, and a `-bin` version to avoid 4hr rebuilds.
# the non-bin varieties *seem* to ship the wrong `electron.headers` property.
# - maybe they can work if i manually DL and ship the corresponding headers
electron' = electron_35-bin;
buildNpmArch = if stdenv.buildPlatform.isAarch64 then "arm64" else "x64";
hostNpmArch = if stdenv.hostPlatform.isAarch64 then "arm64" else "x64";
crossNpmArchExt = if buildNpmArch == hostNpmArch then "" else "-${hostNpmArch}";
in
stdenv.mkDerivation (finalAttrs: {
pname = "signal-desktop-from-src";
version = "7.60.0";
src = fetchFromGitHub {
owner = "signalapp";
repo = "Signal-Desktop";
leaveDotGit = true; # signal calculates the release date via `git`
rev = "v${finalAttrs.version}";
# XXX: hash is unstable because of the git pack dir
hash = "sha256-SjRYeJBs36dLxZdhioF8v9w1zOZkjBUnbM+W+/cg26Q=";
};
pnpmDeps = pnpm_10.fetchDeps {
inherit (finalAttrs) pname version src patches;
hash = "sha256-mVC7dOsBcBrOEuz7t4xMv1QX2ZgfA5EjnRYunZEx73E=";
};
patches = [
# ./debug.patch
# fix bug that signal launches in the background on wayland
# - <https://github.com/signalapp/Signal-Desktop/issues/6368>
# - without this, signal can be started with `signal-desktop & ; sleep 5; signal-desktop`
# - the second instance wakes the first one, and then exits
./show-on-launch.patch
# XXX(2024-09-27): not sure what no-mac-screen-share.patch was needed for? build fix? it was introduced by me in 2024-07-xx
# ./no-mac-screen-share.patch
];
postPatch = ''
# unpin nodejs. i should probably *try* to keep these vaguely in sync, but it seems to work decently with these out of sync too (at least, if the major versions match?)
sed -i 's/"node": .*/"node": "*"/' package.json
# don't populate fallback DNS mappings, and don't try to install electron-builder deps during build:
substituteInPlace package.json \
--replace-fail \
'"build:dns-fallback": "node ts/scripts/generate-dns-fallback.js"' \
'"build:dns-fallback": "true"' \
--replace-fail \
'"electron:install-app-deps": "electron-builder install-app-deps"' \
'"electron:install-app-deps": "true"'
# fixes build failure:
# > Fusing electron at /build/source/release/linux-unpacked/signal-desktop inspect-arguments=false
# > EACCES: permission denied, open '/build/source/release/linux-unpacked/signal-desktop' failedTask=build stackTrace=Error: EACCES: permission denied, open '/build/source/release/linux-unpacked/signal-desktop'
# electron "fusing" (electron.flipFuses) seems to be configuring which functionality electron will support at runtime.
# notably: ELECTRON_RUN_AS_NODE, cookie encryption, NODE_OPTIONS env var, --inspect-* CLI args, app.asar validation
# skipping the fuse process seems relatively inconsequential
substituteInPlace ts/scripts/after-pack.ts \
--replace-fail 'await fuseElectron' '//await fuseElectron'
'';
nativeBuildInputs = [
asar # used during fixup
autoPatchelfHook
git # to calculate build date
gnused
makeShellWrapper
nodejs
# nodejs.python
python3
rsync
wrapGAppsHook
pkgsBuildHost.pnpm_10.configHook #< XXX: buildPackages because it doesn't splice right (fixes cross compilation)
];
buildInputs = [
alsa-lib
at-spi2-atk
at-spi2-core
atk
cups
electron'
flac
gdk-pixbuf
gtk3
libpulseaudio
libwebp
libxslt
libgbm
nodejs
nspr
nss
pango
# so that bettersqlite may link against sqlcipher (see patch)
# but i don't know if it actually needs to. just copied this from alpine.
# sqlcipher
];
strictDeps = true;
# disallowedReferences = [ buildPackages.nodejs ]; #< TODO: set when cross compiling
# env.SIGNAL_ENV = "production";
# env.NODE_ENV = "production"; #< XXX setting this causes `node_modules/protobufjs-cli/bin/pbjs` to not be fetched...
# env.ELECTRON_SKIP_BINARY_DOWNLOAD = "1";
# env.npm_config_arch = buildNpmArch;
# env.npm_config_target_arch = hostNpmArch;
dontWrapGApps = true;
# dontStrip = false;
# makeCacheWritable = true; # "Your cache folder contains root-owned files, due to a bug in previous versions of npm which has since been addressed."
# npmRebuildFlags = [
# # "--offline"
# "--ignore-scripts"
# ];
# pnpmRebuildFlags = [
# # "--offline"
# "--ignore-scripts"
# ];
# NIX_DEBUG = 6;
# should really be `postConfigure`, but `pnpmConfigHook` runs _after_ postConfigure
preBuild = ''
# XXX: Signal does not let clients connect if they're running a version that's > 90d old.
# to calculate the build date, it uses SOURCE_DATE_EPOCH (if set), else `git log`.
# nixpkgs sets SOURCE_DATE_EPOCH to 1980/01/01 by default, so unset it so Signal falls back to git date.
# see: Signal-Desktop/ts/scripts/get-expire-time.ts
export SOURCE_DATE_EPOCH=
# apparently electron projects aren't "stock" node.
# so subprojects which want to use node internals (i.e. call C functions provided by node)
# need to build against electron's versions of the node headers, or something.
# without patching this, Signal can build, but will fail with `undefined symbol: ...` errors at runtime.
# see: <https://www.electronjs.org/docs/latest/tutorial/using-native-node-modules>
export npm_config_nodedir=${electron'.headers}
# patchShebangs --build --update node_modules/{bufferutil/node_modules/node-gyp-build/,node-gyp-build,utf-8-validate/node_modules/node-gyp-build}
# patch these out to remove a runtime reference back to the build bash
# (better, perhaps, would be for these build scripts to not be included in the asar...)
substituteInPlace node_modules/dashdash/etc/dashdash.bash_completion.in --replace-fail '#!/bin/bash' '#!/bin/sh'
# substituteInPlace node_modules/pino/inc-version.sh --replace-fail '#!/bin/bash' '#!/bin/sh'
substituteInPlace node_modules/pino/inc-version.sh --replace-fail '#!${stdenv.shell}' '#!/bin/sh'
# provide necessities which were skipped as part of --ignore-scripts
rsync -arv ${ringrtcPrebuild}/ node_modules/@signalapp/ringrtc/
${if sqlcipherTarball != null then ''
# option 1: replace only the sqlcipher plugin with Signal's prebuilt version,
# and build the rest of better-sqlite3 from source
cp ${sqlcipherTarball} node_modules/@signalapp/better-sqlite3/deps/sqlcipher.tar.gz
'' else if betterSqlitePrebuild != null then ''
# option 2: replace the entire better-sqlite3 library with the prebuilt version from nixpkgs' signal-desktop
rsync -arv ${betterSqlitePrebuild}/ node_modules/@signalapp/better-sqlite3/
# patch so signal doesn't try to *rebuild* better-sqlite3
substituteInPlace node_modules/@signalapp/better-sqlite3/package.json \
--replace-fail '"download": "node ./deps/download.js"' '"download": "true"' \
--replace-fail '"build-release": "node-gyp rebuild --release"' '"build-release": "true"' \
--replace-fail '"install": "pnpm run download && pnpm run build-release"' '"install": "true"'
'' else
# XXX(2025-03-27): seems that signal can build *and run* without any patching of sqlcipher/better-sqlite now
""
}
# pushd node_modules/@signalapp/better-sqlite3
# # node-gyp isn't consistently linked into better-sqlite's `node_modules` (maybe due to version mismatch with signal-desktop's node-gyp?)
# PATH="$PATH:$(pwd)/../../.bin" pnpm --offline run build-release
# popd
# pushd node_modules/@signalapp/libsignal-client
# npx node-gyp rebuild
# popd
# run signal's own `postinstall`:
# - npm run build:acknowledgments
# - npm exec patch-package
# - npm run electron:install-app-deps
pnpm run postinstall
'';
# excerpts from package.json:
# - "build": "run-s --print-label generate build:esbuild:prod build:release"
# - "generate": "npm-run-all build-protobuf build:esbuild build:dns-fallback build:icu-types build:compact-locales sass get-expire-time copy-components",
# - "build-protobuf": "npm run build-module-protobuf",
# - "build-module-protobuf": "pbjs --target static-module --force-long --no-typeurl --no-verify --no-create --no-convert --wrap commonjs --out ts/protobuf/compiled.js protos/*.proto && pbts --no-comments --out ts/protobuf/compiled.d.ts ts/protobuf/compiled.js",
# - "build:esbuild": "node scripts/esbuild.js",
# - "build:dns-fallback": "node ts/scripts/generate-dns-fallback.js",
# - "build:icu-types": "node ts/scripts/generate-icu-types.js",
# - "build:compact-locales": "node ts/scripts/generate-compact-locales.js",
# - "sass": "sass stylesheets/manifest.scss:stylesheets/manifest.css stylesheets/manifest_bridge.scss:stylesheets/manifest_bridge.css",
# - "get-expire-time": "node ts/scripts/get-expire-time.js",
# - "copy-components": "node ts/scripts/copy.js",
# - "build:esbuild:prod": "node scripts/esbuild.js --prod",
# - "build:release": "cross-env SIGNAL_ENV=production npm run build:electron -- --config.directories.output=release",
# - "build:electron": "electron-builder --config.extraMetadata.environment=$SIGNAL_ENV",
#
# i can't call toplevel `build` because some steps fail (e.g. dns-fallback) and can be skipped instead,
# while other steps fail (electron-builder) and need patching, but npm doesn't plumb the necessary flags through.
# so instead i call each step individually.
buildPhase = ''
runHook preBuild
pnpm run generate
pnpm run build:esbuild:prod --offline --frozen-lockfile
SIGNAL_ENV=production \
pnpm exec electron-builder \
--config.extraMetadata.environment=production \
--config.directories.output=release \
--${hostNpmArch} \
--config.electronDist=${electron'}/libexec/electron \
--config.electronVersion=${electron'.version} \
--dir
runHook postBuild
'';
installPhase = ''
runHook preInstall
# directory structure follows the upstream `signal-desktop` nix package
mkdir -p $out/lib
cp -R release/linux${crossNpmArchExt}-unpacked $out/lib/signal-desktop
# cp -R release/linux-unpacked/resources $out/lib/signal-desktop/resources
# cp -R release/linux-unpacked/locales $out/lib/signal-desktop/locales
mkdir $out/bin
runHook postInstall
'';
preFixup = ''
# fixup the app.asar to:
# - use host nodejs
# - use host libpulse.so
asar extract $out/lib/signal-desktop/resources/app.asar unpacked
rm $out/lib/signal-desktop/resources/app.asar
patchShebangs --host --update unpacked
patchelf --add-needed ${libpulseaudio}/lib/libpulse.so unpacked/node_modules/@signalapp/ringrtc/build/linux/libringrtc-*.node
cp -R unpacked "$asar"
asar pack unpacked $out/lib/signal-desktop/resources/app.asar
# patchShebangs --host --update $out/lib/signal-desktop/resources
# patchelf --add-needed ${libpulseaudio}/lib/libpulse.so $out/lib/signal-desktop/resources/app.asar.unpacked/node_modules/@signalapp/ringrtc/build/linux/libringrtc-*.node
# # XXX: add --ozone-platform-hint=auto to make it so that NIXOS_OZONE_WL isn't *needed*.
# # electron should auto-detect x11 v.s. wayland: launching with `NIXOS_OZONE_WL=1` is an optional way to force it when debugging.
# # xdg-utils: needed for ozone-platform-hint=auto to work
# # else `LaunchProcess: failed to execvp: xdg-settings`
# makeShellWrapper ${lib.getExe electron'} $out/bin/signal-desktop \
# "''${gappsWrapperArgs[@]}" \
# --add-flags $out/lib/signal-desktop/resources/app.asar \
# --suffix PATH : ${lib.makeBinPath [ xdg-utils ]} \
# --add-flags --ozone-platform-hint=auto \
# --add-flags "\''${WAYLAND_DISPLAY:+--ozone-platform=wayland --enable-features=WaylandWindowDecorations}" \
# --inherit-argv0
makeShellWrapper $out/lib/signal-desktop/signal-desktop $out/bin/signal-desktop \
"''${gappsWrapperArgs[@]}" \
--suffix PATH : ${lib.makeBinPath [ xdg-utils ]} \
--add-flag --ozone-platform-hint=auto \
--add-flags "\''${WAYLAND_DISPLAY:+--ozone-platform=wayland --enable-features=WaylandWindowDecorations}" \
--inherit-argv0
'';
outputs = [ "out" "asar" ];
passthru = {
inherit ringrtcPrebuild betterSqlitePrebuild;
# inherit ringrtcPrebuild sqlcipherTarball;
# inherit bettersqlitePatch signal-fts5-extension;
# updateScript = gitUpdater {
# rev-prefix = "v";
# ignoredVersions = "beta";
# };
updateScript = nix-update-script {
# ignore beta versions
extraArgs = [ "--version-regex" "v([0-9.]+)" ];
};
# XXX(2025-07-05): 7.60.0 -> 7.61.0 upgrade disconnects me from Signal's network;
# 7.60.0 > 7.61.0 is also an irreversible database upgrade; to rollback, one must restore the database from a fs snapshot or backup.
updateWithSuper = false;
};
meta = {
description = "Private, simple, and secure messenger";
longDescription = ''
Signal Desktop is an Electron application that links with your
"Signal Android" or "Signal iOS" app.
'';
homepage = "https://signal.org/";
changelog = "https://github.com/signalapp/Signal-Desktop/releases/tag/v${finalAttrs.version}";
license = lib.licenses.agpl3Only;
};
})