@@ -103,6 +103,12 @@ MXDEV?=mxdev@git+https://github.com/mxstack/mxdev.git
103103# Default: mxmake
104104MXMAKE? =-e .
105105
106+ # How to invoke QA tools. 'uvx' (default) runs tools via uvx
107+ # without installing them into the venv. 'venv' installs tools into the
108+ # virtual environment (legacy behavior).
109+ # Default: uvx
110+ TOOL_RUNNER? =uvx
111+
106112# # qa.ruff
107113
108114# Source folder to scan for Python files to run ruff on.
@@ -119,11 +125,10 @@ RUFF_FIXES?=false
119125# Default: false
120126RUFF_UNSAFE_FIXES? =false
121127
122- # # qa.isort
123-
124- # Source folder to scan for Python files to run isort on.
125- # Default: src
126- ISORT_SRC? =src
128+ # Ruff version to use with uvx (e.g., 0.6.8).
129+ # Leave empty for latest. Only used when TOOL_RUNNER=uvx.
130+ # No default value.
131+ RUFF_VERSION? =
127132
128133# # docs.sphinx
129134
@@ -167,6 +172,11 @@ TY_SRC?=src
167172# No default value.
168173TY_PYTHON_VERSION? =
169174
175+ # ty version to use with uvx (e.g., 0.0.1a1).
176+ # Leave empty for latest. Only used when TOOL_RUNNER=uvx.
177+ # No default value.
178+ TY_VERSION? =
179+
170180# # qa.test
171181
172182# The command which gets executed. Defaults to the location the
@@ -275,6 +285,29 @@ else
275285UV_OUTDATED: =false
276286endif
277287
288+ # #############################################################################
289+ # Tool Runner Configuration
290+ # #############################################################################
291+
292+ ifeq ("$(TOOL_RUNNER ) ","uvx")
293+ # uvx mode: tools run via uvx, no installation into venv needed
294+ SKIP_TOOL_INSTALL: =true
295+ # RUN_TOOL(tool_name, version, args)
296+ # Example: $(call RUN_TOOL,ruff,0.6.8,check src)
297+ # If version is empty: uvx ruff check src
298+ # If version is set: uvx ruff==0.6.8 check src
299+ define RUN_TOOL
300+ uvx $(1 )$(if $(strip $(2 ) ) ,==$(strip $(2 ) ) ,) $(3 )
301+ endef
302+ else
303+ # venv mode: tools installed in venv, direct invocation
304+ SKIP_TOOL_INSTALL: =false
305+ # RUN_TOOL(tool_name, version, args) - version ignored in venv mode
306+ define RUN_TOOL
307+ $(1 ) $(3 )
308+ endef
309+ endif
310+
278311MXENV_TARGET: =$(SENTINEL_FOLDER ) /mxenv.sentinel
279312$(MXENV_TARGET ) : $(SENTINEL )
280313 # Validation: Check Python version if not using global uv
@@ -363,24 +396,31 @@ RUFF_FIX_FLAGS=--fix
363396endif
364397endif
365398
399+ # Installation target (only in venv mode)
400+ ifneq ("$(SKIP_TOOL_INSTALL ) ","true")
366401RUFF_TARGET: =$(SENTINEL_FOLDER ) /ruff.sentinel
367402$(RUFF_TARGET ) : $(MXENV_TARGET )
368403 @echo " Install Ruff"
369404 @$(PYTHON_PACKAGE_COMMAND ) install ruff
370405 @touch $(RUFF_TARGET )
406+ INSTALL_TARGETS+ =$(RUFF_TARGET )
407+ endif
408+
409+ # Conditional dependency
410+ RUFF_DEPENDENCY: =$(if $(filter true,$(SKIP_TOOL_INSTALL ) ) ,,$(RUFF_TARGET ) )
371411
372412.PHONY : ruff-check
373- ruff-check : $(RUFF_TARGET )
413+ ruff-check : $(RUFF_DEPENDENCY )
374414 @echo " Run ruff check"
375- @ruff check $(RUFF_SRC )
415+ @$( call RUN_TOOL,ruff, $( RUFF_VERSION ) , check $(RUFF_SRC ) )
376416
377417.PHONY : ruff-format
378- ruff-format : $(RUFF_TARGET )
418+ ruff-format : $(RUFF_DEPENDENCY )
379419 @echo " Run ruff format"
380- @ruff format $(RUFF_SRC )
420+ @$( call RUN_TOOL,ruff, $( RUFF_VERSION ) , format $(RUFF_SRC ) )
381421ifeq ("$(RUFF_FIXES ) ","true")
382422 @echo "Run ruff check $(RUFF_FIX_FLAGS)"
383- @ruff check $(RUFF_FIX_FLAGS) $(RUFF_SRC)
423+ @$(call RUN_TOOL,ruff,$(RUFF_VERSION), check $(RUFF_FIX_FLAGS) $(RUFF_SRC) )
384424endif
385425
386426.PHONY : ruff-dirty
@@ -389,10 +429,10 @@ ruff-dirty:
389429
390430.PHONY : ruff-clean
391431ruff-clean : ruff-dirty
432+ ifneq ("$(SKIP_TOOL_INSTALL ) ","true")
392433 @test -e $(MXENV_PYTHON) && $(MXENV_PYTHON) -m pip uninstall -y ruff || :
434+ endif
393435 @rm -rf .ruff_cache
394-
395- INSTALL_TARGETS+ =$(RUFF_TARGET )
396436CHECK_TARGETS+ =ruff-check
397437FORMAT_TARGETS+ =ruff-format
398438DIRTY_TARGETS+ =ruff-dirty
@@ -558,27 +598,34 @@ ifneq ($(TY_PYTHON_VERSION),)
558598TY_FLAGS+ =--python-version $(TY_PYTHON_VERSION )
559599endif
560600
601+ # Installation target (only in venv mode)
602+ ifneq ("$(SKIP_TOOL_INSTALL ) ","true")
561603TY_TARGET: =$(SENTINEL_FOLDER ) /ty.sentinel
562604$(TY_TARGET ) : $(MXENV_TARGET )
563605 @echo " Install ty"
564606 @$(PYTHON_PACKAGE_COMMAND ) install ty
565607 @touch $(TY_TARGET )
608+ INSTALL_TARGETS+ =$(TY_TARGET )
609+ endif
610+
611+ # Conditional dependency
612+ TY_DEPENDENCY: =$(if $(filter true,$(SKIP_TOOL_INSTALL ) ) ,,$(TY_TARGET ) )
566613
567614.PHONY : ty
568- ty : $(PACKAGES_TARGET ) $(TY_TARGET )
615+ ty : $(PACKAGES_TARGET ) $(TY_DEPENDENCY )
569616 @echo " Run ty"
570- @ty check $(TY_FLAGS ) $(TY_SRC )
617+ @$( call RUN_TOOL,ty, $( TY_VERSION ) , check $(TY_FLAGS ) $(TY_SRC ) )
571618
572619.PHONY : ty-dirty
573620ty-dirty :
574621 @rm -f $(TY_TARGET )
575622
576623.PHONY : ty-clean
577624ty-clean : ty-dirty
625+ ifneq ("$(SKIP_TOOL_INSTALL ) ","true")
578626 @test -e $(MXENV_PYTHON) && $(MXENV_PYTHON) -m pip uninstall -y ty || :
627+ endif
579628 @rm -rf .ty
580-
581- INSTALL_TARGETS+ =$(TY_TARGET )
582629CHECK_TARGETS+ =ty
583630TYPECHECK_TARGETS+ =ty
584631CLEAN_TARGETS+ =ty-clean
0 commit comments