Development scripts
It's common to have development scripts that you don't want to publish in a Python package, but that you might want to run ergonomically from Nix.
This pattern shows how to:
- Take a directory of development scripts (
examples/
) - Wrap the scripts in a virtualenv
- Make scripts runnable using
nix run
flake.nix
{
description = "Using Nix Flake apps to run scripts with uv2nix";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
pyproject-nix = {
url = "github:pyproject-nix/pyproject.nix";
inputs.nixpkgs.follows = "nixpkgs";
};
uv2nix = {
url = "github:pyproject-nix/uv2nix";
inputs.pyproject-nix.follows = "pyproject-nix";
inputs.nixpkgs.follows = "nixpkgs";
};
pyproject-build-systems = {
url = "github:pyproject-nix/build-system-pkgs";
inputs.pyproject-nix.follows = "pyproject-nix";
inputs.uv2nix.follows = "uv2nix";
inputs.nixpkgs.follows = "nixpkgs";
};
};
outputs =
{
nixpkgs,
uv2nix,
pyproject-nix,
pyproject-build-systems,
...
}:
let
inherit (nixpkgs) lib;
inherit (lib) filterAttrs hasSuffix;
workspace = uv2nix.lib.workspace.loadWorkspace { workspaceRoot = ./.; };
overlay = workspace.mkPyprojectOverlay {
sourcePreference = "wheel"; # or sourcePreference = "sdist";
};
pyprojectOverrides = _final: _prev: {
# Implement build fixups here.
};
pkgs = nixpkgs.legacyPackages.x86_64-linux;
python = pkgs.python312;
pythonSet =
(pkgs.callPackage pyproject-nix.build.packages {
inherit python;
}).overrideScope
(
lib.composeManyExtensions [
pyproject-build-systems.overlays.default
overlay
pyprojectOverrides
]
);
venv = pythonSet.mkVirtualEnv "development-scripts-default-env" workspace.deps.default;
in
{
apps.x86_64-linux =
let
# Example base directory
basedir = ./examples;
# Get a list of regular Python files in example directory
files = filterAttrs (name: type: type == "regular" && hasSuffix ".py" name) (
builtins.readDir basedir
);
in
# Map over files to:
# - Rewrite script shebangs as shebangs pointing to the virtualenv
# - Strip .py suffixes from attribute names
# Making a script "greet.py" runnable as "nix run .#greet"
lib.mapAttrs' (
name: _:
lib.nameValuePair (lib.removeSuffix ".py" name) (
let
script = basedir + "/${name}";
# Patch script shebang
program = pkgs.runCommand name { buildInputs = [ venv ]; } ''
cp ${script} $out
chmod +x $out
patchShebangs $out
'';
in
{
type = "app";
program = "${program}";
}
)
) files;
};
}