Skip to content

Commit 88608e6

Browse files
tensornullCometAPI
andauthored
ci: pin Python build backend and harden dependency updates
Co-authored-by: CometAPI <support@cometapi.com>
1 parent 0230ddd commit 88608e6

6 files changed

Lines changed: 86 additions & 7 deletions

File tree

‎.github/dependabot.yml‎

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,23 @@ updates:
88
time: "04:00"
99
timezone: Etc/UTC
1010
open-pull-requests-limit: 10
11+
allow:
12+
- dependency-type: production
13+
# Toolchain majors require a coordinated compatibility review.
14+
# This filter affects version updates, not security updates.
15+
- dependency-type: development
16+
update-types:
17+
- version-update:semver-minor
18+
- version-update:semver-patch
19+
groups:
20+
production-dependencies:
21+
dependency-type: production
22+
development-dependencies:
23+
dependency-type: development
24+
ignore:
25+
- dependency-name: openai
26+
versions:
27+
- ">=3"
1128
labels:
1229
- dependencies
1330
- python

‎CONTRIBUTING.md‎

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,13 @@ when it is not cached. A passing static workflow check does not mean the
4343
workflow has run successfully on GitHub.
4444

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

5254
If a check is unavailable, say so in the pull request rather than describing
5355
it as passing.

‎pyproject.toml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ dev = [
4747
]
4848

4949
[build-system]
50-
requires = ["hatchling>=1.27.0,<2.0.0"]
50+
requires = ["hatchling==1.32.4"]
5151
build-backend = "hatchling.build"
5252

5353
[tool.hatch.build.targets.sdist]

‎scripts/_checks.py‎

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121

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

283285

286+
def require_exact_build_system(root: Path = PROJECT_ROOT) -> None:
287+
"""Require the reviewed isolated build backend and exact version constraint."""
288+
path = root / "pyproject.toml"
289+
try:
290+
if sys.version_info >= (3, 11):
291+
import tomllib
292+
else:
293+
import tomli as tomllib
294+
except ImportError as exc:
295+
raise CheckError("TOML parsing requires tomli on Python 3.10") from exc
296+
try:
297+
document = cast(dict[str, object], tomllib.loads(path.read_text(encoding="utf-8")))
298+
except (OSError, UnicodeError, tomllib.TOMLDecodeError) as exc:
299+
raise CheckError(f"cannot read {path.name}: {exc}") from exc
300+
build_system = document.get("build-system")
301+
if not isinstance(build_system, dict):
302+
raise CheckError("pyproject.toml has no [build-system] table")
303+
build_system = cast(dict[str, object], build_system)
304+
backend = build_system.get("build-backend")
305+
requires = build_system.get("requires")
306+
if backend != BUILD_SYSTEM_BACKEND or requires != [BUILD_SYSTEM_REQUIREMENT]:
307+
raise CheckError(
308+
"[build-system] must use exactly "
309+
f"{BUILD_SYSTEM_BACKEND!r} with requires=[{BUILD_SYSTEM_REQUIREMENT!r}]"
310+
)
311+
312+
284313
def read_project_version(root: Path = PROJECT_ROOT) -> str:
285314
"""Return the static PEP 621 project version."""
286315
version = read_project_metadata(root).get("version")

‎scripts/check_workflows.py‎

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,19 @@
1313
import yaml
1414

1515
try:
16-
from ._checks import CANONICAL_ACTIVE_MODEL, PROJECT_ROOT, CheckError
16+
from ._checks import (
17+
CANONICAL_ACTIVE_MODEL,
18+
PROJECT_ROOT,
19+
CheckError,
20+
require_exact_build_system,
21+
)
1722
except ImportError: # Direct execution from the repository root.
18-
from _checks import CANONICAL_ACTIVE_MODEL, PROJECT_ROOT, CheckError
23+
from _checks import (
24+
CANONICAL_ACTIVE_MODEL,
25+
PROJECT_ROOT,
26+
CheckError,
27+
require_exact_build_system,
28+
)
1929

2030
RELEASE_PLEASE_BASELINE_SHA = "31b68904141489ca04932edbf305ccf88af09372"
2131
RELEASE_PLEASE_LOCK_JSONPATH = "$.package[?(@.name.value == 'cometapi')].version"
@@ -1864,6 +1874,7 @@ def main() -> int:
18641874
default=PROJECT_ROOT / ".release-please-manifest.json",
18651875
)
18661876
args = parser.parse_args()
1877+
require_exact_build_system(PROJECT_ROOT)
18671878
paths = check_workflow_inventory(args.ci_workflow.parent)
18681879
publish_text = args.publish_workflow.read_text(encoding="utf-8")
18691880
check_publish_workflow(

‎tests/test_release_documents.py‎

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
CheckError,
2424
exact_release_version_violations,
2525
read_project_version,
26+
require_exact_build_system,
2627
)
2728
from scripts.check_artifacts import check_metadata, check_sdist, check_wheel
2829
from scripts.check_version import require_public_preview_docs, require_releasable_docs
@@ -31,6 +32,25 @@
3132
VERSION_SCRIPT = PROJECT_ROOT / "scripts" / "check_version.py"
3233

3334

35+
def test_build_system_requires_exact_hatchling_constraint(tmp_path: Path) -> None:
36+
pyproject = tmp_path / "pyproject.toml"
37+
pyproject.write_text(
38+
'[build-system]\nrequires = ["hatchling==1.32.4"]\nbuild-backend = "hatchling.build"\n',
39+
encoding="utf-8",
40+
)
41+
require_exact_build_system(tmp_path)
42+
43+
for invalid_build_system in (
44+
'[build-system]\nrequires = ["hatchling>=1.27.0,<2.0.0"]\n'
45+
'build-backend = "hatchling.build"\n',
46+
'[build-system]\nrequires = ["setuptools==80.0.0"]\n'
47+
'build-backend = "setuptools.build_meta"\n',
48+
):
49+
pyproject.write_text(invalid_build_system, encoding="utf-8")
50+
with pytest.raises(CheckError, match=r"\[build-system\].*exactly"):
51+
require_exact_build_system(tmp_path)
52+
53+
3454
def _copy_version_checker(root: Path) -> Path:
3555
scripts = root / "scripts"
3656
scripts.mkdir(exist_ok=True)

0 commit comments

Comments
 (0)