Add reproducible Nix dev environment

Replace the Python venv with a Nix devshell pinning ansible-core 2.21.3,
ansible-lint, yamllint and a Python with proxmoxer/requests. The Python
dependencies share the interpreter that runs ansible, so pve-*.yml plays
on implicit localhost can import proxmoxer without inventory changes.

The shellHook exports absolute ANSIBLE_CONFIG, ANSIBLE_INVENTORY,
ANSIBLE_ROLES_PATH and ANSIBLE_COLLECTIONS_PATH, so commands work from
the repository root as well as from ansible/.

Also un-ignore .envrc, which the global gitignore hides, and ignore the
stray .ansible/ runtime directory.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GTocXkGUUazHdKKd3r9k71
This commit is contained in:
Dmitry
2026-08-26 22:09:59 +03:00
co-authored by Claude Opus 5
parent c676be81ec
commit b953909e0a
4 changed files with 123 additions and 0 deletions
+3
View File
@@ -0,0 +1,3 @@
# direnv: load the Nix dev shell defined in flake.nix
# Enable once per checkout with: direnv allow
use flake
+6
View File
@@ -20,3 +20,9 @@ __pycache__/
.direnv/ .direnv/
result result
result-* result-*
# direnv shell hook lives in the repo (global gitignore hides it)
!.envrc
# Ansible local runtime dir (facts cache, locks)
.ansible/
Generated
+27
View File
@@ -0,0 +1,27 @@
{
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1787498568,
"narHash": "sha256-9i/VTdusq/+NM/tz+J1Re+ojkMB8MBf0QshnYfzHz30=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "56c02bc00adcf003215cc4bd996d6efaf4cff188",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"nixpkgs": "nixpkgs"
}
}
},
"root": "root",
"version": 7
}
+87
View File
@@ -0,0 +1,87 @@
{
description = "HomeLab infras reproducible Ansible control-plane dev shell";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
};
outputs = { self, nixpkgs }:
let
systems = [ "x86_64-linux" "aarch64-linux" ];
forAllSystems = f:
nixpkgs.lib.genAttrs systems (system: f nixpkgs.legacyPackages.${system});
in
{
devShells = forAllSystems (pkgs:
let
# One Python environment carrying ansible-core *and* the libraries the
# modules import in-process (community.proxmox needs proxmoxer +
# requests). Ansible sets ansible_python_interpreter for the implicit
# localhost to sys.executable, so `hosts: localhost` and
# `delegate_to: localhost` tasks pick these up automatically.
pythonEnv = pkgs.python3.withPackages (ps: with ps; [
ansible-core
proxmoxer
requests
]);
in
{
default = pkgs.mkShell {
name = "homelab-infras";
packages = [
pythonEnv
pkgs.ansible-lint
pkgs.yamllint
pkgs.git
pkgs.jq
pkgs.openssh
pkgs.curl
pkgs.gnumake
];
shellHook = ''
repo_root="$(${pkgs.git}/bin/git rev-parse --show-toplevel 2>/dev/null || pwd)"
ansible_dir="$repo_root/ansible"
if [ -f "$ansible_dir/ansible.cfg" ]; then
# ansible.cfg spells its paths relative to ansible/, so pin them
# absolutely here that makes the shell usable from the repo
# root as well as from inside ansible/.
export ANSIBLE_CONFIG="$ansible_dir/ansible.cfg"
export ANSIBLE_INVENTORY="$ansible_dir/inventory/hosts.yml"
export ANSIBLE_ROLES_PATH="$ansible_dir/roles"
export ANSIBLE_COLLECTIONS_PATH="$ansible_dir/collections"
else
echo "warn: $ansible_dir/ansible.cfg not found run commands from ansible/" >&2
fi
# Banner goes to stderr so `nix develop --command ...` keeps a
# clean stdout for scripting.
{
_ver_ansible="$(ansible --version 2>/dev/null | head -n1 | tr -d '[]' | awk '{print $3}')"
_ver_lint="$(NO_COLOR=1 ansible-lint --version 2>/dev/null | tail -n1 | awk '{print $2}')"
_ver_yaml="$(yamllint --version 2>/dev/null | awk '{print $2}')"
_ver_py="$(python3 --version 2>&1 | awk '{print $2}')"
echo "HomeLab infras dev shell"
echo " ansible-core $_ver_ansible"
echo " ansible-lint $_ver_lint"
echo " yamllint $_ver_yaml"
echo " python $_ver_py (proxmoxer, requests)"
echo ""
echo "ANSIBLE_CONFIG=$ANSIBLE_CONFIG"
echo ""
echo "Galaxy collections are NOT installed automatically. Run once:"
echo " ansible-galaxy collection install -r $ansible_dir/requirements.yml -p $ansible_dir/collections"
echo ""
unset _ver_ansible _ver_lint _ver_yaml _ver_py
} >&2
'';
};
});
formatter = forAllSystems (pkgs: pkgs.nixpkgs-fmt);
};
}