release: bind refreeze trust and source closure
This commit is contained in:
@@ -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.
|
||||
- `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.
|
||||
- `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
|
||||
|
||||
@@ -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
|
||||
not add the offline override to the online `pull` command.
|
||||
|
||||
`release-package.py` validates the current four-application Docker import plan,
|
||||
including archive size/SHA-256 and the expected config-based image IDs used for
|
||||
offline Compose overrides. That is not yet a clean-host offline installation
|
||||
closure: PostgreSQL, the PostgreSQL client, Redis, MinIO, mc, Nginx, and the
|
||||
backup helper do not all have approved import archives, and the packaged backup
|
||||
and rollback-compatibility records are only self-hashed rather than externally
|
||||
signed. Consequently `verify` reports structural validity without apply
|
||||
eligibility, plans expose the blocking reasons, and the normal `--apply` entry
|
||||
point fails before creating state or loading an image. Contract Web remains an
|
||||
identity-bound, separately deployed artifact; this backend executor never claims
|
||||
that the full cross-host release has been installed or rolled back.
|
||||
`release-package.py` keeps package v1 available for read-only historical
|
||||
verification. Package v2 binds four application images and seven infrastructure
|
||||
images, their offline archives and import order, exact Compose image IDs,
|
||||
third-party materials, and one explicit recovery strategy. A v2 apply is still
|
||||
fail-closed unless the fixed technical trust baseline is provisioned, the raw
|
||||
external recovery evidence is independently revalidated, all dynamic blockers
|
||||
are empty, and the exact operation authorization is present. The executor then
|
||||
loads, tags, and inspects each image before Compose and runs its built-in
|
||||
post-apply checks; a failure leaves reconciliation state instead of being treated
|
||||
as success. The first-release recovery planner never performs a restore, and a
|
||||
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
|
||||
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-infrastructure-image-bundle.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",
|
||||
)
|
||||
MAX_EVIDENCE_AGE = timedelta(hours=24)
|
||||
@@ -1453,10 +1454,9 @@ def verify_and_build_output(
|
||||
) -> dict[str, Any]:
|
||||
"""Revalidate every raw input and return one non-writing output document.
|
||||
|
||||
``package_validator_for_tests`` exists only to exercise the v2 contract
|
||||
before the official validator lands in the integration branch. The CLI
|
||||
never exposes or supplies it and therefore always loads the source-bound
|
||||
official validator.
|
||||
``package_validator_for_tests`` exists only for isolated unit fixtures.
|
||||
The CLI never exposes or supplies it and therefore always loads the
|
||||
source-bound official validator and its complete trust-input closure.
|
||||
"""
|
||||
|
||||
if output_kind not in {"plan", "install-readiness-receipt"}:
|
||||
|
||||
@@ -1710,6 +1710,10 @@ def validate_release_package(
|
||||
"yuqei-infra/scripts/build-third-party-materials.py",
|
||||
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",
|
||||
Path(snapshot_module.__file__),
|
||||
|
||||
@@ -55,15 +55,24 @@ RELEASE_CONTROL_PATHS = (
|
||||
"yuqei-infra/deploy/preflight-production.py",
|
||||
"yuqei-infra/deploy/verify-production-compose.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/scripts/linux_release_common.py",
|
||||
"yuqei-infra/scripts/run-linux-release-build.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_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",
|
||||
)
|
||||
CONTRACT_LOCK_PATH = REQUIREMENT_LOCK_PATHS[0]
|
||||
|
||||
@@ -86,12 +86,24 @@ RUNTIME_COMPONENTS = {
|
||||
"yuqei-infra/requirements/",
|
||||
"yuqei-infra/deploy/",
|
||||
"yuqei-infra/release/",
|
||||
"yuqei-infra/release-trust/third-party-materials-baseline.json",
|
||||
"yuqei-infra/scripts/linux_release_common.py",
|
||||
"yuqei-infra/scripts/run-linux-release-build.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-worktree-ownership.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 = (
|
||||
@@ -125,15 +137,24 @@ REQUIRED_WORKTREE_ANCHORS = (
|
||||
"yuqei-infra/deploy/preflight-production.py",
|
||||
"yuqei-infra/deploy/verify-production-compose.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/scripts/linux_release_common.py",
|
||||
"yuqei-infra/scripts/run-linux-release-build.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_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",
|
||||
)
|
||||
|
||||
@@ -394,12 +415,15 @@ def closure_audit(repo: Path, entries: list[dict[str, Any]]) -> dict[str, Any]:
|
||||
"yuqei-infra/nginx/",
|
||||
"yuqei-infra/deploy/",
|
||||
"yuqei-infra/release/",
|
||||
"yuqei-infra/release-trust/",
|
||||
)
|
||||
)
|
||||
or path in {
|
||||
"yuqei-infra/scripts/linux_release_common.py",
|
||||
"yuqei-infra/scripts/run-linux-release-build.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 = [
|
||||
|
||||
@@ -759,17 +759,27 @@ def test_package_rejects_wrong_trusted_build_receipt_from_official_validator(
|
||||
|
||||
|
||||
@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(
|
||||
tmp_path: Path,
|
||||
mutation: str,
|
||||
relative: str,
|
||||
) -> None:
|
||||
case = _case(tmp_path)
|
||||
for relative in (
|
||||
for required in (
|
||||
"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",
|
||||
):
|
||||
assert relative in planner.PACKAGE_VALIDATOR_SOURCE_CLOSURE
|
||||
target = case["repo_root"] / "yuqei-infra/scripts/build-third-party-materials.py"
|
||||
assert required in planner.PACKAGE_VALIDATOR_SOURCE_CLOSURE
|
||||
target = case["repo_root"].joinpath(*relative.split("/"))
|
||||
if mutation == "missing":
|
||||
target.unlink()
|
||||
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/unreviewed-plan.md", "needs_manual_review"),
|
||||
("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:
|
||||
@@ -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:
|
||||
assembler_hardening_test = (
|
||||
"yuqei-infra/tests/test_installable_release_assembler_hardening.py"
|
||||
release_control_tests = (
|
||||
"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
|
||||
assert assembler_hardening_test in ownership.REQUIRED_WORKTREE_ANCHORS
|
||||
assert assembler_hardening_test in ownership.RUNTIME_COMPONENTS["deployment"]
|
||||
assert ownership.classify_path(assembler_hardening_test)[0] == "release_test_include"
|
||||
for release_control_test in release_control_tests:
|
||||
assert release_control_test in manifest.RELEASE_CONTROL_PATHS
|
||||
assert release_control_test in ownership.REQUIRED_WORKTREE_ANCHORS
|
||||
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 = {
|
||||
"yuqei-infra/docker/contract-api.Dockerfile": (
|
||||
"ARG PYTHON_BASE_IMAGE=python:3.12-slim@sha256:44ff437bba879d4941b710a369a8f19266aea34b29002807f0c487fabc9eec9b",
|
||||
|
||||
@@ -89,6 +89,92 @@ class PackageFixture:
|
||||
_STRICT_BUILD_TEMPLATE: dict[str, Any] | None = None
|
||||
_REAL_INFRA_TOOL: 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(
|
||||
@@ -145,6 +231,9 @@ def _extend_source_fixture_with_release_validators(helpers, original, tmp_path:
|
||||
destination = repo.joinpath(*Path(relative).parts)
|
||||
destination.parent.mkdir(parents=True, exist_ok=True)
|
||||
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", ".")
|
||||
commit_env = os.environ.copy()
|
||||
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
|
||||
|
||||
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()
|
||||
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 = json.loads(installable_path.read_text(encoding="utf-8"))
|
||||
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()
|
||||
mit = text_dir / "MIT.txt"
|
||||
gpl = text_dir / "GPL-2.0-only.txt"
|
||||
mit.write_text("MIT fixture license text\n", encoding="utf-8")
|
||||
gpl.write_text("GPL-2.0-only fixture license text\n", encoding="utf-8")
|
||||
mit.write_bytes(_FIXTURE_MIT_TEXT)
|
||||
gpl.write_bytes(_FIXTURE_GPL_TEXT)
|
||||
spdx_licenses = materials_root / "spdx/licenses.json"
|
||||
spdx_exceptions = materials_root / "spdx/exceptions.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(
|
||||
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 = {
|
||||
"path": "src/main.py",
|
||||
"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
|
||||
)
|
||||
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:
|
||||
helpers._fixture_source = original_source_fixture
|
||||
receipt_sha256 = _sha(bundle["receipt_path"])
|
||||
@@ -688,7 +790,11 @@ def _strict_build_template(tmp_path: Path) -> dict[str, Any]:
|
||||
output=installable,
|
||||
)
|
||||
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 = {
|
||||
"bundle_root": bundle["root"],
|
||||
"repo": bundle["repo"],
|
||||
@@ -1488,6 +1594,34 @@ def test_default_verify_fully_validates_without_writes_or_docker(tmp_path: Path)
|
||||
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:
|
||||
fixture = _make_package(tmp_path)
|
||||
_use_v2_predecessor_recovery(fixture)
|
||||
|
||||
Reference in New Issue
Block a user