From 21e8b62ebaacd8e04e547db3d63d8b02d05bc88e Mon Sep 17 00:00:00 2001 From: Matt Galligan Date: Tue, 16 Jun 2026 14:36:14 -0400 Subject: [PATCH] fix: refresh package cache during pypi smoke --- docs/usage/README.md | 12 +++++---- scripts/check_pypi_smoke.py | 23 +++++++++++++++-- tests/test_pypi_smoke.py | 51 +++++++++++++++++++++++++++++++++++++ 3 files changed, 79 insertions(+), 7 deletions(-) create mode 100644 tests/test_pypi_smoke.py diff --git a/docs/usage/README.md b/docs/usage/README.md index f489a49..5d19d37 100644 --- a/docs/usage/README.md +++ b/docs/usage/README.md @@ -48,10 +48,10 @@ published package: just pypi-smoke -- --package-spec outfitter-dispatch==0.5.0 ``` -The smoke installs with `uvx`, uses a temporary `DISPATCH_HOME`, verifies the -derived `models` schema, starts the daemon, reads the live App Server model -catalog, verifies the cached registry read, checks the empty first-run lane list, -and shuts the daemon down. +The smoke installs with `uvx --refresh-package outfitter-dispatch`, uses a +temporary `DISPATCH_HOME`, verifies the derived `models` schema, starts the +daemon, reads the live App Server model catalog, verifies the cached registry +read, checks the empty first-run lane list, and shuts the daemon down. For an agent-level live scenario against the in-tree CLI, run: @@ -178,7 +178,9 @@ just check ``` After the GitHub Release publishes to PyPI, run `just pypi-smoke -- --package-spec -outfitter-dispatch==` to verify the public install path. +outfitter-dispatch==` to verify the public install path. The smoke +refreshes the package under test in uv's cache, so an immediate post-publish +check does not reuse an early "version not found" resolver result. Then create and publish a GitHub Release for the same tag, for example `v0.1.0`. Do not upload with a long-lived PyPI token unless the trusted diff --git a/scripts/check_pypi_smoke.py b/scripts/check_pypi_smoke.py index 997dddd..119d568 100644 --- a/scripts/check_pypi_smoke.py +++ b/scripts/check_pypi_smoke.py @@ -43,7 +43,9 @@ def main(argv: list[str] | None = None) -> int: _expect(models.get("source") == "app-server", models) _expect(_nonempty_list(models.get("models")), models) configured = models.get("configured_default") - _expect(isinstance(configured, dict), models) + if not isinstance(configured, dict): + _expect(False, models) + raise AssertionError("unreachable") _expect(isinstance(configured.get("model"), str), models) cached = _dispatch_json( @@ -105,8 +107,17 @@ def _dispatch( timeout: float = 90.0, check: bool = True, ) -> subprocess.CompletedProcess[str]: + package_name = _package_name(package_spec) result = subprocess.run( - ["uvx", "--from", package_spec, "dispatch", *args], + [ + "uvx", + "--refresh-package", + package_name, + "--from", + package_spec, + "dispatch", + *args, + ], env=env, text=True, capture_output=True, @@ -121,6 +132,14 @@ def _dispatch( return result +def _package_name(package_spec: str) -> str: + spec = package_spec.strip() + for separator in ("[", " @ ", "===", "==", "~=", "!=", ">=", "<=", ">", "<"): + if separator in spec: + return spec.split(separator, 1)[0].strip() + return spec + + def _dispatch_json( package_spec: str, args: list[str], diff --git a/tests/test_pypi_smoke.py b/tests/test_pypi_smoke.py new file mode 100644 index 0000000..ae31455 --- /dev/null +++ b/tests/test_pypi_smoke.py @@ -0,0 +1,51 @@ +"""Tests for the published-package smoke helper.""" + +from __future__ import annotations + +import subprocess +from pathlib import Path + +import pytest +from scripts import check_pypi_smoke + + +def test_package_name_from_package_spec() -> None: + assert check_pypi_smoke._package_name("outfitter-dispatch==0.8.0") == "outfitter-dispatch" + assert check_pypi_smoke._package_name("outfitter-dispatch[dev]>=0.8") == "outfitter-dispatch" + assert ( + check_pypi_smoke._package_name( + "outfitter-dispatch @ https://example.test/outfitter-dispatch.whl" + ) + == "outfitter-dispatch" + ) + + +def test_dispatch_refreshes_target_package_cache( + monkeypatch: pytest.MonkeyPatch, tmp_path: Path +) -> None: + calls: list[list[str]] = [] + + def fake_run(cmd: list[str], **_kwargs: object) -> subprocess.CompletedProcess[str]: + calls.append(cmd) + return subprocess.CompletedProcess(cmd, 0, stdout="dispatch 0.8.0\n", stderr="") + + monkeypatch.setattr(subprocess, "run", fake_run) + + result = check_pypi_smoke._dispatch( + "outfitter-dispatch==0.8.0", + ["--version"], + {"DISPATCH_HOME": str(tmp_path)}, + ) + + assert result.stdout == "dispatch 0.8.0\n" + assert calls == [ + [ + "uvx", + "--refresh-package", + "outfitter-dispatch", + "--from", + "outfitter-dispatch==0.8.0", + "dispatch", + "--version", + ] + ]