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
17 changes: 17 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,23 @@ updates:
time: "04:00"
timezone: Etc/UTC
open-pull-requests-limit: 10
allow:
- dependency-type: production
# Toolchain majors require a coordinated compatibility review.
# This filter affects version updates, not security updates.
- dependency-type: development
update-types:
- version-update:semver-minor
- version-update:semver-patch
groups:
production-dependencies:
dependency-type: production
development-dependencies:
dependency-type: development
ignore:
- dependency-name: openai
versions:
- ">=3"
labels:
- dependencies
- python
Expand Down
10 changes: 6 additions & 4 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,13 @@ when it is not cached. A passing static workflow check does not mean the
workflow has run successfully on GitHub.

Both Hatch build targets explicitly emit Core Metadata 2.4, which the locked
Twine toolchain can validate. Build isolation resolves Hatchling separately from
`uv.lock`, so relying on its default metadata version can break an unchanged
checkout. The build regression test checks both the sdist-to-wheel path and a
Twine toolchain can validate. The build backend is pinned to
`hatchling==1.32.4` because build isolation resolves it separately from
`uv.lock`; the workflow semantic check rejects a floating or alternate backend
constraint. The build regression test checks both the sdist-to-wheel path and a
direct wheel build with the locked Twine validator. Change the metadata format
only together with verified support in the validation and publication tools.
or backend pin only together with verified support in the validation and
publication tools.

If a check is unavailable, say so in the pull request rather than describing
it as passing.
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ dev = [
]

[build-system]
requires = ["hatchling>=1.27.0,<2.0.0"]
requires = ["hatchling==1.32.4"]
build-backend = "hatchling.build"

[tool.hatch.build.targets.sdist]
Expand Down
29 changes: 29 additions & 0 deletions scripts/_checks.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@

PROJECT_ROOT = Path(__file__).resolve().parents[1]
DIST_NAME = "cometapi"
BUILD_SYSTEM_BACKEND = "hatchling.build"
BUILD_SYSTEM_REQUIREMENT = "hatchling==1.32.4"
CANONICAL_ACTIVE_MODEL = "gpt-5.6-sol"
CANONICAL_AUTHOR = "CometAPI"
CANONICAL_COPYRIGHT = "Copyright (c) 2026 CometAPI"
Expand Down Expand Up @@ -281,6 +283,33 @@ def read_project_metadata(root: Path = PROJECT_ROOT) -> dict[str, object]:
return cast(dict[str, object], project)


def require_exact_build_system(root: Path = PROJECT_ROOT) -> None:
"""Require the reviewed isolated build backend and exact version constraint."""
path = root / "pyproject.toml"
try:
if sys.version_info >= (3, 11):
import tomllib
else:
import tomli as tomllib
except ImportError as exc:
raise CheckError("TOML parsing requires tomli on Python 3.10") from exc
try:
document = cast(dict[str, object], tomllib.loads(path.read_text(encoding="utf-8")))
except (OSError, UnicodeError, tomllib.TOMLDecodeError) as exc:
raise CheckError(f"cannot read {path.name}: {exc}") from exc
build_system = document.get("build-system")
if not isinstance(build_system, dict):
raise CheckError("pyproject.toml has no [build-system] table")
build_system = cast(dict[str, object], build_system)
backend = build_system.get("build-backend")
requires = build_system.get("requires")
if backend != BUILD_SYSTEM_BACKEND or requires != [BUILD_SYSTEM_REQUIREMENT]:
raise CheckError(
"[build-system] must use exactly "
f"{BUILD_SYSTEM_BACKEND!r} with requires=[{BUILD_SYSTEM_REQUIREMENT!r}]"
)


def read_project_version(root: Path = PROJECT_ROOT) -> str:
"""Return the static PEP 621 project version."""
version = read_project_metadata(root).get("version")
Expand Down
15 changes: 13 additions & 2 deletions scripts/check_workflows.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,19 @@
import yaml

try:
from ._checks import CANONICAL_ACTIVE_MODEL, PROJECT_ROOT, CheckError
from ._checks import (
CANONICAL_ACTIVE_MODEL,
PROJECT_ROOT,
CheckError,
require_exact_build_system,
)
except ImportError: # Direct execution from the repository root.
from _checks import CANONICAL_ACTIVE_MODEL, PROJECT_ROOT, CheckError
from _checks import (
CANONICAL_ACTIVE_MODEL,
PROJECT_ROOT,
CheckError,
require_exact_build_system,
)

RELEASE_PLEASE_BASELINE_SHA = "31b68904141489ca04932edbf305ccf88af09372"
RELEASE_PLEASE_LOCK_JSONPATH = "$.package[?(@.name.value == 'cometapi')].version"
Expand Down Expand Up @@ -1864,6 +1874,7 @@ def main() -> int:
default=PROJECT_ROOT / ".release-please-manifest.json",
)
args = parser.parse_args()
require_exact_build_system(PROJECT_ROOT)
paths = check_workflow_inventory(args.ci_workflow.parent)
publish_text = args.publish_workflow.read_text(encoding="utf-8")
check_publish_workflow(
Expand Down
20 changes: 20 additions & 0 deletions tests/test_release_documents.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
CheckError,
exact_release_version_violations,
read_project_version,
require_exact_build_system,
)
from scripts.check_artifacts import check_metadata, check_sdist, check_wheel
from scripts.check_version import require_public_preview_docs, require_releasable_docs
Expand All @@ -31,6 +32,25 @@
VERSION_SCRIPT = PROJECT_ROOT / "scripts" / "check_version.py"


def test_build_system_requires_exact_hatchling_constraint(tmp_path: Path) -> None:
pyproject = tmp_path / "pyproject.toml"
pyproject.write_text(
'[build-system]\nrequires = ["hatchling==1.32.4"]\nbuild-backend = "hatchling.build"\n',
encoding="utf-8",
)
require_exact_build_system(tmp_path)

for invalid_build_system in (
'[build-system]\nrequires = ["hatchling>=1.27.0,<2.0.0"]\n'
'build-backend = "hatchling.build"\n',
'[build-system]\nrequires = ["setuptools==80.0.0"]\n'
'build-backend = "setuptools.build_meta"\n',
):
pyproject.write_text(invalid_build_system, encoding="utf-8")
with pytest.raises(CheckError, match=r"\[build-system\].*exactly"):
require_exact_build_system(tmp_path)


def _copy_version_checker(root: Path) -> Path:
scripts = root / "scripts"
scripts.mkdir(exist_ok=True)
Expand Down
Loading