Complete guided release console workflow
Dependency Audit / dependency-audit (push) Successful in 2m35s
Deployment Installer / deployment-installer (push) Successful in 6s
Security Audit / security-audit (push) Failing after 15s

This commit is contained in:
2026-07-31 05:46:50 +02:00
parent b4248a849e
commit f1fd143ef5
13 changed files with 2332 additions and 117 deletions
+176 -13
View File
@@ -9,6 +9,7 @@ import tempfile
from types import SimpleNamespace
import unittest
from unittest.mock import patch
import zipfile
META_ROOT = Path(__file__).resolve().parents[1]
@@ -17,6 +18,7 @@ if str(RELEASE_ROOT) not in sys.path:
sys.path.insert(0, str(RELEASE_ROOT))
from govoplan_release.model import RepositorySpec # noqa: E402
from govoplan_release.artifact_identity import inspect_python_wheel # noqa: E402
from govoplan_release.git_state import sanitized_git_environment # noqa: E402
from govoplan_release.release_execution import ( # noqa: E402
ExecutorSpec,
@@ -30,6 +32,7 @@ from govoplan_release.release_execution import ( # noqa: E402
require_trusted_release_runtime,
verify_frozen_repository_tag_receipt,
verify_repository_preflight_binding,
verify_candidate_installation,
_clone_catalog_sources,
_checkout_frozen_source,
_flatpak_proxy_environment,
@@ -489,20 +492,180 @@ class ReleaseExecutionTests(unittest.TestCase):
expected_receipt={"kind": "repository_state"},
)
def test_commit_step_has_no_durable_executor(self) -> None:
self.assertIsNone(
executor_spec(
{
"id": "govoplan-files:commit",
"status": "planned",
"mutating": True,
"repo": "govoplan-files",
"source_binding": {
"kind": "repository_state",
},
}
)
def test_commit_step_has_narrow_confirmation(self) -> None:
spec = executor_spec(
{
"id": "govoplan-files:commit",
"status": "planned",
"mutating": True,
"repo": "govoplan-files",
"source_binding": {
"kind": "repository_state",
},
}
)
self.assertIsNotNone(spec)
self.assertEqual("commit", spec.kind)
self.assertEqual("COMMIT", spec.confirmation)
def test_version_and_commit_executors_bind_only_release_metadata(self) -> None:
with tempfile.TemporaryDirectory() as temp_dir:
workspace = Path(temp_dir)
repository = workspace / "govoplan-files"
remote = workspace / "files.git"
_git(workspace, "init", "--bare", str(remote))
_git(workspace, "init", "-b", "main", str(repository))
_git(repository, "config", "user.name", "Release Test")
_git(repository, "config", "user.email", "release@example.test")
(repository / "pyproject.toml").write_text(
'[project]\nname = "govoplan-files"\nversion = "1.2.3"\n',
encoding="utf-8",
)
_git(repository, "add", "pyproject.toml")
_git(repository, "commit", "-m", "Initial")
_git(repository, "remote", "add", "origin", str(remote))
_git(repository, "push", "-u", "origin", "main")
repository_spec = _repository_spec(remote)
plan_step = {
"id": "govoplan-files:version",
"repo": "govoplan-files",
"source_binding": {"kind": "repository_state"},
}
with (
patch(
"govoplan_release.release_execution.load_repository_specs",
return_value=(repository_spec,),
),
patch(
"govoplan_release.release_execution.require_trusted_release_runtime"
),
):
before = repository_state_receipt(
repo="govoplan-files",
target_tag="v1.2.4",
workspace_root=workspace,
)
_result, updated = execute_repository_step(
spec=ExecutorSpec(
"version",
"UPDATE",
"version_metadata_updated",
True,
),
plan_step=plan_step,
repo_versions={"govoplan-files": "1.2.4"},
workspace_root=workspace,
remote="origin",
expected_receipt=before,
)
self.assertFalse(updated["worktree_clean"])
self.assertNotEqual(
before["worktree_sha256"],
updated["worktree_sha256"],
)
plan_step["id"] = "govoplan-files:commit"
_result, committed = execute_repository_step(
spec=ExecutorSpec(
"commit",
"COMMIT",
"release_commit_created",
True,
),
plan_step=plan_step,
repo_versions={"govoplan-files": "1.2.4"},
workspace_root=workspace,
remote="origin",
expected_receipt=updated,
)
self.assertTrue(committed["worktree_clean"])
self.assertNotEqual(before["head"], committed["head"])
self.assertIn(
'version = "1.2.4"',
(repository / "pyproject.toml").read_text(encoding="utf-8"),
)
def test_candidate_installation_verifies_installed_metadata(self) -> None:
with tempfile.TemporaryDirectory() as temp_dir:
workspace = Path(temp_dir)
repository = workspace / "govoplan-files"
repository.mkdir()
(repository / "pyproject.toml").write_text(
'[project]\nname = "govoplan-files"\nversion = "1.2.4"\n',
encoding="utf-8",
)
artifacts = workspace / "candidate" / "artifacts"
artifacts.mkdir(parents=True)
wheel = artifacts / "govoplan_files-1.2.4-py3-none-any.whl"
members = {
"govoplan_files/__init__.py": '__version__ = "1.2.4"\n',
"govoplan_files-1.2.4.dist-info/METADATA": (
"Metadata-Version: 2.1\n"
"Name: govoplan-files\n"
"Version: 1.2.4\n"
),
"govoplan_files-1.2.4.dist-info/WHEEL": (
"Wheel-Version: 1.0\n"
"Generator: release-test\n"
"Root-Is-Purelib: true\n"
"Tag: py3-none-any\n"
),
}
record = "\n".join(f"{name},," for name in members)
record += "\ngovoplan_files-1.2.4.dist-info/RECORD,,\n"
with zipfile.ZipFile(wheel, "w") as archive:
for name, content in members.items():
archive.writestr(name, content)
archive.writestr(
"govoplan_files-1.2.4.dist-info/RECORD",
record,
)
identity = inspect_python_wheel(wheel)
channels = workspace / "candidate" / "channels"
channels.mkdir()
(channels / "stable.json").write_text(
json.dumps(
{
"release": {
"selected_units": [
{
"repo": "govoplan-files",
"version": "1.2.4",
}
],
"artifacts": [
{
"package_name": "govoplan-files",
"archive_sha256": identity.archive_sha256,
}
],
},
"core_release": {},
"modules": [
{
"python_package": "govoplan-files",
"python_ref": (
"govoplan-files @ "
"git+ssh://git@example.test/GovOPlaN/"
"govoplan-files.git@v1.2.4"
),
}
],
}
)
+ "\n",
encoding="utf-8",
)
result = verify_candidate_installation(
candidate_path=workspace / "candidate",
repo_versions={"govoplan-files": "1.2.4"},
)
self.assertEqual("verified", result["status"])
self.assertEqual("govoplan-files", result["packages"][0]["repo"])
self.assertEqual("1.2.4", result["packages"][0]["version"])
def test_frozen_checkout_uses_receipt_commit_not_live_worktree(self) -> None:
with tempfile.TemporaryDirectory() as temp_dir:
+32
View File
@@ -47,6 +47,38 @@ branch_labels: Union[str, Sequence[str], None] = None
self.assertEqual(migration.depends_on, ("core",))
self.assertEqual(migration.branch_labels, ())
def test_development_wrapper_uses_release_revision_metadata(self) -> None:
audit = load_audit_module()
with tempfile.TemporaryDirectory(prefix="migration-audit-test-") as directory:
root = Path(directory)
versions = root / "versions"
development = root / "dev_versions"
versions.mkdir()
development.mkdir()
release = versions / "1234_example.py"
release.write_text(
'revision = "1234"\n'
'down_revision = "base"\n'
'depends_on = "core"\n'
"branch_labels = None\n",
encoding="utf-8",
)
wrapper = development / release.name
wrapper.write_text(
"revision = _migration.revision\n"
"down_revision = _migration.down_revision\n"
"depends_on = _migration.depends_on\n"
"branch_labels = _migration.branch_labels\n",
encoding="utf-8",
)
migration = audit.parse_migration_file("govoplan-core", wrapper)
self.assertIsNotNone(migration)
self.assertEqual("1234", migration.revision)
self.assertEqual(("base",), migration.down_revisions)
self.assertEqual(("core",), migration.depends_on)
def test_release_baseline_matches_current_heads_in_strict_report(self) -> None:
audit = load_audit_module()
migrations = [
+106 -15
View File
@@ -23,7 +23,7 @@ from govoplan_release.selective_planner import build_selective_release_plan # n
class ReleasePlanGuidanceTests(unittest.TestCase):
def test_unprepared_target_has_structured_remediation_and_recommendation(
def test_unprepared_target_gets_bounded_version_and_commit_steps(
self,
) -> None:
with tempfile.TemporaryDirectory() as temp_dir:
@@ -40,20 +40,25 @@ class ReleasePlanGuidanceTests(unittest.TestCase):
repo_versions={"govoplan-files": "1.2.4"},
)
self.assertEqual("blocked", plan.status)
self.assertFalse(plan.source_preflight_ready)
finding = next(
item
for item in plan.gate_findings
if item.code == "repository_version_alignment"
self.assertEqual("attention", plan.status)
self.assertTrue(plan.source_preflight_ready)
self.assertFalse(
any(
item.code == "repository_version_alignment"
for item in plan.gate_findings
)
)
self.assertEqual("govoplan-files", finding.repo)
self.assertEqual("pyproject.toml", finding.source)
self.assertEqual("1.2.4", finding.expected)
self.assertEqual("1.2.3", finding.actual)
self.assertIn("Regenerate lockfiles", finding.remediation)
self.assertEqual("resolve_release_gate", plan.recommended_action.id)
self.assertEqual("govoplan-files", plan.recommended_action.repo)
steps = {step.id: step for step in plan.dry_run_steps}
self.assertEqual("planned", steps["govoplan-files:version"].status)
self.assertEqual("planned", steps["govoplan-files:commit"].status)
self.assertIn("pyproject.toml", steps["govoplan-files:version"].detail)
self.assertTrue(
any(
"version metadata will be updated" in warning
for warning in plan.units[0].warnings
)
)
self.assertIn("python-package", plan.units[0].capabilities)
def test_aligned_target_recommends_non_mutating_tag_preview(self) -> None:
with tempfile.TemporaryDirectory() as temp_dir:
@@ -74,6 +79,91 @@ class ReleasePlanGuidanceTests(unittest.TestCase):
self.assertEqual("preview_source_release", plan.recommended_action.id)
self.assertIn("Preview Tag + Publish", plan.recommended_action.remediation)
def test_mixed_plan_tags_modules_before_core_and_aligns_before_push(
self,
) -> None:
with tempfile.TemporaryDirectory() as temp_dir:
workspace = Path(temp_dir)
for repo_name in ("govoplan-core", "govoplan-files"):
repo_path = workspace / repo_name
repo_path.mkdir()
(repo_path / "pyproject.toml").write_text(
f'[project]\nname = "{repo_name}"\nversion = "1.2.3"\n',
encoding="utf-8",
)
repositories = tuple(
RepositorySnapshot(
spec=RepositorySpec(
name=repo_name,
category="core" if repo_name == "govoplan-core" else "module",
subtype="platform" if repo_name == "govoplan-core" else "infrastructure",
remote=f"git@example.test:GovOPlaN/{repo_name}.git",
path=repo_name,
),
absolute_path=str(workspace / repo_name),
exists=True,
is_git=True,
has_head=True,
branch="main",
versions=VersionSnapshot(pyproject="1.2.3"),
local_target_tag_exists=False,
)
for repo_name in ("govoplan-core", "govoplan-files")
)
base = dashboard(workspace=workspace, version="1.2.3")
mixed = ReleaseDashboard(
generated_at=base.generated_at,
meta_root=base.meta_root,
workspace_root=base.workspace_root,
target_version=None,
target_tag=None,
online=False,
include_migrations=False,
summary=DashboardSummary(
repository_count=2,
missing_count=0,
dirty_count=0,
ahead_count=0,
behind_count=0,
no_head_count=0,
error_count=0,
safe_directory_count=0,
local_target_tag_missing_count=0,
status="ready",
),
repositories=repositories,
catalog=base.catalog,
)
plan = build_selective_release_plan(
mixed,
selected_repos=("govoplan-core", "govoplan-files"),
repo_versions={
"govoplan-core": "1.2.3",
"govoplan-files": "1.2.3",
},
)
self.assertEqual(
["govoplan-files", "govoplan-core"],
[unit.repo for unit in plan.units],
)
step_ids = [step.id for step in plan.dry_run_steps]
self.assertLess(
step_ids.index("govoplan-files:tag"),
step_ids.index("govoplan-core:tag"),
)
self.assertLess(
step_ids.index("govoplan-core:tag"),
step_ids.index("release:alignment"),
)
self.assertLess(
step_ids.index("release:alignment"),
step_ids.index("govoplan-files:push"),
)
self.assertEqual("release:install-verify", step_ids[-1])
core = next(unit for unit in plan.units if unit.repo == "govoplan-core")
self.assertIn("core-release-bundle", core.capabilities)
def test_malformed_version_metadata_is_a_structured_blocker(self) -> None:
cases = (
("package.json", "{not-json\n", "JSONDecodeError"),
@@ -167,7 +257,8 @@ class ReleasePlanGuidanceTests(unittest.TestCase):
self.assertIn("data-run-step-id", webui)
self.assertIn("invalidateReleaseDraft", webui)
self.assertIn("Installation Verification", webui)
self.assertIn("external release-integration CI gate", webui)
self.assertIn("receipt-bound local gate", webui)
self.assertIn('step.id === "release:install-verify"', webui)
self.assertIn('const inspectionNotices = (summary.missing_count || 0)', webui)
self.assertIn('(summary.repository_count || 0) === 0', webui)
+105
View File
@@ -0,0 +1,105 @@
from __future__ import annotations
import json
from pathlib import Path
import sys
import tempfile
import unittest
META_ROOT = Path(__file__).resolve().parents[1]
RELEASE_ROOT = META_ROOT / "tools" / "release"
if str(RELEASE_ROOT) not in sys.path:
sys.path.insert(0, str(RELEASE_ROOT))
from govoplan_release.version_metadata import ( # noqa: E402
apply_version_metadata_mutations,
version_metadata_mutations,
)
class ReleaseVersionMetadataTests(unittest.TestCase):
def test_updates_recognized_metadata_without_changing_interface_versions(
self,
) -> None:
with tempfile.TemporaryDirectory() as temp_dir:
root = Path(temp_dir)
package = root / "src" / "govoplan_example"
backend = package / "backend"
webui = root / "webui"
backend.mkdir(parents=True)
webui.mkdir()
(root / "pyproject.toml").write_text(
'[project]\nname = "govoplan-example"\nversion = "1.2.3"\n',
encoding="utf-8",
)
(root / "package.json").write_text(
'{"name":"root","version":"1.2.3"}\n',
encoding="utf-8",
)
(webui / "package.json").write_text(
'{"name":"@govoplan/example-webui","version":"1.2.3"}\n',
encoding="utf-8",
)
(webui / "package-lock.json").write_text(
json.dumps(
{
"name": "@govoplan/example-webui",
"version": "1.2.3",
"packages": {
"": {
"name": "@govoplan/example-webui",
"version": "1.2.3",
}
},
}
)
+ "\n",
encoding="utf-8",
)
(backend / "manifest.py").write_text(
"def get_manifest():\n"
" return ModuleManifest(\n"
' id="example",\n'
' version="1.2.3",\n'
" provides_interfaces=(\n"
' ModuleInterfaceProvider(name="example.api", version="4.0"),\n'
" ),\n"
" )\n",
encoding="utf-8",
)
(package / "__init__.py").write_text(
'__version__ = "1.2.3"\n',
encoding="utf-8",
)
preview = version_metadata_mutations(
root,
target_version="1.2.4",
)
changed = apply_version_metadata_mutations(
root,
target_version="1.2.4",
)
self.assertEqual(
{mutation.path for mutation in preview},
set(changed),
)
self.assertEqual(
"1.2.4",
json.loads((webui / "package-lock.json").read_text())[
"packages"
][""]["version"],
)
manifest = (backend / "manifest.py").read_text(encoding="utf-8")
self.assertIn('version="1.2.4"', manifest)
self.assertIn('version="4.0"', manifest)
self.assertEqual(
'__version__ = "1.2.4"\n',
(package / "__init__.py").read_text(encoding="utf-8"),
)
if __name__ == "__main__":
unittest.main()