fix(release): prepare aligned 0.1.45 composition and guarded candidate sequencing
This commit is contained in:
@@ -39,6 +39,9 @@ def tag_repositories(
|
||||
A release tag is only created for a clean, aligned, non-behind worktree.
|
||||
Both local and remote tags are resolved to commits before mutation so an
|
||||
existing immutable tag can never be moved by this operation.
|
||||
Module-only local candidate tags precede Core's release-lock regeneration;
|
||||
their cross-Core composition gate applies before publication, not creation.
|
||||
Core candidate tags still require a complete aligned release bundle.
|
||||
"""
|
||||
|
||||
workspace = resolve_workspace_root(workspace_root)
|
||||
@@ -47,13 +50,19 @@ def tag_repositories(
|
||||
selected = tuple(dict.fromkeys(repos))
|
||||
results: list[dict[str, object]] = []
|
||||
bundle_issues_by_repo: dict[str, list[str]] = {}
|
||||
for issue in selected_release_webui_bundle_issues(
|
||||
repo_versions={repo: repo_versions.get(repo, "") for repo in selected},
|
||||
workspace=workspace,
|
||||
):
|
||||
bundle_issues_by_repo.setdefault(issue.repo, []).append(
|
||||
f"{issue.source}={issue.actual!r}, expected {issue.expected!r} ({issue.message})"
|
||||
)
|
||||
# Core's final lock is generated from reviewed local module tags. Requiring
|
||||
# that lock before those tags exist makes the documented sequence circular.
|
||||
# This is only a local module staging exception: Core-selected batches and
|
||||
# every publication still run the cross-repository gate, and each selected
|
||||
# repository's own version/lock checks below are always enforced.
|
||||
if push or "govoplan-core" in selected:
|
||||
for issue in selected_release_webui_bundle_issues(
|
||||
repo_versions={repo: repo_versions.get(repo, "") for repo in selected},
|
||||
workspace=workspace,
|
||||
):
|
||||
bundle_issues_by_repo.setdefault(issue.repo, []).append(
|
||||
f"{issue.source}={issue.actual!r}, expected {issue.expected!r} ({issue.message})"
|
||||
)
|
||||
|
||||
if apply:
|
||||
preflight = tag_repositories(
|
||||
|
||||
@@ -269,6 +269,7 @@ def _render_python_version(
|
||||
except SyntaxError as exc:
|
||||
raise VersionMetadataError(f"Python metadata is malformed: {path.name}") from exc
|
||||
values: list[ast.Constant] = []
|
||||
module_version_references = 0
|
||||
for node in ast.walk(tree):
|
||||
if not isinstance(node, ast.Call) or _call_name(node.func) != target_name:
|
||||
continue
|
||||
@@ -279,6 +280,28 @@ def _render_python_version(
|
||||
and isinstance(keyword.value.value, str)
|
||||
):
|
||||
values.append(keyword.value)
|
||||
elif (
|
||||
keyword.arg == keyword_name
|
||||
and isinstance(keyword.value, ast.Name)
|
||||
and keyword.value.id == "MODULE_VERSION"
|
||||
):
|
||||
module_version_references += 1
|
||||
if module_version_references:
|
||||
if module_version_references != 1 or values:
|
||||
raise VersionMetadataError(
|
||||
f"Python metadata has multiple {target_name}.{keyword_name} values: {path.name}"
|
||||
)
|
||||
rendered, found = _render_python_assignment(
|
||||
payload,
|
||||
path=path,
|
||||
assignment_name="MODULE_VERSION",
|
||||
version=version,
|
||||
)
|
||||
if not found:
|
||||
raise VersionMetadataError(
|
||||
f"Python metadata has no literal MODULE_VERSION declaration: {path.name}"
|
||||
)
|
||||
return rendered, True
|
||||
if not values:
|
||||
return payload, False
|
||||
if len(values) != 1:
|
||||
|
||||
Reference in New Issue
Block a user