From 3a6605e584eaa31bbd0ba2edad7acb373c57d5ea Mon Sep 17 00:00:00 2001 From: Matthew Hrehirchuk Date: Tue, 3 Mar 2026 21:37:23 -0700 Subject: [PATCH 1/3] chore: set up nix flake, devshell --- .gitignore | 3 ++ flake.lock | 116 ++++++++++++++++++++++++++++++++++++++++++++ flake.nix | 140 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 259 insertions(+) create mode 100644 flake.lock create mode 100644 flake.nix diff --git a/.gitignore b/.gitignore index 3e685c8..288a658 100644 --- a/.gitignore +++ b/.gitignore @@ -45,3 +45,6 @@ tmp/ # Temporary files *.tmp *.bak + +# Nix build result +/result diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..776aed7 --- /dev/null +++ b/flake.lock @@ -0,0 +1,116 @@ +{ + "nodes": { + "crane": { + "locked": { + "lastModified": 1772560058, + "narHash": "sha256-NuVKdMBJldwUXgghYpzIWJdfeB7ccsu1CC7B+NfSoZ8=", + "owner": "ipetkov", + "repo": "crane", + "rev": "db590d9286ed5ce22017541e36132eab4e8b3045", + "type": "github" + }, + "original": { + "owner": "ipetkov", + "repo": "crane", + "type": "github" + } + }, + "flake-parts": { + "inputs": { + "nixpkgs-lib": "nixpkgs-lib" + }, + "locked": { + "lastModified": 1772408722, + "narHash": "sha256-rHuJtdcOjK7rAHpHphUb1iCvgkU3GpfvicLMwwnfMT0=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "f20dc5d9b8027381c474144ecabc9034d6a839a3", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, + "helix-src": { + "flake": false, + "locked": { + "lastModified": 1752849097, + "narHash": "sha256-RFSzGAcB0mMg/02ykYfTWXzQjLFu2CJ4BkS5HZ/6pBo=", + "owner": "helix-editor", + "repo": "helix", + "rev": "a05c151bb6e8e9c65ec390b0ae2afe7a5efd619b", + "type": "github" + }, + "original": { + "owner": "helix-editor", + "ref": "25.07.1", + "repo": "helix", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1772554988, + "narHash": "sha256-8Kb+MSE6QYVX1S96aZOluOMVfvSEOs70vgX980qVUaY=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "87f6b6e02cb3f87a1be4f939326c94c8af9d55d8", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-lib": { + "locked": { + "lastModified": 1772328832, + "narHash": "sha256-e+/T/pmEkLP6BHhYjx6GmwP5ivonQQn0bJdH9YrRB+Q=", + "owner": "nix-community", + "repo": "nixpkgs.lib", + "rev": "c185c7a5e5dd8f9add5b2f8ebeff00888b070742", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "nixpkgs.lib", + "type": "github" + } + }, + "root": { + "inputs": { + "crane": "crane", + "flake-parts": "flake-parts", + "helix-src": "helix-src", + "nixpkgs": "nixpkgs", + "rust-overlay": "rust-overlay" + } + }, + "rust-overlay": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1772593411, + "narHash": "sha256-47WOnCSyOL6AghZiMIJaTLWM359DHe3be9R1cNCdGUE=", + "owner": "oxalica", + "repo": "rust-overlay", + "rev": "a741b36b77440f5db15fcf2ab6d7d592d2f9ee8f", + "type": "github" + }, + "original": { + "owner": "oxalica", + "repo": "rust-overlay", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..3c9bc9b --- /dev/null +++ b/flake.nix @@ -0,0 +1,140 @@ +{ + description = "Interactive trainer for learning Helix editor keybindings"; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; + flake-parts.url = "github:hercules-ci/flake-parts"; + rust-overlay = { + url = "github:oxalica/rust-overlay"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + crane.url = "github:ipetkov/crane"; + helix-src = { + url = "github:helix-editor/helix/25.07.1"; + flake = false; + }; + }; + + outputs = inputs: + inputs.flake-parts.lib.mkFlake {inherit inputs;} { + systems = ["x86_64-linux" "aarch64-linux" "x86_64-darwin" "aarch64-darwin"]; + + perSystem = { + self', + pkgs, + lib, + system, + ... + }: let + rustToolchain = pkgs.rust-bin.stable."1.89.0".default; + craneLib = (inputs.crane.mkLib pkgs).overrideToolchain rustToolchain; + + nativeBuildInputs = with pkgs; [ + pkg-config + rustToolchain + ]; + + buildInputs = with pkgs; + [ + oniguruma # syntect regex-onig + ] + ++ lib.optionals stdenv.isLinux [ + alsa-lib # rodio audio playback + ] + ++ lib.optionals stdenv.isDarwin [ + darwin.apple_sdk.frameworks.AudioUnit + darwin.apple_sdk.frameworks.CoreAudio + darwin.apple_sdk.frameworks.CoreFoundation + ]; + + # Patch the vendored deps to include languages.toml that + # helix-loader expects via include_bytes!("../../languages.toml") + vendorDir = craneLib.vendorCargoDeps {src = ./.;}; + patchedVendorDir = pkgs.runCommand "patched-vendor-deps" {} '' + cp -rL ${vendorDir} $out + chmod -R +w $out + for loader_dir in $out/*/helix-loader-*; do + if [ -d "$loader_dir" ]; then + cp ${inputs.helix-src}/languages.toml "$(dirname "$loader_dir")/languages.toml" + fi + done + substituteInPlace $out/config.toml \ + --replace-fail "${vendorDir}" "$out" + ''; + + src = let + # Start with crane's standard Rust source filter + cargoFilter = craneLib.filterCargoSources; + # Also include non-Rust assets embedded via include_bytes!/include_str! + assetsFilter = path: _type: let + relPath = lib.removePrefix (toString ./. + "/") (toString path); + in + lib.hasPrefix "assets/" relPath + || lib.hasPrefix "quests/" relPath + || lib.hasPrefix "scenarios/" relPath + || lib.hasPrefix "locales/" relPath; + in + lib.cleanSourceWith { + src = ./.; + filter = path: type: (cargoFilter path type) || (assetsFilter path type); + }; + + commonArgs = { + inherit src; + strictDeps = true; + cargoVendorDir = patchedVendorDir; + inherit nativeBuildInputs buildInputs; + RUSTONIG_SYSTEM_LIBONIG = "1"; + }; + + cargoArtifacts = craneLib.buildDepsOnly commonArgs; + in { + _module.args.pkgs = import inputs.nixpkgs { + inherit system; + overlays = [(import inputs.rust-overlay)]; + }; + + packages = { + helix-trainer = craneLib.buildPackage (commonArgs + // { + inherit cargoArtifacts; + # Some tests require HOME/filesystem access unavailable in the sandbox + doCheck = false; + meta = { + description = "Interactive trainer for learning Helix editor keybindings"; + homepage = "https://github.com/bug-ops/helix-trainer"; + license = lib.licenses.mit; + mainProgram = "helix-trainer"; + }; + }); + default = self'.packages.helix-trainer; + }; + + checks = { + inherit (self'.packages) helix-trainer; + + clippy = craneLib.cargoClippy (commonArgs + // { + inherit cargoArtifacts; + cargoClippyExtraArgs = "--all-targets -- --deny warnings"; + }); + + fmt = craneLib.cargoFmt { + inherit src; + }; + }; + + devShells.default = craneLib.devShell { + checks = self'.checks; + + packages = with pkgs; [ + cargo-watch + cargo-deny + ]; + + inherit buildInputs; + RUSTONIG_SYSTEM_LIBONIG = "1"; + }; + }; + }; +} From cff9155820db4153d57380e600910f80090a852e Mon Sep 17 00:00:00 2001 From: Matthew Hrehirchuk Date: Tue, 3 Mar 2026 21:40:36 -0700 Subject: [PATCH 2/3] docs: add nix flake notes in readme, contributing --- CONTRIBUTING.md | 4 +++- README.md | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6f3bab2..622f356 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -19,10 +19,12 @@ We follow a strict PR-based workflow. All changes go through: git clone https://github.com/bug-ops/helix-trainer.git cd helix-trainer +# Enter the dev shell (provides Rust 1.89, cargo-watch, cargo-deny, and native deps) +nix develop + # Create feature branch git checkout -b feature/amazing-feature ``` - ## Pre-Commit Checks > [!IMPORTANT] diff --git a/README.md b/README.md index 762a80e..6182fa2 100644 --- a/README.md +++ b/README.md @@ -95,6 +95,24 @@ cd helix-trainer-*/ > [!TIP] > Verify checksums with `sha256sum -c helix-trainer-*.sha256` +### Nix + +Run directly without installing: + +```bash +nix run github:bug-ops/helix-trainer +``` + +Or add to your NixOS/home-manager configuration: + +```nix +# flake.nix +inputs.helix-trainer.url = "github:bug-ops/helix-trainer"; + +# configuration.nix or home.nix +environment.systemPackages = [ inputs.helix-trainer.packages.${system}.default ]; +``` + ### Build from source > [!WARNING] From 94de4068d1e86570e2d1954bae2e5ad0cf40e1e3 Mon Sep 17 00:00:00 2001 From: Matthew Hrehirchuk Date: Tue, 3 Mar 2026 21:43:49 -0700 Subject: [PATCH 3/3] chore: add nextest to nix flake --- flake.nix | 1 + 1 file changed, 1 insertion(+) diff --git a/flake.nix b/flake.nix index 3c9bc9b..f20deac 100644 --- a/flake.nix +++ b/flake.nix @@ -130,6 +130,7 @@ packages = with pkgs; [ cargo-watch cargo-deny + cargo-nextest ]; inherit buildInputs;