diff --git a/README.md b/README.md index 85ffce6..0238be3 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,57 @@ +[![project chat](https://img.shields.io/badge/zulip-join_chat-brightgreen.svg)](https://nixos.zulipchat.com/#narrow/stream/413950-nix) + # just-flake -Convenient way to use [`just`](https://just.systems/) in your Nix devShells + +Use [`just`](https://just.systems/) in your Nix devShells with re-usable and share-able targets. + +> [!WARNING] +> Module options API is subject to change. + + +## Usage + +In your `flake.nix`: + +```nix +# In flake-parts' perSystem +{ + just-flake.features = { + treefmt.enable = true; + rust.enable = true; + convco.enable = true; + hello = { + enable = true; + justfile = '' + hello: + echo Hello World + ''; + }; + }; +} +``` + +In your `justfile`: + +```just +# See flake.nix (just-flake) +import 'just-flake.just' + +# Display the list of recipes +default: + @just --list +``` + +Then, add `config.just-flake.outputs.devShell` to the `inputsFrom` of your devShell. + +Resulting devShell banner: + +``` +🍎🍎 Run 'just ' to get started +Available recipes: + changelog # Generate CHANGELOG.md using recent commits + default # Display the list of recipes + fmt # Auto-format the source tree using treefmt + hello + test # Run and watch 'cargo test' + w # Compile and watch the project +``` \ No newline at end of file diff --git a/flake-module.nix b/flake-module.nix index 52a5a97..1150695 100644 --- a/flake-module.nix +++ b/flake-module.nix @@ -1,12 +1,64 @@ { - perSystem = { pkgs, ... }: { - devShells.just = pkgs.mkShell { - packages = [ pkgs.just ]; - shellHook = '' - echo - echo "🍎🍎 Run 'just ' to get started" - just - ''; - }; + perSystem = { config, pkgs, lib, ... }: { + options = + let + inherit (lib) types; + in + { + just-flake = lib.mkOption { + default = { }; + type = types.submoduleWith { + specialArgs = { inherit pkgs; }; + modules = [{ + imports = [ + ./nix/features.nix + ]; + + options = { + commonFileName = lib.mkOption { + type = lib.types.str; + default = "just-flake.just"; + description = '' + The name of the common justfile generated by this module. + ''; + }; + + outputs.devShell = lib.mkOption { + type = lib.types.package; + readOnly = true; + description = '' + The output devShell to include in `inputsFrom`. + ''; + }; + }; + }]; + }; + }; + }; + config = + let + cfg = config.just-flake; + in + { + just-flake.outputs.devShell = + let + commonJustfile = pkgs.writeTextFile { + name = "justfile"; + text = + lib.concatStringsSep "\n" + (lib.mapAttrsToList (name: feature: feature.outputs.justfile) cfg.features); + }; + in + pkgs.mkShell { + packages = [ pkgs.just ]; + shellHook = '' + ln -sf ${builtins.toString commonJustfile} ./${cfg.commonFileName} + + echo + echo "🍎🍎 Run 'just ' to get started" + just --list + ''; + }; + }; }; } diff --git a/nix/feature.nix b/nix/feature.nix new file mode 100644 index 0000000..1daca27 --- /dev/null +++ b/nix/feature.nix @@ -0,0 +1,32 @@ +{ config, name, lib, pkgs, ... }: + +{ + options = { + enable = lib.mkEnableOption "Enable this feature"; + justfile = lib.mkOption { + type = lib.types.either lib.types.str lib.types.path; + description = '' + The justfile representing this feature. + ''; + apply = x: + if builtins.isPath x then x else + pkgs.writeTextFile { + name = "${name}.just"; + text = x; + }; + }; + outputs.justfile = lib.mkOption { + type = lib.types.str; + readOnly = true; + description = '' + The justfile code for importing this feature's justfile. + + See https://just.systems/man/en/chapter_53.html + ''; + default = + if config.enable + then "import '${builtins.toString config.justfile}'" + else ""; + }; + }; +} diff --git a/nix/features.nix b/nix/features.nix new file mode 100644 index 0000000..b811de6 --- /dev/null +++ b/nix/features.nix @@ -0,0 +1,61 @@ +# This largely inspired by the use of freeformType in +# https://github.com/cachix/git-hooks.nix/blob/master/modules/hooks.nix +{ pkgs, lib, ... }: + +let + inherit (lib) types; + featureMod = { + imports = [ ./feature.nix ]; + config._module.args = { inherit pkgs; }; + }; + featureType = types.submodule featureMod; +in +{ + imports = [{ + options.features = lib.mkOption { + type = types.submoduleWith { + modules = [{ freeformType = types.attrsOf featureType; }]; + specialArgs = { inherit pkgs; }; + }; + default = { }; + }; + }]; + + # NOTE: At somepoint, we may want to add `settings` options to some of these features. + options.features = { + convco = lib.mkOption { + description = "Add the 'changelog' target calling convco"; + type = types.submodule { imports = [ featureMod ]; }; + }; + rust = lib.mkOption { + description = "Add 'w' and 'test' targets for running cargo"; + type = types.submodule { imports = [ featureMod ]; }; + }; + treefmt = lib.mkOption { + description = "Add the 'fmt' target to format source tree using treefmt"; + type = types.submodule { imports = [ featureMod ]; }; + }; + }; + + config.features = lib.mapAttrs (_: lib.mapAttrs (_: lib.mkDefault)) { + convco.justfile = '' + # Generate CHANGELOG.md using recent commits + changelog: + convco changelog -p "" + ''; + rust.justfile = '' + # Compile and watch the project + w: + cargo watch + + # Run and watch 'cargo test' + test: + cargo watch -s "cargo test" + ''; + treefmt.justfile = '' + # Auto-format the source tree using treefmt + fmt: + treefmt + ''; + }; +}