nsecbunkerd/package.nix
Padreug 662dd21a60
Some checks failed
Docker image / build-and-push-image (push) Has been cancelled
fix(nix): include prisma CLI + scripts/, wrapper invokes start.js
Three correctness fixes to the nix derivation that mirror the Dockerfile
correctness fixes:

1. Drop `pnpm prune --prod --ignore-scripts` from the build phase. The
   prune step removed the prisma CLI (devDependency) from the output,
   so the runtime invocation of `prisma migrate deploy` had nothing to
   exec. Same trap the upstream Dockerfile fell into via `--prod` install.

2. Copy `scripts/` into `$out/share/nsecbunkerd/` alongside dist,
   node_modules, prisma, templates. Without it the launcher script
   (which contains the migration step) wasn't present.

3. The makeWrapper target switches from `dist/index.js` to
   `scripts/start.js`. Same change the Dockerfile ENTRYPOINT got in
   the previous commit. Also adds nodejs_20 to PATH so `npm` is
   resolvable from inside start.js, and drops `--chdir` so the caller
   (systemd, docker compose) controls cwd — start.js now resolves
   sibling paths from `__dirname`, independently committed.

The `patchNdk` substitution narrows from the old `workspace:*` form
(no longer in the package.json after fork commit 06272c8) to the
current `"2.8.1"` → `"^2.8.1"` rewrite needed to align package.json
with the lockfile under --frozen-lockfile.

Remaining known gap: nixpkgs ships prisma-engines 7.7.0 while the
JS prisma CLI in node_modules is 5.4.1, an RPC vocabulary mismatch
that breaks the migrate step at runtime (`Method not found:
listMigrationDirectories`). Either bump prisma JS to ^7.x or overlay
prisma-engines to 5.4.1. Out of scope for this commit; docker build
unaffected.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 17:08:42 +02:00

143 lines
4.5 KiB
Nix

{
lib,
stdenv,
pnpm_8,
nodejs_20,
makeWrapper,
prisma-engines,
openssl,
sqlite,
python311,
pkg-config,
node-gyp,
}:
let
# Fork commit `06272c8` ("pin @nostr-dev-kit/ndk to 2.8.1 instead of
# workspace:*") changed package.json to a pinned `"2.8.1"`, but the
# pnpm-lock.yaml still expresses the spec as `"^2.8.1"` (the way
# `pnpm add` originally generated it). pnpm with --frozen-lockfile
# rejects that mismatch. Patching package.json to use the caret form
# is non-semantic (2.8.1 is still the resolved version) and aligns
# both files. Same fix the Dockerfile-side already handles via
# `--no-frozen-lockfile`; in nix we prefer frozen + a targeted patch.
patchNdk = ''
substituteInPlace package.json \
--replace-fail '"@nostr-dev-kit/ndk": "2.8.1"' \
'"@nostr-dev-kit/ndk": "^2.8.1"'
'';
prismaEnv = {
PRISMA_SCHEMA_ENGINE_BINARY = lib.getExe' prisma-engines "schema-engine";
PRISMA_QUERY_ENGINE_BINARY = lib.getExe' prisma-engines "query-engine";
PRISMA_QUERY_ENGINE_LIBRARY = "${prisma-engines}/lib/libquery_engine.node";
PRISMA_INTROSPECTION_ENGINE_BINARY = lib.getExe' prisma-engines "introspection-engine";
PRISMA_FMT_BINARY = lib.getExe' prisma-engines "prisma-fmt";
PRISMA_CLIENT_ENGINE_TYPE = "binary";
};
in
stdenv.mkDerivation (finalAttrs: {
pname = "nsecbunkerd";
version = "0.10.5";
src = ./.;
pnpmDeps = pnpm_8.fetchDeps {
inherit (finalAttrs) pname version src;
fetcherVersion = 2;
prePnpmInstall = patchNdk;
hash = "sha256-dQ+TX5jf1ZQKGoPCZgWaFwpAC3uP6iL1ZSxS0mFNdP8=";
};
postPatch = patchNdk;
nativeBuildInputs = [
pnpm_8.configHook
pnpm_8
nodejs_20
makeWrapper
node-gyp
python311
pkg-config
];
buildInputs = [
openssl
sqlite
];
env = prismaEnv;
buildPhase = ''
runHook preBuild
export npm_config_nodedir=${nodejs_20}
pnpm config set nodedir ${nodejs_20}
# configHook ran with --ignore-scripts; re-run install to trigger
# native-module postinstall (bcrypt). --offline keeps it inside the
# store seeded by configHook.
pnpm install --force --offline --frozen-lockfile --reporter=append-only
pnpm prisma generate
pnpm build
# Do NOT `pnpm prune --prod` here the prisma CLI lives in
# devDependencies and `scripts/start.js` invokes it at boot via
# `npx prisma migrate deploy`. Without the CLI, the migration step
# silently fails (npx falls back to downloading prisma fresh, which
# OOMs on most containers) and the SQLite db stays empty. See
# `aiolabs/nsecbunkerd#7` diagnosis 2026-05-27.
find node_modules -xtype l -delete
runHook postBuild
'';
installPhase = ''
runHook preInstall
mkdir -p $out/{bin,share/nsecbunkerd}
# scripts/ MUST be copied it contains the start.js launcher that
# runs `prisma migrate deploy` before spawning the daemon. The
# upstream packaging (and the upstream Dockerfile) bypassed this by
# invoking dist/index.js directly, leaving migrations unapplied.
cp -r dist node_modules prisma scripts templates package.json \
$out/share/nsecbunkerd/
# Wrapper invokes scripts/start.js, which runs `prisma migrate deploy`
# then spawns dist/index.js. start.js resolves sibling paths from
# __dirname, so the caller (systemd unit, docker compose, etc.) can
# set its own WorkingDirectory for the writable state dir without
# interfering with how the launcher finds its own package files.
# NSEC_BUNKER_CONFIG_DIR can override the config directory location;
# by default it's `./config` relative to cwd.
makeWrapper ${lib.getExe nodejs_20} $out/bin/nsecbunkerd \
--add-flags $out/share/nsecbunkerd/scripts/start.js \
--set NODE_ENV production \
--prefix PATH : ${lib.makeBinPath [ openssl nodejs_20 ]} \
${
lib.concatStringsSep " \\\n " (
lib.mapAttrsToList (n: v: "--set ${n} ${lib.escapeShellArg v}") prismaEnv
)
}
makeWrapper ${lib.getExe nodejs_20} $out/bin/nsecbunker-client \
--chdir $out/share/nsecbunkerd \
--add-flags $out/share/nsecbunkerd/dist/client/client.js \
--set NODE_ENV production
runHook postInstall
'';
passthru = {
inherit prisma-engines;
};
meta = {
description = "Nostr remote signing daemon (NIP-46)";
homepage = "https://github.com/kind-0/nsecbunkerd";
license = lib.licenses.mit;
mainProgram = "nsecbunkerd";
platforms = lib.platforms.linux;
};
})