Repository: wntrblm/nox
Pull request #952 (2025-04-28 11:57:53)

chore(deps): bump astral-sh/setup-uv from 5 to 6 in the actions group
Bumps the actions group with 1 update: [astral-sh/setup-uv](https://github.com/astral-sh/setup-uv).

Updates `astral-sh/setup-uv` from 5 to 6
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a href="https://github.com/astral-sh/setup-uv/releases">astral-sh/setup-uv's releases</a>.</em></p>
<blockquote>
<h2>v6.0.0 🌈 activate-environment and working-directory</h2>
<h2>Changes</h2>
<p>This version contains some breaking changes which have been gathering up for a while. Lets dive into them:</p>
<ul>
<li><a href="https://github.com/astral-sh/setup-uv/blob/HEAD/#activate-environment">Activate environment</a></li>
<li><a href="https://github.com/astral-sh/setup-uv/blob/HEAD/#working-directory">Working Directory</a></li>
<li><a href="https://github.com/astral-sh/setup-uv/blob/HEAD/#default-cache-dependency-glob">Default <code>cache-dependency-glob</code></a></li>
<li><a href="https://github.com/astral-sh/setup-uv/blob/HEAD/#use-default-cache-dir-on-self-hosted-runners">Use default cache dir on self hosted runners</a></li>
</ul>
<h3>Activate environment</h3>
<p>In previous versions using the input <code>python-version</code> automatically activated a venv at the repository root.
This led to some unwanted side-effects, was sometimes unexpected and not flexible enough.</p>
<p>The venv activation is now explicitly controlled with the new input <code>activate-environment</code> (false by default):</p>
<pre lang="yaml"><code>- name: Install the latest version of uv and activate the environment
  uses: astral-sh/setup-uv@v6
  with:
    activate-environment: true
- run: uv pip install pip
</code></pre>
<p>The venv gets created by the <a href="https://docs.astral.sh/uv/pip/environments/"><code>uv venv</code></a> command so the python version is controlled by the <code>python-version</code> input or the files <code>pyproject.toml</code>, <code>uv.toml</code>, <code>.python-version</code> in the <code>working-directory</code>.</p>
<h3>Working Directory</h3>
<p>The new input <code>working-directory</code> controls where we look for <code>pyproject.toml</code>, <code>uv.toml</code> and <code>.python-version</code> files
which are used to determine the version of uv and python to install.</p>
<p>It can also be used to control where the venv gets created.</p>
<pre lang="yaml"><code>- name: Install uv based on the config files in the working-directory
  uses: astral-sh/setup-uv@v6
  with:
    working-directory: my/subproject/dir
</code></pre>
<blockquote>
<p>[!CAUTION]</p>
<p>The inputs <code>pyproject-file</code> and <code>uv-file</code> have been removed.</p>
</blockquote>
<h3>Default <code>cache-dependency-glob</code></h3>
<p><a href="https://github.com/ssbarnea"><code>@​ssbarnea</code></a> found out that the default <code>cache-dependency-glob</code> was not suitable for a lot of users.</p>
<p>The old default</p>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a href="https://github.com/astral-sh/setup-uv/commit/c7f87aa956e4c323abf06d5dec078e358f6b4d04"><code>c7f87aa</code></a> bump to v6 in README (<a href="https://redirect.github.com/astral-sh/setup-uv/issues/382">#382</a>)</li>
<li><a href="https://github.com/astral-sh/setup-uv/commit/aadfaf08d64f83cdd98eea14fdab8eb08f73656c"><code>aadfaf0</code></a> Change default cache-dependency-glob (<a href="https://redirect.github.com/astral-sh/setup-uv/issues/352">#352</a>)</li>
<li><a href="https://github.com/astral-sh/setup-uv/commit/a0f9da6273a171f2d94cce2036eaf5a07fefa23c"><code>a0f9da6</code></a> No default UV_CACHE_DIR on selfhosted runners (<a href="https://redirect.github.com/astral-sh/setup-uv/issues/380">#380</a>)</li>
<li><a href="https://github.com/astral-sh/setup-uv/commit/ec4c6916287cf1471f9f803d79ef6a0a04520e81"><code>ec4c691</code></a> new inputs activate-environment and working-directory (<a href="https://redirect.github.com/astral-sh/setup-uv/issues/381">#381</a>)</li>
<li><a href="https://github.com/astral-sh/setup-uv/commit/aa1290542ebcd3b6932d825ed2b40807f82b2fdd"><code>aa12905</code></a> chore: update known checksums for 0.6.16 (<a href="https://redirect.github.com/astral-sh/setup-uv/issues/378">#378</a>)</li>
<li><a href="https://github.com/astral-sh/setup-uv/commit/fcaddda076a8158a712b6d64986baf606c446694"><code>fcaddda</code></a> chore: update known checksums for 0.6.15 (<a href="https://redirect.github.com/astral-sh/setup-uv/issues/377">#377</a>)</li>
<li><a href="https://github.com/astral-sh/setup-uv/commit/fb3a0a97fac846cb3395265a3087ab94ad3ca2a0"><code>fb3a0a9</code></a> log info on venv activation (<a href="https://redirect.github.com/astral-sh/setup-uv/issues/375">#375</a>)</li>
<li>See full diff in <a href="https://github.com/astral-sh/setup-uv/compare/v5...v6">compare view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=astral-sh/setup-uv&package-manager=github_actions&previous-version=5&new-version=6)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency
- `@dependabot ignore <dependency name> major version` will close this group update PR and stop Dependabot creating any more for the specific dependency's major version (unless you unignore this specific dependency's major version or upgrade to it yourself)
- `@dependabot ignore <dependency name> minor version` will close this group update PR and stop Dependabot creating any more for the specific dependency's minor version (unless you unignore this specific dependency's minor version or upgrade to it yourself)
- `@dependabot ignore <dependency name>` will close this group update PR and stop Dependabot creating any more for the specific dependency (unless you unignore this specific dependency or upgrade to it yourself)
- `@dependabot unignore <dependency name>` will remove all of the ignore conditions of the specified dependency
- `@dependabot unignore <dependency name> <ignore condition>` will remove the ignore condition of the specified dependency and ignore conditions


</details>


Patch:
diff --git a/.github/workflows/action.yml b/.github/workflows/action.yml
index 9a3fb13..9f15106 100644
--- a/.github/workflows/action.yml
+++ b/.github/workflows/action.yml
@@ -34,7 +34,7 @@ jobs:
       - uses: actions/checkout@v4
         with:
           persist-credentials: false
-      - uses: astral-sh/setup-uv@v5
+      - uses: astral-sh/setup-uv@v6
       - uses: ./
       - run: nox --non-interactive --error-on-missing-interpreter --session github_actions_default_tests
 
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index a7f0ead..df71304 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -48,7 +48,7 @@ jobs:
           python-version: ${{ matrix.python-version }}
           allow-prereleases: true
       - name: Setup uv
-        uses: astral-sh/setup-uv@v5
+        uses: astral-sh/setup-uv@v6
       - name: Set up Miniconda
         uses: conda-incubator/setup-miniconda@v3
         with:
@@ -80,7 +80,7 @@ jobs:
         with:
           python-version: "3.12"
       - name: Setup uv
-        uses: astral-sh/setup-uv@v5
+        uses: astral-sh/setup-uv@v6
       - name: Install Nox-under-test
         run:  uv pip install --system .
       - name: Download individual coverage reports
@@ -119,7 +119,7 @@ jobs:
         with:
           python-version: "3.12"
       - name: Setup uv
-        uses: astral-sh/setup-uv@v5
+        uses: astral-sh/setup-uv@v6
       - name: Install Nox-under-test
         run:  uv pip install --system .
       - name: Docs
diff --git a/nox/virtualenv.py b/nox/virtualenv.py
index 3b97ab1..b4b1a83 100644
--- a/nox/virtualenv.py
+++ b/nox/virtualenv.py
@@ -82,33 +82,39 @@ _BLACKLISTED_ENV_VARS = frozenset(
 
 
 def find_uv() -> tuple[bool, str, version.Version]:
-    uv_on_path = shutil.which("uv")
+    uv_name = os.environ.get("UV", None)
+    uv_on_path = shutil.which(uv_name or "uv")
 
     # Look for uv in Nox's environment, to handle `pipx install nox[uv]`.
-    with contextlib.suppress(ImportError, FileNotFoundError):
-        from uv import find_uv_bin
+    if uv_name is None:
+        with contextlib.suppress(ImportError, FileNotFoundError):
+            from uv import find_uv_bin
 
-        uv_bin = find_uv_bin()
+            uv_bin = find_uv_bin()
 
-        uv_vers = uv_version(uv_bin)
-        if uv_vers > version.Version("0"):
-            # If the returned value is the same as calling "uv" already, don't
-            # expand (simpler logging)
-            if uv_on_path and Path(uv_bin).samefile(uv_on_path):
-                return True, "uv", uv_vers
+            uv_vers = uv_version(uv_bin)
+            if uv_vers > version.Version("0"):
+                # If the returned value is the same as calling "uv" already, don't
+                # expand (simpler logging)
+                if uv_on_path and Path(uv_bin).samefile(uv_on_path):
+                    return True, "uv", uv_vers
 
-            return True, uv_bin, uv_vers
+                return True, uv_bin, uv_vers
 
     # Fall back to PATH.
-    uv_vers = uv_version("uv")
-    return uv_on_path is not None and uv_vers > version.Version("0"), "uv", uv_vers
+    uv_vers = uv_version(uv_name or "uv")
+    return (
+        uv_on_path is not None and uv_vers > version.Version("0"),
+        uv_name or "uv",
+        uv_vers,
+    )
 
 
 def uv_version(uv_bin: str) -> version.Version:
     """Returns uv's version defaulting to 0.0 if uv is not available"""
     try:
         ret = subprocess.run(
-            [uv_bin, "version", "--output-format", "json"],
+            [uv_bin, "self", "version", "--output-format", "json"],
             check=False,
             text=True,
             capture_output=True,
@@ -118,8 +124,19 @@ def uv_version(uv_bin: str) -> version.Version:
         logger.info("uv binary not found.")
         return version.Version("0.0")
 
+    if ret.returncode == 2:
+        # uv < 0.7
+        ret = subprocess.run(
+            [uv_bin, "version", "--output-format", "json"],
+            check=False,
+            text=True,
+            capture_output=True,
+            encoding="utf-8",
+        )
+
     if ret.returncode == 0 and ret.stdout:
         return version.Version(json.loads(ret.stdout).get("version"))
+
     logger.info("Failed to establish uv's version.")
     return version.Version("0.0")
 


Test patch:
diff --git a/tests/test_virtualenv.py b/tests/test_virtualenv.py
index d8223fb..f2d532b 100644
--- a/tests/test_virtualenv.py
+++ b/tests/test_virtualenv.py
@@ -722,21 +722,151 @@ UV_IN_PIPX_VENV = "/home/user/.local/pipx/venvs/nox/bin/uv"
 
 
 @pytest.mark.parametrize(
-    ("which_result", "find_uv_bin_result", "found", "path", "vers", "vers_rc"),
+    (
+        "uv_env",
+        "which_result",
+        "find_uv_bin_result",
+        "found",
+        "path",
+        "vers",
+        "vers_rc",
+    ),
     [
-        ("/usr/bin/uv", UV_IN_PIPX_VENV, True, UV_IN_PIPX_VENV, "0.5.0", 0),
-        ("/usr/bin/uv", UV_IN_PIPX_VENV, True, UV_IN_PIPX_VENV, "0.6.0", 0),
-        ("/usr/bin/uv", UV_IN_PIPX_VENV, False, "uv", "0.0.0", 0),
-        ("/usr/bin/uv", UV_IN_PIPX_VENV, False, "uv", "0.6.0", 1),
-        ("/usr/bin/uv", None, True, "uv", "0.6.0", 0),
-        ("/usr/bin/uv", None, False, "uv", "0.0.0", 0),
-        ("/usr/bin/uv", None, False, "uv", "0.6.0", 1),
-        (None, UV_IN_PIPX_VENV, True, UV_IN_PIPX_VENV, "0.5.0", 0),
-        (None, None, False, "uv", "0.5.0", 0),
+        pytest.param(
+            None,
+            "/usr/bin/uv",
+            UV_IN_PIPX_VENV,
+            True,
+            UV_IN_PIPX_VENV,
+            "0.5.0",
+            0,
+            id="pkg_pipx_uv_0.5",
+        ),
+        pytest.param(
+            None,
+            "/usr/bin/uv",
+            UV_IN_PIPX_VENV,
+            True,
+            UV_IN_PIPX_VENV,
+            "0.6.0",
+            0,
+            id="pkg_pipx_uv_0.6",
+        ),
+        pytest.param(
+            "custom",
+            "/usr/bin/uv",
+            UV_IN_PIPX_VENV,
+            True,
+            "custom",
+            "0.6.0",
+            0,
+            id="UV_pkg_pipx_uv_0.6",
+        ),
+        pytest.param(
+            None,
+            "/usr/bin/uv",
+            UV_IN_PIPX_VENV,
+            True,
+            UV_IN_PIPX_VENV,
+            "0.7.0",
+            0,
+            id="pkg_pipx_uv_0.7",
+        ),
+        pytest.param(
+            "custom",
+            "/usr/bin/uv",
+            UV_IN_PIPX_VENV,
+            True,
+            "custom",
+            "0.7.0",
+            0,
+            id="UV_pkg_pipx_uv_0.7",
+        ),
+        pytest.param(
+            None,
+            "/usr/bin/uv",
+            UV_IN_PIPX_VENV,
+            False,
+            "uv",
+            "0.0.0",
+            0,
+            id="pkg_system_uv_0.0",
+        ),
+        pytest.param(
+            None,
+            "/usr/bin/uv",
+            UV_IN_PIPX_VENV,
+            False,
+            "uv",
+            "0.6.0",
+            1,
+            id="pkg_system_uv_0.6_broken",
+        ),
+        pytest.param(
+            None,
+            "/usr/bin/uv",
+            UV_IN_PIPX_VENV,
+            False,
+            "uv",
+            "0.7.0",
+            1,
+            id="pkg_system_uv_0.7_broken",
+        ),
+        pytest.param(
+            None, "/usr/bin/uv", None, True, "uv", "0.7.0", 0, id="system_uv_0.7"
+        ),
+        pytest.param(
+            None, "/usr/bin/uv", None, True, "uv", "0.6.0", 0, id="system_uv_0.6"
+        ),
+        pytest.param(
+            None, "/usr/bin/uv", None, False, "uv", "0.0.0", 0, id="system_uv_0.0"
+        ),
+        pytest.param(
+            None,
+            "/usr/bin/uv",
+            None,
+            False,
+            "uv",
+            "0.6.0",
+            1,
+            id="system_uv_0.6_broken",
+        ),
+        pytest.param(
+            None,
+            "/usr/bin/uv",
+            None,
+            False,
+            "uv",
+            "0.7.0",
+            1,
+            id="system_uv_0.7_broken",
+        ),
+        pytest.param(
+            None,
+            None,
+            UV_IN_PIPX_VENV,
+            True,
+            UV_IN_PIPX_VENV,
+            "0.5.0",
+            0,
+            id="pipx_uv_0.5",
+        ),
+        pytest.param(
+            None,
+            None,
+            UV_IN_PIPX_VENV,
+            True,
+            UV_IN_PIPX_VENV,
+            "0.7.0",
+            0,
+            id="pipx_uv_0.7",
+        ),
+        pytest.param(None, None, None, False, "uv", "0.5.0", 0, id="no_uv_0.5"),
     ],
 )
 def test_find_uv(
     monkeypatch: pytest.MonkeyPatch,
+    uv_env: str | None,
     which_result: str | None,
     find_uv_bin_result: str | None,
     found: bool,
@@ -744,21 +874,32 @@ def test_find_uv(
     vers: str,
     vers_rc: int,
 ) -> None:
+    monkeypatch.delenv("UV", raising=False)
+    if uv_env:
+        monkeypatch.setenv("UV", uv_env)
+
     def find_uv_bin() -> str:
         if find_uv_bin_result:
             return find_uv_bin_result
         raise FileNotFoundError()
 
-    def mock_run(*args: object, **kwargs: object) -> subprocess.CompletedProcess[str]:
+    def mock_run(*args: Any, **kwargs: object) -> subprocess.CompletedProcess[str]:
+        if version.Version(vers) < version.Version("0.7.0") and "self" in args[0]:
+            return subprocess.CompletedProcess(
+                args=args[0],
+                returncode=2,
+            )
         return subprocess.CompletedProcess(
-            args=["uv", "version", "--output-format", "json"],
+            args=args[0],
             stdout=f'{{"version": "{vers}", "commit_info": null}}',
             returncode=vers_rc,
         )
 
     monkeypatch.setattr(subprocess, "run", mock_run)
 
-    monkeypatch.setattr(shutil, "which", lambda _: which_result)
+    monkeypatch.setattr(
+        shutil, "which", lambda x: which_result if x.endswith("uv") else uv_env
+    )
     monkeypatch.setattr(Path, "samefile", lambda a, b: a == b)
     monkeypatch.setitem(
         sys.modules, "uv", types.SimpleNamespace(find_uv_bin=find_uv_bin)
@@ -787,7 +928,7 @@ def test_uv_version(
 ) -> None:
     def mock_run(*args: object, **kwargs: object) -> subprocess.CompletedProcess[str]:
         return subprocess.CompletedProcess(
-            args=["uv", "version", "--output-format", "json"],
+            args=["uv", "self", "version", "--output-format", "json"],
             stdout=stdout,
             returncode=return_code,
         )