mirror of
https://github.com/zadam/trilium.git
synced 2026-09-12 19:50:20 +05:00
431 lines
17 KiB
Nix
431 lines
17 KiB
Nix
{
|
|
description = "Trilium Notes (experimental flake)";
|
|
|
|
inputs = {
|
|
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
|
|
flake-utils.url = "github:numtide/flake-utils";
|
|
pnpm2nix = {
|
|
url = "github:FliegendeWurst/pnpm2nix-nzbr/main";
|
|
inputs = {
|
|
flake-utils.follows = "flake-utils";
|
|
nixpkgs.follows = "nixpkgs";
|
|
};
|
|
};
|
|
};
|
|
|
|
outputs =
|
|
{
|
|
self,
|
|
nixpkgs,
|
|
flake-utils,
|
|
pnpm2nix,
|
|
}:
|
|
flake-utils.lib.eachDefaultSystem (
|
|
system:
|
|
let
|
|
pkgs = import nixpkgs { inherit system; };
|
|
|
|
electronVersion = packageJsonDesktop.devDependencies.electron;
|
|
# `or null` because a major bump lands in apps/desktop/package.json long before
|
|
# nixpkgs has the matching electron_<major> attribute; without it the flake dies
|
|
# with an attribute error instead of falling through to the pinned binary below.
|
|
electronFromNixpkgs = pkgs."electron_${lib.versions.major electronVersion}" or null;
|
|
|
|
# nixpkgs lags behind the Electron version pinned in apps/desktop/package.json —
|
|
# often by a whole major — and its source build cannot be bumped without
|
|
# upstream's Chromium dependency hashes. Build the exact pinned version from
|
|
# Electron's official binary release instead, reusing the nixpkgs builder.
|
|
#
|
|
# Don't refresh these by hand — `pnpm chore:update-flake-electron` rewrites both
|
|
# bindings from the release's SHASUMS256.txt, and the update-nix-flake workflow
|
|
# opens a PR whenever apps/desktop/package.json moves ahead of the pin.
|
|
pinnedElectronVersion = "44.2.0";
|
|
pinnedElectronHashes = {
|
|
x86_64-linux = "574f7d8cd2a82d77812849729a282b86639b050de120d58b138a126d16b48692";
|
|
aarch64-linux = "8693fd67332d417775dc2ffc470f4c05eda9d0ed1ac329e4866e108afaa4ddda";
|
|
aarch64-darwin = "f906dff5d054b1b92e5711781b13cc206fd7139ce66467503b9d0a3e6fbc9b02";
|
|
headers = "0kai5x3ia32cch7b51v6y5cmi2by58ca2j8qkwiplnz81lhky8q8";
|
|
};
|
|
mkElectronBin = pkgs.callPackage (
|
|
pkgs.path + "/pkgs/development/tools/electron/binary/generic.nix"
|
|
) { };
|
|
|
|
# The nixpkgs Linux builder rewrites the rpath of Electron's ANGLE libraries with
|
|
# an unguarded `patchelf ... lib*GL*`. Electron 44 links ANGLE into the main binary
|
|
# and ships no libEGL.so/libGLESv2.so, so the glob expands to nothing and patchelf
|
|
# exits with "missing filename". Let that one command tolerate an empty match; it
|
|
# still patches the libraries on releases that do ship them.
|
|
angleLibGlob = "$out/libexec/electron/lib*GL*";
|
|
tolerateMissingAngleLibs =
|
|
drv:
|
|
drv.overrideAttrs (prev: {
|
|
postFixup = lib.throwIf (!lib.hasInfix angleLibGlob prev.postFixup) ''
|
|
The nixpkgs Electron builder no longer runs patchelf over ${angleLibGlob};
|
|
drop tolerateMissingAngleLibs from flake.nix.
|
|
'' (builtins.replaceStrings [ angleLibGlob ] [ "${angleLibGlob} || true" ] prev.postFixup);
|
|
});
|
|
|
|
# Guarded on Linux because only that branch of the builder defines postFixup.
|
|
pinnedElectron =
|
|
let
|
|
bin = mkElectronBin pinnedElectronVersion pinnedElectronHashes;
|
|
in
|
|
if stdenv.hostPlatform.isLinux then tolerateMissingAngleLibs bin else bin;
|
|
|
|
electron =
|
|
if electronFromNixpkgs != null && electronFromNixpkgs.version == electronVersion then
|
|
electronFromNixpkgs
|
|
else
|
|
lib.throwIf (pinnedElectronVersion != electronVersion) ''
|
|
flake.nix pins Electron ${pinnedElectronVersion}, but apps/desktop/package.json wants ${electronVersion}.
|
|
Refresh pinnedElectronVersion/pinnedElectronHashes in flake.nix, or drop the override if nixpkgs ships ${electronVersion}.
|
|
'' pinnedElectron;
|
|
|
|
nodejs = pkgs.nodejs_24;
|
|
# pnpm creates an overly long PATH env variable for child processes.
|
|
# This patch deduplicates entries in PATH, which results in an equivalent but shorter entry.
|
|
# https://github.com/pnpm/pnpm/issues/6106
|
|
# https://github.com/pnpm/pnpm/issues/8552
|
|
pnpm = (pkgs.pnpm_11.overrideAttrs (prev: {
|
|
postInstall = prev.postInstall + ''
|
|
patch $out/libexec/pnpm/dist/pnpm.mjs ${./patches/pnpm-PATH-reduction.patch}
|
|
'';
|
|
# pnpm sometimes fails with ERR_PNPM_ENOENT
|
|
# https://github.com/pnpm/pnpm/issues/12880
|
|
# "fix" from https://github.com/dniku/selfhostblocks/commit/ee6fc4f04fe34714fb8676704048d8d76aba85b7
|
|
postFixup = (prev.postFixup or "") + (if pkgs.stdenv.hostPlatform.isLinux then ''
|
|
mv "$out/bin/pnpm" "$out/bin/.pnpm-unwrapped"
|
|
cat > "$out/bin/pnpm" <<EOF
|
|
#!${stdenv.shell}
|
|
if [ "\''${1-}" = install ]; then
|
|
cpuMask="\$(${lib.getExe' pkgs.util-linux "taskset"} -cp "\$\$")"
|
|
# Keep only the cpuset reported after the colon.
|
|
cpuMask="\''${cpuMask##*: }"
|
|
# Pick the first comma-separated segment from the cpuset.
|
|
firstCpu="\''${cpuMask%%,*}"
|
|
# If that segment is a range, pick its first CPU.
|
|
firstCpu="\''${firstCpu%%-*}"
|
|
exec ${lib.getExe' pkgs.util-linux "taskset"} -c "\$firstCpu" "$out/bin/.pnpm-unwrapped" "\$@"
|
|
else
|
|
exec "$out/bin/.pnpm-unwrapped" "\$@"
|
|
fi
|
|
EOF
|
|
chmod +x "$out/bin/pnpm"
|
|
'' else "");
|
|
}));
|
|
inherit (pkgs)
|
|
copyDesktopItems
|
|
darwin
|
|
lib
|
|
makeBinaryWrapper
|
|
makeDesktopItem
|
|
makeShellWrapper
|
|
removeReferencesTo
|
|
stdenv
|
|
wrapGAppsHook3
|
|
xcodebuild
|
|
which
|
|
;
|
|
|
|
fullCleanSourceFilter =
|
|
name: type:
|
|
(lib.cleanSourceFilter name type)
|
|
&& (
|
|
let
|
|
baseName = baseNameOf (toString name);
|
|
in
|
|
# No need to copy the flake.
|
|
# No need to copy local copy of node_modules.
|
|
baseName != "flake.nix" && baseName != "flake.lock" && baseName != "node_modules"
|
|
);
|
|
fullCleanSource =
|
|
src:
|
|
lib.cleanSourceWith {
|
|
filter = fullCleanSourceFilter;
|
|
src = src;
|
|
};
|
|
|
|
# Minimal source used for pnpm2nix's dependency-fetching derivation.
|
|
# Only the files pnpm actually needs to resolve and fetch dependencies
|
|
# are included, so unrelated source changes don't bust the deps cache.
|
|
workspaceSourceFilter =
|
|
name: type:
|
|
let
|
|
baseName = baseNameOf (toString name);
|
|
rootStr = toString ./.;
|
|
relPath = lib.removePrefix "${rootStr}/" (toString name);
|
|
inPatches = relPath == "patches" || lib.hasPrefix "patches/" relPath;
|
|
in
|
|
(lib.cleanSourceFilter name type)
|
|
&& baseName != "node_modules"
|
|
&& (
|
|
type == "directory"
|
|
|| baseName == "package.json"
|
|
|| baseName == "pnpm-workspace.yaml"
|
|
|| baseName == "pnpm-lock.yaml"
|
|
|| inPatches
|
|
);
|
|
workspaceSource = lib.cleanSourceWith {
|
|
filter = workspaceSourceFilter;
|
|
src = ./.;
|
|
};
|
|
packageJson = builtins.fromJSON (builtins.readFile ./package.json);
|
|
packageJsonDesktop = builtins.fromJSON (builtins.readFile ./apps/desktop/package.json);
|
|
|
|
makeApp =
|
|
{
|
|
app,
|
|
buildTask,
|
|
mainProgram,
|
|
installCommands,
|
|
preBuildCommands ? "",
|
|
}:
|
|
pnpm2nix.packages.${system}.mkPnpmPackage rec {
|
|
pname = "trilium-${app}";
|
|
version = packageJson.version + (lib.optionalString (self ? shortRev) "-${self.shortRev}");
|
|
|
|
src = fullCleanSource ./.;
|
|
packageJSON = ./package.json;
|
|
pnpmLockYaml = ./pnpm-lock.yaml;
|
|
|
|
workspace = workspaceSource;
|
|
pnpmWorkspaceYaml = ./pnpm-workspace.yaml;
|
|
|
|
inherit nodejs pnpm;
|
|
|
|
extraNodeModuleSources = [
|
|
rec {
|
|
name = "patches";
|
|
value = ./patches;
|
|
}
|
|
];
|
|
|
|
# avoid ERR_PNPM_RESOLUTION_SHAPE_MISMATCH errors
|
|
env.PNPM_CONFIG_TRUST_LOCKFILE = "true";
|
|
|
|
# remove pnpm version override
|
|
preConfigure = ''
|
|
node -e "const p = require('./package.json'); delete p.packageManager; require('fs').writeFileSync('package.json', JSON.stringify(p, null, 2) + '\n')"
|
|
'';
|
|
|
|
postConfigure =
|
|
''
|
|
chmod +x node_modules/electron/install.js
|
|
patchShebangs --build node_modules
|
|
'';
|
|
|
|
extraNativeBuildInputs =
|
|
[
|
|
nodejs.python
|
|
removeReferencesTo
|
|
]
|
|
++ lib.optionals (app == "desktop" || app == "edit-docs") [
|
|
copyDesktopItems
|
|
# required for NIXOS_OZONE_WL expansion
|
|
# https://github.com/NixOS/nixpkgs/issues/172583
|
|
makeShellWrapper
|
|
wrapGAppsHook3
|
|
|
|
# For the launcher wrapper generated in installCommands:
|
|
which
|
|
electron
|
|
]
|
|
++ lib.optionals (app == "server" || app == "build-docs") [
|
|
makeBinaryWrapper
|
|
]
|
|
++ lib.optionals stdenv.hostPlatform.isDarwin [
|
|
xcodebuild
|
|
darwin.cctools
|
|
];
|
|
dontWrapGApps = true;
|
|
|
|
preBuild = ''
|
|
${preBuildCommands}
|
|
'';
|
|
|
|
scriptFull = "pnpm run ${buildTask}";
|
|
|
|
installPhase = ''
|
|
runHook preInstall
|
|
|
|
${installCommands}
|
|
|
|
runHook postInstall
|
|
'';
|
|
|
|
# Symlinks pointing to /build directory are not allowed in the Nix store.
|
|
# This removes all dangling symlinks that point to the temporary build directory.
|
|
postFixup = ''
|
|
find $out/opt -type l -lname '/build/*' -delete || true
|
|
'';
|
|
|
|
components = [
|
|
"packages/ckeditor5"
|
|
"packages/codemirror"
|
|
"packages/commons"
|
|
"packages/highlightjs"
|
|
"packages/turndown-plugin-gfm"
|
|
|
|
"apps/build-docs"
|
|
"apps/client"
|
|
"apps/db-compare"
|
|
"apps/desktop"
|
|
"apps/dump-db"
|
|
"apps/edit-docs"
|
|
"apps/server"
|
|
"packages/trilium-e2e"
|
|
];
|
|
|
|
desktopItems = lib.optionals (app == "desktop") [
|
|
(makeDesktopItem {
|
|
name = "Trilium Notes";
|
|
exec = meta.mainProgram;
|
|
icon = "trilium";
|
|
comment = meta.description;
|
|
desktopName = "Trilium Notes";
|
|
categories = [ "Office" ];
|
|
startupWMClass = "Trilium Notes";
|
|
})
|
|
];
|
|
|
|
meta = {
|
|
description = "Trilium: ${app}";
|
|
inherit mainProgram;
|
|
};
|
|
};
|
|
|
|
desktop = makeApp {
|
|
app = "desktop";
|
|
# better-sqlite3 v13 is N-API based and ships prebuilt binaries that
|
|
# load unchanged under Electron, so there is no native rebuild step
|
|
# (and no need for ELECTRON_NODEDIR) any more.
|
|
preBuildCommands = ''
|
|
pnpm postinstall
|
|
'';
|
|
buildTask = "desktop:build";
|
|
mainProgram = "trilium";
|
|
installCommands = ''
|
|
mkdir -p $out/{bin,share/icons/hicolor/512x512/apps,opt/trilium}
|
|
cp --archive apps/desktop/dist/* $out/opt/trilium
|
|
cp apps/client/src/assets/icon.png $out/share/icons/hicolor/512x512/apps/trilium.png
|
|
makeShellWrapper ${lib.getExe electron} $out/bin/trilium \
|
|
"''${gappsWrapperArgs[@]}" \
|
|
--add-flags "\''${NIXOS_OZONE_WL:+\''${WAYLAND_DISPLAY:+--ozone-platform-hint=auto --enable-features=WaylandWindowDecorations --enable-wayland-ime=true}}" \
|
|
--set-default ELECTRON_IS_DEV 0 \
|
|
--set TRILIUM_RESOURCE_DIR $out/opt/trilium \
|
|
--add-flags $out/opt/trilium/main.mjs
|
|
'';
|
|
};
|
|
|
|
server = makeApp {
|
|
app = "server";
|
|
# Important note: if pnpm throws an error similar to the follow at the end of `pnpm rebuild`,
|
|
# you should ensure the version of tsx (and other dependencies mentioned in the error) is identical project-wide.
|
|
# ERR_PNPM_MISSING_HOISTED_LOCATIONS
|
|
# vite@7.1.5(@types/node@24.3.0)(jiti@2.5.1)(less@4.1.3)(lightningcss@1.30.1)
|
|
# (sass-embedded@1.91.0)(sass@1.91.0)(terser@5.43.1)(tsx@4.20.5)(yaml@2.8.1)
|
|
# is not found in hoistedLocations inside node_modules/.modules.yaml
|
|
preBuildCommands = ''
|
|
pushd apps/server
|
|
pnpm rebuild
|
|
popd
|
|
'';
|
|
buildTask = "server:build";
|
|
mainProgram = "trilium-server";
|
|
installCommands = ''
|
|
# No better-sqlite3 cleanup needed here any more: the server build
|
|
# trims deps/, src/ and build/ (which held the store paths that used
|
|
# to need remove-references-to) out of dist itself.
|
|
mkdir -p $out/{bin,opt/trilium-server}
|
|
cp --archive apps/server/dist/* $out/opt/trilium-server
|
|
makeWrapper ${lib.getExe nodejs} $out/bin/trilium-server \
|
|
--add-flags $out/opt/trilium-server/main.mjs
|
|
'';
|
|
};
|
|
|
|
edit-docs = makeApp {
|
|
app = "edit-docs";
|
|
preBuildCommands = ''
|
|
pnpm postinstall
|
|
'';
|
|
buildTask = "edit-docs:build";
|
|
mainProgram = "trilium-edit-docs";
|
|
installCommands = ''
|
|
mkdir -p $out/{bin,opt/trilium-edit-docs}
|
|
cp --archive apps/edit-docs/dist/* $out/opt/trilium-edit-docs
|
|
makeShellWrapper ${lib.getExe electron} $out/bin/trilium-edit-docs \
|
|
--set-default ELECTRON_IS_DEV 0 \
|
|
--set TRILIUM_RESOURCE_DIR $out/opt/trilium-edit-docs \
|
|
--add-flags $out/opt/trilium-edit-docs/edit-docs.cjs
|
|
'';
|
|
};
|
|
|
|
build-docs = makeApp {
|
|
app = "build-docs";
|
|
preBuildCommands = ''
|
|
pushd apps/server
|
|
pnpm rebuild || true
|
|
popd
|
|
'';
|
|
buildTask = "client:build && pnpm run server:build && pnpm run --filter build-docs build";
|
|
mainProgram = "trilium-build-docs";
|
|
installCommands = ''
|
|
mkdir -p $out/{bin,opt/trilium-build-docs}
|
|
|
|
# Copy build-docs dist
|
|
cp --archive apps/build-docs/dist/* $out/opt/trilium-build-docs
|
|
|
|
# Copy server dist (needed for runtime)
|
|
mkdir -p $out/opt/trilium-build-docs/server
|
|
cp --archive apps/server/dist/* $out/opt/trilium-build-docs/server/
|
|
|
|
# Copy client dist (needed for runtime)
|
|
mkdir -p $out/opt/trilium-build-docs/client
|
|
cp --archive apps/client/dist/* $out/opt/trilium-build-docs/client/
|
|
|
|
# Copy share-theme (needed for exports)
|
|
mkdir -p $out/opt/trilium-build-docs/packages/share-theme
|
|
cp --archive packages/share-theme/dist/* $out/opt/trilium-build-docs/packages/share-theme/
|
|
|
|
# Create wrapper script
|
|
makeWrapper ${lib.getExe nodejs} $out/bin/trilium-build-docs \
|
|
--add-flags $out/opt/trilium-build-docs/cli.cjs \
|
|
--set TRILIUM_RESOURCE_DIR $out/opt/trilium-build-docs/server
|
|
'';
|
|
};
|
|
|
|
in
|
|
{
|
|
packages.desktop = desktop;
|
|
packages.server = server;
|
|
packages.edit-docs = edit-docs;
|
|
packages.build-docs = build-docs;
|
|
|
|
packages.default = desktop;
|
|
|
|
# Not something to install — it is here so the pinned Electron binary can be
|
|
# built (and therefore its hashes verified) on its own, without going through
|
|
# a full desktop build. The update-nix-flake workflow does exactly that.
|
|
packages.electron = electron;
|
|
|
|
devShells.default = pkgs.mkShell {
|
|
buildInputs = [
|
|
nodejs
|
|
pnpm
|
|
electron
|
|
nodejs.python
|
|
# For the browser-mode tests (packages/ckeditor5). The Chromium Playwright downloads
|
|
# for itself is dynamically linked against libraries no NixOS system provides, so it
|
|
# dies on a missing libxcb.so.1.
|
|
pkgs.chromium
|
|
];
|
|
|
|
# Read by packages/ckeditor5/vitest.config.ts and passed to Playwright as
|
|
# `launchOptions.executablePath`. Without it Playwright launches its own Chromium and the
|
|
# suite cannot start.
|
|
CHROME_BIN = "${pkgs.chromium}/bin/chromium";
|
|
};
|
|
}
|
|
);
|
|
}
|