From 7760c77952101c8537c08c7fb9d87748ca869a29 Mon Sep 17 00:00:00 2001 From: Kevin Veen-Birkenbach Date: Sun, 7 Dec 2025 22:14:29 +0100 Subject: [PATCH] Use pyproject-based Nix flake build and fix install logic for pkgmgr - Add pyproject.toml to define package-manager as a Python application - Declare setuptools build backend (setuptools.build_meta) and wheel - Expose CLI entrypoint via [project.scripts] as `pkgmgr = pkgmgr.cli:main` - Define PyYAML as a base runtime dependency - Update flake.nix to build pkgmgr via python311Packages.buildPythonApplication - Use format = "pyproject" and src = ./. (current git checkout) - Add setuptools and wheel to nativeBuildInputs for the backend - Add pyyaml to propagatedBuildInputs to match pyproject dependencies - Provide a devShell that includes the built pkgmgr, git, and Ansible - Expose `nix run .#pkgmgr` and `nix run .#default` as flake apps - Fix Makefile install target for Nix shells - Treat install as a no-op when IN_NIX_SHELL is set (skip venv / pip) - In non-Nix environments, create ~/.venvs/pkgmgr, install deps, and wire automatic activation into shell rc files - Keep Arch/Manjaro-specific aur_builder/yay setup behind pacman check - Adjust PKGBUILD prepare() to correctly copy the full project tree - Stop excluding the top-level src directory from rsync - Still exclude .git, .github, pkg, and srcpkg This unifies the installation workflow across Arch, Nix, and local venvs, and ensures pkgmgr builds cleanly inside the Docker-based Nix devShell tests. Reference: ChatGPT-assisted refactor & debugging session on 2025-12-07. https://chatgpt.com/share/6935ee1f-6c0c-800f-bb32-434c4051bd1e --- Makefile | 70 ++++++++++++------------- PKGBUILD | 8 ++- flake.nix | 137 ++++++++++++++++++++----------------------------- pyproject.toml | 42 +++++++++++++++ 4 files changed, 137 insertions(+), 120 deletions(-) create mode 100644 pyproject.toml diff --git a/Makefile b/Makefile index c809d57..bcbf3d5 100644 --- a/Makefile +++ b/Makefile @@ -55,43 +55,43 @@ test: build install: @if [ -n "$$IN_NIX_SHELL" ]; then \ echo "Nix shell detected (IN_NIX_SHELL=1). Skipping venv/pip install – handled by Nix flake."; \ - exit 0; \ - fi - @echo "Making 'main.py' executable..." - @chmod +x main.py - @echo "Checking if global user virtual environment exists..." - @mkdir -p "$$HOME/.venvs" - @if [ ! -d "$$HOME/.venvs/pkgmgr" ]; then \ - echo "Creating global venv at $$HOME/.venvs/pkgmgr..."; \ - python3 -m venv "$$HOME/.venvs/pkgmgr"; \ - fi - @echo "Installing required Python packages into $$HOME/.venvs/pkgmgr..." - @$$HOME/.venvs/pkgmgr/bin/python -m ensurepip --upgrade - @$$HOME/.venvs/pkgmgr/bin/pip install --upgrade pip setuptools wheel - @echo "Looking for requirements.txt / _requirements.txt..." - @if [ -f requirements.txt ]; then \ - echo "Installing Python packages from requirements.txt..."; \ - $$HOME/.venvs/pkgmgr/bin/pip install -r requirements.txt; \ - elif [ -f _requirements.txt ]; then \ - echo "Installing Python packages from _requirements.txt..."; \ - $$HOME/.venvs/pkgmgr/bin/pip install -r _requirements.txt; \ else \ - echo "No requirements.txt or _requirements.txt found, skipping dependency installation."; \ + echo "Making 'main.py' executable..."; \ + chmod +x main.py; \ + echo "Checking if global user virtual environment exists..."; \ + mkdir -p "$$HOME/.venvs"; \ + if [ ! -d "$$HOME/.venvs/pkgmgr" ]; then \ + echo "Creating global venv at $$HOME/.venvs/pkgmgr..."; \ + python3 -m venv "$$HOME/.venvs/pkgmgr"; \ + fi; \ + echo "Installing required Python packages into $$HOME/.venvs/pkgmgr..."; \ + "$$HOME/.venvs/pkgmgr/bin/python" -m ensurepip --upgrade; \ + "$$HOME/.venvs/pkgmgr/bin/pip" install --upgrade pip setuptools wheel; \ + echo "Looking for requirements.txt / _requirements.txt..."; \ + if [ -f requirements.txt ]; then \ + echo "Installing Python packages from requirements.txt..."; \ + "$$HOME/.venvs/pkgmgr/bin/pip" install -r requirements.txt; \ + elif [ -f _requirements.txt ]; then \ + echo "Installing Python packages from _requirements.txt..."; \ + "$$HOME/.venvs/pkgmgr/bin/pip" install -r _requirements.txt; \ + else \ + echo "No requirements.txt or _requirements.txt found, skipping dependency installation."; \ + fi; \ + echo "Ensuring $$HOME/.bashrc and $$HOME/.zshrc exist..."; \ + touch "$$HOME/.bashrc" "$$HOME/.zshrc"; \ + echo "Ensuring automatic activation of $$HOME/.venvs/pkgmgr for this user..."; \ + for rc in "$$HOME/.bashrc" "$$HOME/.zshrc"; do \ + rc_line='if [ -d "$${HOME}/.venvs/pkgmgr" ]; then . "$${HOME}/.venvs/pkgmgr/bin/activate"; if [ -n "$${PS1:-}" ]; then echo "Global Python virtual environment '\''~/.venvs/pkgmgr'\'' activated."; fi; fi'; \ + grep -qxF "$${rc_line}" "$$rc" || echo "$${rc_line}" >> "$$rc"; \ + done; \ + echo "Arch/Manjaro detection and optional AUR setup..."; \ + if command -v pacman >/dev/null 2>&1; then \ + $(MAKE) aur_builder_setup; \ + else \ + echo "Not Arch-based (no pacman). Skipping aur_builder/yay setup."; \ + fi; \ + echo "Installation complete. Please restart your shell (or 'exec bash' or 'exec zsh') for the changes to take effect."; \ fi - @echo "Ensuring $$HOME/.bashrc and $$HOME/.zshrc exist..." - @touch "$$HOME/.bashrc" "$$HOME/.zshrc" - @echo "Ensuring automatic activation of $$HOME/.venvs/pkgmgr for this user..." - @for rc in "$$HOME/.bashrc" "$$HOME/.zshrc"; do \ - rc_line='if [ -d "$${HOME}/.venvs/pkgmgr" ]; then . "$${HOME}/.venvs/pkgmgr/bin/activate"; if [ -n "$${PS1:-}" ]; then echo "Global Python virtual environment '\''~/.venvs/pkgmgr'\'' activated."; fi; fi'; \ - grep -qxF "$${rc_line}" "$$rc" || echo "$${rc_line}" >> "$$rc"; \ - done - @echo "Arch/Manjaro detection and optional AUR setup..." - @if command -v pacman >/dev/null 2>&1; then \ - $(MAKE) aur_builder_setup; \ - else \ - echo "Not Arch-based (no pacman). Skipping aur_builder/yay setup."; \ - fi - @echo "Installation complete. Please restart your shell (or 'exec bash' or 'exec zsh') for the changes to take effect." # Only runs on Arch/Manjaro aur_builder_setup: diff --git a/PKGBUILD b/PKGBUILD index 177c4b7..5bb494c 100644 --- a/PKGBUILD +++ b/PKGBUILD @@ -24,13 +24,11 @@ _srcdir_name="source" prepare() { mkdir -p "$srcdir/$_srcdir_name" - - # Copy the full local tree into $srcdir/source, - # but avoid makepkg's own directories and the VCS metadata. rsync -a \ - --exclude="src" \ - --exclude="pkg" \ --exclude=".git" \ + --exclude=".github" \ + --exclude="pkg" \ + --exclude="srcpkg" \ "$startdir/" "$srcdir/$_srcdir_name/" } diff --git a/flake.nix b/flake.nix index 6734a13..6cc9c17 100644 --- a/flake.nix +++ b/flake.nix @@ -13,118 +13,95 @@ let systems = [ "x86_64-linux" "aarch64-linux" ]; - # Helper to build an attribute set for all target systems forAllSystems = f: builtins.listToAttrs (map (system: { name = system; value = f system; }) systems); - in { - # Development shells: `nix develop .#default` - devShells = forAllSystems (system: - let - pkgs = nixpkgs.legacyPackages.${system}; - - # Base Python interpreter - python = pkgs.python311; - - # Python environment with pip + PyYAML so `python -m pip` works - pythonEnv = python.withPackages (ps: with ps; [ - pip - pyyaml - ]); - - # Be robust: use ansible-core if available, otherwise ansible - ansiblePkg = - if pkgs ? ansible-core then pkgs.ansible-core - else pkgs.ansible; - in { - default = pkgs.mkShell { - buildInputs = [ - pythonEnv - pkgs.git - ansiblePkg - ]; - shellHook = '' - echo "Entered pkgmgr development environment for ${system}"; - ''; - }; - } - ); - - # Packages: `nix build .#pkgmgr` or `nix build .#default` + in + { + ########################################################################## + # PACKAGES + ########################################################################## packages = forAllSystems (system: let pkgs = nixpkgs.legacyPackages.${system}; - python = pkgs.python311; - - # Runtime Python for pkgmgr (with pip + PyYAML) - pythonEnv = python.withPackages (ps: with ps; [ - pip - pyyaml - ]); - - # Optional: include Ansible in the runtime closure - ansiblePkg = - if pkgs ? ansible-core then pkgs.ansible-core - else pkgs.ansible; + pyPkgs = pkgs.python311Packages; in rec { - pkgmgr = pkgs.stdenv.mkDerivation { + pkgmgr = pyPkgs.buildPythonApplication { pname = "package-manager"; version = "0.1.1"; - # Use the current repository as the source + # Use the git repo as source src = ./.; - # No traditional configure/build steps - dontConfigure = true; - dontBuild = true; + # Build using pyproject.toml + format = "pyproject"; - # Runtime dependencies: Python (with pip + PyYAML) + Ansible - buildInputs = [ - pythonEnv - ansiblePkg + # Build backend requirements from [build-system] + nativeBuildInputs = [ + pyPkgs.setuptools + pyPkgs.wheel ]; - installPhase = '' - mkdir -p "$out/bin" "$out/lib/package-manager" + # Runtime dependencies (matches [project.dependencies]) + propagatedBuildInputs = [ + pyPkgs.pyyaml + # Add more here if needed, e.g.: + # pyPkgs.click + # pyPkgs.rich + ]; - # Copy the full project tree into the runtime closure - cp -a . "$out/lib/package-manager/" + doCheck = false; - # Wrapper that runs main.py from the copied tree, - # using the pythonEnv interpreter. - cat > "$out/bin/pkgmgr" << 'EOF' -#!${pythonEnv}/bin/python3 -import os -import runpy - -if __name__ == "__main__": - base_dir = os.path.join(os.path.dirname(__file__), "..", "lib", "package-manager") - main_path = os.path.join(base_dir, "main.py") - os.chdir(base_dir) - runpy.run_path(main_path, run_name="__main__") -EOF - - chmod +x "$out/bin/pkgmgr" - ''; + pythonImportsCheck = [ "pkgmgr" ]; }; - - # Default package points to pkgmgr default = pkgmgr; } ); - # Apps: `nix run .#pkgmgr` or `nix run .#default` + ########################################################################## + # DEVELOPMENT SHELL + ########################################################################## + devShells = forAllSystems (system: + let + pkgs = nixpkgs.legacyPackages.${system}; + pkgmgrPkg = self.packages.${system}.pkgmgr; + + ansiblePkg = + if pkgs ? ansible-core then pkgs.ansible-core + else pkgs.ansible; + in + { + default = pkgs.mkShell { + buildInputs = [ + pkgmgrPkg + pkgs.git + ansiblePkg + ]; + + shellHook = '' + echo "Entered pkgmgr development shell for ${system}" + echo "pkgmgr CLI is available via the flake build" + ''; + }; + } + ); + + ########################################################################## + # nix run .#pkgmgr + ########################################################################## apps = forAllSystems (system: let pkgmgrPkg = self.packages.${system}.pkgmgr; - in { + in + { pkgmgr = { type = "app"; program = "${pkgmgrPkg}/bin/pkgmgr"; }; + default = self.apps.${system}.pkgmgr; } ); diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..29bef01 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,42 @@ +[build-system] +requires = [ + "setuptools>=68", + "wheel" +] +build-backend = "setuptools.build_meta" + +[project] +name = "package-manager" +version = "0.1.1" +description = "Kevin's package-manager tool (pkgmgr)" +readme = "README.md" +requires-python = ">=3.11" +license = { text = "MIT" } + +authors = [ + { name = "Kevin Veen-Birkenbach", email = "info@veen.world" } +] + +# Base runtime dependencies +dependencies = [ + "PyYAML>=6.0" +] + +[project.urls] +Homepage = "https://github.com/kevinveenbirkenbach/package-manager" +Source = "https://github.com/kevinveenbirkenbach/package-manager" + +[project.optional-dependencies] +dev = [ + "pytest", + "mypy" +] + +# CLI entrypoint: this is the "pkgmgr" command +[project.scripts] +pkgmgr = "pkgmgr.cli:main" + +# Tell setuptools to find the pkgmgr package +[tool.setuptools.packages.find] +where = ["."] +include = ["pkgmgr*"]