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.
|
- `../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"}:
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
Reference in New Issue
Block a user