From 670c6e54d703266515e5ef3aa0b5735b7a19ae2a Mon Sep 17 00:00:00 2001 From: m3tam3re Date: Sat, 4 Jul 2026 09:16:30 +0200 Subject: [PATCH] refactor: replace n8n package with overlay override --- docs/packages/README.md | 9 +- docs/packages/n8n.md | 26 ++---- flake.nix | 8 +- overlays/mods/default.nix | 58 ++++++++++++- pkgs/default.nix | 1 - pkgs/n8n/default.nix | 163 ------------------------------------- pkgs/n8n/update.sh | 29 ------- tests/n8n-overlay-test.nix | 42 ++++++++++ 8 files changed, 119 insertions(+), 217 deletions(-) delete mode 100644 pkgs/n8n/default.nix delete mode 100755 pkgs/n8n/update.sh create mode 100644 tests/n8n-overlay-test.nix diff --git a/docs/packages/README.md b/docs/packages/README.md index 1a1df1f..9d65654 100644 --- a/docs/packages/README.md +++ b/docs/packages/README.md @@ -21,7 +21,6 @@ These packages are built from source in `pkgs//`: | `launch-webapp` | Launch web applications | Shell | `pkgs/launch-webapp/` | | `mem0` | AI memory assistant with vector storage | Python | `pkgs/mem0/` | | `msty-studio` | Msty Studio application | Python | `pkgs/msty-studio/` | -| `n8n` | Workflow automation tool | Node.js | `pkgs/n8n/` | | `openshell` | AI shell assistant | Go | `pkgs/openshell/` | | `openwork` | Open-source Claude Cowork alternative powered by opencode | AppImage | `pkgs/openwork/` | | `pomodoro-timer` | Pomodoro timer utility | Shell | `pkgs/pomodoro-timer/` | @@ -31,6 +30,14 @@ These packages are built from source in `pkgs//`: | `vibetyper` | Typing practice tool | Python | `pkgs/vibetyper/` | | `zellij-ps` | Project switcher for Zellij | Rust | `pkgs/zellij-ps/` | +### Overlay Modifications + +These entries override upstream nixpkgs packages via `overlays.modifications`. They are also included in `overlays.default`. + +| Package | Source | Modification | Location | +|---------|--------|--------------|----------| +| `n8n` | `nixpkgs#n8n` | Pins a newer release and fetch hashes | `overlays/mods/default.nix` | + ### Pass-Through Packages These packages are imported directly from flake inputs with minor modifications: diff --git a/docs/packages/n8n.md b/docs/packages/n8n.md index d762302..a17a5c1 100644 --- a/docs/packages/n8n.md +++ b/docs/packages/n8n.md @@ -20,32 +20,24 @@ n8n (pronounced "n-eight-n") is a workflow automation tool that helps you connec ## Installation +This repository does not publish a standalone `n8n` package. Instead, it provides an overlay that overrides the upstream nixpkgs `n8n` package with a newer pinned release when needed. + ### Via Overlay ```nix -{pkgs, ...}: { +{inputs, pkgs, ...}: { + nixpkgs.overlays = [ + inputs.m3ta-nixpkgs.overlays.default + # Or, if you only want package overrides: + # inputs.m3ta-nixpkgs.overlays.modifications + ]; + environment.systemPackages = with pkgs; [ n8n ]; } ``` -### Direct Reference - -```nix -{pkgs, ...}: { - environment.systemPackages = with pkgs; [ - inputs.m3ta-nixpkgs.packages.${pkgs.system}.n8n - ]; -} -``` - -### Run Directly - -```bash -nix run git+https://code.m3ta.dev/m3tam3re/nixpkgs#n8n -``` - ## Usage ### Basic Usage diff --git a/flake.nix b/flake.nix index c0d1e9d..b2bb4d0 100644 --- a/flake.nix +++ b/flake.nix @@ -49,12 +49,13 @@ # Overlays - can be imported in your system configuration overlays = { - # Default overlay: adds all custom packages + # Default overlay: adds all custom packages and applies package modifications. default = final: prev: import ./pkgs { pkgs = final; inputs = inputs; - }; + } + // import ./overlays/mods {inherit prev;}; modifications = final: prev: import ./overlays/mods {inherit prev;}; }; @@ -104,6 +105,9 @@ ${pkgs.alejandra}/bin/alejandra --check ${./.} touch $out ''; + n8n-overlay = import ./tests/n8n-overlay-test.nix { + inherit pkgs inputs self; + }; # Lib unit tests lib-agents = import ./tests/lib/agents-test.nix { inherit pkgs; diff --git a/overlays/mods/default.nix b/overlays/mods/default.nix index db7e62b..c1532ce 100644 --- a/overlays/mods/default.nix +++ b/overlays/mods/default.nix @@ -1,7 +1,57 @@ {prev}: { - # Package modifications - # This overlay contains package overrides and modifications + # n8n advances quickly. Keep using nixpkgs' package expression and only override + # the release-specific fetch hashes when we need a newer stable than upstream. + n8n = prev.n8n.overrideAttrs (finalAttrs: previousAttrs: { + version = "2.29.4"; - # Add more modifications here as needed - # example-package = prev.example-package.override { ... }; + src = prev.fetchFromGitHub { + owner = "n8n-io"; + repo = "n8n"; + tag = "n8n@${finalAttrs.version}"; + hash = "sha256-Vc10fXKn21PSlv6Sk+5zyi2efkKup1Vi+9+vyJd2I1k="; + }; + + pnpmDeps = prev.fetchPnpmDeps { + inherit (finalAttrs) pname version src; + pnpm = prev.pnpm_10; + fetcherVersion = 3; + hash = "sha256-2UFKP5bo06afaMPD+dIYQ7O0NiyZai4i8vkidi7HfxE="; + }; + + preBuild = '' + # Force sass-embedded to use our dart-sass instead of bundled binaries. + # The bundled Dart binary can't run in the Nix sandbox (no /lib64/ld-linux-x86-64.so.2). + for dep in node_modules/.pnpm/sass-embedded@*; do + substituteInPlace "$dep/node_modules/sass-embedded/dist/lib/src/compiler-path.js" \ + --replace-fail \ + 'compilerCommand = (() => {' \ + 'compilerCommand = (() => { return ["${prev.lib.getExe prev.dart-sass}"];' + done + ''; + + buildPhase = '' + runHook preBuild + + pushd node_modules/sqlite3 + node-gyp rebuild + popd + + # isolated-vm is a native addon required by n8n-nodes-base (Merge node SQL sandbox) + # since n8n 2.11.x; must be compiled before pnpm build runs generate-metadata + pushd node_modules/isolated-vm + node-gyp rebuild + popd + + # TODO: use deploy after resolved https://github.com/pnpm/pnpm/issues/5315 + pnpm build --filter=n8n + + runHook postBuild + ''; + + meta = + previousAttrs.meta + // { + changelog = "https://github.com/n8n-io/n8n/releases/tag/n8n@${finalAttrs.version}"; + }; + }); } diff --git a/pkgs/default.nix b/pkgs/default.nix index 9d8234c..c99b6e6 100644 --- a/pkgs/default.nix +++ b/pkgs/default.nix @@ -23,7 +23,6 @@ in { launch-webapp = pkgs.callPackage ./launch-webapp {}; mem0 = pkgs.callPackage ./mem0 {}; msty-studio = pkgs.callPackage ./msty-studio {}; - n8n = pkgs.callPackage ./n8n {}; pomodoro-timer = pkgs.callPackage ./pomodoro-timer {}; rofi-project-opener = pkgs.callPackage ./rofi-project-opener {}; stt-ptt = pkgs.callPackage ./stt-ptt {}; diff --git a/pkgs/n8n/default.nix b/pkgs/n8n/default.nix deleted file mode 100644 index 41dcc25..0000000 --- a/pkgs/n8n/default.nix +++ /dev/null @@ -1,163 +0,0 @@ -{ - stdenv, - lib, - nixosTests, - fetchFromGitHub, - nodejs, - pnpm_10, - fetchPnpmDeps, - pnpmConfigHook, - python3, - node-gyp, - cctools, - xcbuild, - dart-sass, - libkrb5, - libmongocrypt, - libpq, - makeWrapper, -}: let - python = python3.withPackages ( - ps: - with ps; [ - websockets - ] - ); -in - stdenv.mkDerivation (finalAttrs: { - pname = "n8n"; - version = "2.29.4"; - - src = fetchFromGitHub { - owner = "n8n-io"; - repo = "n8n"; - tag = "n8n@${finalAttrs.version}"; - hash = "sha256-Vc10fXKn21PSlv6Sk+5zyi2efkKup1Vi+9+vyJd2I1k="; - }; - - pnpmDeps = fetchPnpmDeps { - inherit (finalAttrs) pname version src; - pnpm = pnpm_10; - fetcherVersion = 3; - hash = "sha256-2UFKP5bo06afaMPD+dIYQ7O0NiyZai4i8vkidi7HfxE="; - }; - - nativeBuildInputs = - [ - pnpmConfigHook - pnpm_10 - python3 # required to build sqlite3 bindings - node-gyp # required to build sqlite3 bindings - makeWrapper - ] - ++ lib.optional stdenv.hostPlatform.isDarwin [ - cctools - xcbuild - ]; - - buildInputs = [ - nodejs - libkrb5 - libmongocrypt - libpq - ]; - - preBuild = '' - # Force sass-embedded to use our dart-sass instead of bundled binaries. - # The bundled Dart binary can't run in the Nix sandbox (no /lib64/ld-linux-x86-64.so.2). - for dep in node_modules/.pnpm/sass-embedded@*; do - substituteInPlace "$dep/node_modules/sass-embedded/dist/lib/src/compiler-path.js" \ - --replace-fail \ - 'compilerCommand = (() => {' \ - 'compilerCommand = (() => { return ["${lib.getExe dart-sass}"];' - done - ''; - - buildPhase = '' - runHook preBuild - - pushd node_modules/sqlite3 - node-gyp rebuild - popd - - # isolated-vm is a native addon required by n8n-nodes-base (Merge node SQL sandbox) - # since n8n 2.11.x; must be compiled before pnpm build runs generate-metadata - pushd node_modules/isolated-vm - node-gyp rebuild - popd - - # TODO: use deploy after resolved https://github.com/pnpm/pnpm/issues/5315 - pnpm build --filter=n8n - - runHook postBuild - ''; - - preInstall = '' - echo "Removing non-deterministic and unnecessary files" - - find -type d -name .turbo -exec rm -rf {} + - rm node_modules/.modules.yaml - rm packages/nodes-base/dist/types/nodes.json - - CI=true pnpm --ignore-scripts prune --prod - find -type f \( -name "*.ts" -o -name "*.map" \) -exec rm -rf {} + - rm -rf node_modules/.pnpm/{typescript*,prettier*} - shopt -s globstar - # https://github.com/pnpm/pnpm/issues/3645 - find node_modules packages/**/node_modules -xtype l -delete - - echo "Removed non-deterministic and unnecessary files" - ''; - - installPhase = '' - runHook preInstall - - mkdir -p $out/{bin,lib/n8n} - cp -r {packages,node_modules} $out/lib/n8n - - makeWrapper $out/lib/n8n/packages/cli/bin/n8n $out/bin/n8n \ - --set N8N_RELEASE_TYPE "stable" - - # JavaScript runner - makeWrapper ${nodejs}/bin/node $out/bin/n8n-task-runner \ - --add-flags "$out/lib/n8n/packages/@n8n/task-runner/dist/start.js" - - # Python runner - mkdir -p $out/lib/n8n-task-runner-python - cp -r packages/@n8n/task-runner-python/* $out/lib/n8n-task-runner-python/ - makeWrapper ${python}/bin/python $out/bin/n8n-task-runner-python \ - --add-flags "$out/lib/n8n-task-runner-python/src/main.py" \ - --prefix PYTHONPATH : "$out/lib/n8n-task-runner-python" - - runHook postInstall - ''; - - passthru = { - tests = nixosTests.n8n; - updateScript = ./update.sh; - }; - - # this package has ~80000 files, these take too long and seem to be unnecessary - dontStrip = true; - dontPatchELF = true; - dontRewriteSymlinks = true; - - meta = { - description = "Free and source-available fair-code licensed workflow automation tool"; - longDescription = '' - Free and source-available fair-code licensed workflow automation tool. - Easily automate tasks across different services. - ''; - homepage = "https://n8n.io"; - changelog = "https://github.com/n8n-io/n8n/releases/tag/n8n@${finalAttrs.version}"; - maintainers = with lib.maintainers; [ - gepbird - AdrienLemaire - sweenu - wrbbz - ]; - license = lib.licenses.sustainableUse; - mainProgram = "n8n"; - platforms = lib.platforms.unix; - }; - }) diff --git a/pkgs/n8n/update.sh b/pkgs/n8n/update.sh deleted file mode 100755 index 5e94801..0000000 --- a/pkgs/n8n/update.sh +++ /dev/null @@ -1,29 +0,0 @@ -#!/usr/bin/env nix-shell -#!nix-shell -i bash -p bash curl jq nix-update cacert git nix -set -euo pipefail - -# n8n releases are published with two tags per version: -# - "n8n@X.Y.Z" - the versioned tag -# - "stable" - always points to the latest stable version -# -# We query the "stable" tag and extract the version from its target commitish (e.g., "release/2.18.5"). -# This ensures we always get the actual latest stable version, not the most recently created tag. - -set -euo pipefail - -# Get the directory where this script lives (should be pkgs/n8n/) -script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" - -# Get the nixpkgs root (parent of pkgs/) -nixpkgs_root="$(cd "$script_dir/../.." && pwd)" - -cd "$nixpkgs_root" - -# Query the "stable" tag and extract version from target_commitish (e.g., "release/2.18.5") -new_version=$(curl -s "https://api.github.com/repos/n8n-io/n8n/releases/tags/stable" | jq --raw-output '.target_commitish | ltrimstr("release/")') - -echo "Latest stable version: n8n@${new_version}" -echo "Running from: $(pwd)" - -# Use --flake --system to properly evaluate the flake-based package -nix-update --flake --system x86_64-linux n8n --version "$new_version" diff --git a/tests/n8n-overlay-test.nix b/tests/n8n-overlay-test.nix new file mode 100644 index 0000000..c5e3c22 --- /dev/null +++ b/tests/n8n-overlay-test.nix @@ -0,0 +1,42 @@ +{ + pkgs, + inputs, + self, +}: let + localPackages = import ../pkgs {inherit pkgs inputs;}; + mkPkgsWithOverlay = overlay: + import inputs.nixpkgs { + system = pkgs.stdenv.hostPlatform.system; + config.allowUnfree = true; + overlays = [overlay]; + }; + + modifiedPkgs = mkPkgsWithOverlay self.overlays.modifications; + defaultPkgs = mkPkgsWithOverlay self.overlays.default; + + expectedVersion = "2.29.4"; + expectedSrcHash = "sha256-Vc10fXKn21PSlv6Sk+5zyi2efkKup1Vi+9+vyJd2I1k="; + expectedPnpmHash = "sha256-2UFKP5bo06afaMPD+dIYQ7O0NiyZai4i8vkidi7HfxE="; + expectedChangelog = "https://github.com/n8n-io/n8n/releases/tag/n8n@${expectedVersion}"; + + checkN8nOverride = overlayName: package: + if package.version != expectedVersion + then throw "${overlayName} n8n version is ${package.version}, expected ${expectedVersion}" + else if package.src.outputHash != expectedSrcHash + then throw "${overlayName} n8n src hash is ${package.src.outputHash}, expected ${expectedSrcHash}" + else if package.pnpmDeps.outputHash != expectedPnpmHash + then throw "${overlayName} n8n pnpmDeps hash is ${package.pnpmDeps.outputHash}, expected ${expectedPnpmHash}" + else if package.meta.changelog != expectedChangelog + then throw "${overlayName} n8n changelog is ${package.meta.changelog}, expected ${expectedChangelog}" + else "ok"; +in + pkgs.runCommand "n8n-overlay-test" { + localN8nAbsent = + if localPackages ? n8n + then throw "pkgs/default.nix must not export a local n8n package" + else "ok"; + modificationsOverlayN8n = checkN8nOverride "overlays.modifications" modifiedPkgs.n8n; + defaultOverlayN8n = checkN8nOverride "overlays.default" defaultPkgs.n8n; + } '' + touch $out + ''