fix: align release gate with AI Redis service

This commit is contained in:
2026-09-21 20:45:41 +08:00
parent 5688dd13c5
commit 5f4f0d7f64
3 changed files with 52 additions and 13 deletions
+5 -5
View File
@@ -766,7 +766,7 @@ def validate_compose_gate_report(
if not isinstance(expected_digest, str) or not SHA256_RE.fullmatch(expected_digest):
raise ReleaseValidationError(f"source manifest lacks Compose input: {expected_path}")
validated_inputs.append({"path": expected_path, "sha256": expected_digest})
if report["application_service_count"] != 9 or report["production_service_count"] != 16:
if report["application_service_count"] != 9 or report["production_service_count"] != 17:
raise ReleaseValidationError("production Compose gate service inventory drift")
applications_without_web = [
@@ -781,10 +781,10 @@ def validate_compose_gate_report(
]
expected_cases = []
for profile_name, profiles, service_count, includes_web in (
("default", [], 13, False),
("backend-hosted-web", ["backend-hosted-web"], 14, True),
("ops", ["ops"], 15, False),
("backend-hosted-web+ops", ["backend-hosted-web", "ops"], 16, True),
("default", [], 14, False),
("backend-hosted-web", ["backend-hosted-web"], 15, True),
("ops", ["ops"], 16, False),
("backend-hosted-web+ops", ["backend-hosted-web", "ops"], 17, True),
):
applications = applications_without_web + (["contract-web"] if includes_web else [])
for mode in ("online", "offline"):
@@ -102,6 +102,7 @@ TRUSTED_PRODUCTION_SERVICES = (
"postgres",
"postgres-init-databases",
"redis",
"ai-redis",
"minio",
"contract-api-migrate",
"contract-api",
@@ -528,12 +528,10 @@ def _compose_gate_report(source: dict[str, Any]) -> tuple[bytes, dict[str, Any]]
]
cases = []
case_index = 0
for profile_name, profiles, service_count, includes_web in (
("default", [], 13, False),
("backend-hosted-web", ["backend-hosted-web"], 14, True),
("ops", ["ops"], 15, False),
("backend-hosted-web+ops", ["backend-hosted-web", "ops"], 16, True),
):
for profile_name, profile_tuple in builder.TRUSTED_PROFILE_CASES:
profiles = list(profile_tuple)
service_count = len(builder._required_compose_services(profile_tuple))
includes_web = "backend-hosted-web" in profile_tuple
applications = applications_without_web + (["contract-web"] if includes_web else [])
for mode in ("online", "offline"):
case_index += 1
@@ -555,8 +553,8 @@ def _compose_gate_report(source: dict[str, Any]) -> tuple[bytes, dict[str, Any]]
{"path": path, "sha256": source_records[path]["sha256"]}
for path in common.COMPOSE_GATE_POLICY["compose_inputs"]
],
"application_service_count": 9,
"production_service_count": 16,
"application_service_count": len(builder.TRUSTED_APPLICATION_SERVICE_IMAGE_ENV),
"production_service_count": len(builder.TRUSTED_PRODUCTION_SERVICES),
"cases": cases,
}
return env_bytes, report
@@ -583,6 +581,46 @@ def test_synthetic_compose_gate_env_covers_required_interpolation() -> None:
assert values["BACKEND_TLS_TRUSTED_CA_FILE"] == "/synthetic/tls/trusted-ca.pem"
def _compose_service_blocks(relative_path: str) -> dict[str, list[str]]:
blocks: dict[str, list[str]] = {}
current_service: str | None = None
in_services = False
for line in (REPO_ROOT / relative_path).read_text(encoding="utf-8").splitlines():
if not in_services:
in_services = line == "services:"
continue
if line and not line[0].isspace():
break
match = re.fullmatch(r" ([a-z0-9][a-z0-9-]*):\s*", line)
if match:
current_service = match.group(1)
blocks[current_service] = []
elif current_service is not None:
blocks[current_service].append(line)
return blocks
def test_compose_gate_inventory_matches_candidate_compose_sources() -> None:
compose_services: set[str] = set()
for relative_path in common.COMPOSE_GATE_POLICY["compose_inputs"]:
compose_services.update(_compose_service_blocks(relative_path))
assert compose_services == set(builder.TRUSTED_PRODUCTION_SERVICES)
production_blocks = _compose_service_blocks(
"yuqei-infra/compose/docker-compose.production.yml"
)
actual_profiles: dict[str, str] = {}
for service_name, lines in production_blocks.items():
profile_lines = [line for line in lines if line.strip().startswith("profiles:")]
if not profile_lines:
continue
assert len(profile_lines) == 1
profiles = re.findall(r'"([a-z0-9][a-z0-9-]*)"', profile_lines[0])
assert len(profiles) == 1
actual_profiles[service_name] = profiles[0]
assert actual_profiles == dict(builder.TRUSTED_PROFILE_ONLY_SERVICES)
def _compose_model(
*, mode: str, profile_name: str, profiles: tuple[str, ...]
) -> dict[str, Any]: