refactor(nix): consolidate deploy flake into root flake with pure ISO builds
Move deploy/nixos/flake.nix into the root flake.nix, adding mkAtmApp for pure Nix builds of the Electron app (no local pnpm needed). Simplify build-iso.sh to a thin wrapper around `nix build .#iso-<model>`. Add douro hardware configuration. Streamline live.nix to consume the Nix-built app package. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
f5f00108aa
commit
625cebed25
8 changed files with 832 additions and 342 deletions
173
nix/mkAtmApp.nix
Normal file
173
nix/mkAtmApp.nix
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
# Pure Nix derivation for the Lamassu ATM Electron app.
|
||||
#
|
||||
# Uses fetchPnpmDeps + pnpmConfigHook to build entirely inside the Nix sandbox,
|
||||
# eliminating the need for --impure or a local pnpm install.
|
||||
#
|
||||
# Usage (from flake.nix):
|
||||
# mkAtmApp = import ./nix/mkAtmApp.nix { inherit pkgs pkgs-unstable; src = self; };
|
||||
# atm-app-douro = mkAtmApp { model = "douro"; fiatCode = "GTQ"; };
|
||||
|
||||
{ pkgs, pkgs-unstable, src }:
|
||||
|
||||
{ model, fiatCode }:
|
||||
|
||||
let
|
||||
nodejs = pkgs-unstable.nodejs_22;
|
||||
pnpm = pkgs-unstable.pnpm_9;
|
||||
electron = pkgs-unstable.electron;
|
||||
|
||||
# Electron's Node.js headers — needed to compile native addons (better-sqlite3)
|
||||
# that use V8 C++ API (not N-API). Must match the Electron version exactly.
|
||||
electronHeaders = pkgs.fetchurl {
|
||||
url = "https://electronjs.org/headers/v${electron.version}/node-v${electron.version}-headers.tar.gz";
|
||||
hash = "sha256-xtGHm/2Sb0ILU4yUVAqXTRLwzf8yiXxD0zIIrBk8NDw=";
|
||||
};
|
||||
in
|
||||
pkgs.stdenv.mkDerivation (finalAttrs: {
|
||||
pname = "lamassu-atm-app";
|
||||
version = "0.1.0";
|
||||
|
||||
inherit src;
|
||||
|
||||
# Filter to only the workspace packages needed for @lamassu/machine
|
||||
pnpmWorkspaces = [
|
||||
"@lamassu/machine..." # "..." suffix = include transitive workspace deps
|
||||
];
|
||||
|
||||
pnpmDeps = pkgs-unstable.fetchPnpmDeps {
|
||||
inherit (finalAttrs) pname version src pnpmWorkspaces;
|
||||
inherit pnpm;
|
||||
fetcherVersion = 3;
|
||||
hash = "sha256-i8nFieH6VSibEDDDFtMqko1+JQ1MWxBCTNUJ6Jo8mE4=";
|
||||
};
|
||||
|
||||
nativeBuildInputs = [
|
||||
nodejs
|
||||
pnpm
|
||||
pkgs-unstable.pnpmConfigHook
|
||||
pkgs.python3 # node-gyp (better-sqlite3)
|
||||
pkgs.pkg-config
|
||||
pkgs.autoPatchelfHook # patch .node ELF binaries
|
||||
];
|
||||
|
||||
buildInputs = [
|
||||
pkgs.sqlite.dev # better-sqlite3
|
||||
pkgs.libudev-zero # serialport
|
||||
pkgs.stdenv.cc.cc.lib # libstdc++
|
||||
];
|
||||
|
||||
env = {
|
||||
ELECTRON_SKIP_BINARY_DOWNLOAD = "1";
|
||||
# Vite compile-time variables (baked into the frontend bundle)
|
||||
VITE_LAMASSU_MACHINE_MODEL = model;
|
||||
VITE_LAMASSU_FIAT_CODE = fiatCode;
|
||||
};
|
||||
|
||||
buildPhase = ''
|
||||
runHook preBuild
|
||||
|
||||
# Extract Electron's Node.js headers for native addon compilation.
|
||||
# better-sqlite3 uses the V8 C++ API (not N-API), so it MUST be compiled
|
||||
# against Electron's headers — not Node.js headers — or you get
|
||||
# "undefined symbol: _ZN2v811HandleScopeC1EPNS_7IsolateE" at runtime.
|
||||
electron_nodedir=$(mktemp -d)
|
||||
tar -xzf ${electronHeaders} -C "$electron_nodedir" --strip-components=1
|
||||
|
||||
echo "=== Rebuilding better-sqlite3 against Electron ${electron.version} headers ==="
|
||||
pushd node_modules/.pnpm/better-sqlite3@*/node_modules/better-sqlite3
|
||||
HOME=$TMPDIR ${nodejs}/bin/npx --yes node-gyp rebuild \
|
||||
--nodedir="$electron_nodedir" \
|
||||
--arch=x64
|
||||
popd
|
||||
|
||||
# Build the Electron app (turbo builds all workspace deps + app)
|
||||
pnpm --filter="@lamassu/machine..." build
|
||||
|
||||
runHook postBuild
|
||||
'';
|
||||
|
||||
# Cherry-pick only the runtime node_modules needed by the Electron app.
|
||||
# Vite bundles most JS deps — these are native addons and dynamic imports
|
||||
# that can't be bundled.
|
||||
installPhase = ''
|
||||
runHook preInstall
|
||||
|
||||
mkdir -p $out/node_modules/{@lamassu,@serialport}
|
||||
|
||||
# Helper: find a package dir inside the pnpm virtual store.
|
||||
# pnpm store dirs look like: node_modules/.pnpm/<name>@<ver>[_<peer-suffix>]/node_modules/<name>
|
||||
# The glob must handle version + optional peer suffixes.
|
||||
copy_pnpm_pkg() {
|
||||
local pkg="$1" # e.g. "debug" or "@serialport/stream"
|
||||
local dest="$2" # e.g. "$out/node_modules/debug"
|
||||
# Convert @scope/name to @scope+name for the .pnpm dir prefix
|
||||
local store_prefix="''${pkg/\//-}" # debug -> debug, @serialport/stream -> @serialport-stream
|
||||
store_prefix="''${store_prefix//@/@}"
|
||||
# Try globbing; sort to pick the first match deterministically
|
||||
local found=$(find node_modules/.pnpm -maxdepth 1 -name "''${store_prefix}@*" -type d | sort | head -1)
|
||||
if [ -z "$found" ]; then
|
||||
# Fallback: scoped packages use + instead of -
|
||||
store_prefix="''${pkg/\//+}"
|
||||
found=$(find node_modules/.pnpm -maxdepth 1 -name "''${store_prefix}@*" -type d | sort | head -1)
|
||||
fi
|
||||
if [ -z "$found" ]; then
|
||||
echo "ERROR: could not find pnpm store dir for $pkg" >&2
|
||||
exit 1
|
||||
fi
|
||||
cp -rL "$found/node_modules/$pkg" "$dest"
|
||||
}
|
||||
|
||||
# Electron app build outputs
|
||||
cp -r apps/machine/dist $out/dist
|
||||
cp -r apps/machine/dist-electron $out/dist-electron
|
||||
cp apps/machine/package.json $out/package.json
|
||||
|
||||
# --- Native modules (not bundleable by Vite) ---
|
||||
|
||||
# better-sqlite3 (compiled native addon)
|
||||
copy_pnpm_pkg better-sqlite3 $out/node_modules/better-sqlite3
|
||||
copy_pnpm_pkg bindings $out/node_modules/bindings
|
||||
copy_pnpm_pkg file-uri-to-path $out/node_modules/file-uri-to-path
|
||||
|
||||
# @lamassu/hal (workspace package, dynamically imported for hardware access)
|
||||
mkdir -p $out/node_modules/@lamassu/hal/dist
|
||||
cp -rL packages/hal/dist/* $out/node_modules/@lamassu/hal/dist/
|
||||
cp packages/hal/package.json $out/node_modules/@lamassu/hal/package.json
|
||||
|
||||
# serialport (native RS232 driver chain)
|
||||
copy_pnpm_pkg serialport $out/node_modules/serialport
|
||||
copy_pnpm_pkg @serialport/stream $out/node_modules/@serialport/stream
|
||||
|
||||
# bindings-cpp: only dist/ + prebuilds/ + package.json (build/ has broken symlinks)
|
||||
mkdir -p $out/node_modules/@serialport/bindings-cpp
|
||||
bcpp_store=$(find node_modules/.pnpm -maxdepth 1 -name "@serialport+bindings-cpp@*" -type d | sort | head -1)
|
||||
cp -rL "$bcpp_store/node_modules/@serialport/bindings-cpp/dist" $out/node_modules/@serialport/bindings-cpp/dist
|
||||
cp -rL "$bcpp_store/node_modules/@serialport/bindings-cpp/prebuilds" $out/node_modules/@serialport/bindings-cpp/prebuilds
|
||||
cp "$bcpp_store/node_modules/@serialport/bindings-cpp/package.json" $out/node_modules/@serialport/bindings-cpp/package.json
|
||||
|
||||
copy_pnpm_pkg @serialport/bindings-interface $out/node_modules/@serialport/bindings-interface
|
||||
copy_pnpm_pkg @serialport/binding-mock $out/node_modules/@serialport/binding-mock
|
||||
|
||||
# Transitive deps of serialport
|
||||
copy_pnpm_pkg debug $out/node_modules/debug
|
||||
copy_pnpm_pkg ms $out/node_modules/ms
|
||||
copy_pnpm_pkg node-addon-api $out/node_modules/node-addon-api
|
||||
copy_pnpm_pkg node-gyp-build $out/node_modules/node-gyp-build
|
||||
copy_pnpm_pkg lodash-es $out/node_modules/lodash-es
|
||||
|
||||
# serialport parser sub-packages (barrel import requires all 10)
|
||||
for parser in \
|
||||
parser-byte-length parser-cctalk parser-delimiter \
|
||||
parser-inter-byte-timeout parser-packet-length parser-readline \
|
||||
parser-ready parser-regex parser-slip-encoder parser-spacepacket; do
|
||||
copy_pnpm_pkg "@serialport/$parser" "$out/node_modules/@serialport/$parser"
|
||||
done
|
||||
|
||||
runHook postInstall
|
||||
'';
|
||||
|
||||
# autoPatchelfHook will scan $out for .node ELF binaries and patch their
|
||||
# RPATH to find libstdc++, libudev, libsqlite3, etc.
|
||||
# The musl prebuild ships alongside glibc — we only use glibc on NixOS.
|
||||
autoPatchelfIgnoreMissingDeps = [ "libc.musl-x86_64.so.1" ];
|
||||
})
|
||||
Loading…
Add table
Add a link
Reference in a new issue