Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 47 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,54 @@ jobs:
include-hidden-files: true
if-no-files-found: error

# No uv CLI exists in any MSYS2 repo (the msys `uv` package only ships the

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but what about https://packages.msys2.org/base/mingw-w64-uv? it may be broken a bit (msys2/MINGW-packages#21091), but it exists

# uv_build backend), so the uv backend is exercised via the standalone uv
# binary from setup-uv
msys2:
runs-on: windows-latest
continue-on-error: true
defaults:
run:
shell: msys2 {0}
steps:
- uses: actions/checkout@v6.0.3
with:
persist-credentials: false
- uses: msys2/setup-msys2@v2.32.0
with:
msystem: MINGW64

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we recommend using UCRT64 environment, because MINGW64 is about to be deprecated

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! I'll try to make a follow up soon

update: true
# Inherit the runner PATH so the standalone uv (added to GITHUB_PATH
# by setup-uv, which runs after this step) is visible to MinGW Python
path-type: inherit
install: >-
mingw-w64-x86_64-python
mingw-w64-x86_64-python-pip
mingw-w64-x86_64-python-virtualenv
mingw-w64-x86_64-gcc
- name: Setup uv
uses: astral-sh/setup-uv@v8.2.0
- name: Install Nox-under-test
run: python -m pip install .
# Force virtualenv so the session venv stays a MinGW interpreter
# (_IS_MINGW=True). uv stays on PATH, so the uv-backend tests still
# build real uv venvs from within MinGW
- name: Run tests under MinGW
run: |
pyver=$(python -c "import sys; print('{}.{}'.format(*sys.version_info))")
echo "MSYS2 MinGW Python: $pyver"
python -c "import shutil; print('uv discovered at:', shutil.which('uv'))"
nox --session "tests-$pyver" --force-venv-backend virtualenv -- --durations=10
- name: Save coverage report
uses: actions/upload-artifact@v7
with:
name: coverage-${{ github.job }}
path: .coverage.*
include-hidden-files: true
if-no-files-found: error

coverage:
needs: build
needs: [build, msys2]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6.0.3
Expand Down
6 changes: 5 additions & 1 deletion noxfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,13 @@
import platform
import shutil
import sys
import sysconfig

import nox

ON_WINDOWS_CI = "CI" in os.environ and sys.platform.startswith("win32")
# uv has no MinGW wheel and won't build there; its tests skip without it.
IS_MINGW = sysconfig.get_platform().startswith("mingw")

nox.needs_version = ">=2025.02.09"
nox.options.default_venv_backend = "uv|virtualenv"
Expand All @@ -53,7 +56,8 @@ def tests(session: nox.Session) -> None:
parallel = [] if sys.platform.startswith("win32") else ["--numprocesses=auto"]

session.create_tmp() # Fixes permission errors on Windows
session.install(*PYPROJECT["dependency-groups"]["test"], "uv")
uv = [] if IS_MINGW else ["uv"]
session.install(*PYPROJECT["dependency-groups"]["test"], *uv)
session.install("-e.[tox-to-nox,pbs]")
session.run("coverage", "erase", env=env)
session.run(
Expand Down
13 changes: 13 additions & 0 deletions tests/test_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import shutil
import subprocess
import sys
import sysconfig
from importlib import metadata
from pathlib import Path
from typing import TYPE_CHECKING, Any, Literal
Expand All @@ -39,6 +40,15 @@
RESOURCES = os.path.join(os.path.dirname(__file__), "resources")
VERSION = metadata.version("nox")

# Script mode installs deps with uv, which can't inspect the MinGW interpreter.
# Fixed by #1117; xfail (non-strict) keeps the experimental MSYS2 job green.
IS_MINGW = sysconfig.get_platform().startswith("mingw")
xfail_mingw_uv = pytest.mark.xfail(
IS_MINGW,
reason="script mode needs uv, which can't inspect the MinGW interpreter (#1088, fixed by #1117)",
strict=False,
)


# This is needed because CI systems will mess up these tests due to the
# way Nox handles the --session parameter's default value. This avoids that
Expand Down Expand Up @@ -1128,6 +1138,7 @@ def test_symlink_sym_not(monkeypatch: pytest.MonkeyPatch) -> None:
assert res.returncode == 1


@xfail_mingw_uv
def test_noxfile_script_mode(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.delenv("NOX_SCRIPT_MODE", raising=False)
job = subprocess.run(
Expand Down Expand Up @@ -1172,6 +1183,7 @@ def test_noxfile_no_script_mode(monkeypatch: pytest.MonkeyPatch) -> None:
assert "No module named 'cowsay'" in job.stderr


@xfail_mingw_uv
def test_noxfile_script_mode_exec(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.delenv("NOX_SCRIPT_MODE", raising=False)
job = subprocess.run(
Expand All @@ -1192,6 +1204,7 @@ def test_noxfile_script_mode_exec(monkeypatch: pytest.MonkeyPatch) -> None:
assert "another_world" in job.stdout


@xfail_mingw_uv
def test_noxfile_script_mode_url_req() -> None:
job = subprocess.run(
[
Expand Down
27 changes: 24 additions & 3 deletions tests/test_virtualenv.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,22 @@
from nox.virtualenv import CondaEnv, ProcessEnv, VirtualEnv

IS_WINDOWS = sys.platform.startswith("win")
# Under MSYS2/MinGW, sys.platform is "win32" but the native venv layout is POSIX
# ("bin"/"python"), so Windows-layout assertions must exclude this case.
IS_MINGW = nox.virtualenv._IS_MINGW
HAS_UV = shutil.which("uv") is not None
RAISE_ERROR = "RAISE_ERROR"
VIRTUALENV_VERSION = metadata.version("virtualenv")

has_uv = pytest.mark.skipif(not HAS_UV, reason="Missing uv command.")
# Under MinGW, uv cannot inspect the native interpreter, so creating a uv venv
# from it fails. Fixed by #1117; xfail (non-strict) keeps the experimental MSYS2
# job green until then, and lets these xpass once the fix lands.
xfail_mingw_uv = pytest.mark.xfail(
IS_MINGW,
reason="uv can't inspect the MinGW interpreter (#1088, fixed by #1117)",
strict=False,
)


class TextProcessResult(NamedTuple):
Expand Down Expand Up @@ -465,6 +476,7 @@ def test_create_args_old_uv(
assert run_mock.call_args.args[0][-1] != "--clear"


@xfail_mingw_uv
@has_uv
def test_uv_creation(
make_one: Callable[..., tuple[VirtualEnv, Path]],
Expand Down Expand Up @@ -591,10 +603,12 @@ def test_bin_paths(
assert len(venv.bin_paths) == 1
assert venv.bin_paths[0] == venv.bin

assert str(dir_.joinpath("Scripts" if IS_WINDOWS else "bin")) == venv.bin
win_layout = IS_WINDOWS and not IS_MINGW
assert str(dir_.joinpath("Scripts" if win_layout else "bin")) == venv.bin


@mock.patch("nox.virtualenv._PLATFORM", new="win32")
@mock.patch("nox.virtualenv._IS_MINGW", new=False)
def test_bin_windows(
make_one: Callable[..., tuple[VirtualEnv | ProcessEnv, Path]],
) -> None:
Expand Down Expand Up @@ -630,11 +644,17 @@ def test_create(
assert venv.env["CONDA_PREFIX"] is None
assert "NOT_CONDA_PREFIX" not in venv.env

if IS_WINDOWS:
if IS_WINDOWS and not IS_MINGW:
assert dir_.joinpath("Scripts", "python.exe").exists()
assert dir_.joinpath("Scripts", "pip.exe").exists()
assert dir_.joinpath("Lib").exists()
assert str(dir_.joinpath("Scripts")) in venv.bin_paths
elif IS_MINGW:
# MinGW uses a POSIX bin/ dir but Windows-style ``.exe`` executables.
assert dir_.joinpath("bin", "python.exe").exists()
assert dir_.joinpath("bin", "pip.exe").exists()
assert dir_.joinpath("lib").exists()
assert str(dir_.joinpath("bin")) in venv.bin_paths
else:
assert dir_.joinpath("bin", "python").exists()
assert dir_.joinpath("bin", "pip").exists()
Expand Down Expand Up @@ -845,7 +865,7 @@ def test_micromamba_channel_environment(
("virtualenv", "venv", True),
("venv", "virtualenv", True),
("virtualenv", "uv", True),
pytest.param("uv", "virtualenv", False, marks=has_uv),
pytest.param("uv", "virtualenv", False, marks=[has_uv, xfail_mingw_uv]),
pytest.param("conda", "virtualenv", False, marks=pytest.mark.conda),
],
)
Expand Down Expand Up @@ -905,6 +925,7 @@ def test_create_reuse_stale_virtualenv_environment(
assert not reused


@xfail_mingw_uv
@has_uv
def test_create_reuse_uv_environment(
make_one: Callable[..., tuple[VirtualEnv | ProcessEnv, Path]],
Expand Down
Loading