Issue with yarn/fetchYarnDeps

Trying to package a yarn project and I’m using fetchYarnDeps to prefetch the dependencies from the yarn.lock file. However building the package fails with an error about yarn being unable to fetch a package nop when running in offline mode even though the exact package exists in the offline cache. Running yarn locally with an offline cache set manually I get the same error for a different package pretter which also exists in the cache :thinking:

{ fetchFromGitHub
, fetchYarnDeps
, electron
, gcc
, gnumake
, mkYarnPackage
, python
}:

mkYarnPackage rec {
  pname = "signal-desktop";
  version = "7.5.1";

  src = fetchFromGitHub {
    owner = "signalapp";
    repo = "Signal-Desktop";
    rev = "v${version}";
    hash = "sha256-++XRYI36LbFDGuDJDU2qpy2NG5iOGyFxhy/gRxfHXbI=";
  };

  yarnLock = ./yarn.lock;

  offlineCache = fetchYarnDeps {
    yarnLock = ./yarn.lock;
    hash = "sha256-FoHZgmnZ4rfDRr/hmfoDT66+wr5uRlOAwudAp8/T6nM=";
  };
}
$ nix build ../nixpkgs#signal-desktop
error: builder for '/nix/store/q3vyq508hc4nn6sw8303xhilhl7bxnlj-signal-desktop-modules-7.5.1.drv' failed with exit code 1;
       last 10 log lines:
       > Running phase: configurePhase
       > Running phase: buildPhase
       > yarn config v1.22.22
       > success Set "yarn-offline-mirror" to "/nix/store/g13wp66qp0mzw1ngz3li98xhs26kanvn-offline".
       > Done in 0.01s.
       > yarn install v1.22.22
       > [1/4] Resolving packages...
       > error Can't make a request in offline mode ("https://registry.yarnpkg.com/nop/-/nop-1.0.0.tgz")
       > info Visit https://yarnpkg.com/en/docs/cli/install for documentation about this command.
       > 
       For full logs, run 'nix log /nix/store/q3vyq508hc4nn6sw8303xhilhl7bxnlj-signal-desktop-modules-7.5.1.drv'.
error: 1 dependencies of derivation '/nix/store/irapbr670xdrmf18npj4sgyxw4hrqp8l-signal-desktop-7.5.1.drv' failed to build

$ ls /nix/store/g13wp66qp0mzw1ngz3li98xhs26kanvn-offline/nop___nop_1.0.0.tgz
/nix/store/g13wp66qp0mzw1ngz3li98xhs26kanvn-offline/nop___nop_1.0.0.tgz

I’ve always avoided yarn2nix-moretea (the package that provides mkYarnPackage), and resorted to doing the logic manually. (mkYarnPackage requires package.json to also be vendored to avoid IFD, which is bad IMHO)

Here’s what worked for me, atleast up to the point that it gets past the yarn install call

{
  lib,
  stdenv,
  fetchFromGitHub,
  fixup-yarn-lock,
  yarn,
  nodejs,
  fetchYarnDeps,
}:

stdenv.mkDerivation (finalAttrs: {
  pname = "signal-desktop";
  version = "7.5.1";

  src = fetchFromGitHub {
    owner = "signalapp";
    repo = "Signal-Desktop";
    rev = "v${finalAttrs.version}";
    hash = "sha256-++XRYI36LbFDGuDJDU2qpy2NG5iOGyFxhy/gRxfHXbI=";
  };

  postPatch = ''
    substituteInPlace package.json \
        --replace-fail '"node": "20.9.0"' ""
  '';

  offlineCache = fetchYarnDeps {
    yarnLock = "${finalAttrs.src}/yarn.lock";
    hash = "sha256-15Z4MyQk8ZeP7oZ1GWoHFhSGsW222t98O7AftcnexSA=";
  };

  nativeBuildInputs = [
    yarn
    fixup-yarn-lock
    nodejs
  ];

  configurePhase = ''
    runHook preConfigure

    export HOME=$(mktemp -d)
    yarn config --offline set yarn-offline-mirror ${finalAttrs.offlineCache}
    fixup-yarn-lock yarn.lock
    yarn install --offline --frozen-lockfile --ignore-platform --ignore-scripts --no-progress --non-interactive
    patchShebangs node_modules/

    runHook postConfigure
  '';
})
2 Likes

By the way, I also saw that there were other yarn.lock files in the project, so if you also need those sub-packages, you would have also needed to leave mkYarnPackage, because it only supports 1 lockfile IIRC.

I’ve recently fixed up a package that has many lockfiles, so here it is as an example: nixpkgs/pkgs/development/tools/redisinsight/default.nix at e9be42459999a253a9f92559b1f5b72e1b44c13d · NixOS/nixpkgs · GitHub

1 Like

Thanks for the pointers. I’ve made some progress on this, but now I’m running into an odd error in the buildPhase about permissions when copying electron to the build destination. I’ve included the current state of my package and build logs here.

{ fetchFromGitHub
, fetchYarnDeps
, electron
, gcc
, gnumake
, mkYarnPackage
, fixup-yarn-lock
, yarn
, nodejs_21
, python3
, lib
, stdenv
}:

stdenv.mkDerivation (finalAttrs: {
  pname = "signal-desktop";
  version = "7.5.1";

  src = fetchFromGitHub {
      owner = "signalapp";
      repo = "Signal-Desktop";
      rev = "v${finalAttrs.version}";
      hash = "sha256-++XRYI36LbFDGuDJDU2qpy2NG5iOGyFxhy/gRxfHXbI=";
    };

  offlineCache = fetchYarnDeps {
    yarnLock = "${finalAttrs.src}/yarn.lock";
    hash = "sha256-15Z4MyQk8ZeP7oZ1GWoHFhSGsW222t98O7AftcnexSA=";
  };

  dangerOfflineCache = fetchYarnDeps {
    yarnLock = "${finalAttrs.src}/danger/yarn.lock";
    hash = "sha256-CnTZwi93xQxYCviFEnkvtVud6bj8D3wOsjafnFue0ag=";
  };

  scOfflineCache = fetchYarnDeps {
    yarnLock = "${finalAttrs.src}/sticker-creator/yarn.lock";
    hash = "sha256-K+8yLBrz1ianDopa5Aztg21udrHGqLB/aGMFx+tNzNw=";
  };

  nativeBuildInputs = [
    fixup-yarn-lock
    gcc
    gnumake
    nodejs_21
    python3
    yarn
  ];

  patches = [ ./remove-stuff.patch ];

  postPatch = ''
    substituteInPlace package.json \
        --replace-fail '"node": "20.9.0"' ""
  '';


  ELECTRON_SKIP_BINARY_DOWNLOAD = "1";

  configurePhase = ''
    runHook preConfigure

    export HOME=$(mktemp -d)

    configureDependencies () {
      yarn config --offline set yarn-offline-mirror $1
      fixup-yarn-lock "$2/yarn.lock"
      yarn install --offline --cwd "$2" --frozen-lockfile --ignore-platform --ignore-scripts --no-progress --non-interactive

      patchShebangs "$2/node_modules/"
    }

    configureDependencies ${finalAttrs.offlineCache} "."
    configureDependencies ${finalAttrs.dangerOfflineCache} "./danger"
    configureDependencies ${finalAttrs.scOfflineCache} "./sticker-creator"

    runHook postConfigure
  '';

  buildPhase = ''
    runHook preBuild

    yarn --offline run generate
    yarn --offline run build:esbuild:prod
    yarn --offline run build:release \
      --dir \
      --config.npmRebuild=false \
      --config.electronDist=${electron}/libexec/electron \
      --config.electronVersion=${electron.version}

    runHook postBuild
  '';

  installPhase = ''
    # Placeholder code for when buildPhase works

    mkdir $out
    cp -R release/. $out
  '';
})

You could probably just copy the electron libexec directory to somewhere into the build environment with cp -r --no-preserve=all, however I think that might be unnecessary here. Looks like this is using Electron Fuses, which modifies the electron binary to disable some unneeded features. However, we usually just discard the copied electron directory anyway in favor of the one provided by nixpkgs, so I think you should just remove this line from the source and it should hopefully be fine.

(by the way, I couldn’t build it because of the missing patch file)

oh right, here is the patch file:

From d5da67ff3f24bb506069f65b3e7b32c2f9475669 Mon Sep 17 00:00:00 2001
From: skykanin <3789764+skykanin@users.noreply.github.com>
Date: Sun, 5 May 2024 21:32:33 +0200
Subject: [PATCH] remove-stuff

---
 package.json | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/package.json b/package.json
index 20794b95a..ff9d6b56f 100644
--- a/package.json
+++ b/package.json
@@ -19,7 +19,7 @@
     "postinstall": "yarn build:acknowledgments && patch-package && yarn electron:install-app-deps",
     "postuninstall": "yarn build:acknowledgments",
     "start": "electron .",
-    "generate": "npm-run-all build-protobuf build:esbuild build:dns-fallback build:icu-types build:compact-locales sass get-expire-time copy-components",
+    "generate": "npm-run-all build-protobuf build:esbuild build:icu-types build:compact-locales sass get-expire-time copy-components",
     "build-release": "yarn run build",
     "sign-release": "node ts/updater/generateSignature.js",
     "notarize": "echo 'No longer necessary'",
@@ -377,7 +377,6 @@
           "minOSVersion": "19.0.0"
         }
       },
-      "sign": "./ts/scripts/sign-macos.js",
       "singleArchFiles": "node_modules/@signalapp/{libsignal-client/prebuilds/**,ringrtc/build/**}",
       "target": [
         {
@@ -405,7 +404,6 @@
       "signingHashAlgorithms": [
         "sha256"
       ],
-      "sign": "./ts/scripts/sign-windows.js",
       "publisherName": "Signal Messenger, LLC",
       "icon": "build/icons/win/icon.ico",
       "publish": [
-- 
2.44.0

I was getting errors about unexpected config field sign which is why I patched those out, but I don’t understand why because sign is a valid config field for electron-build.

When trying to run the app I get an error about a missing library libringrtc. According to the package.json ringrtc is a dependency and should be part of the node modules in the app.asar archive however when I run this build it’s missing, only libsignal-client is present.

{ fetchFromGitHub
, fetchYarnDeps
, electron
, gcc
, gnumake
, makeWrapper
, fixup-yarn-lock
, yarn
, nodejs
, python3
, lib
, stdenv
, libpulseaudio
, patchelf
, autoPatchelfHook
}:

let
  arch = if stdenv.targetPlatform.isAarch64 then "arm64" else "x64";
in stdenv.mkDerivation (finalAttrs: {
  pname = "signal-desktop";
  version = "7.5.1";

  src = fetchFromGitHub {
      owner = "signalapp";
      repo = "Signal-Desktop";
      rev = "v${finalAttrs.version}";
      hash = "sha256-++XRYI36LbFDGuDJDU2qpy2NG5iOGyFxhy/gRxfHXbI=";
    };

  offlineCache = fetchYarnDeps {
    yarnLock = "${finalAttrs.src}/yarn.lock";
    hash = "sha256-15Z4MyQk8ZeP7oZ1GWoHFhSGsW222t98O7AftcnexSA=";
  };

  dangerOfflineCache = fetchYarnDeps {
    yarnLock = "${finalAttrs.src}/danger/yarn.lock";
    hash = "sha256-CnTZwi93xQxYCviFEnkvtVud6bj8D3wOsjafnFue0ag=";
  };

  scOfflineCache = fetchYarnDeps {
    yarnLock = "${finalAttrs.src}/sticker-creator/yarn.lock";
    hash = "sha256-K+8yLBrz1ianDopa5Aztg21udrHGqLB/aGMFx+tNzNw=";
  };

  nativeBuildInputs = [
    fixup-yarn-lock
    gcc
    gnumake
    makeWrapper
    nodejs
    python3
    yarn
  ];

  patches = [
    ./remove-stuff.patch
    ./remove-electron-fuse.patch
  ];

  postPatch = ''
    substituteInPlace package.json \
      --replace-fail '"node": "20.9.0"' ""
  '';

  ELECTRON_SKIP_BINARY_DOWNLOAD = "1";

  configurePhase = ''
    runHook preConfigure

    export HOME=$(mktemp -d)

    configureDependencies () {
      yarn config --offline set yarn-offline-mirror $1
      fixup-yarn-lock "$2/yarn.lock"
      yarn install --offline --cwd "$2" --frozen-lockfile --ignore-platform --ignore-scripts --no-progress --non-interactive

      patchShebangs "$2/node_modules/"
    }

    configureDependencies ${finalAttrs.offlineCache} "."
    configureDependencies ${finalAttrs.dangerOfflineCache} "./danger"
    configureDependencies ${finalAttrs.scOfflineCache} "./sticker-creator"

    runHook postConfigure
  '';

  buildPhase = ''
    runHook preBuild

    yarn --offline run generate
    yarn --offline run build:esbuild:prod
    yarn --offline run build:release \
      --dir \
      --config.npmRebuild=false \
      --config.electronDist=${electron}/libexec/electron \
      --config.electronVersion=${electron.version}

    runHook postBuild
  '';

  installPhase = ''
    runHook preInstall

    set -xe

    mkdir -p "$out/share/${finalAttrs.pname}/app"

    ls -ln release/*-unpacked/

    # This should work, but doesn't
    # cp -r release/*-unpacked/{locales, resources{,.pak}} "$out/share/${finalAttrs.pname}/app"

    cp -r release/*-unpacked/resources "$out/share/${finalAttrs.pname}/app"

    install -Dm 666 ${libpulseaudio}/lib/libpulse.so -t $out/share/${finalAttrs.pname}/build/linux

    mv $out/share/${finalAttrs.pname}/build/linux/libpulse.so $out/share/${finalAttrs.pname}/build/linux/libringrtc-${arch}.node

    install -Dm 444 release/*-unpacked/locales/* -t "$out/share/${finalAttrs.pname}/app/locales/*"
    install -Dm 444 release/*-unpacked/resources.pak -t "$out/share/${finalAttrs.pname}/app"
    install -Dm 444 build/icons/png/* -t "$out/share/icons/*"

    makeWrapper '${lib.getExe electron}' "$out/bin/${finalAttrs.pname}" \
      --add-flags "$out/share/${finalAttrs.pname}/app/resources/app.asar" \
      --add-flags "\''${NIXOS_OZONE_WL:+\''${WAYLAND_DISPLAY:+--ozone-platform-hint=auto --enable-features=WaylandWindowDecorations}}" \
      --set-default ELECTRON_FORCE_IS_PACKAGED 1 \
      --inherit-argv0

    # libringrtc needs to be patched to use a nix version of the library
    # however it seems the node module ringrtc is missing from the app.asar archive.
    #
    # patchelf --add-needed ${libpulseaudio}/lib/libpulse.so "$out/share/${finalAttrs.pname}/app/resources/app.asar.unpacked/node_modules/@signalapp/ringrtc/build/linux/libringrtc-${arch}.node"

    runHook postInstall
  '';
})
From 46327f55203ca8fcad68ac15f0bfc6b7a065c8f9 Mon Sep 17 00:00:00 2001
From: skykanin <3789764+skykanin@users.noreply.github.com>
Date: Sun, 5 May 2024 23:50:47 +0200
Subject: [PATCH] remove electron fuse

---
 ts/scripts/after-pack.ts | 1 -
 1 file changed, 1 deletion(-)

diff --git a/ts/scripts/after-pack.ts b/ts/scripts/after-pack.ts
index 66befe480..a8e3f60ca 100644
--- a/ts/scripts/after-pack.ts
+++ b/ts/scripts/after-pack.ts
@@ -8,6 +8,5 @@ import { afterPack as pruneMacOSRelease } from './prune-macos-release';
 
 export async function afterPack(context: AfterPackContext): Promise<void> {
   await pruneMacOSRelease(context);
-  await fuseElectron(context);
   await copyPacks(context);
 }
-- 
2.44.0