From 959b6ea17da3645b0e98d39ddc42723b2b052cd8 Mon Sep 17 00:00:00 2001 From: Padreug Date: Sun, 20 Sep 2026 21:53:07 +0200 Subject: [PATCH] feat: nix flake with package, NixOS module and dev shell nix run / nix build produce a wrapped CLI that pins libpcsclite by store path, the package build runs the test suite (so nix flake check is CI), programs.boltcard-writer.enable installs it and turns on pcscd, and nix develop / nix-shell give the uv workflow used on Arch. Co-Authored-By: Claude Fable 5.1 --- README.md | 27 ++++++++++++++---- flake.lock | 27 ++++++++++++++++++ flake.nix | 80 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ shell.nix | 5 ++-- 4 files changed, 132 insertions(+), 7 deletions(-) create mode 100644 flake.lock create mode 100644 flake.nix diff --git a/README.md b/README.md index 56ee18d..6ed8741 100644 --- a/README.md +++ b/README.md @@ -164,14 +164,31 @@ If the user cannot access the reader (permission errors from pcscd), Arch's pcsclite uses polkit: the session must be a local active login, or add a rule for `org.debian.pcsc-lite.access_pcsc` / `access_card`. -## NixOS +## Nix / NixOS -```nix -services.pcscd.enable = true; # ships the ccid driver +The repo is a flake. Run it without installing anything: + +```sh +nix run git+https://git.atitlan.io/aiolabs/boltcard-writer -- readers +nix run git+https://git.atitlan.io/aiolabs/boltcard-writer -- write '' ``` -then in the repo `nix-shell` (provides `uv` + `libpcsclite`) and use -`uv run boltcard-writer …` as above. +pcscd must be running on the host (`services.pcscd.enable = true;` on NixOS, +which also ships the CCID driver). On NixOS the module does that for you: + +```nix +{ + inputs.boltcard-writer.url = "git+https://git.atitlan.io/aiolabs/boltcard-writer"; + # in your configuration: + imports = [ inputs.boltcard-writer.nixosModules.default ]; + programs.boltcard-writer.enable = true; # installs the CLI + pcsc_scan, enables pcscd +} +``` + +`packages.default` / `overlays.default` expose the package on its own. The +package build runs the test suite, so `nix flake check` doubles as CI. For +hacking, `nix develop` (or `nix-shell`) gives `uv`, Python and `libpcsclite`; +then `uv run boltcard-writer …` and `uv run pytest` work as on Arch. ## Development diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..d9c0837 --- /dev/null +++ b/flake.lock @@ -0,0 +1,27 @@ +{ + "nodes": { + "nixpkgs": { + "locked": { + "lastModified": 1789785513, + "narHash": "sha256-B44WL6h0XoLjJ41bUPJk0X5SDinLCII//6EcBLXKiJ0=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "20b1ddd1aa5ace70c9468305030aa4f9ef79671b", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..e622789 --- /dev/null +++ b/flake.nix @@ -0,0 +1,80 @@ +{ + description = "Write, verify and wipe Bolt Cards (NTAG 424 DNA) with a PC/SC NFC reader"; + + inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + + outputs = { self, nixpkgs }: + let + systems = [ "x86_64-linux" "aarch64-linux" ]; + forAllSystems = f: nixpkgs.lib.genAttrs systems (system: f nixpkgs.legacyPackages.${system}); + + mkPackage = pkgs: + pkgs.python3Packages.buildPythonApplication { + pname = "boltcard-writer"; + version = "0.1.0"; + pyproject = true; + src = self; + + build-system = [ pkgs.python3Packages.hatchling ]; + dependencies = with pkgs.python3Packages; [ click cryptography ]; + + # pcsc.py loads libpcsclite via ctypes; pin the store path so the + # binary works on any system that runs pcscd, no LD_LIBRARY_PATH. + makeWrapperArgs = [ "--set-default" "PCSCLITE_LIB" "${pkgs.pcsclite.lib}/lib/libpcsclite.so.1" ]; + + nativeCheckInputs = [ pkgs.python3Packages.pytestCheckHook ]; + + meta = { + description = "Write, verify and wipe Bolt Cards (NTAG 424 DNA) from a Linux PC with a USB NFC reader"; + homepage = "https://git.atitlan.io/aiolabs/boltcard-writer"; + license = pkgs.lib.licenses.mit; + mainProgram = "boltcard-writer"; + platforms = pkgs.lib.platforms.linux; + }; + }; + in + { + packages = forAllSystems (pkgs: rec { + boltcard-writer = mkPackage pkgs; + default = boltcard-writer; + }); + + apps = forAllSystems (pkgs: { + default = { + type = "app"; + program = "${self.packages.${pkgs.system}.default}/bin/boltcard-writer"; + }; + }); + + overlays.default = final: prev: { boltcard-writer = mkPackage final; }; + + # programs.boltcard-writer.enable = true; -> installs the CLI and + # enables pcscd with the CCID driver (what the ACR1252U needs). + nixosModules.default = { config, lib, pkgs, ... }: + let cfg = config.programs.boltcard-writer; in { + options.programs.boltcard-writer = { + enable = lib.mkEnableOption "boltcard-writer and the pcscd it needs"; + package = lib.mkOption { + type = lib.types.package; + default = self.packages.${pkgs.system}.default; + description = "boltcard-writer package to install"; + }; + }; + config = lib.mkIf cfg.enable { + environment.systemPackages = [ cfg.package pkgs.pcsc-tools ]; + services.pcscd.enable = true; + }; + }; + + # `nix flake check` builds the package, which runs the pytest suite. + checks = forAllSystems (pkgs: { package = self.packages.${pkgs.system}.default; }); + + devShells = forAllSystems (pkgs: { + default = pkgs.mkShell { + packages = [ pkgs.uv pkgs.python3 pkgs.pcsclite pkgs.pcsc-tools ]; + PCSCLITE_LIB = "${pkgs.pcsclite.lib}/lib/libpcsclite.so.1"; + UV_PYTHON = "${pkgs.python3}/bin/python3"; + }; + }); + }; +} diff --git a/shell.nix b/shell.nix index 209f00e..8c12ab5 100644 --- a/shell.nix +++ b/shell.nix @@ -1,7 +1,8 @@ -# NixOS / nix users: `nix-shell` then `uv run boltcard-writer ...`. -# pcscd itself must run on the host: services.pcscd.enable = true; +# Non-flake entry point: `nix-shell` then `uv run boltcard-writer ...`. +# Same environment as the flake's devShell. pcscd must run on the host. { pkgs ? import { } }: pkgs.mkShell { packages = [ pkgs.uv pkgs.python3 pkgs.pcsclite pkgs.pcsc-tools ]; PCSCLITE_LIB = "${pkgs.pcsclite.lib}/lib/libpcsclite.so.1"; + UV_PYTHON = "${pkgs.python3}/bin/python3"; }