11# NetLab Development Makefile
22
3- .PHONY : help dev install check check-ci lint format test qt clean build check-dist publish-test publish info
3+ .PHONY : help venv clean-venv dev install check check-ci lint format test qt build clean check-dist publish-test publish info hooks check-python
44
55.DEFAULT_GOAL := help
66
7- VENV_BIN := $(PWD ) /netlab-venv/bin
8- PYTHON := $(if $(wildcard $(VENV_BIN ) /python) ,$(VENV_BIN ) /python,python3)
7+ # --------------------------------------------------------------------------
8+ # Python interpreter detection
9+ # --------------------------------------------------------------------------
10+ # VENV_BIN: path to local virtualenv bin directory
11+ VENV_BIN := $(PWD ) /venv/bin
12+
13+ # PY_BEST: scan for newest supported Python (used when creating new venvs)
14+ # Supports 3.11-3.13 to match common CI matrix
15+ PY_BEST := $(shell for v in 3.13 3.12 3.11; do command -v python$$v >/dev/null 2>&1 && { echo python$$v; exit 0; }; done; command -v python3 2>/dev/null || command -v python 2>/dev/null)
16+
17+ # PY_PATH: active python3/python on PATH (respects CI setup-python and activated venvs)
18+ PY_PATH := $(shell command -v python3 2>/dev/null || command -v python 2>/dev/null)
19+
20+ # PYTHON: interpreter used for all commands
21+ # 1. Use local venv if present
22+ # 2. Otherwise use active python on PATH (important for CI)
23+ # 3. Fall back to best available version
24+ # 4. Final fallback to 'python3' literal for clear error messages
25+ PYTHON ?= $(if $(wildcard $(VENV_BIN ) /python) ,$(VENV_BIN ) /python,$(if $(PY_PATH ) ,$(PY_PATH ) ,$(if $(PY_BEST ) ,$(PY_BEST ) ,python3) ) )
26+
27+ # Derived tool commands (always use -m to ensure correct environment)
928PIP := $(PYTHON ) -m pip
1029PYTEST := $(PYTHON ) -m pytest
1130RUFF := $(PYTHON ) -m ruff
@@ -15,12 +34,14 @@ help:
1534 @echo " 🔧 NetLab Development Commands"
1635 @echo " "
1736 @echo " Setup & Installation:"
18- @echo " make install - Install package for usage (no dev dependencies )"
37+ @echo " make venv - Create a local virtualenv (./venv )"
1938 @echo " make dev - Full development environment (package + dev deps + hooks)"
39+ @echo " make install - Install package for usage (no dev dependencies)"
40+ @echo " make clean-venv - Remove virtual environment"
2041 @echo " "
2142 @echo " Code Quality & Testing:"
22- @echo " make check - Run lint + tests"
23- @echo " make check-ci - Run non-mutating checks and tests (CI entrypoint)"
43+ @echo " make check - Run pre-commit (auto-fix) + tests, then lint "
44+ @echo " make check-ci - Run non-mutating lint + tests (CI entrypoint)"
2445 @echo " make lint - Run only linting (non-mutating: ruff + pyright)"
2546 @echo " make format - Auto-format code with ruff"
2647 @echo " make test - Run tests with coverage"
@@ -35,19 +56,56 @@ help:
3556 @echo " "
3657 @echo " Utilities:"
3758 @echo " make info - Show project information"
59+ @echo " make hooks - Run pre-commit on all files"
60+ @echo " make check-python - Check if venv Python matches best available"
3861
3962dev :
4063 @echo " 🚀 Setting up development environment..."
41- @bash dev/setup-dev.sh
64+ @if [ ! -x " $( VENV_BIN) /python" ]; then \
65+ if [ -z " $( PY_BEST) " ]; then \
66+ echo " ❌ Error: No Python interpreter found (python3 or python)" ; \
67+ exit 1; \
68+ fi ; \
69+ echo " 🐍 Creating virtual environment with $( PY_BEST) ..." ; \
70+ $(PY_BEST ) -m venv venv || { echo " ❌ Failed to create venv" ; exit 1; }; \
71+ if [ ! -x " $( VENV_BIN) /python" ]; then \
72+ echo " ❌ Error: venv creation failed - $( VENV_BIN) /python not found" ; \
73+ exit 1; \
74+ fi ; \
75+ $(VENV_BIN ) /python -m pip install -U pip setuptools wheel; \
76+ fi
77+ @echo " 📦 Installing dev dependencies..."
78+ @$(VENV_BIN ) /python -m pip install -e .' [dev]'
79+ @echo " 🔗 Installing pre-commit hooks..."
80+ @$(VENV_BIN ) /python -m pre_commit install --install-hooks
81+ @echo " ✅ Dev environment ready. Activate with: source venv/bin/activate"
82+ @$(MAKE ) check-python
83+
84+ venv :
85+ @echo " 🐍 Creating virtual environment in ./venv ..."
86+ @if [ -z " $( PY_BEST) " ]; then \
87+ echo " ❌ Error: No Python interpreter found (python3 or python)" ; \
88+ exit 1; \
89+ fi
90+ @$(PY_BEST ) -m venv venv || { echo " ❌ Failed to create venv" ; exit 1; }
91+ @if [ ! -x " $( VENV_BIN) /python" ]; then \
92+ echo " ❌ Error: venv creation failed - $( VENV_BIN) /python not found" ; \
93+ exit 1; \
94+ fi
95+ @$(VENV_BIN ) /python -m pip install -U pip setuptools wheel
96+ @echo " ✅ venv ready. Activate with: source venv/bin/activate"
97+
98+ clean-venv :
99+ @rm -rf venv/
42100
43101install :
44102 @echo " 📦 Installing package for usage (no dev dependencies)..."
45103 @$(PIP ) install -e .
46104
47105check :
48106 @echo " 🔍 Running complete code quality checks and tests..."
49- @$(MAKE ) lint
50107 @PYTHON=$(PYTHON ) bash dev/run-checks.sh
108+ @$(MAKE ) lint
51109
52110check-ci :
53111 @echo " 🔍 Running CI checks (non-mutating lint + tests)..."
@@ -83,14 +141,13 @@ build:
83141
84142clean :
85143 @echo " 🧹 Cleaning build artifacts and cache files..."
86- @rm -rf build/
87- @rm -rf dist/
88- @rm -rf * .egg-info/
89- @find . -type f -name " *.pyc" -delete
90- @find . -type d -name " __pycache__" -exec rm -rf {} + 2> /dev/null || true
91- @find . -type f -name " *.pyo" -delete
92- @find . -type f -name " *~" -delete
93- @find . -type f -name " *.orig" -delete
144+ @rm -rf build/ dist/ * .egg-info/
145+ @rm -rf .pytest_cache .ruff_cache .mypy_cache htmlcov .coverage coverage.xml coverage-* .xml .benchmarks .pytest-benchmark || true
146+ @find . -path " ./venv" -prune -o -type f -name " *.pyc" -delete 2> /dev/null || true
147+ @find . -path " ./venv" -prune -o -type d -name " __pycache__" -exec rm -rf {} + 2> /dev/null || true
148+ @find . -path " ./venv" -prune -o -type f -name " *.pyo" -delete 2> /dev/null || true
149+ @find . -path " ./venv" -prune -o -type f -name " *~" -delete 2> /dev/null || true
150+ @find . -path " ./venv" -prune -o -type f -name " *.orig" -delete 2> /dev/null || true
94151 @echo " ✅ Cleanup complete!"
95152
96153check-dist :
@@ -125,7 +182,9 @@ info:
125182 @echo " ============================="
126183 @echo " "
127184 @echo " 🐍 Python Environment:"
128- @echo " Python version: $$ ($( PYTHON) --version)"
185+ @echo " Python (active): $$ ($( PYTHON) --version)"
186+ @echo " Python (best): $$ ($( PY_BEST) --version 2>/dev/null || echo 'missing')"
187+ @$(MAKE ) check-python
129188 @echo " Package version: $$ ($( PYTHON) -c 'import importlib.metadata as m; print(m.version(" netlab" ))' 2>/dev/null || echo 'Not installed')"
130189 @echo " Virtual environment: $$ (echo $$ VIRTUAL_ENV | sed 's|.*/||' || echo 'None active')"
131190 @echo " "
@@ -136,3 +195,17 @@ info:
136195 @echo " Pyright: $$ ($( PYTHON) -m pyright --version 2>/dev/null | head -1 || echo 'Not installed')"
137196 @echo " Build: $$ ($( PYTHON) -m build --version 2>/dev/null | sed 's/build //' | sed 's/ (.*//' || echo 'Not installed')"
138197 @echo " Twine: $$ ($( PYTHON) -m twine --version 2>/dev/null | grep -o 'twine version [0-9.]*' | cut -d' ' -f3 || echo 'Not installed')"
198+
199+ hooks :
200+ @echo " 🔗 Running pre-commit on all files..."
201+ @$(PRECOMMIT ) run --all-files || (echo " Some pre-commit hooks failed. Fix and re-run." && exit 1)
202+
203+ check-python :
204+ @if [ -x " $( VENV_BIN) /python" ]; then \
205+ VENV_VER=$$($(VENV_BIN ) /python -c "import sys; print(f'{sys.version_info.major}.{sys.version_info.minor}') " 2>/dev/null || echo " unknown" ); \
206+ BEST_VER=$$($(PY_BEST ) -c "import sys; print(f'{sys.version_info.major}.{sys.version_info.minor}') " 2>/dev/null || echo " unknown" ); \
207+ if [ -n " $$ VENV_VER" ] && [ -n " $$ BEST_VER" ] && [ " $$ VENV_VER" != " $$ BEST_VER" ]; then \
208+ echo " ⚠️ WARNING: venv Python ($$ VENV_VER) != best available Python ($$ BEST_VER)" ; \
209+ echo " Run 'make clean-venv && make dev' to recreate venv if desired" ; \
210+ fi ; \
211+ fi
0 commit comments