From ea82d91b75599e6e9891630569aee36931382759 Mon Sep 17 00:00:00 2001 From: Kevin Veen-Birkenbach Date: Sat, 31 Oct 2026 18:42:41 +0100 Subject: [PATCH] fix(release): skip pyproject.toml without a static project version A pyproject.toml that holds only tool config (no [project] table) or lists "version" in [project].dynamic now prints an INFO line and is skipped, like a missing file. A [project] table with neither a version nor a dynamic entry still raises. Co-Authored-By: Claude Opus 5.5 (1M context) --- src/pkgmgr/actions/release/files/pyproject.py | 10 +++++ .../unit/pkgmgr/actions/release/test_files.py | 43 ++++++++++++++++--- 2 files changed, 47 insertions(+), 6 deletions(-) diff --git a/src/pkgmgr/actions/release/files/pyproject.py b/src/pkgmgr/actions/release/files/pyproject.py index 1547513..0a88938 100644 --- a/src/pkgmgr/actions/release/files/pyproject.py +++ b/src/pkgmgr/actions/release/files/pyproject.py @@ -3,6 +3,11 @@ from __future__ import annotations import os import re +try: + import tomllib +except ImportError: + import tomli as tomllib # type: ignore + def update_pyproject_version( pyproject_path: str, new_version: str, preview: bool = False @@ -18,6 +23,11 @@ def update_pyproject_version( print(f"[WARN] Could not read pyproject.toml: {exc}") return + project = tomllib.loads(content).get("project") + if project is None or "version" in project.get("dynamic", []): + print("[INFO] pyproject.toml declares no static version, skipping.") + return + m = re.search(r"(?ms)^\s*\[project\]\s*$.*?(?=^\s*\[|\Z)", content) if not m: raise RuntimeError("Missing [project] section in pyproject.toml") diff --git a/tests/unit/pkgmgr/actions/release/test_files.py b/tests/unit/pkgmgr/actions/release/test_files.py index cd9dd0d..5410b4c 100644 --- a/tests/unit/pkgmgr/actions/release/test_files.py +++ b/tests/unit/pkgmgr/actions/release/test_files.py @@ -88,12 +88,15 @@ class TestUpdatePyprojectVersion(unittest.TestCase): self.assertIn("Missing version key", str(cm.exception)) - def test_update_pyproject_version_raises_when_project_section_missing(self) -> None: + def test_update_pyproject_version_skips_tool_only_pyproject(self) -> None: original = ( textwrap.dedent( """ - name = "example" - version = "0.1.0" + [dependency-groups] + lint = ["ruff==0.17.3"] + + [tool.ruff] + line-length = 88 """ ).strip() + "\n" @@ -104,10 +107,38 @@ class TestUpdatePyprojectVersion(unittest.TestCase): with open(path, "w", encoding="utf-8") as f: f.write(original) - with self.assertRaises(RuntimeError) as cm: - update_pyproject_version(path, "1.2.3", preview=False) + update_pyproject_version(path, "1.2.3", preview=False) - self.assertIn("Missing [project] section", str(cm.exception)) + with open(path, encoding="utf-8") as f: + content = f.read() + + self.assertEqual(content, original) + + def test_update_pyproject_version_skips_dynamic_version(self) -> None: + original = ( + textwrap.dedent( + """ + [project] + name = "example" + dynamic = [ + "version", + ] + """ + ).strip() + + "\n" + ) + + with tempfile.TemporaryDirectory() as tmpdir: + path = os.path.join(tmpdir, "pyproject.toml") + with open(path, "w", encoding="utf-8") as f: + f.write(original) + + update_pyproject_version(path, "1.2.3", preview=False) + + with open(path, encoding="utf-8") as f: + content = f.read() + + self.assertEqual(content, original) def test_update_pyproject_version_missing_file_is_skipped(self) -> None: with tempfile.TemporaryDirectory() as tmpdir: