cv/flake.nix
Konstantin Fickel a1a8ddf893
All checks were successful
Continuous Integration / Build Package (push) Successful in 1m31s
Continuous Integration / Lint, Check & Test (push) Successful in 1m36s
build: switch from devenv to nix flakes with uv2nix
- Replace devenv setup with pure nix flakes using uv2nix, pyproject-nix,
  pyproject-build-systems, and git-hooks.nix (adapted from hokusai)
- Add forgejo CI pipeline with lint/check/test and build jobs
- Wrap cv binary with LD_LIBRARY_PATH for weasyprint native dependencies
- Move basedpyright to dev dependency group, add pytest and ruff
- Fix package imports to use hatch source remapping (src/ layout)
- Update .envrc to use flake, expand .gitignore

BREAKING CHANGE: devenv is no longer used, run direnv allow to reload
2026-03-05 21:25:55 +01:00

232 lines
6.6 KiB
Nix

{
description = "Generate a pretty CV from a Markdown File";
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";
};
git-hooks = {
url = "github:cachix/git-hooks.nix";
inputs.nixpkgs.follows = "nixpkgs";
};
};
outputs =
{
self,
nixpkgs,
pyproject-nix,
uv2nix,
pyproject-build-systems,
git-hooks,
...
}:
let
inherit (nixpkgs) lib;
forAllSystems = lib.genAttrs lib.systems.flakeExposed;
workspace = uv2nix.lib.workspace.loadWorkspace { workspaceRoot = ./.; };
overlay = workspace.mkPyprojectOverlay {
sourcePreference = "wheel";
};
editableOverlay = workspace.mkEditablePyprojectOverlay {
root = "$REPO_ROOT";
};
pythonSets = forAllSystems (
system:
let
pkgs = nixpkgs.legacyPackages.${system};
inherit (pkgs) stdenv;
baseSet = pkgs.callPackage pyproject-nix.build.packages {
python = pkgs.python313;
};
pyprojectOverrides = _final: prev: {
cv = prev.cv.overrideAttrs (old: {
passthru = old.passthru // {
tests = (old.passthru.tests or { }) // {
pytest = stdenv.mkDerivation {
name = "${_final.cv.name}-pytest";
inherit (_final.cv) src;
nativeBuildInputs = [
(_final.mkVirtualEnv "cv-pytest-env" {
cv = [ "dev" ];
})
];
dontConfigure = true;
buildPhase = ''
runHook preBuild
# Exit code 5 means no tests collected allow it so the
# check succeeds on an empty test suite.
pytest || [ $? -eq 5 ]
runHook postBuild
'';
installPhase = ''
runHook preInstall
touch $out
runHook postInstall
'';
};
};
};
});
};
in
baseSet.overrideScope (
lib.composeManyExtensions [
pyproject-build-systems.overlays.default
overlay
pyprojectOverrides
]
)
);
mkGitHooksCheck =
system:
let
pkgs = nixpkgs.legacyPackages.${system};
pythonSet = pythonSets.${system};
venv = pythonSet.mkVirtualEnv "cv-check-env" workspace.deps.default;
in
git-hooks.lib.${system}.run {
src = ./.;
hooks = {
basedpyright = {
enable = true;
entry = "${pkgs.basedpyright}/bin/basedpyright --pythonpath ${venv}/bin/python --project ${
pkgs.writeText "pyrightconfig.json" (
builtins.toJSON {
reportMissingTypeStubs = false;
reportUnnecessaryTypeIgnoreComment = false;
}
)
}";
files = "\\.py$";
types = [ "file" ];
};
ruff.enable = true;
ruff-format.enable = true;
commitizen.enable = true;
};
};
in
{
packages = forAllSystems (
system:
let
pythonSet = pythonSets.${system};
pkgs = nixpkgs.legacyPackages.${system};
inherit (pkgs.callPackages pyproject-nix.build.util { }) mkApplication;
weasyprintLibs = pkgs.lib.makeLibraryPath [
pkgs.glib
pkgs.pango
pkgs.harfbuzz
pkgs.fontconfig
pkgs.gdk-pixbuf
];
unwrapped = mkApplication {
venv = pythonSet.mkVirtualEnv "cv-env" workspace.deps.default;
package = pythonSet.cv;
};
in
rec {
cv =
pkgs.runCommand "cv-${unwrapped.version or "0.1.0"}"
{
nativeBuildInputs = [ pkgs.makeWrapper ];
meta = unwrapped.meta or { };
}
''
mkdir -p $out/bin
for bin in ${unwrapped}/bin/*; do
makeWrapper "$bin" "$out/bin/$(basename "$bin")" \
--prefix LD_LIBRARY_PATH : "${weasyprintLibs}"
done
'';
default = cv;
}
);
checks = forAllSystems (
system:
let
pythonSet = pythonSets.${system};
in
{
inherit (pythonSet.cv.passthru.tests) pytest;
pre-commit = mkGitHooksCheck system;
}
);
devShells = forAllSystems (
system:
let
pkgs = nixpkgs.legacyPackages.${system};
editablePyprojectOverrides = _final: prev: {
cv = prev.cv.overrideAttrs (old: {
nativeBuildInputs = (old.nativeBuildInputs or [ ]) ++ [
_final.editables
];
});
};
pythonSet = pythonSets.${system}.overrideScope (
lib.composeManyExtensions [
editableOverlay
editablePyprojectOverrides
]
);
virtualenv = pythonSet.mkVirtualEnv "cv-dev-env" workspace.deps.all;
in
{
default = pkgs.mkShell {
packages = [
virtualenv
pkgs.uv
];
env = {
UV_NO_SYNC = "1";
UV_PYTHON = pythonSet.python.interpreter;
UV_PYTHON_DOWNLOADS = "never";
LD_LIBRARY_PATH = lib.makeLibraryPath [
pkgs.glib
pkgs.pango
pkgs.harfbuzz
pkgs.fontconfig
pkgs.gdk-pixbuf
];
};
shellHook = ''
unset PYTHONPATH
export REPO_ROOT=$(git rev-parse --show-toplevel)
${(mkGitHooksCheck system).shellHook}
'';
};
}
);
};
}