Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions .github/workflows/nix-build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
name: Nix build

# Builds the flake packages on a Nix runner whenever the Nix packaging changes,
# so the prebuilt-desktop wrapper and the server package are verified in CI
# (the maintainers don't have a Nix machine to test on locally).

on:
push:
branches: [main]
paths: ['flake.nix', 'flake.lock', 'packaging/nix/**']
pull_request:
paths: ['flake.nix', 'flake.lock', 'packaging/nix/**']
workflow_dispatch:

permissions:
contents: read

jobs:
build:
runs-on: ubuntu-latest
env:
# cache.nixos.org occasionally corrupts NAR streams over HTTP/2; dodge it.
NIX_CONFIG: |
http2 = false
download-attempts = 5
connect-timeout = 20
steps:
- uses: actions/checkout@v4
- name: Install Nix
uses: DeterminateSystems/nix-installer-action@main
- name: Build packages
run: nix build --fallback --print-build-logs .#zennotes-desktop .#zennotes-server
23 changes: 16 additions & 7 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,23 @@
packages = forAllSystems (system:
let
pkgs = nixpkgs.legacyPackages.${system};
in
rec {
zennotes-desktop = pkgs.callPackage ./packaging/nix/package-desktop.nix { };

zennotes-server = pkgs.callPackage ./packaging/nix/package-server.nix { };

default = zennotes-desktop;
}
in
{ inherit zennotes-server; }
# The desktop package wraps the prebuilt linux-x64 release tarball, so it
# only exists on x86_64-linux; elsewhere the server is the default.
// (
if system == "x86_64-linux" then
let
zennotes-desktop = pkgs.callPackage ./packaging/nix/package-desktop.nix { };
in
{
inherit zennotes-desktop;
default = zennotes-desktop;
}
else
{ default = zennotes-server; }
)
);

devShell = forAllSystems (system:
Expand Down
145 changes: 110 additions & 35 deletions packaging/nix/package-desktop.nix
Original file line number Diff line number Diff line change
@@ -1,65 +1,139 @@
{
stdenv,
lib,
buildNpmPackage,
fetchFromGitHub,
makeDesktopItem,
copyDesktopItems,
electron,
stdenv,
fetchurl,
autoPatchelfHook,
makeWrapper,
wrapGAppsHook3,
copyDesktopItems,
makeDesktopItem,

# Electron / Chromium runtime libraries
alsa-lib,
at-spi2-atk,
at-spi2-core,
atk,
cairo,
cups,
dbus,
expat,
fontconfig,
freetype,
gdk-pixbuf,
glib,
gtk3,
libdrm,
libGL,
libgbm,
libnotify,
libpulseaudio,
libuuid,
libxkbcommon,
nspr,
nss,
pango,
systemd,
wayland,
xorg,

installCLI ? false,
CLIcommand ? "zen",
commandLineArgs ? "",
}:
let
releaseData = lib.importJSON ./release-data.json;
in
buildNpmPackage (finalAttrs: {
# Packaged from the official prebuilt linux-x64 release tarball rather than built
# from source, so `nix run github:ZenNotes/zennotes` downloads the same signed
# binary the AppImage/deb/AUR ship instead of compiling Electron locally. Bump
# `version` + `desktopHash` in release-data.json per release (see README.md).
stdenv.mkDerivation (finalAttrs: {
pname = "zennotes-desktop";
inherit (releaseData) version npmDepsHash;
inherit (releaseData) version;

src = fetchFromGitHub {
owner = "ZenNotes";
repo = "zennotes";
tag = "v${finalAttrs.version}";
inherit (releaseData) hash;
src = fetchurl {
url = "https://github.com/ZenNotes/zennotes/releases/download/v${finalAttrs.version}/ZenNotes-${finalAttrs.version}-linux-x64.tar.gz";
hash = releaseData.desktopHash;
};

npmWorkspace = "apps/desktop";

env.ELECTRON_SKIP_BINARY_DOWNLOAD = "1";
sourceRoot = "ZenNotes-${finalAttrs.version}-linux-x64";

nativeBuildInputs = [
autoPatchelfHook
makeWrapper
]
++ lib.optionals stdenv.hostPlatform.isLinux [
wrapGAppsHook3
copyDesktopItems
];

buildInputs = [
alsa-lib
at-spi2-atk
at-spi2-core
atk
cairo
cups
dbus
expat
fontconfig
freetype
gdk-pixbuf
glib
gtk3
libdrm
libgbm
libuuid
libxkbcommon
nspr
nss
pango
stdenv.cc.cc # libstdc++
xorg.libX11
xorg.libXcomposite
xorg.libXcursor
xorg.libXdamage
xorg.libXext
xorg.libXfixes
xorg.libXi
xorg.libXrandr
xorg.libXrender
xorg.libXScrnSaver
xorg.libXtst
xorg.libxcb
];

# dlopen'd at runtime (not in DT_NEEDED), so keep them on the wrapper's path.
runtimeDependencies = [
(lib.getLib systemd)
libGL
libnotify
libpulseaudio
wayland
];

dontConfigure = true;
dontBuild = true;
# We invoke makeWrapper manually and splice in gappsWrapperArgs ourselves.
dontWrapGApps = true;

installPhase = ''
runHook preInstall

mkdir -p $out/lib/node_modules/zennotes-monorepo
cp -r . $out/lib/node_modules/zennotes-monorepo/
# The SUID chrome-sandbox can't be made setuid in the Nix store; drop it so
# Electron falls back to the user-namespace sandbox (same as the Flatpak).
rm -f chrome-sandbox

mkdir -p $out/share/zennotes
cp -r . $out/share/zennotes

for icon in apps/desktop/build/icons/*.png; do
# Icons + desktop entry ship inside the tarball's arch-extras tree.
for icon in $out/share/zennotes/resources/arch-extras/icons/*.png; do
size="$(basename "$icon" .png)"
install -Dm644 $icon $out/share/icons/hicolor/$size/apps/${finalAttrs.pname}.png
install -Dm644 "$icon" "$out/share/icons/hicolor/$size/apps/${finalAttrs.pname}.png"
done

mkdir -p $out/bin
makeWrapper ${electron}/bin/electron $out/bin/${finalAttrs.pname} \
--add-flags "$out/lib/node_modules/zennotes-monorepo/apps/desktop" \
makeWrapper $out/share/zennotes/ZenNotes $out/bin/${finalAttrs.pname} \
"''${gappsWrapperArgs[@]}" \
--add-flags "\''${NIXOS_OZONE_WL:+\''${WAYLAND_DISPLAY:+--ozone-platform-hint=auto}}" \
${lib.optionalString (commandLineArgs != "") "--add-flags ${lib.escapeShellArg commandLineArgs}"}

${lib.optionalString installCLI ''
makeWrapper ${electron}/libexec/electron/electron $out/bin/${CLIcommand} \
--set ELECTRON_RUN_AS_NODE 1 \
--add-flags "$out/lib/node_modules/zennotes-monorepo/apps/desktop/out/main/cli.js"
''}

runHook postInstall
'';

Expand All @@ -84,12 +158,13 @@ buildNpmPackage (finalAttrs: {
];

meta = {
description = "Keyboard-first local Markdown notes with Vim motions, diagrams, and MCP integration";
description = "Keyboard-first local Markdown notes with Vim motions, diagrams, and MCP integration (prebuilt binary)";
homepage = "https://zennotes.org/";
changelog = "https://github.com/ZenNotes/zennotes/releases/tag/v${finalAttrs.version}";
license = lib.licenses.mit;
maintainers = with lib.maintainers; [ justkrysteq ];
mainProgram = finalAttrs.pname;
inherit (electron.meta) platforms;
sourceProvenance = with lib.sourceTypes; [ binaryNativeCode ];
platforms = [ "x86_64-linux" ];
};
})
3 changes: 2 additions & 1 deletion packaging/nix/release-data.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@
"version": "2.4.0",
"hash": "sha256-v+0Iyxz0B5IQ6FnzD6WecIfrM1j4PUzizH63JMvkp8Y=",
"npmDepsHash": "sha256-OgehLwGj216oBUJOuEmzxzzr/BlwoUaE5IAbT5rvgTQ=",
"vendorHash": "sha256-wYBF7CjM6AvoWMWql9hFmIaj6pCmli4vOef6POyGkfU="
"vendorHash": "sha256-wYBF7CjM6AvoWMWql9hFmIaj6pCmli4vOef6POyGkfU=",
"desktopHash": "sha256-Y+l/bhP/l9bUesPAP+f9wHGEmMSU8gezgmlqppYVxas="
}
Loading