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
47 changes: 47 additions & 0 deletions .github/scripts/cargo_manifest_guard.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#
# Copyright 2026 The Ethos maintainers
#
# Licensed under the Apache License, Version 2.0 (the "License");
#

from __future__ import annotations

import re
from typing import Protocol


class _Asserts(Protocol):
def assertIn(self, member: object, container: object, msg: object = ...) -> None: ...
def assertRegex(self, text: str, expected_regex: str, msg: object = ...) -> None: ...


def workspace_package_version(workspace_manifest: str) -> str:
match = re.search(
r"(?ms)^\[workspace\.package\]\s+.*?^version = \"([^\"]+)\"",
workspace_manifest,
)
if match is None:
raise AssertionError("workspace manifest is missing [workspace.package] version")
return match.group(1)


def assert_workspace_version_is_semver(case: _Asserts, workspace_manifest: str) -> str:
version = workspace_package_version(workspace_manifest)
case.assertRegex(version, r"^\d+\.\d+\.\d+$")
return version


def assert_workspace_dependency_uses_workspace_version(
case: _Asserts,
workspace_manifest: str,
*,
dependency: str,
package: str,
path: str,
default_features_false: bool = False,
) -> None:
version = assert_workspace_version_is_semver(case, workspace_manifest)
expected = f'{dependency} = {{ package = "{package}", path = "{path}", version = "{version}"'
case.assertIn(expected, workspace_manifest)
if default_features_false:
case.assertIn("default-features = false", workspace_manifest)
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import unittest
from pathlib import Path

from cargo_manifest_guard import assert_workspace_dependency_uses_workspace_version
from makefile_guard import target_block


Expand Down Expand Up @@ -126,10 +127,13 @@ def test_candidate_manifests_are_activated_and_non_candidates_stay_blocked(self)
lockfile = read(ROOT / "Cargo.lock")
verify = read(ROOT / "crates/ethos-verify/Cargo.toml")
pdf = read(ROOT / "crates/ethos-pdf/Cargo.toml")
self.assertIn(
'ethos-core = { package = "ethos-doc-core", path = "crates/ethos-core", '
'version = "0.1.2", default-features = false }',
assert_workspace_dependency_uses_workspace_version(
self,
workspace,
dependency="ethos-core",
package="ethos-doc-core",
path="crates/ethos-core",
default_features_false=True,
)
self.assertIn('name = "ethos-doc-core"', lockfile)
self.assertNotIn('name = "ethos-core"', lockfile)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
from pathlib import Path
from typing import Any

from cargo_manifest_guard import assert_workspace_version_is_semver
from makefile_guard import target_block


Expand Down Expand Up @@ -493,7 +494,7 @@ def test_candidate_crates_remain_publish_false_until_later_approval(self) -> Non
self.assertNotIn("publish = false", verify)
self.assertIn('name = "ethos-pdf"', pdf)
self.assertNotIn("publish = false", pdf)
self.assertIn('version = "0.1.2"', read(ROOT / "Cargo.toml"))
assert_workspace_version_is_semver(self, read(ROOT / "Cargo.toml"))

def test_evidence_status_matches_decider_input(self) -> None:
status = load_json(PREP)["evidence_review_status"]
Expand Down Expand Up @@ -611,7 +612,7 @@ def test_semver_package_version_decision_prep_keeps_version_unselected(self) ->
"semver_decision_inputs_recorded_version_unselected_publication_blocked",
review["review_state"],
)
self.assertIn('version = "0.1.2"', cargo)
assert_workspace_version_is_semver(self, cargo)
self.assertIn('reserved_crates_io_version = "0.0.0-reserved.0"', core_manifest)
self.assertIn('reserved_crates_io_version = "0.0.0-reserved.0"', verify_manifest)
self.assertIn('reserved_crates_io_version = "0.0.0-reserved.0"', pdf_manifest)
Expand Down Expand Up @@ -667,7 +668,7 @@ def test_combined_package_publication_decision_prep_bundle_blocks_all_actions(se
self.assertIn('name = "ethos-doc-core"', core_manifest)
self.assertIn('name = "ethos-verify"', verify_manifest)
self.assertIn('name = "ethos-pdf"', pdf_manifest)
self.assertIn('version = "0.1.2"', cargo)
assert_workspace_version_is_semver(self, cargo)

def test_package_publication_approval_request_packet_keeps_all_actions_blocked(self) -> None:
packet = load_json(PREP)["package_publication_approval_request_packet"]
Expand Down Expand Up @@ -741,7 +742,7 @@ def test_package_publication_pre_approval_gap_ledger_keeps_resolution_inputs_exp
self.assertIn('name = "ethos-doc-core"', core_manifest)
self.assertIn('name = "ethos-verify"', verify_manifest)
self.assertIn('name = "ethos-pdf"', pdf_manifest)
self.assertIn('version = "0.1.2"', cargo)
assert_workspace_version_is_semver(self, cargo)

def test_pdfium_boundary_keeps_ethos_pdf_held_until_confirmed(self) -> None:
approved = load_json(PREP)["approved_package_publication_prep"]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import unittest
from pathlib import Path

from cargo_manifest_guard import workspace_package_version
from makefile_guard import target_block


Expand Down Expand Up @@ -121,7 +122,7 @@ def test_candidate_activation_script_passes_with_registry_equivalent_consumer(se
commands = [entry["command"] for entry in result["commands"]]

self.assertEqual("pass", result["status"])
self.assertEqual("0.1.2", result["candidate_version"])
self.assertEqual(workspace_package_version(read(ROOT / "Cargo.toml")), result["candidate_version"])
self.assertEqual(["ethos-doc-core", "ethos-verify", "ethos-pdf"], result["candidate_packages"])
self.assertEqual("pass", result["registry_equivalent_consumer_check"])
self.assertTrue(result["source_candidate_manifests_activated"])
Expand Down Expand Up @@ -149,9 +150,10 @@ def test_candidate_activation_preserves_import_and_dependency_shape(self) -> Non
self.assertEqual(["full"], activation["pdf_core_features"])
self.assertTrue(all(checks.values()))
self.assertEqual({"ethos-doc-core", "ethos-verify", "ethos-pdf"}, set(artifacts))
candidate_version = self.result["candidate_version"]
for artifact in artifacts.values():
self.assertRegex(artifact["sha256"], r"^[0-9a-f]{64}$")
self.assertTrue(artifact["crate_file"].endswith("-0.1.2.crate"))
self.assertTrue(artifact["crate_file"].endswith(f"-{candidate_version}.crate"))

def test_source_candidate_manifests_are_activated_and_profile_copy_is_in_sync(self) -> None:
core_manifest = read(ROOT / "crates/ethos-core/Cargo.toml")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import unittest
from pathlib import Path

from cargo_manifest_guard import workspace_package_version
from makefile_guard import target_block


Expand Down Expand Up @@ -115,7 +116,7 @@ def test_current_registry_equivalent_assembly_passes_after_manifest_activation(s
commands = [entry["command"] for entry in result["commands"]]

self.assertEqual("pass", result["status"])
self.assertEqual("0.1.2", result["candidate_version"])
self.assertEqual(workspace_package_version(read(ROOT / "Cargo.toml")), result["candidate_version"])
self.assertEqual(["ethos-doc-core", "ethos-verify", "ethos-pdf"], result["candidate_packages"])
self.assertEqual("pass", result["registry_equivalent_consumer_check"])
self.assertTrue(result["source_manifest_activation_applied"])
Expand All @@ -139,9 +140,10 @@ def test_artifacts_and_manifest_shape_are_current(self) -> None:
self.assertEqual(["grounding", "verify-types"], activation["verify_core_features"])
self.assertEqual(["full"], activation["pdf_core_features"])
self.assertEqual({"ethos-doc-core", "ethos-verify", "ethos-pdf"}, set(artifacts))
candidate_version = self.result["candidate_version"]
for artifact in artifacts.values():
self.assertRegex(artifact["sha256"], r"^[0-9a-f]{64}$")
self.assertTrue(artifact["crate_file"].endswith("-0.1.2.crate"))
self.assertTrue(artifact["crate_file"].endswith(f"-{candidate_version}.crate"))

def test_source_candidate_manifests_are_activated_while_tags_and_registry_stay_absent(self) -> None:
for manifest in (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
from pathlib import Path
from typing import Any

from cargo_manifest_guard import assert_workspace_version_is_semver
from makefile_guard import target_block


Expand Down Expand Up @@ -176,7 +177,7 @@ def test_version_tag_policy_record_keeps_placeholder_and_workspace_versions_sepa
record = normalized(record_path(RECORDS["version_tag_policy"]))
root_manifest = read(ROOT / "Cargo.toml")

self.assertIn('version = "0.1.2"', root_manifest)
assert_workspace_version_is_semver(self, root_manifest)
self.assertIn("Workspace package version is `0.1.0`", record)
self.assertIn("`0.0.0-reserved.0` placeholders", record)
self.assertIn("`ethos-source-snapshot-660f268`", record)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import unittest
from pathlib import Path

from cargo_manifest_guard import assert_workspace_dependency_uses_workspace_version
from makefile_guard import target_block


Expand Down Expand Up @@ -125,10 +126,13 @@ def test_source_manifests_have_activated_but_blocked_shape(self) -> None:
verify = read(ROOT / "crates/ethos-verify/Cargo.toml")
pdf = read(ROOT / "crates/ethos-pdf/Cargo.toml")

self.assertIn(
'ethos-core = { package = "ethos-doc-core", path = "crates/ethos-core", '
'version = "0.1.2", default-features = false }',
assert_workspace_dependency_uses_workspace_version(
self,
workspace,
dependency="ethos-core",
package="ethos-doc-core",
path="crates/ethos-core",
default_features_false=True,
)
self.assertIn('name = "ethos-doc-core"', core)
self.assertIn('[lib]\nname = "ethos_core"', core)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import unittest
from pathlib import Path

from cargo_manifest_guard import assert_workspace_dependency_uses_workspace_version
from makefile_guard import target_block


Expand Down Expand Up @@ -113,9 +114,13 @@ def test_current_manifests_remain_unmigrated_source_tree_manifests(self) -> None
self.assertNotIn("publish = false", core)
self.assertNotIn("publish = false", verify)
self.assertNotIn("publish = false", pdf)
self.assertIn(
'ethos-core = { package = "ethos-doc-core", path = "crates/ethos-core", version = "0.1.2", default-features = false }',
assert_workspace_dependency_uses_workspace_version(
self,
workspace,
dependency="ethos-core",
package="ethos-doc-core",
path="crates/ethos-core",
default_features_false=True,
)
self.assertIn('ethos-core = { workspace = true, features = ["grounding", "verify-types"] }', verify)
self.assertIn('ethos-core = { workspace = true, features = ["full"] }', pdf)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import unittest
from pathlib import Path

from cargo_manifest_guard import assert_workspace_version_is_semver
from makefile_guard import target_block


Expand Down Expand Up @@ -106,7 +107,7 @@ def test_current_versions_and_manifests_stay_source_tree_only(self) -> None:
verify = read(ROOT / "crates/ethos-verify/Cargo.toml")
pdf = read(ROOT / "crates/ethos-pdf/Cargo.toml")

self.assertIn('version = "0.1.2"', workspace)
assert_workspace_version_is_semver(self, workspace)
self.assertIn('reserved_crates_io_version = "0.0.0-reserved.0"', core)
self.assertIn('reserved_crates_io_version = "0.0.0-reserved.0"', verify)
self.assertIn('reserved_crates_io_version = "0.0.0-reserved.0"', pdf)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import unittest
from pathlib import Path

from cargo_manifest_guard import assert_workspace_dependency_uses_workspace_version
from makefile_guard import target_block


Expand Down Expand Up @@ -110,9 +111,13 @@ def test_current_manifests_stay_source_tree_only(self) -> None:
self.assertNotIn("publish = false", core)
self.assertNotIn("publish = false", verify)
self.assertNotIn("publish = false", pdf)
self.assertIn(
'ethos-core = { package = "ethos-doc-core", path = "crates/ethos-core", version = "0.1.2", default-features = false }',
assert_workspace_dependency_uses_workspace_version(
self,
workspace,
dependency="ethos-core",
package="ethos-doc-core",
path="crates/ethos-core",
default_features_false=True,
)
self.assertIn('ethos-core = { workspace = true, features = ["grounding", "verify-types"] }', verify)
self.assertIn('ethos-core = { workspace = true, features = ["full"] }', pdf)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import unittest
from pathlib import Path

from cargo_manifest_guard import assert_workspace_version_is_semver
from makefile_guard import target_block


Expand Down Expand Up @@ -138,7 +139,7 @@ def test_binding_points_at_activated_candidate_manifest_state(self) -> None:
)
self.assertIn('reserved_crates_io_version = "0.0.0-reserved.0"', text, str(manifest))

self.assertIn('version = "0.1.2"', read(ROOT / "Cargo.toml"))
assert_workspace_version_is_semver(self, read(ROOT / "Cargo.toml"))

for manifest in (
ROOT / "crates/ethos-cli/Cargo.toml",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import unittest
from pathlib import Path

from cargo_manifest_guard import assert_workspace_version_is_semver
from makefile_guard import target_block


Expand Down Expand Up @@ -105,7 +106,7 @@ def test_current_manifests_and_versions_stay_unchanged(self) -> None:
verify = read(ROOT / "crates/ethos-verify/Cargo.toml")
pdf = read(ROOT / "crates/ethos-pdf/Cargo.toml")

self.assertIn('version = "0.1.2"', workspace)
assert_workspace_version_is_semver(self, workspace)
self.assertIn('reserved_crates_io_version = "0.0.0-reserved.0"', core)
self.assertIn('reserved_crates_io_version = "0.0.0-reserved.0"', verify)
self.assertIn('reserved_crates_io_version = "0.0.0-reserved.0"', pdf)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import unittest
from pathlib import Path

from cargo_manifest_guard import assert_workspace_version_is_semver
from makefile_guard import target_block


Expand Down Expand Up @@ -101,7 +102,7 @@ def test_workspace_and_reserved_versions_stay_separate(self) -> None:
adr = normalized(ROOT / "docs/decisions/ADR-0006-package-identifiers.md")
record = normalized(RECORD)

self.assertIn('version = "0.1.2"', root_manifest)
assert_workspace_version_is_semver(self, root_manifest)
self.assertIn("0.0.0-reserved.0", adr)
self.assertIn("Workspace package version `0.1.0` remains a source-tree version", record)
self.assertIn("ADR-0006 crates.io reservations remain `0.0.0-reserved.0` placeholders", record)
Expand Down
2 changes: 1 addition & 1 deletion .github/scripts/test_npm_binary_package_scaffold.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ def test_package_metadata_is_bounded_to_supported_release_targets(self) -> None:
package = json.loads(read(PACKAGE_JSON))

self.assertEqual("@docushell/ethos-pdf", package["name"])
self.assertEqual("0.1.2", package["version"])
self.assertRegex(package["version"], r"^\d+\.\d+\.\d+$")
self.assertEqual("Apache-2.0", package["license"])
self.assertEqual({"ethos": "./bin/ethos-pdf.js"}, package["bin"])
self.assertIn("vendor/", package["files"])
Expand Down
12 changes: 10 additions & 2 deletions .github/scripts/test_python_public_api_policy.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,13 @@ def pyproject_text() -> str:
return read(PYPROJECT)


def pyproject_version() -> str:
match = re.search(r'^version = "([^"]+)"$', pyproject_text(), re.MULTILINE)
if match is None:
raise AssertionError("pyproject.toml is missing a project version")
return match.group(1)


def parse_init_assignments() -> dict[str, object]:
module = ast.parse(read(INIT))
values: dict[str, object] = {}
Expand All @@ -70,9 +77,10 @@ def parse_init_assignments() -> dict[str, object]:
class PythonPublicApiPolicyTests(unittest.TestCase):
def test_package_metadata_declares_public_release_policy(self) -> None:
text = pyproject_text()
version = pyproject_version()

self.assertIn('name = "ethos-pdf"', text)
self.assertIn('version = "0.1.2"', text)
self.assertRegex(version, r"^\d+\.\d+\.\d+$")
self.assertIn('requires-python = ">=3.8"', text)
self.assertIn('license = "Apache-2.0"', text)
self.assertNotIn('license = { text = "Apache-2.0" }', text)
Expand All @@ -83,7 +91,7 @@ def test_package_metadata_declares_public_release_policy(self) -> None:
def test_public_module_version_and_all_match_metadata(self) -> None:
assignments = parse_init_assignments()

self.assertEqual("0.1.2", assignments["__version__"])
self.assertEqual(pyproject_version(), assignments["__version__"])
self.assertEqual(list(PUBLIC_API), assignments["__all__"])

def test_readme_documents_semver_and_exact_public_api_boundary(self) -> None:
Expand Down
2 changes: 1 addition & 1 deletion .github/scripts/test_release_artifact_workflow_prep.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ def test_workflow_generates_draft_artifacts_without_publication(self) -> None:
self.assertIn("cargo build --locked --release -p ethos-cli", text)
self.assertIn("write_release_artifact_inventory.py", text)
self.assertIn("smoke_release_cli_artifact.py", text)
self.assertIn('--expected-version "ethos 0.1.2"', text)
self.assertIn('--expected-version "ethos 0.2.0"', text)
self.assertIn("--target \"${{ matrix.artifact_target }}\"", text)
self.assertIn("*.smoke.json", text)
self.assertIn("validate_release_artifact_inventory.py", text)
Expand Down
Loading
Loading