fix(release): prepare aligned 0.1.45 composition and guarded candidate sequencing

This commit is contained in:
2026-09-08 01:52:08 +02:00
parent 1cec4ee1d8
commit 6a8f53b87d
17 changed files with 588 additions and 72 deletions
+15
View File
@@ -0,0 +1,15 @@
import assert from "node:assert/strict";
import { findTypeOnlyJsxImports } from "../tools/checks/check-jsx-value-imports.mjs";
const findings = (source) => findTypeOnlyJsxImports([{ path: "/fixture.tsx", source }]).map((item) => item.component);
assert.deepEqual(findings('import type { FormGrid, AuthInfo } from "@govoplan/core-webui"; const page = <Dialog><FormGrid /></Dialog>;'), ["FormGrid"]);
assert.deepEqual(findings('import { type FormGrid as Layout } from "ui"; const page = <Layout>Content</Layout>;'), ["Layout"]);
assert.deepEqual(findings('import type Layout from "ui"; const page = <Layout />;'), ["Layout"]);
assert.deepEqual(findings('import type * as ui from "ui"; const page = <ui.Layout />;'), ["ui.Layout"]);
assert.deepEqual(findings('import type { FormGrid } from "ui"; const page = <div title={<FormGrid />} />;'), ["FormGrid"]);
assert.deepEqual(findings('import { FormGrid, type AuthInfo } from "ui"; const page = <FormGrid />;'), []);
assert.deepEqual(findings('import type { FormGrid } from "ui"; function Page({ FormGrid }: Props) { return <FormGrid />; }'), []);
assert.deepEqual(findings('import type * as ui from "ui"; function Page(ui: RuntimeControls) { return <ui.Layout />; }'), []);
assert.deepEqual(findings('import type { Layout } from "ui"; const page: Layout = {};'), []);
assert.deepEqual(findings('import type { input } from "ui"; const page = <input />;'), []);
console.log("JSX runtime-import AST regression tests passed (10 cases).");
+45 -2
View File
@@ -25,6 +25,11 @@ from govoplan_core.core.institutional import (
TemporalRevision,
service_launch_capability,
)
from govoplan_core.core.temporal import (
TemporalDataContext,
bind_temporal_data_context,
reset_temporal_data_context,
)
from govoplan_cases.backend.party_context import CasePartyContext
from govoplan_cases.backend.db.models import (
CaseAccessGrant,
@@ -134,6 +139,13 @@ class _Registry:
class InstitutionalGovernanceJourneyTests(unittest.TestCase):
def setUp(self) -> None:
# Portal's effective_at does not replace the SQL provider's request-local
# read clock. Keep both on the journey date, without bypassing validity
# filtering or extending the fixture's finite publication interval.
token = bind_temporal_data_context(TemporalDataContext(evaluated_at=NOW))
self.addCleanup(reset_temporal_data_context, token)
def test_service_to_formal_outcome_retains_governed_context(self) -> None:
engine = create_engine("sqlite+pysqlite:///:memory:")
for table in (
@@ -220,13 +232,44 @@ class InstitutionalGovernanceJourneyTests(unittest.TestCase):
service_launch_capability("case"): object(),
}
)
entry = PortalServiceDirectory(service_registry).list_entries(
directory = PortalServiceDirectory(service_registry)
for outside_interval in (
service.temporal.valid_from - timedelta(microseconds=1),
service.temporal.valid_to,
):
with self.subTest(outside_interval=outside_interval):
token = bind_temporal_data_context(
TemporalDataContext(evaluated_at=outside_interval)
)
try:
# Keep Portal inside the valid interval: the real SQL
# provider must still exclude a service outside its own
# temporal read context, before Portal can project it.
self.assertEqual(
(),
directory.list_entries(
session,
principal,
tenant_id="tenant-1",
effective_at=NOW,
audiences=("resident",),
),
)
finally:
reset_temporal_data_context(token)
entries = directory.list_entries(
session,
principal,
tenant_id="tenant-1",
effective_at=NOW,
audiences=("resident",),
)[0]
)
self.assertEqual(
(service.reference,),
tuple(entry.definition.reference for entry in entries),
)
entry = entries[0]
self.assertTrue(entry.available)
intake = CaseServiceIntake().plan(
entry.definition,
+18 -3
View File
@@ -62,22 +62,37 @@ class PackageRegistryReleaseTests(unittest.TestCase):
encoding="utf-8"
)
)["project"]["version"]
meta_package = ROOT / "packages/govoplan-meta/pyproject.toml"
meta_project = tomllib.loads(
meta_package.read_text(encoding="utf-8")
)["project"]
expected_tasks_pin = next(
requirement
for requirement in (
*meta_project["dependencies"],
*meta_project["optional-dependencies"]["full"],
)
if requirement.startswith("govoplan-tasks==")
)
selected = PACKAGE_SET.parse_meta_package(
ROOT / "packages/govoplan-meta/pyproject.toml",
meta_package,
core_version=core_version,
)
by_name = {item["name"]: item for item in selected}
self.assertIn("govoplan-core", by_name)
self.assertIn("govoplan-records", by_name)
self.assertEqual("0.1.22", by_name["govoplan-tasks"]["version"])
self.assertEqual(
expected_tasks_pin,
f"govoplan-tasks=={by_name['govoplan-tasks']['version']}",
)
payload = PACKAGE_SET.generate_package_set(
core_version=core_version,
requirements=ROOT / "requirements-release.txt",
workspace=ROOT.parent,
profile="full",
meta_package=ROOT / "packages/govoplan-meta/pyproject.toml",
meta_package=meta_package,
)
self.assertEqual("full", payload["profile"])
self.assertEqual(len(selected), len(payload["python"]))
+238 -14
View File
@@ -1,6 +1,7 @@
from __future__ import annotations
import json
import runpy
import subprocess
import sys
import tempfile
@@ -214,21 +215,23 @@ class ReleaseRepositoryTagTests(unittest.TestCase):
git(self.repo, "commit", "-m", "Add release WebUI composition")
git(self.repo, "push", "origin", "main")
result = tag_repositories(
repos=("govoplan-core", "govoplan-campaign"),
repo_versions={"govoplan-core": "0.1.10", "govoplan-campaign": "0.1.10"},
workspace_root=self.workspace,
apply=True,
push=True,
)
for push in (False, True):
with self.subTest(push=push):
result = tag_repositories(
repos=("govoplan-core", "govoplan-campaign"),
repo_versions={"govoplan-core": "0.1.10", "govoplan-campaign": "0.1.10"},
workspace_root=self.workspace,
apply=True,
push=push,
)
self.assertEqual("blocked", result["status"])
self.assertIn("no selected repository was mutated", result["detail"])
self.assertEqual("skipped", result["repositories"][0]["status"])
self.assertEqual("blocked", result["repositories"][1]["status"])
self.assertIn("release WebUI composition gate failed", result["repositories"][1]["detail"])
for repository in (self.repo, self.remote, campaign, campaign_remote):
self.assertFalse(ref_exists(repository, "refs/tags/v0.1.10"))
self.assertEqual("blocked", result["status"])
self.assertIn("no selected repository was mutated", result["detail"])
self.assertEqual("skipped", result["repositories"][0]["status"])
self.assertEqual("blocked", result["repositories"][1]["status"])
self.assertIn("release WebUI composition gate failed", result["repositories"][1]["detail"])
for repository in (self.repo, self.remote, campaign, campaign_remote):
self.assertFalse(ref_exists(repository, "refs/tags/v0.1.10"))
def test_api_keeps_legacy_source_release_preview_only(self) -> None:
with TestClient(
@@ -300,6 +303,183 @@ class ReleaseRepositoryTagTests(unittest.TestCase):
self.assertIn("Signed Website Catalog", ui.text)
self.assertIn("Apply + Website Tag", ui.text)
def test_local_module_candidate_precedes_core_lock_but_publication_does_not(self) -> None:
campaign, campaign_remote = self._staged_campaign_bundle()
remotes = (self.remote, self.manifest_remote, campaign_remote)
remote_refs = {path: git_text(path, "show-ref") for path in remotes}
arguments = {
"repos": ("govoplan-campaign",),
"repo_versions": {"govoplan-campaign": "0.1.10"},
"workspace_root": self.workspace,
}
preview = tag_repositories(**arguments, apply=False, push=False)
self.assertEqual("planned", preview["status"], preview)
self.assertFalse(ref_exists(campaign, "refs/tags/v0.1.10"))
candidate = tag_repositories(**arguments, apply=True, push=False)
self.assertEqual("tagged", candidate["status"], candidate)
self.assertEqual("tag", git_text(campaign, "cat-file", "-t", "v0.1.10"))
tag_object = git_text(campaign, "rev-parse", "v0.1.10")
head = git_text(campaign, "rev-parse", "HEAD")
self.assertEqual(head, git_text(campaign, "rev-parse", "v0.1.10^{commit}"))
for apply in (False, True):
with self.subTest(publish_apply=apply):
blocked = tag_repositories(**arguments, apply=apply, push=True)
self.assertEqual("blocked", blocked["status"], blocked)
self.assertIn(
"release WebUI composition gate failed",
blocked["repositories"][0]["detail"],
)
self.assertEqual(tag_object, git_text(campaign, "rev-parse", "v0.1.10"))
self.assertEqual(remote_refs, {path: git_text(path, "show-ref") for path in remotes})
lock_path = self.repo / "webui" / "package-lock.release.json"
lock = json.loads(lock_path.read_text(encoding="utf-8"))
locked_campaign = lock["packages"]["node_modules/@govoplan/campaign-webui"]
locked_campaign["version"] = "0.1.10"
locked_campaign["resolved"] = f"git+ssh://git@example.test/acme/govoplan-campaign.git#{head}"
lock_path.write_text(json.dumps(lock) + "\n", encoding="utf-8")
git(self.repo, "add", "webui/package-lock.release.json")
git(self.repo, "commit", "-m", "Resolve reviewed local Campaign candidate")
core_candidate = tag_repositories(
repos=("govoplan-core",),
repo_versions={"govoplan-core": "0.1.10"},
workspace_root=self.workspace,
apply=True,
push=False,
)
self.assertEqual("tagged", core_candidate["status"], core_candidate)
self.assertEqual(remote_refs, {path: git_text(path, "show-ref") for path in remotes})
published = tag_repositories(**arguments, apply=True, push=True)
self.assertEqual("published", published["status"], published)
self.assertEqual(tag_object, git_text(campaign_remote, "rev-parse", "v0.1.10"))
self.assertEqual(head, git_text(campaign_remote, "rev-parse", "refs/heads/main"))
self.assertEqual(remote_refs[self.remote], git_text(self.remote, "show-ref"))
def test_local_core_candidate_still_requires_resolved_module_tags(self) -> None:
campaign, campaign_remote = self._staged_campaign_bundle()
for selected in (("govoplan-core",), ("govoplan-campaign", "govoplan-core")):
with self.subTest(selected=selected):
result = tag_repositories(
repos=selected,
repo_versions={repo: "0.1.10" for repo in selected},
workspace_root=self.workspace,
apply=True,
push=False,
)
self.assertEqual("blocked", result["status"], result)
core_row = next(row for row in result["repositories"] if row["repo"] == "govoplan-core")
self.assertIn("version alignment gate failed", core_row["detail"])
self.assertIn("@govoplan/campaign-webui:resolved", core_row["detail"])
self.assertIn("expected 'local tag v0.1.10'", core_row["detail"])
for repository in (self.repo, self.remote, campaign, campaign_remote):
self.assertFalse(ref_exists(repository, "refs/tags/v0.1.10"))
def test_local_module_candidate_preserves_version_and_worktree_gates(self) -> None:
campaign, campaign_remote = self._staged_campaign_bundle()
arguments = {
"repos": ("govoplan-campaign",),
"workspace_root": self.workspace,
"apply": True,
"push": False,
}
mismatch = tag_repositories(**arguments, repo_versions={"govoplan-campaign": "0.1.11"})
self.assertEqual("blocked", mismatch["status"], mismatch)
self.assertIn("version alignment gate failed", mismatch["repositories"][0]["detail"])
(campaign / "unreviewed.txt").write_text("operator work\n", encoding="utf-8")
dirty = tag_repositories(**arguments, repo_versions={"govoplan-campaign": "0.1.10"})
self.assertEqual("blocked", dirty["status"], dirty)
self.assertIn("worktree is not clean", dirty["repositories"][0]["detail"])
for repository in (campaign, campaign_remote):
for tag in ("v0.1.10", "v0.1.11"):
self.assertFalse(ref_exists(repository, f"refs/tags/{tag}"))
def test_local_module_candidate_preserves_manifest_gate(self) -> None:
campaign, campaign_remote = self._staged_campaign_bundle()
replace_with_unscoped_workflow_manifest(self.manifest_repo)
result = tag_repositories(
repos=("govoplan-campaign",),
repo_versions={"govoplan-campaign": "0.1.10"},
workspace_root=self.workspace,
apply=True,
push=False,
)
self.assertEqual("blocked", result["status"], result)
self.assertIn("scope-conditioned alternative", result["repositories"][0]["detail"])
for repository in (campaign, campaign_remote):
self.assertFalse(ref_exists(repository, "refs/tags/v0.1.10"))
def test_local_module_candidate_preserves_remote_tag_immutability(self) -> None:
campaign, campaign_remote = self._staged_campaign_bundle()
git(campaign, "tag", "-a", "v0.1.10", "-m", "Existing immutable tag", "v0.1.9^{commit}")
git(campaign, "push", "origin", "refs/tags/v0.1.10")
remote_refs = git_text(campaign_remote, "show-ref")
for local_exists in (True, False):
with self.subTest(local_exists=local_exists):
if not local_exists:
git(campaign, "tag", "-d", "v0.1.10")
result = tag_repositories(
repos=("govoplan-campaign",),
repo_versions={"govoplan-campaign": "0.1.10"},
workspace_root=self.workspace,
apply=True,
push=False,
)
self.assertEqual("blocked", result["status"], result)
self.assertIn("immutable tag", result["repositories"][0]["detail"])
self.assertIn("not HEAD", result["repositories"][0]["detail"])
self.assertEqual(remote_refs, git_text(campaign_remote, "show-ref"))
self.assertEqual(local_exists, ref_exists(campaign, "refs/tags/v0.1.10"))
def _staged_campaign_bundle(self) -> tuple[Path, Path]:
campaign, remote = create_release_repo(
root=self.root,
workspace=self.workspace,
name="govoplan-campaign",
version="0.1.9",
)
campaign_webui = campaign / "webui"
campaign_webui.mkdir()
package_path = campaign_webui / "package.json"
package_path.write_text(
'{"name":"@govoplan/campaign-webui","version":"0.1.9"}\n', encoding="utf-8"
)
git(campaign, "add", "webui/package.json")
git(campaign, "commit", "-m", "Prior Campaign WebUI package")
git(campaign, "tag", "-a", "v0.1.9", "-m", "Prior Campaign release")
git(campaign, "push", "origin", "main", "refs/tags/v0.1.9")
prior_commit = git_text(campaign, "rev-parse", "HEAD")
for path in (campaign / "pyproject.toml", package_path):
path.write_text(path.read_text(encoding="utf-8").replace("0.1.9", "0.1.10"), encoding="utf-8")
git(campaign, "add", "pyproject.toml", "webui/package.json")
git(campaign, "commit", "-m", "Reviewed Campaign candidate")
core_webui = self.repo / "webui"
core_webui.mkdir()
dependency_ref = "git+ssh://git@example.test/acme/govoplan-campaign.git#v0.1.10"
package = {
"name": "@govoplan/core-webui",
"version": "0.1.10",
"dependencies": {"@govoplan/campaign-webui": dependency_ref},
}
(core_webui / "package.release.json").write_text(json.dumps(package) + "\n", encoding="utf-8")
(core_webui / "package-lock.release.json").write_text(
json.dumps({"packages": {
"": package,
"node_modules/@govoplan/campaign-webui": {
"version": "0.1.9",
"resolved": f"git+ssh://git@example.test/acme/govoplan-campaign.git#{prior_commit}",
},
}}) + "\n",
encoding="utf-8",
)
git(self.repo, "add", "webui/package.release.json", "webui/package-lock.release.json")
git(self.repo, "commit", "-m", "Stage Core input before candidate lock resolution")
return campaign, remote
def git(cwd: Path, *args: str) -> None:
result = subprocess.run(
@@ -338,9 +518,50 @@ def add_scoped_workflow_manifest(repo: Path) -> None:
backend.mkdir(parents=True)
(package / "__init__.py").write_text("", encoding="utf-8")
(backend / "__init__.py").write_text("", encoding="utf-8")
# This small workspace still has to satisfy the real presentation contract.
# Keep that prerequisite shared by both valid and intentionally unscoped
# documentation fixtures, so each test reaches its intended release gate.
canonical_areas = runpy.run_path(
str(META_ROOT / "tools" / "checks" / "check-manifest-shapes.py")
)["CANONICAL_PRODUCT_AREAS"]
(backend / "release_fixture.py").write_text(
"""from govoplan_core.core.modules import FrontendModule, ProductAreaContribution
from govoplan_core.core.views import ViewSurface
def fixture_frontend():
return FrontendModule(
module_id="access",
view_surfaces=(
ViewSurface(
id="access.section.release-fixture",
module_id="access",
kind="section",
label="Release fixture",
),
),
product_areas=tuple(
ProductAreaContribution(
id=area_id,
module_id="access",
label=label,
icon=icon,
description=description,
order=order,
surface_ids=("access.section.release-fixture",),
)
for area_id, (label, icon, description, order) in CANONICAL_AREAS.items()
),
)
CANONICAL_AREAS = """ + repr(canonical_areas) + "\n",
encoding="utf-8",
)
(backend / "manifest.py").write_text(
"""from govoplan_core.core.modules import DocumentationCondition, DocumentationTopic, ModuleManifest, PermissionDefinition
from govoplan_core.core.provider_governance import declared_module_architecture
from .release_fixture import fixture_frontend
def get_manifest():
@@ -348,6 +569,7 @@ def get_manifest():
id="access",
name="Access",
version="0.1.10",
frontend=fixture_frontend(),
permissions=(
PermissionDefinition(
scope="access:item:read",
@@ -403,6 +625,7 @@ def replace_with_unscoped_workflow_manifest(repo: Path) -> None:
manifest.write_text(
"""from govoplan_core.core.modules import DocumentationTopic, ModuleManifest
from govoplan_core.core.provider_governance import declared_module_architecture
from .release_fixture import fixture_frontend
def get_manifest():
@@ -410,6 +633,7 @@ def get_manifest():
id="access",
name="Access",
version="0.1.10",
frontend=fixture_frontend(),
documentation=(
DocumentationTopic(
id="access.workflow.unscoped",
+35
View File
@@ -13,12 +13,47 @@ if str(RELEASE_ROOT) not in sys.path:
sys.path.insert(0, str(RELEASE_ROOT))
from govoplan_release.version_metadata import ( # noqa: E402
VersionMetadataError,
apply_version_metadata_mutations,
version_metadata_mutations,
)
class ReleaseVersionMetadataTests(unittest.TestCase):
def test_updates_shared_module_version_without_rewriting_independent_interfaces(self) -> None:
with tempfile.TemporaryDirectory() as temp_dir:
root = Path(temp_dir)
backend = root / "src" / "govoplan_example" / "backend"
backend.mkdir(parents=True)
manifest = backend / "manifest.py"
manifest.write_text(
'MODULE_VERSION: str = "1.2.3"\n'
'manifest = ModuleManifest(id="example", version=MODULE_VERSION,\n'
' provides_interfaces=(ModuleInterfaceProvider(name="api", version="2.0"),))\n',
encoding="utf-8",
)
changed = apply_version_metadata_mutations(root, target_version="1.2.4")
self.assertEqual(("src/govoplan_example/backend/manifest.py",), changed)
self.assertIn('MODULE_VERSION: str = "1.2.4"', manifest.read_text())
self.assertIn('version="2.0"', manifest.read_text())
self.assertEqual((), version_metadata_mutations(root, target_version="1.2.4"))
def test_dynamic_module_version_fails_before_any_metadata_is_written(self) -> None:
with tempfile.TemporaryDirectory() as temp_dir:
root = Path(temp_dir)
backend = root / "src" / "govoplan_example" / "backend"
backend.mkdir(parents=True)
project = root / "pyproject.toml"
project.write_text('[project]\nname="govoplan-example"\nversion="1.2.3"\n')
before = project.read_bytes()
(backend / "manifest.py").write_text(
'MODULE_VERSION = compute_version()\n'
'manifest = ModuleManifest(id="example", version=MODULE_VERSION)\n',
)
with self.assertRaisesRegex(VersionMetadataError, "no literal MODULE_VERSION"):
apply_version_metadata_mutations(root, target_version="1.2.4")
self.assertEqual(before, project.read_bytes())
def test_updates_recognized_metadata_without_changing_interface_versions(
self,
) -> None:
+4
View File
@@ -98,6 +98,10 @@ class SecurityAuditWrapperTests(unittest.TestCase):
if [[ "${1:-}" == 'git' && "${2:-}" == '--help' ]]; then
exit 0
fi
if [[ " $* " != *" --redact=100 "* ]]; then
echo 'secret scans must redact reports and logs' >&2
exit 3
fi
output=''
while [[ $# -gt 0 ]]; do
if [[ "$1" == '--report-path' ]]; then