Something went wrong. Try again.
Helpers for shell scripting in your `nix develop` environment
Something went wrong. Try again.
README.md
Nix Command Utils #
Helpers for defining commands for Nix shells under Flakes.
Quickstart #
{
inputs = {
command-utils.url = "github:expede/nix-command-utils";
flake-utils.url = "github:numtide/flake-utils";
# ...
};
outputs = {self, flake-utils, command-utils}:
flake-utils.lib.eachDefaultSystem (system:
let
# Cargo just for example
cargo = "${pkgs.cargo}/bin/cargo";
cmd = command-utils.cmd.${system};
withHelp = command-utils.withHelp.${system};
command_menu = command-utils.commands.${system} {
hello = cmd "Print a hello world message" "echo 'Hello, world!'";
"project:build" = cmd "Build the project"
"${cargo} build";
"project:test" = cmd "Run tests"
"${cargo} test";
};
#...
in
devShells.default = pkgs.mkShell {
nativeBuildInputs = with pkgs;
[
command_menu
# ...
];
#...
};
}
);
}
$ menu
____ _
/ ___|___ _ __ ___ _ __ ___ __ _ _ __ __| |___
| | / _ \| '_ ` _ \| '_ ` _ \ / _` | '_ \ / _` / __|
| |__| (_) | | | | | | | | | | | (_| | | | | (_| \__ \
\____\___/|_| |_| |_|_| |_| |_|\__,_|_| |_|\__,_|___/
┌───────────────┬───────────────────────────────┐
│ Command │ Description │
╞═══════════════╪═══════════════════════════════╡
│ hello │ Print a hello world message │
├───────────────┼───────────────────────────────┤
│ project:build │ Build the project │
├───────────────┼───────────────────────────────┤
│ project:test │ Run tests │
└───────────────┴───────────────────────────────┘
$ hello
⚙️ Running hello...
Hello, world!
Adding Help Text #
Use withHelp to add detailed help text accessible via --help or -h:
command_menu = command-utils.commands.${system} {
hello = cmd "Print a hello world message" "echo 'Hello, world!'";
"rust:bench" = withHelp ''
Usage: rust:bench [OPTIONS]
Options:
--release Run in release mode
--save FILE Save results to FILE
Examples:
rust:bench --release
rust:bench --save baseline.json
'' (cmd "Run benchmarks" "${cargo} bench");
};
$ rust:bench --help
rust:bench - Run benchmarks
Usage: rust:bench [OPTIONS]
Options:
--release Run in release mode
--save FILE Save results to FILE
Examples:
rust:bench --release
rust:bench --save baseline.json
Commands without withHelp pass --help through to the underlying script.
Rust Commands #
Pre-built command sets for Rust development, organized by module:
| Module | Commands |
|---|---|
rust.test |
rust:test, rust:test:doc, rust:test:watch |
rust.bench |
rust:bench, rust:bench:criterion, rust:bench:open |
rust.lint |
rust:clippy, rust:clippy:pedantic, rust:clippy:fix |
rust.fmt |
rust:fmt, rust:fmt:check |
rust.build |
rust:build, rust:build:release, rust:check |
rust.doc |
rust:doc, rust:doc:open, rust:doc:check |
rust.watch |
rust:watch:build, rust:watch:clippy, rust:watch:test |
rust.audit |
rust:audit |
rust.semver |
rust:semver |
rust.msrv |
rust:msrv:verify, rust:msrv:find |
rust.ci |
rust:ci (fmt, clippy, test, doc, release build) |
rust.all |
All of the above |
Importing Specific Modules #
let
cargo = "${rust-toolchain}/bin/cargo";
cargo-watch = "${pkgs.cargo-watch}/bin/cargo-watch";
rust = command-utils.rust.${system};
command_menu = command-utils.commands.${system} (
# Pick only what you need
rust.test { inherit cargo cargo-watch; }
// rust.lint { inherit cargo; }
// rust.fmt { inherit cargo; }
// {
# Add your own commands
hello = cmd "Say hello" "echo 'Hello!'";
}
);
in
# ...
Importing All Rust Commands #
let
cargo = "${rust-toolchain}/bin/cargo";
cargo-watch = "${pkgs.cargo-watch}/bin/cargo-watch";
cargo-criterion = "${pkgs.cargo-criterion}/bin/cargo-criterion";
cargo-audit = "${pkgs.cargo-audit}/bin/cargo-audit";
cargo-semver-checks = "${pkgs.cargo-semver-checks}/bin/cargo-semver-checks";
xdg-open = "${pkgs.xdg-utils}/bin/xdg-open";
rust = command-utils.rust.${system};
command_menu = command-utils.commands.${system} (
rust.all {
inherit cargo cargo-watch cargo-criterion cargo-audit cargo-semver-checks xdg-open;
}
// {
# Add project-specific commands
"db:migrate" = cmd "Run database migrations" "${cargo} run --bin migrate";
}
);
in
# ...
Module Parameters #
Each module takes a configuration attrset. Optional tools enable additional commands:
| Module | Required | Optional |
|---|---|---|
rust.test |
cargo |
cargo-watch → rust:test:watch |
rust.bench |
cargo |
cargo-criterion → rust:bench:criterion, xdg-open → rust:bench:open |
rust.lint |
cargo |
— |
rust.fmt |
cargo |
— |
rust.build |
cargo |
— |
rust.doc |
cargo |
— |
rust.watch |
cargo-watch |
— |
rust.audit |
cargo-audit |
— |
rust.semver |
cargo-semver-checks |
— |
rust.msrv |
cargo |
cargo-msrv → rust:msrv:find |
rust.ci |
cargo |
— |
rust.all |
cargo |
cargo-watch, cargo-criterion, cargo-audit, cargo-semver-checks, cargo-msrv, xdg-open |
pnpm Commands #
Pre-built command sets for JavaScript/TypeScript development with pnpm:
| Module | Commands |
|---|---|
pnpm.install |
pnpm:install, pnpm:install:frozen |
pnpm.build |
pnpm:build, pnpm:build:watch |
pnpm.dev |
pnpm:dev |
pnpm.test |
pnpm:test, pnpm:test:watch, pnpm:test:coverage |
pnpm.lint |
pnpm:lint, pnpm:lint:fix |
pnpm.fmt |
pnpm:fmt, pnpm:fmt:check |
pnpm.typecheck |
pnpm:typecheck, pnpm:typecheck:watch |
pnpm.ci |
pnpm:ci (install, lint, typecheck, test, build) |
pnpm.all |
All of the above |
Importing Specific Modules #
let
pnpm' = "${pkgs.pnpm}/bin/pnpm";
pnpm = command-utils.pnpm.${system};
command_menu = command-utils.commands.${system} (
pnpm.install { pnpm = pnpm'; }
// pnpm.test { pnpm = pnpm'; }
// pnpm.lint { pnpm = pnpm'; }
);
in
# ...
Importing All pnpm Commands #
let
pnpm' = "${pkgs.pnpm}/bin/pnpm";
pnpm = command-utils.pnpm.${system};
command_menu = command-utils.commands.${system} (
pnpm.all { pnpm = pnpm'; }
// {
# Add project-specific commands
storybook = cmd "Start Storybook" "${pnpm'} storybook";
}
);
in
# ...
Module Parameters #
All pnpm modules require only the pnpm executable path.
Wasm Commands #
Pre-built command sets for wasm-pack and wasm-bindgen development:
| Module | Commands |
|---|---|
wasm.build |
wasm:build:web, wasm:build:nodejs, wasm:build:bundler |
wasm.release |
wasm:release:web, wasm:release:nodejs, wasm:release:bundler |
wasm.test |
wasm:test:node, wasm:test:chrome, wasm:test:firefox, wasm:test:safari |
wasm.doc |
wasm:doc, wasm:doc:open |
wasm.watch |
wasm:watch:build, wasm:watch:test |
wasm.ci |
wasm:ci (test node + chrome) |
wasm.all |
All of the above |
Importing Wasm Commands #
let
wasm-pack = "${pkgs.wasm-pack}/bin/wasm-pack";
cargo = "${rust-toolchain}/bin/cargo";
cargo-watch = "${pkgs.cargo-watch}/bin/cargo-watch";
gzip = "${pkgs.gzip}/bin/gzip";
wasm = command-utils.wasm.${system};
command_menu = command-utils.commands.${system} (
wasm.build { inherit wasm-pack; }
// wasm.release { inherit wasm-pack gzip; }
// wasm.test { inherit wasm-pack; features = "browser_test"; }
);
in
# ...
Monorepo Support #
For projects where Wasm lives in a subdirectory, use the path parameter:
wasm.build { inherit wasm-pack; path = "./my_wasm_crate"; }
wasm.release { inherit wasm-pack gzip; path = "./my_wasm_crate"; }
wasm.test { inherit wasm-pack; path = "./my_wasm_crate"; }
Module Parameters #
| Module | Required | Optional |
|---|---|---|
wasm.build |
wasm-pack |
path (default: .) |
wasm.release |
wasm-pack |
path, gzip → compress output |
wasm.test |
wasm-pack |
path, features → --features flag |
wasm.doc |
cargo |
xdg-open → wasm:doc:open |
wasm.watch |
cargo-watch |
— |
wasm.ci |
wasm-pack |
path, features |
wasm.all |
wasm-pack, cargo |
path, gzip, features, cargo-watch, xdg-open |
Project Structure #
nix-command-utils/
├── flake.nix # Main entry point
├── lib/
│ ├── core.nix # cmd, withHelp, command, commands
│ ├── rust.nix # Rust command modules
│ ├── pnpm.nix # pnpm command modules
│ └── wasm.nix # Wasm command modules
├── README.md
└── LICENSE