From 872b1dfb47b16693637cab95a6d3c5e0c2db43ce Mon Sep 17 00:00:00 2001 From: Andrew Reisner Date: Wed, 11 Feb 2026 18:06:49 -0500 Subject: [PATCH] add nix flake. --- .gitignore | 5 ++ README.md | 64 ++++++++++++++ flake.lock | 118 +++++++++++++++++++++++++ flake.nix | 255 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 442 insertions(+) create mode 100644 flake.lock create mode 100644 flake.nix diff --git a/.gitignore b/.gitignore index 800a4e2c0954886e86f38ae1a0d88ba8d69962a8..b8bd7753c628b612ecb3851517d8aac8f6403974 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,8 @@ compile_commands.json .clangd/ .cache/ + +# Nix +result +result-* +.direnv/ diff --git a/README.md b/README.md index 25208997743f902ccea065978ad942adeaddfbdd..e20f96e153c6dff69552fe123eb257c4e068153d 100644 --- a/README.md +++ b/README.md @@ -57,3 +57,67 @@ This can also be used to control which fork is used, though only upstream `qmk_f 1. (First time only) `git submodule add https://github.com/qmk/qmk_firmware.git` 1. (To update) `git submodule update --init --recursive` 1. Commit your changes to your userspace repository + +## Building with Nix + +This repository includes a Nix flake for reproducible builds. The flake automatically fetches the correct QMK firmware fork for each keyboard type: + +| Keyboard | QMK Fork | +|----------|----------| +| ZSA Voyager | [zsa/qmk_firmware](https://github.com/zsa/qmk_firmware) | +| Keyball39 | [holykeebs/qmk_firmware](https://github.com/holykeebs/qmk_firmware) | +| Ploopyco Trackball | [qmk/qmk_firmware](https://github.com/qmk/qmk_firmware) (upstream) | + +### Available packages + +| Package | Keyboard | Keymap | +|---------|----------|--------| +| `voyager-pers` | zsa/voyager | pers | +| `voyager-ball` | zsa/voyager | ball | +| `voyager-mod` | zsa/voyager | mod | +| `keyball39-init` | keyball/keyball39 | init | +| `ploopyco-trackball-rev1_005-pers` | ploopyco/trackball/rev1_005 | pers | +| `ploopyco-trackball-rev1_007-pers` | ploopyco/trackball/rev1_007 | pers | +| `all` | All keyboards | - | + +### Build a specific keyboard + +```bash +nix build .#voyager-pers +nix build .#keyball39-init +nix build .#ploopyco-trackball-rev1_007-pers +``` + +Firmware files will be in `./result/`. + +### Build all keyboards + +```bash +nix build +``` + +### Development shell + +Enter a shell with all QMK tooling (AVR/ARM toolchains, Python dependencies, flashing utilities): + +```bash +nix develop +``` + +### Flash firmware + +```bash +# ZSA Voyager (using wally-cli) +nix run .#flash-voyager + +# Or manually after building +wally-cli ./result/zsa_voyager_pers.bin +``` + +### Update QMK firmware versions + +```bash +nix flake update +``` + +This updates all QMK firmware forks to their latest versions. The `flake.lock` file pins specific commits for reproducibility. diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000000000000000000000000000000000000..77999a4b39508c712c0277a2faf1a16bf5647b1d --- /dev/null +++ b/flake.lock @@ -0,0 +1,118 @@ +{ + "nodes": { + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "keyball-qmk-firmware": { + "flake": false, + "locked": { + "lastModified": 1763193823, + "narHash": "sha256-MB3MnvXprF4LsnFB0tTHRi8SWiBaCfrWXjQWX1BQYNA=", + "ref": "refs/heads/hk-master", + "rev": "19fd205b8411ce787d26c899cc5ad09ee4d77cb6", + "revCount": 28836, + "submodules": true, + "type": "git", + "url": "https://github.com/holykeebs/qmk_firmware" + }, + "original": { + "submodules": true, + "type": "git", + "url": "https://github.com/holykeebs/qmk_firmware" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1770562336, + "narHash": "sha256-ub1gpAONMFsT/GU2hV6ZWJjur8rJ6kKxdm9IlCT0j84=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "d6c71932130818840fc8fe9509cf50be8c64634f", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "qmk-firmware": { + "flake": false, + "locked": { + "lastModified": 1770841410, + "narHash": "sha256-BCLpMbcO3IS8qxRcfLff7HvjnsBKAlBweAVTGLpKDZc=", + "ref": "refs/heads/master", + "rev": "45ccd2e9351b979f8eb8fa359db732e5374b0d53", + "revCount": 29267, + "submodules": true, + "type": "git", + "url": "https://github.com/qmk/qmk_firmware" + }, + "original": { + "submodules": true, + "type": "git", + "url": "https://github.com/qmk/qmk_firmware" + } + }, + "root": { + "inputs": { + "flake-utils": "flake-utils", + "keyball-qmk-firmware": "keyball-qmk-firmware", + "nixpkgs": "nixpkgs", + "qmk-firmware": "qmk-firmware", + "zsa-qmk-firmware": "zsa-qmk-firmware" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, + "zsa-qmk-firmware": { + "flake": false, + "locked": { + "lastModified": 1769675113, + "narHash": "sha256-JQ2A7UHvClZg2PhIbIxWMW8+3qWyPZpcNsCqSqkjY28=", + "ref": "refs/heads/firmware25", + "rev": "2f7463bddd017600910958528a2632f26bd67a70", + "revCount": 28639, + "submodules": true, + "type": "git", + "url": "https://github.com/zsa/qmk_firmware" + }, + "original": { + "submodules": true, + "type": "git", + "url": "https://github.com/zsa/qmk_firmware" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000000000000000000000000000000000000..8ce1df38b55081afe7fb6f067fc2b06dfc422bf4 --- /dev/null +++ b/flake.nix @@ -0,0 +1,255 @@ +{ + description = "QMK Userspace - Personal keyboard firmware configurations"; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + flake-utils.url = "github:numtide/flake-utils"; + + # Upstream QMK firmware (for Ploopyco and other standard keyboards) + qmk-firmware = { + url = "git+https://github.com/qmk/qmk_firmware?submodules=1"; + flake = false; + }; + + # ZSA fork of QMK firmware (for Voyager, Moonlander, Planck EZ, Ergodox EZ) + zsa-qmk-firmware = { + url = "git+https://github.com/zsa/qmk_firmware?submodules=1"; + flake = false; + }; + + # Holykeebs fork of QMK firmware (for Keyball keyboards) + keyball-qmk-firmware = { + url = "git+https://github.com/holykeebs/qmk_firmware?submodules=1"; + flake = false; + }; + }; + + outputs = { self, nixpkgs, flake-utils, qmk-firmware, zsa-qmk-firmware, keyball-qmk-firmware }: + flake-utils.lib.eachDefaultSystem (system: + let + pkgs = import nixpkgs { + inherit system; + config.allowUnfree = true; + }; + + # Common QMK build dependencies + qmkBuildInputs = with pkgs; [ + # AVR toolchain (for ATmega-based keyboards like Ploopyco) + pkgsCross.avr.buildPackages.gcc + avrdude + + # ARM toolchain (for ARM-based keyboards like Voyager, Keyball) + gcc-arm-embedded + + # Build tools + gnumake + git + which + diffutils + util-linux + + # Python with QMK dependencies + (python3.withPackages (ps: with ps; [ + pip + setuptools + wheel + pillow + qmk + hid + pyusb + hjson + jsonschema + milc + pygments + dotty-dict + ])) + + # Additional dependencies + libusb1 + dfu-programmer + dfu-util + teensy-loader-cli + qmk + ]; + + # Helper to link userspace into QMK firmware + setupUserspace = qmkSrc: pkgs.runCommand "qmk-with-userspace" {} '' + cp -r ${qmkSrc} $out + chmod -R +w $out + + # Link keyboards from userspace + if [ -d ${self}/keyboards ]; then + for kb in ${self}/keyboards/*; do + kbname=$(basename $kb) + if [ -d "$out/keyboards/$kbname" ]; then + # Merge with existing keyboard (copy keymaps) + cp -r $kb/* $out/keyboards/$kbname/ 2>/dev/null || true + else + # New keyboard, copy entirely + cp -r $kb $out/keyboards/ + fi + done + fi + + # Link users directory + if [ -d ${self}/users ]; then + cp -r ${self}/users/* $out/users/ 2>/dev/null || true + fi + + # Link layouts + if [ -d ${self}/layouts ]; then + for layout in ${self}/layouts/*; do + layoutname=$(basename $layout) + mkdir -p $out/layouts/$layoutname + cp -r $layout/* $out/layouts/$layoutname/ 2>/dev/null || true + done + fi + ''; + + # QMK sources with userspace integrated + qmkUpstream = setupUserspace qmk-firmware; + qmkZsa = setupUserspace zsa-qmk-firmware; + qmkKeyball = setupUserspace keyball-qmk-firmware; + + # Build a QMK firmware + buildFirmware = { name, keyboard, keymap, qmkSource }: + pkgs.stdenv.mkDerivation { + pname = "qmk-${name}"; + version = "0.1.0"; + + src = qmkSource; + + nativeBuildInputs = qmkBuildInputs; + + buildPhase = '' + runHook preBuild + + # QMK needs a writable home directory + export HOME="$NIX_BUILD_TOP/home" + mkdir -p $HOME + + # Build the firmware + make ${keyboard}:${keymap} + + runHook postBuild + ''; + + installPhase = '' + runHook preInstall + + mkdir -p $out + # Copy all generated firmware files + find . -maxdepth 1 \( -name "*.hex" -o -name "*.bin" -o -name "*.uf2" \) \ + -exec cp {} $out/ \; + + runHook postInstall + ''; + + # Don't try to strip firmware files + dontStrip = true; + dontPatchELF = true; + dontFixup = true; + }; + + # Define keyboard builds + keyboards = { + # ZSA Voyager - uses ZSA QMK fork + voyager-pers = buildFirmware { + name = "voyager-pers"; + keyboard = "zsa/voyager"; + keymap = "pers"; + qmkSource = qmkZsa; + }; + + voyager-ball = buildFirmware { + name = "voyager-ball"; + keyboard = "zsa/voyager"; + keymap = "ball"; + qmkSource = qmkZsa; + }; + + voyager-mod = buildFirmware { + name = "voyager-mod"; + keyboard = "zsa/voyager"; + keymap = "mod"; + qmkSource = qmkZsa; + }; + + # Keyball39 - uses holykeebs QMK fork + keyball39-init = buildFirmware { + name = "keyball39-init"; + keyboard = "keyball/keyball39"; + keymap = "init"; + qmkSource = qmkKeyball; + }; + + # Ploopyco Trackball - uses upstream QMK + ploopyco-trackball-rev1_005-pers = buildFirmware { + name = "ploopyco-trackball-rev1_005-pers"; + keyboard = "ploopyco/trackball/rev1_005"; + keymap = "pers"; + qmkSource = qmkUpstream; + }; + + ploopyco-trackball-rev1_007-pers = buildFirmware { + name = "ploopyco-trackball-rev1_007-pers"; + keyboard = "ploopyco/trackball/rev1_007"; + keymap = "pers"; + qmkSource = qmkUpstream; + }; + }; + + in { + # Individual keyboard packages + packages = keyboards // { + # Build all keyboards + all = pkgs.symlinkJoin { + name = "qmk-all-keyboards"; + paths = builtins.attrValues keyboards; + }; + + default = self.packages.${system}.all; + }; + + # Development shell with QMK tooling + devShells.default = pkgs.mkShell { + buildInputs = qmkBuildInputs ++ (with pkgs; [ + # Additional dev tools + qmk + ]); + + shellHook = '' + echo "QMK Userspace Development Environment" + echo "" + echo "Available keyboards:" + echo " ZSA (use zsa/qmk_firmware):" + echo " - zsa/voyager:pers" + echo " - zsa/voyager:ball" + echo " - zsa/voyager:mod" + echo "" + echo " Keyball (use holykeebs/qmk_firmware):" + echo " - keyball/keyball39:init" + echo "" + echo " Ploopyco (use upstream qmk_firmware):" + echo " - ploopyco/trackball/rev1_005:pers" + echo " - ploopyco/trackball/rev1_007:pers" + echo "" + echo "Build with: nix build .#" + echo " e.g.: nix build .#voyager-pers" + echo " nix build .#keyball39-init" + echo " nix build .#all" + echo "" + ''; + }; + + # Apps for flashing firmware + apps = { + flash-voyager = flake-utils.lib.mkApp { + drv = pkgs.writeShellScriptBin "flash-voyager" '' + ${pkgs.wally-cli}/bin/wally-cli ${self.packages.${system}.voyager-pers}/*.bin + ''; + }; + }; + } + ); +}