fix(release): reject unpackageable tagged artifacts
This commit is contained in:
@@ -7,6 +7,7 @@ import json
|
||||
from pathlib import Path
|
||||
import re
|
||||
import subprocess
|
||||
import tomllib
|
||||
|
||||
from .git_state import collect_versions
|
||||
from .workspace import load_repository_specs, resolve_repo_path
|
||||
@@ -164,7 +165,7 @@ def selected_repository_version_issues(
|
||||
|
||||
|
||||
def release_composition_issues(meta_root: Path, *, core_root: Path) -> tuple[VersionAlignmentIssue, ...]:
|
||||
"""Require backend and WebUI release references for a module to agree."""
|
||||
"""Require release references to agree and point at installable artifacts."""
|
||||
|
||||
python_refs = _python_release_refs(meta_root / "requirements-release.txt")
|
||||
webui_refs = _webui_release_refs(core_root / "webui" / "package.release.json")
|
||||
@@ -184,9 +185,74 @@ def release_composition_issues(meta_root: Path, *, core_root: Path) -> tuple[Ver
|
||||
message="frontend release ref must match the backend release ref",
|
||||
)
|
||||
)
|
||||
workspace = meta_root.parent
|
||||
for repo, version in sorted(python_refs.items()):
|
||||
repo_path = workspace / repo
|
||||
if repo_path.exists():
|
||||
issues.extend(
|
||||
_release_artifact_issues(
|
||||
repo=repo,
|
||||
repo_path=repo_path,
|
||||
version=version,
|
||||
artifact="pyproject.toml",
|
||||
)
|
||||
)
|
||||
for repo, version in sorted(webui_refs.items()):
|
||||
repo_path = workspace / repo
|
||||
if repo_path.exists():
|
||||
issues.extend(
|
||||
_release_artifact_issues(
|
||||
repo=repo,
|
||||
repo_path=repo_path,
|
||||
version=version,
|
||||
artifact="webui/package.json",
|
||||
)
|
||||
)
|
||||
return tuple(issues)
|
||||
|
||||
|
||||
def _release_artifact_issues(
|
||||
*,
|
||||
repo: str,
|
||||
repo_path: Path,
|
||||
version: str,
|
||||
artifact: str,
|
||||
) -> list[VersionAlignmentIssue]:
|
||||
tag = f"v{version.removeprefix('v')}"
|
||||
payload = _git_tag_file(repo_path, tag, artifact)
|
||||
if payload is None:
|
||||
return [
|
||||
VersionAlignmentIssue(
|
||||
repo=repo,
|
||||
source=f"{tag}:{artifact}",
|
||||
expected="installable package metadata",
|
||||
actual="missing or unreadable",
|
||||
message="release tag must contain its declared package artifact",
|
||||
)
|
||||
]
|
||||
try:
|
||||
if artifact == "pyproject.toml":
|
||||
parsed = tomllib.loads(payload)
|
||||
project = parsed.get("project")
|
||||
artifact_version = project.get("version") if isinstance(project, dict) else None
|
||||
else:
|
||||
parsed = json.loads(payload)
|
||||
artifact_version = parsed.get("version") if isinstance(parsed, dict) else None
|
||||
except (json.JSONDecodeError, tomllib.TOMLDecodeError):
|
||||
artifact_version = None
|
||||
if artifact_version == version.removeprefix("v"):
|
||||
return []
|
||||
return [
|
||||
VersionAlignmentIssue(
|
||||
repo=repo,
|
||||
source=f"{tag}:{artifact}",
|
||||
expected=version.removeprefix("v"),
|
||||
actual=str(artifact_version or ""),
|
||||
message="release artifact version must match its immutable tag",
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
def candidate_catalog_version_issues(payload: object) -> tuple[VersionAlignmentIssue, ...]:
|
||||
"""Validate versions, refs, and selected-unit provenance inside a catalog."""
|
||||
|
||||
@@ -514,6 +580,20 @@ def _git_tag_commit(repo_path: Path, tag: str) -> str | None:
|
||||
return value if result.returncode == 0 and value else None
|
||||
|
||||
|
||||
def _git_tag_file(repo_path: Path, tag: str, relative_path: str) -> str | None:
|
||||
if not (repo_path / ".git").exists():
|
||||
return None
|
||||
result = subprocess.run(
|
||||
["git", "-C", str(repo_path), "show", f"{tag}:{relative_path}"],
|
||||
check=False,
|
||||
text=True,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.DEVNULL,
|
||||
timeout=10,
|
||||
)
|
||||
return result.stdout if result.returncode == 0 else None
|
||||
|
||||
|
||||
def _json_version(path: Path) -> str | None:
|
||||
value = _payload_version(_json_object(path))
|
||||
return value
|
||||
|
||||
Reference in New Issue
Block a user