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
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ dependencies = [
[project.optional-dependencies]
dev = [
"pytest>=7.0",
"tomli>=2.0.1; python_version < '3.11'",
"pytest-cov>=4.0",
"ruff>=0.1.0",
"black>=23.0",
Expand Down
47 changes: 28 additions & 19 deletions scripts/verify_release_ready.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,18 @@
- Dependencies are available
"""

import os
import re
import subprocess
import sys
from pathlib import Path

from packaging.utils import InvalidName, canonicalize_name

if sys.version_info >= (3, 11):
import tomllib
else:
import tomli as tomllib


def check_version():
"""Check that version is valid and follows semantic versioning."""
Expand Down Expand Up @@ -48,24 +54,27 @@ def check_pyproject_toml():
print(" ❌ pyproject.toml not found")
return False

content = pyproject_file.read_text()
required_fields = {
'name': r'name\s*=\s*["\']openhcs["\']',
'version': r'version\s*=',
'description': r'description\s*=',
'authors': r'authors\s*=',
'build-backend': r'build-backend\s*=\s*["\']setuptools\.build_meta["\']',
}

all_found = True
for field, pattern in required_fields.items():
if not re.search(pattern, content):
print(f" ❌ Missing or invalid field: {field}")
all_found = False

if all_found:
print(" ✅ All required fields present")
return all_found
with pyproject_file.open("rb") as project_file:
declaration = tomllib.load(project_file)
project = declaration.get("project", {})
name = project.get("name")
if not isinstance(name, str):
print(" ❌ Missing or invalid project.name")
return False
try:
canonicalize_name(name, validate=True)
except InvalidName:
print(" ❌ Invalid project.name")
return False
for field in ("version", "description", "authors"):
if not project.get(field):
print(f" ❌ Missing or invalid project.{field}")
return False
if not declaration.get("build-system", {}).get("build-backend"):
print(" ❌ Missing build-system.build-backend")
return False
print(f" ✅ Declared project metadata present: {name}")
return True


def check_readme():
Expand Down
22 changes: 22 additions & 0 deletions tests/test_release_readiness.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
"""Readiness checks consume the repository's declared project metadata."""

import runpy
from pathlib import Path

import pytest


CHECKS = runpy.run_path(str(Path(__file__).parents[1] / "scripts/verify_release_ready.py"))
PROJECT = (Path(__file__).parents[1] / "pyproject.toml").read_text()


def test_actual_project_declaration_is_release_ready(monkeypatch):
monkeypatch.chdir(Path(__file__).parents[1])
assert CHECKS["check_pyproject_toml"]()


@pytest.mark.parametrize("name", ('name = "invalid name"', ""))
def test_missing_or_invalid_project_name_is_rejected(tmp_path, monkeypatch, name):
(tmp_path / "pyproject.toml").write_text(PROJECT.replace('name = "python-introspect"', name))
monkeypatch.chdir(tmp_path)
assert not CHECKS["check_pyproject_toml"]()
Loading