release: bind refreeze trust and source closure

This commit is contained in:
2026-09-27 03:31:43 +08:00
parent 007193e35b
commit 87fd517376
8 changed files with 239 additions and 31 deletions
+20 -12
View File
@@ -21,7 +21,12 @@ The current RC-05 through RC-08 release must follow the repository [mandatory de
- `../compose/.env.production.example`: reviewed variable inventory. The real file belongs at `/etc/yuqei/production.env`, mode `0600`, outside the repository. - `../compose/.env.production.example`: reviewed variable inventory. The real file belongs at `/etc/yuqei/production.env`, mode `0600`, outside the repository.
- `preflight-production.py`: read-only production configuration gate. - `preflight-production.py`: read-only production configuration gate.
- `verify-production-compose.py`: read-only merged-model gate. It renders Docker Compose JSON for online and offline modes across the default, `backend-hosted-web`, `ops`, and combined profile sets. - `verify-production-compose.py`: read-only merged-model gate. It renders Docker Compose JSON for online and offline modes across the default, `backend-hosted-web`, `ops`, and combined profile sets.
- `release-package.py`: backend-only release-package verifier and planner. Its production apply entry point remains deliberately closed until the complete infrastructure-image import plan and externally trusted backup/compatibility approvals are available. - `release-package.py`: backend-only release-package verifier, planner, and guarded executor. Package v1 is read-only compatibility input; package v2 can reach apply only after all eleven image imports, third-party materials, the selected recovery strategy, external evidence, and exact authorization pass validation.
- `plan-first-release-snapshot-restore.py`: verifies first-release pre-install snapshot/backup/restore-test evidence and can mint a short-lived install-readiness receipt. It emits plans and receipts only; it does not restore data.
- `plan-rollback-rehearsal.py`: verifies the frozen package and source-bound release tools before emitting a non-writing rollback rehearsal plan.
- `../scripts/build-infrastructure-image-bundle.py`: file-only validator for the four application and seven infrastructure image archives, security reports, content-addressed layers, and offline import plan.
- `../scripts/build-third-party-materials.py`: deterministic third-party notice, license-text, and corresponding-source material builder/verifier.
- `../release-trust/third-party-materials-baseline.json`: source-frozen technical trust baseline. The shipped baseline intentionally remains `NOT_PROVISIONED` until reviewed real catalog, license, and source identities are supplied.
## Prepare without changing traffic ## Prepare without changing traffic
@@ -78,17 +83,20 @@ a registry. A companion overlay that introduces another image-bearing service ne
an explicit offline pull policy of its own before it is approved for this path. Do an explicit offline pull policy of its own before it is approved for this path. Do
not add the offline override to the online `pull` command. not add the offline override to the online `pull` command.
`release-package.py` validates the current four-application Docker import plan, `release-package.py` keeps package v1 available for read-only historical
including archive size/SHA-256 and the expected config-based image IDs used for verification. Package v2 binds four application images and seven infrastructure
offline Compose overrides. That is not yet a clean-host offline installation images, their offline archives and import order, exact Compose image IDs,
closure: PostgreSQL, the PostgreSQL client, Redis, MinIO, mc, Nginx, and the third-party materials, and one explicit recovery strategy. A v2 apply is still
backup helper do not all have approved import archives, and the packaged backup fail-closed unless the fixed technical trust baseline is provisioned, the raw
and rollback-compatibility records are only self-hashed rather than externally external recovery evidence is independently revalidated, all dynamic blockers
signed. Consequently `verify` reports structural validity without apply are empty, and the exact operation authorization is present. The executor then
eligibility, plans expose the blocking reasons, and the normal `--apply` entry loads, tags, and inspects each image before Compose and runs its built-in
point fails before creating state or loading an image. Contract Web remains an post-apply checks; a failure leaves reconciliation state instead of being treated
identity-bound, separately deployed artifact; this backend executor never claims as success. The first-release recovery planner never performs a restore, and a
that the full cross-host release has been installed or rolled back. successful technical verification is not legal approval, distribution
clearance, UAT evidence, production authorization, or public-release approval.
Contract Web remains an identity-bound, separately deployed artifact, so this
backend executor also does not claim that the cross-host release was installed.
The production override also replaces `ai-platform-prompt-assets-import` with the The production override also replaces `ai-platform-prompt-assets-import` with the
importer's default dry-run command. That Compose service may validate and report the importer's default dry-run command. That Compose service may validate and report the
@@ -79,6 +79,7 @@ PACKAGE_VALIDATOR_SOURCE_CLOSURE = (
"yuqei-infra/scripts/build-installable-release.py", "yuqei-infra/scripts/build-installable-release.py",
"yuqei-infra/scripts/build-infrastructure-image-bundle.py", "yuqei-infra/scripts/build-infrastructure-image-bundle.py",
"yuqei-infra/scripts/build-third-party-materials.py", "yuqei-infra/scripts/build-third-party-materials.py",
"yuqei-infra/release-trust/third-party-materials-baseline.json",
"yuqei-infra/scripts/linux_release_common.py", "yuqei-infra/scripts/linux_release_common.py",
) )
MAX_EVIDENCE_AGE = timedelta(hours=24) MAX_EVIDENCE_AGE = timedelta(hours=24)
@@ -1453,10 +1454,9 @@ def verify_and_build_output(
) -> dict[str, Any]: ) -> dict[str, Any]:
"""Revalidate every raw input and return one non-writing output document. """Revalidate every raw input and return one non-writing output document.
``package_validator_for_tests`` exists only to exercise the v2 contract ``package_validator_for_tests`` exists only for isolated unit fixtures.
before the official validator lands in the integration branch. The CLI The CLI never exposes or supplies it and therefore always loads the
never exposes or supplies it and therefore always loads the source-bound source-bound official validator and its complete trust-input closure.
official validator.
""" """
if output_kind not in {"plan", "install-readiness-receipt"}: if output_kind not in {"plan", "install-readiness-receipt"}:
+4
View File
@@ -1710,6 +1710,10 @@ def validate_release_package(
"yuqei-infra/scripts/build-third-party-materials.py", "yuqei-infra/scripts/build-third-party-materials.py",
Path(third_party_module.__file__), Path(third_party_module.__file__),
), ),
(
"yuqei-infra/release-trust/third-party-materials-baseline.json",
Path(third_party_module.REPOSITORY_TRUST_BASELINE_PATH),
),
( (
"yuqei-infra/deploy/plan-first-release-snapshot-restore.py", "yuqei-infra/deploy/plan-first-release-snapshot-restore.py",
Path(snapshot_module.__file__), Path(snapshot_module.__file__),
@@ -55,15 +55,24 @@ RELEASE_CONTROL_PATHS = (
"yuqei-infra/deploy/preflight-production.py", "yuqei-infra/deploy/preflight-production.py",
"yuqei-infra/deploy/verify-production-compose.py", "yuqei-infra/deploy/verify-production-compose.py",
"yuqei-infra/deploy/release-package.py", "yuqei-infra/deploy/release-package.py",
"yuqei-infra/deploy/plan-first-release-snapshot-restore.py",
"yuqei-infra/deploy/plan-rollback-rehearsal.py",
"yuqei-infra/release-trust/third-party-materials-baseline.json",
"yuqei-infra/release/linux-release-security-policy.v1.json", "yuqei-infra/release/linux-release-security-policy.v1.json",
"yuqei-infra/scripts/linux_release_common.py", "yuqei-infra/scripts/linux_release_common.py",
"yuqei-infra/scripts/run-linux-release-build.py", "yuqei-infra/scripts/run-linux-release-build.py",
"yuqei-infra/scripts/build-installable-release.py", "yuqei-infra/scripts/build-installable-release.py",
"yuqei-infra/scripts/build-infrastructure-image-bundle.py",
"yuqei-infra/scripts/build-third-party-materials.py",
"yuqei-infra/tests/test_production_compose_preflight.py", "yuqei-infra/tests/test_production_compose_preflight.py",
"yuqei-infra/tests/test_production_compose_merge_gate.py", "yuqei-infra/tests/test_production_compose_merge_gate.py",
"yuqei-infra/tests/test_release_package_executor.py", "yuqei-infra/tests/test_release_package_executor.py",
"yuqei-infra/tests/test_linux_release_build_tools.py", "yuqei-infra/tests/test_linux_release_build_tools.py",
"yuqei-infra/tests/test_installable_release_assembler_hardening.py", "yuqei-infra/tests/test_installable_release_assembler_hardening.py",
"yuqei-infra/tests/test_infrastructure_image_bundle.py",
"yuqei-infra/tests/test_first_release_snapshot_restore_planner.py",
"yuqei-infra/tests/test_rollback_rehearsal_planner.py",
"yuqei-infra/tests/test_third_party_materials.py",
"yuqei-infra/tests/test_release_candidate_manifest_tools.py", "yuqei-infra/tests/test_release_candidate_manifest_tools.py",
) )
CONTRACT_LOCK_PATH = REQUIREMENT_LOCK_PATHS[0] CONTRACT_LOCK_PATH = REQUIREMENT_LOCK_PATHS[0]
@@ -86,12 +86,24 @@ RUNTIME_COMPONENTS = {
"yuqei-infra/requirements/", "yuqei-infra/requirements/",
"yuqei-infra/deploy/", "yuqei-infra/deploy/",
"yuqei-infra/release/", "yuqei-infra/release/",
"yuqei-infra/release-trust/third-party-materials-baseline.json",
"yuqei-infra/scripts/linux_release_common.py", "yuqei-infra/scripts/linux_release_common.py",
"yuqei-infra/scripts/run-linux-release-build.py", "yuqei-infra/scripts/run-linux-release-build.py",
"yuqei-infra/scripts/build-installable-release.py", "yuqei-infra/scripts/build-installable-release.py",
"yuqei-infra/scripts/build-infrastructure-image-bundle.py",
"yuqei-infra/scripts/build-third-party-materials.py",
"yuqei-infra/scripts/build-release-candidate-manifest.py", "yuqei-infra/scripts/build-release-candidate-manifest.py",
"yuqei-infra/scripts/build-release-worktree-ownership.py", "yuqei-infra/scripts/build-release-worktree-ownership.py",
"yuqei-infra/tests/test_installable_release_assembler_hardening.py", "yuqei-infra/tests/test_installable_release_assembler_hardening.py",
"yuqei-infra/tests/test_infrastructure_image_bundle.py",
"yuqei-infra/tests/test_first_release_snapshot_restore_planner.py",
"yuqei-infra/tests/test_rollback_rehearsal_planner.py",
"yuqei-infra/tests/test_third_party_materials.py",
"yuqei-infra/tests/test_release_package_executor.py",
"yuqei-infra/tests/test_release_candidate_manifest_tools.py",
"yuqei-infra/tests/test_production_compose_preflight.py",
"yuqei-infra/tests/test_production_compose_merge_gate.py",
"yuqei-infra/tests/test_linux_release_build_tools.py",
), ),
} }
REQUIRED_WORKTREE_ANCHORS = ( REQUIRED_WORKTREE_ANCHORS = (
@@ -125,15 +137,24 @@ REQUIRED_WORKTREE_ANCHORS = (
"yuqei-infra/deploy/preflight-production.py", "yuqei-infra/deploy/preflight-production.py",
"yuqei-infra/deploy/verify-production-compose.py", "yuqei-infra/deploy/verify-production-compose.py",
"yuqei-infra/deploy/release-package.py", "yuqei-infra/deploy/release-package.py",
"yuqei-infra/deploy/plan-first-release-snapshot-restore.py",
"yuqei-infra/deploy/plan-rollback-rehearsal.py",
"yuqei-infra/release-trust/third-party-materials-baseline.json",
"yuqei-infra/release/linux-release-security-policy.v1.json", "yuqei-infra/release/linux-release-security-policy.v1.json",
"yuqei-infra/scripts/linux_release_common.py", "yuqei-infra/scripts/linux_release_common.py",
"yuqei-infra/scripts/run-linux-release-build.py", "yuqei-infra/scripts/run-linux-release-build.py",
"yuqei-infra/scripts/build-installable-release.py", "yuqei-infra/scripts/build-installable-release.py",
"yuqei-infra/scripts/build-infrastructure-image-bundle.py",
"yuqei-infra/scripts/build-third-party-materials.py",
"yuqei-infra/tests/test_production_compose_preflight.py", "yuqei-infra/tests/test_production_compose_preflight.py",
"yuqei-infra/tests/test_production_compose_merge_gate.py", "yuqei-infra/tests/test_production_compose_merge_gate.py",
"yuqei-infra/tests/test_release_package_executor.py", "yuqei-infra/tests/test_release_package_executor.py",
"yuqei-infra/tests/test_linux_release_build_tools.py", "yuqei-infra/tests/test_linux_release_build_tools.py",
"yuqei-infra/tests/test_installable_release_assembler_hardening.py", "yuqei-infra/tests/test_installable_release_assembler_hardening.py",
"yuqei-infra/tests/test_infrastructure_image_bundle.py",
"yuqei-infra/tests/test_first_release_snapshot_restore_planner.py",
"yuqei-infra/tests/test_rollback_rehearsal_planner.py",
"yuqei-infra/tests/test_third_party_materials.py",
"yuqei-infra/tests/test_release_candidate_manifest_tools.py", "yuqei-infra/tests/test_release_candidate_manifest_tools.py",
) )
@@ -394,12 +415,15 @@ def closure_audit(repo: Path, entries: list[dict[str, Any]]) -> dict[str, Any]:
"yuqei-infra/nginx/", "yuqei-infra/nginx/",
"yuqei-infra/deploy/", "yuqei-infra/deploy/",
"yuqei-infra/release/", "yuqei-infra/release/",
"yuqei-infra/release-trust/",
) )
) )
or path in { or path in {
"yuqei-infra/scripts/linux_release_common.py", "yuqei-infra/scripts/linux_release_common.py",
"yuqei-infra/scripts/run-linux-release-build.py", "yuqei-infra/scripts/run-linux-release-build.py",
"yuqei-infra/scripts/build-installable-release.py", "yuqei-infra/scripts/build-installable-release.py",
"yuqei-infra/scripts/build-infrastructure-image-bundle.py",
"yuqei-infra/scripts/build-third-party-materials.py",
} }
] ]
ownership_blockers = [ ownership_blockers = [
@@ -759,17 +759,27 @@ def test_package_rejects_wrong_trusted_build_receipt_from_official_validator(
@pytest.mark.parametrize("mutation", ("missing", "drifted")) @pytest.mark.parametrize("mutation", ("missing", "drifted"))
@pytest.mark.parametrize(
"relative",
(
"yuqei-infra/scripts/build-infrastructure-image-bundle.py",
"yuqei-infra/scripts/build-third-party-materials.py",
"yuqei-infra/release-trust/third-party-materials-baseline.json",
),
)
def test_package_source_closure_binds_dynamic_bundle_builders( def test_package_source_closure_binds_dynamic_bundle_builders(
tmp_path: Path, tmp_path: Path,
mutation: str, mutation: str,
relative: str,
) -> None: ) -> None:
case = _case(tmp_path) case = _case(tmp_path)
for relative in ( for required in (
"yuqei-infra/scripts/build-infrastructure-image-bundle.py", "yuqei-infra/scripts/build-infrastructure-image-bundle.py",
"yuqei-infra/scripts/build-third-party-materials.py", "yuqei-infra/scripts/build-third-party-materials.py",
"yuqei-infra/release-trust/third-party-materials-baseline.json",
): ):
assert relative in planner.PACKAGE_VALIDATOR_SOURCE_CLOSURE assert required in planner.PACKAGE_VALIDATOR_SOURCE_CLOSURE
target = case["repo_root"] / "yuqei-infra/scripts/build-third-party-materials.py" target = case["repo_root"].joinpath(*relative.split("/"))
if mutation == "missing": if mutation == "missing":
target.unlink() target.unlink()
match = "validator source .* is missing" match = "validator source .* is missing"
@@ -302,6 +302,10 @@ RUN python -c "import platform; name, version = platform.libc_ver(); minimum=(2,
("project-docs/上线收口-执行约束-2026-09-10.md", "release_evidence_compact_include"), ("project-docs/上线收口-执行约束-2026-09-10.md", "release_evidence_compact_include"),
("project-docs/unreviewed-plan.md", "needs_manual_review"), ("project-docs/unreviewed-plan.md", "needs_manual_review"),
("yuqei-infra/config/.env.example", "release_source_include"), ("yuqei-infra/config/.env.example", "release_source_include"),
(
"yuqei-infra/release-trust/third-party-materials-baseline.json",
"release_source_include",
),
), ),
) )
def test_worktree_classifier_has_explicit_release_ownership(path: str, category: str) -> None: def test_worktree_classifier_has_explicit_release_ownership(path: str, category: str) -> None:
@@ -312,13 +316,28 @@ def test_worktree_classifier_has_explicit_release_ownership(path: str, category:
def test_manifest_is_deterministic_and_summarizes_complete_committed_graph(tmp_path: Path) -> None: def test_manifest_is_deterministic_and_summarizes_complete_committed_graph(tmp_path: Path) -> None:
assembler_hardening_test = ( release_control_tests = (
"yuqei-infra/tests/test_installable_release_assembler_hardening.py" "yuqei-infra/tests/test_production_compose_preflight.py",
"yuqei-infra/tests/test_production_compose_merge_gate.py",
"yuqei-infra/tests/test_release_package_executor.py",
"yuqei-infra/tests/test_linux_release_build_tools.py",
"yuqei-infra/tests/test_installable_release_assembler_hardening.py",
"yuqei-infra/tests/test_infrastructure_image_bundle.py",
"yuqei-infra/tests/test_first_release_snapshot_restore_planner.py",
"yuqei-infra/tests/test_rollback_rehearsal_planner.py",
"yuqei-infra/tests/test_third_party_materials.py",
"yuqei-infra/tests/test_release_candidate_manifest_tools.py",
) )
assert assembler_hardening_test in manifest.RELEASE_CONTROL_PATHS for release_control_test in release_control_tests:
assert assembler_hardening_test in ownership.REQUIRED_WORKTREE_ANCHORS assert release_control_test in manifest.RELEASE_CONTROL_PATHS
assert assembler_hardening_test in ownership.RUNTIME_COMPONENTS["deployment"] assert release_control_test in ownership.REQUIRED_WORKTREE_ANCHORS
assert ownership.classify_path(assembler_hardening_test)[0] == "release_test_include" assert release_control_test in ownership.RUNTIME_COMPONENTS["deployment"]
assert ownership.classify_path(release_control_test)[0] == "release_test_include"
for release_control_path in manifest.RELEASE_CONTROL_PATHS:
assert ownership._path_matches(
release_control_path,
ownership.RUNTIME_COMPONENTS["deployment"],
)
pinned_bases = { pinned_bases = {
"yuqei-infra/docker/contract-api.Dockerfile": ( "yuqei-infra/docker/contract-api.Dockerfile": (
"ARG PYTHON_BASE_IMAGE=python:3.12-slim@sha256:44ff437bba879d4941b710a369a8f19266aea34b29002807f0c487fabc9eec9b", "ARG PYTHON_BASE_IMAGE=python:3.12-slim@sha256:44ff437bba879d4941b710a369a8f19266aea34b29002807f0c487fabc9eec9b",
@@ -89,6 +89,92 @@ class PackageFixture:
_STRICT_BUILD_TEMPLATE: dict[str, Any] | None = None _STRICT_BUILD_TEMPLATE: dict[str, Any] | None = None
_REAL_INFRA_TOOL: Any | None = None _REAL_INFRA_TOOL: Any | None = None
_INFRA_SYNTHETIC_APPLICATION: dict[str, Any] | None = None _INFRA_SYNTHETIC_APPLICATION: dict[str, Any] | None = None
_FIXTURE_MIT_TEXT = b"MIT fixture license text\n"
_FIXTURE_GPL_TEXT = b"GPL-2.0-only fixture license text\n"
_FIXTURE_SOURCE_BYTES = b"def copyleft_fixture() -> str:\n return 'source'\n"
def _fixture_third_party_trust_baseline(third_tool: Any) -> dict[str, Any]:
licenses_payload = {
"licenseListVersion": "3.29.0",
"licenses": [
{"licenseId": "Apache-2.0", "isDeprecatedLicenseId": False},
{"licenseId": "GPL-2.0-only", "isDeprecatedLicenseId": False},
{"licenseId": "MIT", "isDeprecatedLicenseId": False},
],
}
exceptions_payload = {"licenseListVersion": "3.29.0", "exceptions": []}
def record(raw: bytes) -> dict[str, Any]:
return {"sha256": hashlib.sha256(raw).hexdigest(), "size_bytes": len(raw)}
licenses_raw = release_package.canonical_json_bytes(licenses_payload) + b"\n"
exceptions_raw = release_package.canonical_json_bytes(exceptions_payload) + b"\n"
return {
"schema_version": third_tool.TRUST_BASELINE_SCHEMA,
"baseline_id": "release-package-isolated-test-baseline",
"technical_approval": {
"status": third_tool.ACTIVE_TECHNICAL_TRUST_STATUS,
"reviewer": "isolated-release-package-test-fixture",
"reviewed_at": "2026-09-27T00:00:00+00:00",
"scope": third_tool.TRUST_SCOPE_STATEMENT,
},
"candidate_contract": {
"input_schema": third_tool.INPUT_SCHEMA,
"source_archive_schema": third_tool.SOURCE_ARCHIVE_SCHEMA,
"profiles": sorted(third_tool.PROFILES),
"image_roles": sorted(third_tool.EXPECTED_ROLES),
},
"spdx_catalog": {
"license_list_version": "3.29.0",
"licenses": {
"origin": "https://spdx.org/licenses/licenses.json",
**record(licenses_raw),
},
"exceptions": {
"origin": "https://spdx.org/licenses/exceptions.json",
**record(exceptions_raw),
},
},
"license_texts": [
{
"id": "MIT",
"kind": "license",
"spdx_expression": "MIT",
"aliases": [],
**record(_FIXTURE_MIT_TEXT),
},
{
"id": "GPL-2.0-only",
"kind": "license",
"spdx_expression": "GPL-2.0-only",
"aliases": [],
**record(_FIXTURE_GPL_TEXT),
},
],
"source_materials": [
{
"id": "fixture-copyleft-source",
"kind": "source-archive",
"package_name": "fixture-copyleft",
"package_version": "1.0.0",
"binary_coordinate": "pkg:generic/fixture-copyleft@1.0.0",
"source_coordinate": "pkg:generic/fixture-copyleft-source@1.0.0",
"files": [
{
"path": "src/main.py",
"kind": "source",
**record(_FIXTURE_SOURCE_BYTES),
}
],
}
],
"clearance_claims": {
"legal_approval": False,
"distribution_clearance": False,
"production_authorization": False,
},
}
def _fixture_infrastructure_build_bundle( def _fixture_infrastructure_build_bundle(
@@ -145,6 +231,9 @@ def _extend_source_fixture_with_release_validators(helpers, original, tmp_path:
destination = repo.joinpath(*Path(relative).parts) destination = repo.joinpath(*Path(relative).parts)
destination.parent.mkdir(parents=True, exist_ok=True) destination.parent.mkdir(parents=True, exist_ok=True)
destination.write_bytes((REPO_ROOT / relative).read_bytes()) destination.write_bytes((REPO_ROOT / relative).read_bytes())
third_tool = release_package._load_third_party_materials_module()
trust_path = repo / "yuqei-infra/release-trust/third-party-materials-baseline.json"
_write_json(trust_path, _fixture_third_party_trust_baseline(third_tool))
helpers._git(repo, "add", ".") helpers._git(repo, "add", ".")
commit_env = os.environ.copy() commit_env = os.environ.copy()
commit_env.update( commit_env.update(
@@ -258,7 +347,12 @@ def _record_for(path: Path, root: Path) -> dict[str, Any]:
} }
def _add_v2_release_artifacts(bundle_root: Path, v2_root: Path) -> dict[str, Any]: def _add_v2_release_artifacts(
bundle_root: Path,
v2_root: Path,
*,
repo_root: Path,
) -> dict[str, Any]:
global _INFRA_SYNTHETIC_APPLICATION, _REAL_INFRA_TOOL global _INFRA_SYNTHETIC_APPLICATION, _REAL_INFRA_TOOL
infra_helper = _load_test_helper( infra_helper = _load_test_helper(
@@ -271,6 +365,9 @@ def _add_v2_release_artifacts(bundle_root: Path, v2_root: Path) -> dict[str, Any
) )
real_infra_tool = release_package._load_infrastructure_bundle_module() real_infra_tool = release_package._load_infrastructure_bundle_module()
third_tool = release_package._load_third_party_materials_module() third_tool = release_package._load_third_party_materials_module()
third_tool.REPOSITORY_TRUST_BASELINE_PATH = (
repo_root / "yuqei-infra/release-trust/third-party-materials-baseline.json"
)
installable_path = bundle_root / "installable-release-manifest.json" installable_path = bundle_root / "installable-release-manifest.json"
installable = json.loads(installable_path.read_text(encoding="utf-8")) installable = json.loads(installable_path.read_text(encoding="utf-8"))
commit = installable["candidate"]["commit"] commit = installable["candidate"]["commit"]
@@ -414,8 +511,8 @@ def _add_v2_release_artifacts(bundle_root: Path, v2_root: Path) -> dict[str, Any
text_dir.mkdir() text_dir.mkdir()
mit = text_dir / "MIT.txt" mit = text_dir / "MIT.txt"
gpl = text_dir / "GPL-2.0-only.txt" gpl = text_dir / "GPL-2.0-only.txt"
mit.write_text("MIT fixture license text\n", encoding="utf-8") mit.write_bytes(_FIXTURE_MIT_TEXT)
gpl.write_text("GPL-2.0-only fixture license text\n", encoding="utf-8") gpl.write_bytes(_FIXTURE_GPL_TEXT)
spdx_licenses = materials_root / "spdx/licenses.json" spdx_licenses = materials_root / "spdx/licenses.json"
spdx_exceptions = materials_root / "spdx/exceptions.json" spdx_exceptions = materials_root / "spdx/exceptions.json"
_write_json( _write_json(
@@ -490,7 +587,7 @@ def _add_v2_release_artifacts(bundle_root: Path, v2_root: Path) -> dict[str, Any
contract_api_image = next( contract_api_image = next(
item for item in release_images if item["role"] == "contract-api" item for item in release_images if item["role"] == "contract-api"
) )
source_bytes = b"def copyleft_fixture() -> str:\n return 'source'\n" source_bytes = _FIXTURE_SOURCE_BYTES
source_file_record = { source_file_record = {
"path": "src/main.py", "path": "src/main.py",
"sha256": third_tool._sha256_bytes(source_bytes), "sha256": third_tool._sha256_bytes(source_bytes),
@@ -674,7 +771,12 @@ def _strict_build_template(tmp_path: Path) -> dict[str, Any]:
helpers, original_source_fixture, path helpers, original_source_fixture, path
) )
try: try:
bundle = helpers._bundle(tmp_path.parent / "release-package-strict-template-source") template_source = tmp_path.parent / "release-package-strict-template-source"
# A failed first construction must not poison every later case in the
# module with a half-created shared fixture directory.
if template_source.exists():
shutil.rmtree(template_source)
bundle = helpers._bundle(template_source)
finally: finally:
helpers._fixture_source = original_source_fixture helpers._fixture_source = original_source_fixture
receipt_sha256 = _sha(bundle["receipt_path"]) receipt_sha256 = _sha(bundle["receipt_path"])
@@ -688,7 +790,11 @@ def _strict_build_template(tmp_path: Path) -> dict[str, Any]:
output=installable, output=installable,
) )
v2_root = bundle["root"].parent / "v2-release-artifacts" v2_root = bundle["root"].parent / "v2-release-artifacts"
v2_artifacts = _add_v2_release_artifacts(bundle["root"], v2_root) v2_artifacts = _add_v2_release_artifacts(
bundle["root"],
v2_root,
repo_root=bundle["repo"],
)
_STRICT_BUILD_TEMPLATE = { _STRICT_BUILD_TEMPLATE = {
"bundle_root": bundle["root"], "bundle_root": bundle["root"],
"repo": bundle["repo"], "repo": bundle["repo"],
@@ -1488,6 +1594,34 @@ def test_default_verify_fully_validates_without_writes_or_docker(tmp_path: Path)
assert not state.exists() assert not state.exists()
def test_v2_source_bound_third_party_trust_baseline_drift_is_rejected(
tmp_path: Path,
monkeypatch: pytest.MonkeyPatch,
) -> None:
fixture = _make_package(tmp_path)
frozen_baseline_path = (
fixture.repo
/ "yuqei-infra/release-trust/third-party-materials-baseline.json"
)
baseline = json.loads(frozen_baseline_path.read_text(encoding="utf-8"))
baseline["technical_approval"]["reviewer"] = "unfrozen-drift"
loaded_baseline_path = tmp_path / "ambient-drifted-trust-baseline.json"
_write_json(loaded_baseline_path, baseline)
third_party_module = release_package._load_third_party_materials_module()
monkeypatch.setattr(
third_party_module,
"REPOSITORY_TRUST_BASELINE_PATH",
loaded_baseline_path,
)
with pytest.raises(release_package.ReleasePackageError, match="frozen source manifest"):
release_package.validate_release_package(
fixture.root,
expected_package_sha256=fixture.expected_sha256,
repo_root=fixture.repo,
)
def test_v2_predecessor_recovery_preserves_legacy_plan_semantics(tmp_path: Path) -> None: def test_v2_predecessor_recovery_preserves_legacy_plan_semantics(tmp_path: Path) -> None:
fixture = _make_package(tmp_path) fixture = _make_package(tmp_path)
_use_v2_predecessor_recovery(fixture) _use_v2_predecessor_recovery(fixture)