Merge pull request #228 from living-ip/codex/gcp-nosend-wif-publish-20260722
Some checks are pending
CI / lint-and-test (push) Waiting to run

Publish exact Leoclean no-send release through WIF
This commit is contained in:
twentyOne2x 2026-07-22 06:17:43 +02:00 committed by GitHub
commit 3569f4f2fb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 1808 additions and 86 deletions

View file

@ -100,6 +100,7 @@ jobs:
tests/test_decision_engine_replay.py \ tests/test_decision_engine_replay.py \
tests/test_evaluate_agent_routing.py \ tests/test_evaluate_agent_routing.py \
tests/test_gcp_artifact_workflow.py \ tests/test_gcp_artifact_workflow.py \
tests/test_gcp_leoclean_nosend_release_workflow.py \
tests/test_capture_vps_canonical_postgres_snapshot.py \ tests/test_capture_vps_canonical_postgres_snapshot.py \
tests/test_gcp_infra_execute_canary.py \ tests/test_gcp_infra_execute_canary.py \
tests/test_gcp_infra_readiness_checker.py \ tests/test_gcp_infra_readiness_checker.py \

View file

@ -0,0 +1,333 @@
name: gcp-leoclean-nosend-release
on:
workflow_dispatch:
permissions:
contents: read
id-token: write
concurrency:
group: gcp-leoclean-nosend-release-main
cancel-in-progress: false
env:
PROJECT_ID: teleo-501523
REGION: europe-west6
ARTIFACT_REPOSITORY: teleo
IMAGE_REPOSITORY: europe-west6-docker.pkg.dev/teleo-501523/teleo/leoclean-nosend-staging
WORKLOAD_IDENTITY_PROVIDER: projects/785938879453/locations/global/workloadIdentityPools/github-actions/providers/living-ip-github
ARTIFACT_SERVICE_ACCOUNT: sa-artifact-builder@teleo-501523.iam.gserviceaccount.com
jobs:
publish:
name: Build, smoke, and publish exact no-send release
runs-on: ubuntu-latest
timeout-minutes: 90
steps:
- name: Require manual main dispatch
shell: bash
run: |
set -euo pipefail
test "${GITHUB_EVENT_NAME}" = "workflow_dispatch"
test "${GITHUB_REF}" = "refs/heads/main"
test "${GITHUB_REF_TYPE}" = "branch"
test "${GITHUB_REPOSITORY}" = "living-ip/teleo-infrastructure"
test "${GITHUB_WORKFLOW_REF}" = \
"living-ip/teleo-infrastructure/.github/workflows/gcp-leoclean-nosend-release.yml@refs/heads/main"
- name: Check out exact dispatched revision
uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262
with:
fetch-depth: 0
persist-credentials: false
ref: ${{ github.sha }}
- name: Verify exact clean main revision
shell: bash
run: |
set -euo pipefail
test "$(git rev-parse --verify HEAD)" = "${GITHUB_SHA}"
test "$(git rev-parse --verify refs/remotes/origin/main^{commit})" = "${GITHUB_SHA}"
test -z "$(git status --porcelain=v1 --untracked-files=all)"
- name: Initialize private release paths
shell: bash
run: |
set -euo pipefail
release_root="${RUNNER_TEMP}/leoclean-nosend-release"
install -d -m 0700 "${release_root}"
{
echo "RELEASE_ROOT=${release_root}"
echo "RUNTIME_ARTIFACT=${release_root}/runtime-artifact"
echo "RUNTIME_RECEIPT=${release_root}/evidence/runtime-receipt.json"
echo "OCI_SMOKE_RECEIPT=${release_root}/evidence/oci-smoke.json"
echo "IMAGE_CONTEXT=${release_root}/image-context"
echo "BUILD_PUSH_RECEIPT=${release_root}/receipts/build-push-receipt.json"
echo "PUBLISH_OUTCOME=${release_root}/evidence/publish-outcome.json"
echo "RELEASE_BUNDLE=${release_root}/final/release-v3"
echo "DOCKER_CONFIG=${release_root}/docker-config"
} >>"${GITHUB_ENV}"
- name: Set up exact Python
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065
with:
python-version: "3.11.9"
- name: Install exact no-send build tools
shell: bash
run: |
set -euo pipefail
python -m pip install --disable-pip-version-check 'uv==0.9.30' 'PyYAML==6.0.3'
test "$(uv --version | awk '{print $2}')" = "0.9.30"
test "$(command -v docker)" = "/usr/bin/docker"
- name: Verify fixed staging package target
shell: bash
run: |
set -euo pipefail
python - <<'PY'
import os
from ops import gcp_leoclean_nosend_package as package
assert package.PROJECT == "teleo-501523"
assert package.PLATFORM == "linux/amd64"
assert os.environ["IMAGE_REPOSITORY"] == package.IMAGE_REPOSITORY
assert package.IMAGE_REPOSITORY == "europe-west6-docker.pkg.dev/teleo-501523/teleo/leoclean-nosend-staging"
PY
- name: Pull digest-pinned OCI bases
shell: bash
run: |
set -euo pipefail
docker pull --platform=linux/amd64 \
"ghcr.io/astral-sh/uv:0.9.30@sha256:538e0b39736e7feae937a65983e49d2ab75e1559d35041f9878b7b7e51de91e4"
docker pull --platform=linux/amd64 \
"docker.io/library/python:3.11.9-slim-bookworm@sha256:8fb099199b9f2d70342674bd9dbccd3ed03a258f26bbd1d556822c6dfc60c317"
docker pull --platform=linux/amd64 \
"docker.io/library/postgres:16.14-bookworm@sha256:92620daddcd947f8d5ab5ba66e848702fe443d87fed30c4cea8e389fd78dfc55"
- name: Compile and release-verify exact runtime artifact
shell: bash
run: |
set -euo pipefail
install -d -m 0700 "${RELEASE_ROOT}" "${RELEASE_ROOT}/evidence"
LEOCLEAN_NOSEND_RESULTS_DIR="${RELEASE_ROOT}/evidence" \
LEOCLEAN_NOSEND_RECEIPT="${RUNTIME_RECEIPT}" \
LEOCLEAN_NOSEND_ARTIFACT_OUTPUT="${RUNTIME_ARTIFACT}" \
scripts/run_leoclean_nosend_runtime_canary.sh
- name: Smoke exact no-send OCI surface
shell: bash
run: |
set -euo pipefail
python scripts/run_gcp_leoclean_nosend_oci_smoke.py \
--repo-root "${GITHUB_WORKSPACE}" \
--artifact "${RUNTIME_ARTIFACT}" \
--runtime-receipt "${RUNTIME_RECEIPT}" \
--output "${OCI_SMOKE_RECEIPT}"
- name: Prepare exact candidate context
shell: bash
run: |
set -euo pipefail
identity_contract="${RELEASE_ROOT}/identity-runtime-contract.json"
identity_manifest="${RELEASE_ROOT}/identity-manifest.json"
identity_bundle="${RELEASE_ROOT}/identity"
python ops/gcp_leoclean_nosend_package.py identity-contract \
--repo-root "${GITHUB_WORKSPACE}" \
--artifact "${RUNTIME_ARTIFACT}" \
--receipt "${RUNTIME_RECEIPT}" \
--output "${identity_contract}"
python ops/gcp_leoclean_nosend_package.py generate-identity-manifest \
--repo-root "${GITHUB_WORKSPACE}" \
--artifact "${RUNTIME_ARTIFACT}" \
--receipt "${RUNTIME_RECEIPT}" \
--database-fingerprint fixtures/working-leo/leo-identity-v1/leo-database-fingerprint-v1.json \
--constitution fixtures/working-leo/leo-identity-v1/leo-constitution-v1.json \
--database-identity fixtures/working-leo/leo-identity-v1/leo-database-identity-v1.json \
--identity-source-root "${GITHUB_WORKSPACE}" \
--output "${identity_manifest}"
python ops/gcp_leoclean_nosend_package.py compile-identity \
--repo-root "${GITHUB_WORKSPACE}" \
--artifact "${RUNTIME_ARTIFACT}" \
--receipt "${RUNTIME_RECEIPT}" \
--identity-manifest "${identity_manifest}" \
--identity-source-root "${GITHUB_WORKSPACE}" \
--output "${identity_bundle}"
python ops/gcp_leoclean_nosend_package.py prepare \
--repo-root "${GITHUB_WORKSPACE}" \
--artifact "${RUNTIME_ARTIFACT}" \
--receipt "${RUNTIME_RECEIPT}" \
--identity "${identity_bundle}" \
--identity-source-root "${GITHUB_WORKSPACE}" \
--output "${IMAGE_CONTEXT}"
python - "${OCI_SMOKE_RECEIPT}" "${IMAGE_CONTEXT}/image-input.json" "${GITHUB_SHA}" <<'PY'
import json
import sys
from pathlib import Path
smoke = json.loads(Path(sys.argv[1]).read_text(encoding="utf-8"))
image_input = json.loads(Path(sys.argv[2]).read_text(encoding="utf-8"))
revision = sys.argv[3]
assert smoke["status"] == "pass"
assert smoke["cleanup"]["status"] == "pass"
assert smoke["teleo_git_head"] == revision
assert image_input["runtime"]["teleo_git_head"] == revision
assert smoke["input_sha256"] == image_input["input_sha256"]
assert smoke["identity_sha256"] == image_input["identity"]["bundle_sha256"]
PY
- name: Initialize pre-mutation publish outcome
id: init_outcome
shell: bash
run: |
set -euo pipefail
install -d -m 0700 "$(dirname "${PUBLISH_OUTCOME}")"
python ops/gcp_leoclean_nosend_package.py init-outcome \
--image-input "${IMAGE_CONTEXT}/image-input.json" \
--output-journal "${PUBLISH_OUTCOME}"
- name: Authenticate to Google Cloud through WIF
id: auth
uses: google-github-actions/auth@c200f3691d83b41bf9bbd8638997a462592937ed
with:
workload_identity_provider: ${{ env.WORKLOAD_IDENTITY_PROVIDER }}
service_account: ${{ env.ARTIFACT_SERVICE_ACCOUNT }}
create_credentials_file: true
export_environment_variables: true
- name: Set up exact gcloud
uses: google-github-actions/setup-gcloud@e427ad8a34f8676edf47cf7d7925499adf3eb74f
with:
version: "550.0.0"
- name: Require immutable fixed Artifact Registry repository
shell: bash
run: |
set -euo pipefail
repository_readback="${RUNNER_TEMP}/leoclean-nosend-repository.json"
umask 077
test "$(gcloud auth list --filter=status:ACTIVE --format='value(account)')" = \
"${ARTIFACT_SERVICE_ACCOUNT}"
gcloud artifacts repositories describe "${ARTIFACT_REPOSITORY}" \
--project "${PROJECT_ID}" \
--location "${REGION}" \
--format json >"${repository_readback}"
python - "${repository_readback}" <<'PY'
import json
import sys
from pathlib import Path
repository = json.loads(Path(sys.argv[1]).read_text(encoding="utf-8"))
assert repository["name"] == (
"projects/teleo-501523/locations/europe-west6/repositories/teleo"
)
assert repository["format"] == "DOCKER"
assert repository["dockerConfig"]["immutableTags"] is True
PY
rm -f -- "${repository_readback}"
- name: Configure private Artifact Registry Docker auth
shell: bash
run: |
set -euo pipefail
install -d -m 0700 "${DOCKER_CONFIG}"
gcloud auth configure-docker "${REGION}-docker.pkg.dev" --quiet
test -f "${DOCKER_CONFIG}/config.json"
test ! -L "${DOCKER_CONFIG}/config.json"
chmod 0600 "${DOCKER_CONFIG}/config.json"
- name: Build and push one immutable candidate
id: build_push
shell: bash
run: |
set -euo pipefail
install -d -m 0700 "$(dirname "${BUILD_PUSH_RECEIPT}")"
python ops/gcp_leoclean_nosend_package.py build-push \
--repo-root "${GITHUB_WORKSPACE}" \
--image-context "${IMAGE_CONTEXT}" \
--docker-binary /usr/bin/docker \
--docker-host unix:///var/run/docker.sock \
--docker-config "${DOCKER_CONFIG}" \
--gcloud-binary "$(command -v gcloud)" \
--output-receipt "${BUILD_PUSH_RECEIPT}" \
--outcome-journal "${PUBLISH_OUTCOME}" \
--execute-staging-push
- name: Finalize and validate release v3 bundle
id: finalize
shell: bash
run: |
set -euo pipefail
install -d -m 0700 "$(dirname "${RELEASE_BUNDLE}")"
python ops/gcp_leoclean_nosend_package.py finalize \
--image-input "${IMAGE_CONTEXT}/image-input.json" \
--build-push-receipt "${BUILD_PUSH_RECEIPT}" \
--docker-binary /usr/bin/docker \
--docker-host unix:///var/run/docker.sock \
--docker-config "${DOCKER_CONFIG}" \
--output-bundle "${RELEASE_BUNDLE}"
python ops/gcp_leoclean_nosend_package.py validate \
--release "${RELEASE_BUNDLE}/release.json"
python - "${BUILD_PUSH_RECEIPT}" "${RELEASE_BUNDLE}/release.json" "${OCI_SMOKE_RECEIPT}" "${GITHUB_SHA}" <<'PY'
import json
import sys
from pathlib import Path
receipt = json.loads(Path(sys.argv[1]).read_text(encoding="utf-8"))
release = json.loads(Path(sys.argv[2]).read_text(encoding="utf-8"))
smoke = json.loads(Path(sys.argv[3]).read_text(encoding="utf-8"))
revision = sys.argv[4]
assert receipt["schema"] == "livingip.leocleanNoSendBuildPushReceipt.v1"
assert release["schema"] == "livingip.leocleanNoSendRelease.v3"
assert release["build_push_receipt"] == receipt
assert release["image_input"]["runtime"]["teleo_git_head"] == revision
assert release["image_input"]["input_sha256"] == smoke["input_sha256"]
assert release["image"]["reference"].startswith(
"europe-west6-docker.pkg.dev/teleo-501523/teleo/leoclean-nosend-staging@sha256:"
)
PY
- name: Remove ephemeral Docker authentication
id: cleanup
if: always()
shell: bash
run: |
set -euo pipefail
rm -rf -- "${DOCKER_CONFIG}"
test ! -e "${DOCKER_CONFIG}"
test ! -L "${DOCKER_CONFIG}"
- name: Finalize publish outcome
if: always() && steps.init_outcome.outcome == 'success'
shell: bash
run: |
set -euo pipefail
python ops/gcp_leoclean_nosend_package.py complete-outcome \
--image-input "${IMAGE_CONTEXT}/image-input.json" \
--outcome-journal "${PUBLISH_OUTCOME}" \
--build-push-receipt "${BUILD_PUSH_RECEIPT}" \
--release "${RELEASE_BUNDLE}/release.json" \
--build-outcome "${{ steps.build_push.outcome }}" \
--finalize-outcome "${{ steps.finalize.outcome }}" \
--cleanup-outcome "${{ steps.cleanup.outcome }}"
- name: Upload exact available build and release evidence
if: always()
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02
with:
name: leoclean-nosend-release-${{ github.sha }}
path: |
${{ runner.temp }}/leoclean-nosend-release/evidence/runtime-receipt.json
${{ runner.temp }}/leoclean-nosend-release/evidence/oci-smoke.json
${{ runner.temp }}/leoclean-nosend-release/evidence/publish-outcome.json
${{ runner.temp }}/leoclean-nosend-release/receipts/build-push-receipt.json
${{ runner.temp }}/leoclean-nosend-release/final/release-v3/release.json
${{ runner.temp }}/leoclean-nosend-release/final/release-v3/leoclean-gcp-nosend.service
if-no-files-found: warn
retention-days: 30

View file

@ -4,6 +4,7 @@
from __future__ import annotations from __future__ import annotations
import argparse import argparse
import copy
import ctypes import ctypes
import errno import errno
import fcntl import fcntl
@ -15,6 +16,7 @@ import shutil
import stat import stat
import subprocess import subprocess
import sys import sys
import time
import tomllib import tomllib
import uuid import uuid
from collections.abc import Mapping from collections.abc import Mapping
@ -34,6 +36,7 @@ from scripts import leo_identity_manifest as strict_identity_manifest # noqa: E
IMAGE_INPUT_SCHEMA = "livingip.leocleanNoSendImageInput.v2" IMAGE_INPUT_SCHEMA = "livingip.leocleanNoSendImageInput.v2"
IMAGE_INSPECTION_SCHEMA = "livingip.leocleanNoSendImageInspection.v1" IMAGE_INSPECTION_SCHEMA = "livingip.leocleanNoSendImageInspection.v1"
BUILD_PUSH_RECEIPT_SCHEMA = "livingip.leocleanNoSendBuildPushReceipt.v1" BUILD_PUSH_RECEIPT_SCHEMA = "livingip.leocleanNoSendBuildPushReceipt.v1"
PUBLISH_OUTCOME_SCHEMA = "livingip.leocleanNoSendPublishOutcome.v1"
RELEASE_SCHEMA = "livingip.leocleanNoSendRelease.v3" RELEASE_SCHEMA = "livingip.leocleanNoSendRelease.v3"
ARTIFACT_MANIFEST_SCHEMA = "livingip.leocleanNoSendArtifactManifest.v1" ARTIFACT_MANIFEST_SCHEMA = "livingip.leocleanNoSendArtifactManifest.v1"
ARTIFACT_RECEIPT_SCHEMA = "livingip.leocleanNoSendArtifactVerification.v1" ARTIFACT_RECEIPT_SCHEMA = "livingip.leocleanNoSendArtifactVerification.v1"
@ -50,6 +53,9 @@ SERVICE = "leoclean-gcp-nosend.service"
SERVICE_ACCOUNT = "sa-teleo-staging-vm@teleo-501523.iam.gserviceaccount.com" SERVICE_ACCOUNT = "sa-teleo-staging-vm@teleo-501523.iam.gserviceaccount.com"
PLATFORM = "linux/amd64" PLATFORM = "linux/amd64"
IMAGE_REPOSITORY = "europe-west6-docker.pkg.dev/teleo-501523/teleo/leoclean-nosend-staging" IMAGE_REPOSITORY = "europe-west6-docker.pkg.dev/teleo-501523/teleo/leoclean-nosend-staging"
ARTIFACT_PACKAGE_RESOURCE = (
"projects/teleo-501523/locations/europe-west6/repositories/teleo/packages/leoclean-nosend-staging"
)
CONTAINER_NAME = "livingip-leoclean-nosend" CONTAINER_NAME = "livingip-leoclean-nosend"
LOCAL_BUILD_REPOSITORY = "livingip-leoclean-nosend-build" LOCAL_BUILD_REPOSITORY = "livingip-leoclean-nosend-build"
DOCKER_BINARY = "/usr/bin/docker" DOCKER_BINARY = "/usr/bin/docker"
@ -83,7 +89,9 @@ RUNTIME_UID = 65532
RUNTIME_GID = 65532 RUNTIME_GID = 65532
MAX_DOCKER_INSPECTION_BYTES = 1024 * 1024 MAX_DOCKER_INSPECTION_BYTES = 1024 * 1024
MAX_DOCKER_COMMAND_OUTPUT_BYTES = 1024 * 1024 MAX_DOCKER_COMMAND_OUTPUT_BYTES = 1024 * 1024
MAX_GCLOUD_COMMAND_OUTPUT_BYTES = 1024 * 1024
MAX_BUILD_PUSH_RECEIPT_BYTES = 256 * 1024 MAX_BUILD_PUSH_RECEIPT_BYTES = 256 * 1024
MAX_PUBLISH_OUTCOME_BYTES = 256 * 1024
IMAGE_RUNTIME_CONFIG = { IMAGE_RUNTIME_CONFIG = {
"user": None, "user": None,
"environment": [ "environment": [
@ -138,6 +146,8 @@ IMAGE_RUNTIME_CONFIG = {
HEX_40 = re.compile(r"^[0-9a-f]{40}$") HEX_40 = re.compile(r"^[0-9a-f]{40}$")
HEX_64 = re.compile(r"^[0-9a-f]{64}$") HEX_64 = re.compile(r"^[0-9a-f]{64}$")
IMAGE_REFERENCE = re.compile(rf"^{re.escape(IMAGE_REPOSITORY)}@sha256:([0-9a-f]{{64}})$") IMAGE_REFERENCE = re.compile(rf"^{re.escape(IMAGE_REPOSITORY)}@sha256:([0-9a-f]{{64}})$")
CANDIDATE_TAG = re.compile(r"^candidate-([0-9a-f]{64})$")
CANDIDATE_REFERENCE = re.compile(rf"^{re.escape(IMAGE_REPOSITORY)}:candidate-([0-9a-f]{{64}})$")
FORBIDDEN_DESCRIPTOR_MARKERS = ( FORBIDDEN_DESCRIPTOR_MARKERS = (
"77.42.65.182", "77.42.65.182",
"gcp-teleo-pgvector-standby-postgres-password", "gcp-teleo-pgvector-standby-postgres-password",
@ -1821,6 +1831,244 @@ def build_build_push_receipt(
return receipt return receipt
def _seal_publish_outcome(stable: dict[str, Any]) -> dict[str, Any]:
value = {**stable, "outcome_sha256": canonical_sha256(stable)}
validate_publish_outcome(value)
return value
def validate_publish_outcome(value: dict[str, Any], *, image_input: dict[str, Any] | None = None) -> None:
_require(
set(value)
== {
"schema",
"status",
"source_revision",
"input_sha256",
"candidate",
"registry",
"phase",
"failure_phase",
"cleanup",
"release",
"events",
"outcome_sha256",
},
"publish outcome fields are not exact",
)
_require(value.get("schema") == PUBLISH_OUTCOME_SCHEMA, "publish outcome schema is invalid")
_require(value.get("status") in {"in_progress", "pass", "fail"}, "publish outcome status is invalid")
_require(_is_revision(value.get("source_revision")), "publish outcome source revision is invalid")
_require(_is_sha256(value.get("input_sha256")), "publish outcome input hash is invalid")
if image_input is not None:
validate_image_input(image_input)
_require(
value["source_revision"] == image_input["runtime"]["teleo_git_head"]
and value["input_sha256"] == image_input["input_sha256"],
"publish outcome image-input binding drifted",
)
candidate = value.get("candidate")
_require(
isinstance(candidate, dict) and set(candidate) == {"tag", "reference", "local_build_config_digest"},
"publish outcome candidate fields are not exact",
)
candidate_values = (
candidate.get("tag"),
candidate.get("reference"),
candidate.get("local_build_config_digest"),
)
if candidate_values == (None, None, None):
pass
else:
tag, reference, local_build_config_digest = candidate_values
_require(
isinstance(tag, str)
and CANDIDATE_TAG.fullmatch(tag) is not None
and isinstance(reference, str)
and CANDIDATE_REFERENCE.fullmatch(reference) is not None
and isinstance(local_build_config_digest, str)
and local_build_config_digest.startswith("sha256:")
and _is_sha256(local_build_config_digest.removeprefix("sha256:")),
"publish outcome candidate is invalid",
)
_require(
tag == f"candidate-{value['input_sha256']}" and reference == f"{IMAGE_REPOSITORY}:{tag}",
"candidate binding drifted",
)
registry = value.get("registry")
_require(
isinstance(registry, dict)
and set(registry) == {"repository", "preexisting", "push_attempted", "action", "observed_digest"},
"publish outcome registry fields are not exact",
)
_require(registry.get("repository") == IMAGE_REPOSITORY, "publish outcome repository is not exact")
_require(registry.get("preexisting") in {None, True, False}, "publish outcome preexisting state is invalid")
_require(type(registry.get("push_attempted")) is bool, "publish outcome push-attempt state is invalid")
_require(
registry.get("action")
in {"none", "observed", "fresh_push", "reused", "indeterminate", "reconciled_after_attempt"},
"publish outcome registry action is invalid",
)
observed_digest = registry.get("observed_digest")
_require(
observed_digest is None
or (
isinstance(observed_digest, str)
and observed_digest.startswith("sha256:")
and _is_sha256(observed_digest.removeprefix("sha256:"))
),
"publish outcome observed digest is invalid",
)
candidate_is_set = candidate_values != (None, None, None)
registry_state = (
registry["preexisting"],
registry["push_attempted"],
registry["action"],
observed_digest is not None,
)
_require(
registry_state
in {
(None, False, "none", False),
(False, False, "none", False),
(True, False, "observed", True),
(True, False, "reused", True),
(False, True, "indeterminate", False),
(False, True, "indeterminate", True),
(False, True, "fresh_push", True),
(False, True, "reconciled_after_attempt", True),
},
"publish outcome registry state is inconsistent",
)
_require(
candidate_is_set or registry_state == (None, False, "none", False),
"publish outcome registry activity has no candidate",
)
phase = value.get("phase")
failure_phase = value.get("failure_phase")
_require(isinstance(phase, str) and bool(re.fullmatch(r"[a-z][a-z0-9_]{0,63}", phase)), "publish phase is invalid")
_require(
failure_phase is None
or (isinstance(failure_phase, str) and bool(re.fullmatch(r"[a-z][a-z0-9_]{0,63}", failure_phase))),
"publish failure phase is invalid",
)
_require(
(value["status"] == "fail") == (failure_phase is not None),
"publish failure status and phase disagree",
)
cleanup = value.get("cleanup")
_require(
isinstance(cleanup, dict)
and cleanup
== {
"docker_config": cleanup.get("docker_config"),
"local_daemon": "runner_ephemeral_references_retained",
"remote_candidate": "immutable_evidence_retained",
}
and cleanup.get("docker_config") in {"pending", "pass", "fail"},
"publish cleanup state is invalid",
)
release = value.get("release")
_require(
isinstance(release, dict)
and set(release) == {"build_push_receipt_present", "bundle_present", "release_sha256"}
and type(release.get("build_push_receipt_present")) is bool
and type(release.get("bundle_present")) is bool,
"publish release state is invalid",
)
release_sha256 = release.get("release_sha256")
_require(release_sha256 is None or _is_sha256(release_sha256), "publish release hash is invalid")
if value["status"] == "pass":
_require(
registry["action"] in {"fresh_push", "reused", "reconciled_after_attempt"}
and observed_digest is not None
and cleanup["docker_config"] == "pass"
and release["build_push_receipt_present"] is True
and release["bundle_present"] is True
and _is_sha256(release_sha256),
"passing publish outcome is incomplete",
)
events = value.get("events")
_require(isinstance(events, list) and bool(events), "publish outcome events are missing")
for event in events:
_require(
isinstance(event, dict)
and set(event) == {"phase", "status"}
and isinstance(event.get("phase"), str)
and bool(re.fullmatch(r"[a-z][a-z0-9_]{0,63}", event["phase"]))
and event.get("status")
in {"initialized", "attempted", "absent", "present", "pass", "fail", "recovered", "reused", "skipped"},
"publish outcome event is invalid",
)
stable = {key: item for key, item in value.items() if key != "outcome_sha256"}
_require(value.get("outcome_sha256") == canonical_sha256(stable), "publish outcome self-hash drifted")
_assert_no_forbidden_descriptor_markers(value)
def build_initial_publish_outcome(image_input: dict[str, Any]) -> dict[str, Any]:
validate_image_input(image_input)
stable = {
"schema": PUBLISH_OUTCOME_SCHEMA,
"status": "in_progress",
"source_revision": image_input["runtime"]["teleo_git_head"],
"input_sha256": image_input["input_sha256"],
"candidate": {"tag": None, "reference": None, "local_build_config_digest": None},
"registry": {
"repository": IMAGE_REPOSITORY,
"preexisting": None,
"push_attempted": False,
"action": "none",
"observed_digest": None,
},
"phase": "initialized",
"failure_phase": None,
"cleanup": {
"docker_config": "pending",
"local_daemon": "runner_ephemeral_references_retained",
"remote_candidate": "immutable_evidence_retained",
},
"release": {
"build_push_receipt_present": False,
"bundle_present": False,
"release_sha256": None,
},
"events": [{"phase": "initialized", "status": "initialized"}],
}
return _seal_publish_outcome(stable)
def advance_publish_outcome(
value: dict[str, Any],
*,
phase: str,
event_status: str,
status: str | None = None,
failure_phase: str | None = None,
candidate: dict[str, Any] | None = None,
registry: dict[str, Any] | None = None,
cleanup: dict[str, Any] | None = None,
release: dict[str, Any] | None = None,
) -> dict[str, Any]:
validate_publish_outcome(value)
_require(value["status"] != "pass", "completed publish outcome cannot advance")
stable = copy.deepcopy({key: item for key, item in value.items() if key != "outcome_sha256"})
stable["phase"] = phase
stable["events"].append({"phase": phase, "status": event_status})
if status is not None:
stable["status"] = status
if failure_phase is not None:
stable["failure_phase"] = failure_phase
for key, updates in (("candidate", candidate), ("registry", registry), ("cleanup", cleanup), ("release", release)):
if updates is not None:
stable[key].update(updates)
return _seal_publish_outcome(stable)
def build_image_inspection( def build_image_inspection(
image_input: dict[str, Any], image_input: dict[str, Any],
image_reference: str, image_reference: str,
@ -2133,6 +2381,197 @@ def _bounded_docker_output(result: subprocess.CompletedProcess[bytes], label: st
) )
def _validate_gcloud_binary(gcloud_binary: Path) -> None:
_require(gcloud_binary.is_absolute() and "\x00" not in str(gcloud_binary), "gcloud binary must be absolute")
try:
observed = gcloud_binary.stat(follow_symlinks=False)
except OSError as exc:
raise PackageError("gcloud binary is unavailable") from exc
_require(
stat.S_ISREG(observed.st_mode)
and observed.st_uid in {0, os.geteuid()}
and bool(observed.st_mode & 0o111)
and observed.st_mode & 0o022 == 0,
"gcloud binary posture is unsafe",
)
def _run_gcloud(gcloud_binary: Path, arguments: list[str], *, timeout: int = 60) -> subprocess.CompletedProcess[bytes]:
_validate_gcloud_binary(gcloud_binary)
environment = os.environ.copy()
environment["CLOUDSDK_CORE_DISABLE_PROMPTS"] = "1"
try:
result = subprocess.run(
[str(gcloud_binary), *arguments],
check=False,
capture_output=True,
stdin=subprocess.DEVNULL,
timeout=timeout,
env=environment,
)
except (OSError, subprocess.SubprocessError) as exc:
raise PackageError("registry tag lookup failed") from exc
_require(
len(result.stdout) <= MAX_GCLOUD_COMMAND_OUTPUT_BYTES and len(result.stderr) <= MAX_GCLOUD_COMMAND_OUTPUT_BYTES,
"registry tag lookup output is too large",
)
_require(result.returncode == 0, "registry tag lookup failed")
return result
def _registry_candidate_digest(gcloud_binary: Path, candidate_tag: str) -> str | None:
_require(CANDIDATE_TAG.fullmatch(candidate_tag) is not None, "registry candidate tag is invalid")
expected_tag = f"{ARTIFACT_PACKAGE_RESOURCE}/tags/{candidate_tag}"
expected_version_prefix = f"{ARTIFACT_PACKAGE_RESOURCE}/versions/sha256:"
result = _run_gcloud(
gcloud_binary,
[
"artifacts",
"docker",
"tags",
"list",
IMAGE_REPOSITORY,
"--project",
PROJECT,
f"--filter=tag={expected_tag}",
"--format=json(tag,version)",
"--limit=2",
],
)
try:
rows = _strict_json_loads(result.stdout.decode("utf-8"), "registry candidate lookup")
except UnicodeError as exc:
raise PackageError("registry candidate lookup is invalid") from exc
_require(isinstance(rows, list), "registry candidate lookup is invalid")
if not rows:
return None
_require(
len(rows) == 1
and isinstance(rows[0], dict)
and set(rows[0]) == {"tag", "version"}
and rows[0].get("tag") == expected_tag,
"registry candidate lookup returned an unexpected tag",
)
row = rows[0]
version = row.get("version")
digest = version.removeprefix(expected_version_prefix) if isinstance(version, str) else ""
_require(
version == f"{expected_version_prefix}{digest}" and _is_sha256(digest), "registry candidate version is invalid"
)
return f"sha256:{digest}"
def _wait_registry_candidate_digest(
gcloud_binary: Path,
candidate_tag: str,
*,
attempts: int = 6,
interval_seconds: float = 2.0,
) -> str | None:
_require(type(attempts) is int and attempts > 0, "registry lookup attempts are invalid")
for attempt in range(attempts):
digest = _registry_candidate_digest(gcloud_binary, candidate_tag)
if digest is not None:
return digest
if attempt + 1 < attempts:
time.sleep(interval_seconds)
return None
def _pull_validated_registry_candidate(
image_input: dict[str, Any],
expected_local: dict[str, Any],
manifest_digest: str,
*,
docker_binary: Path,
docker_host: str,
docker_config: Path,
authority: _DockerCacheLock,
) -> dict[str, Any]:
_require(
isinstance(manifest_digest, str)
and manifest_digest.startswith("sha256:")
and _is_sha256(manifest_digest.removeprefix("sha256:")),
"registry candidate digest is invalid",
)
reference = f"{IMAGE_REPOSITORY}@{manifest_digest}"
pulled = _run_docker(
docker_binary,
docker_host,
docker_config,
["pull", "--quiet", "--platform", PLATFORM, reference],
check=True,
timeout=600,
authority=authority,
)
_bounded_docker_output(pulled, "registry candidate pull")
remote_image = _inspect_local_image(
docker_binary,
docker_host,
docker_config,
reference,
authority=authority,
)
remote_configuration = validate_image_configuration(image_input, remote_image)
for field in ("platform", "labels", "runtime_config"):
_require(
remote_configuration[field] == expected_local[field],
"existing registry candidate runtime differs from the exact input",
)
_require(
_target_repository_digests(remote_image) == [reference],
"existing registry candidate digest binding is not exact",
)
verified = _run_docker(
docker_binary,
docker_host,
docker_config,
[
"run",
"--rm",
"--pull=never",
"--platform",
PLATFORM,
"--network=none",
"--read-only",
"--cap-drop=ALL",
"--security-opt=no-new-privileges:true",
"--pids-limit=64",
f"--user={RUNTIME_UID}:{RUNTIME_GID}",
reference,
"verify-build",
image_input["runtime"]["teleo_git_head"],
image_input["runtime"]["artifact_sha256"],
image_input["identity"]["bundle_sha256"],
image_input["input_sha256"],
],
check=True,
timeout=120,
authority=authority,
)
_bounded_docker_output(verified, "registry candidate runtime verification")
try:
verification = _strict_json_loads(verified.stdout.decode("utf-8"), "registry candidate verification")
except UnicodeError as exc:
raise PackageError("registry candidate verification is invalid") from exc
_require(
verification
== {
"schema": "livingip.leocleanNoSendContainerVerification.v1",
"status": "pass",
"teleo_git_head": image_input["runtime"]["teleo_git_head"],
"artifact_sha256": image_input["runtime"]["artifact_sha256"],
"identity_sha256": image_input["identity"]["bundle_sha256"],
"input_sha256": image_input["input_sha256"],
"python_version": PYTHON_VERSION,
"postgres_version": POSTGRES_VERSION,
"cloudsql_ca_sha256": CA_SHA256,
},
"registry candidate verification differs from the exact input",
)
return remote_image
def _inspect_local_image( def _inspect_local_image(
docker_binary: Path, docker_binary: Path,
docker_host: str, docker_host: str,
@ -2282,22 +2721,56 @@ def build_and_push_staging_image(
docker_binary: Path, docker_binary: Path,
docker_host: str, docker_host: str,
docker_config: Path, docker_config: Path,
gcloud_binary: Path,
output_receipt: Path, output_receipt: Path,
outcome_journal: Path,
execute_staging_push: bool, execute_staging_push: bool,
) -> dict[str, Any]: ) -> dict[str, Any]:
"""Build one reviewed context, push one content-bound candidate, and receipt the result.""" """Build and reconcile one content-bound candidate, then receipt the exact digest."""
_require(execute_staging_push is True, "staging image push requires explicit execution authorization") _require(execute_staging_push is True, "staging image push requires explicit execution authorization")
image_input, build_args = validate_prepared_image_context(context) image_input, build_args = validate_prepared_image_context(context)
validate_reviewed_context_source(repo_root, context, image_input) outcome = load_publish_outcome(outcome_journal, image_input=image_input)
_validate_docker_authority(docker_binary, docker_host, docker_config) _require(outcome["status"] == "in_progress", "publish outcome is not active")
_validate_receipt_output_path(output_receipt) current_phase = "preflight"
_validate_receipt_context_separation(context, output_receipt) push_attempted = False
observed_digest: str | None = None
def record(
phase: str,
event_status: str,
*,
status: str | None = None,
failure_phase: str | None = None,
candidate: dict[str, Any] | None = None,
registry: dict[str, Any] | None = None,
release: dict[str, Any] | None = None,
) -> None:
nonlocal outcome, current_phase
current_phase = phase
outcome = advance_publish_outcome(
outcome,
phase=phase,
event_status=event_status,
status=status,
failure_phase=failure_phase,
candidate=candidate,
registry=registry,
release=release,
)
publish_publish_outcome(outcome_journal, outcome, create=False)
build_tag = f"input-{image_input['input_sha256']}" build_tag = f"input-{image_input['input_sha256']}"
build_reference = f"{LOCAL_BUILD_REPOSITORY}:{build_tag}" build_reference = f"{LOCAL_BUILD_REPOSITORY}:{build_tag}"
validate_reviewed_context_source(repo_root, context, image_input)
_validate_docker_authority(docker_binary, docker_host, docker_config)
_validate_gcloud_binary(gcloud_binary)
_validate_receipt_output_path(output_receipt)
_validate_receipt_context_separation(context, output_receipt)
record("preflight", "pass")
try: try:
with _DockerCacheLock(docker_config) as authority: with _DockerCacheLock(docker_config) as authority:
current_phase = "local_build"
_require( _require(
_local_tag_config_id( _local_tag_config_id(
docker_binary, docker_binary,
@ -2366,8 +2839,44 @@ def build_and_push_staging_image(
) )
validate_reviewed_context_source(repo_root, context, post_build_input) validate_reviewed_context_source(repo_root, context, post_build_input)
candidate_tag = f"candidate-{build_config_id.removeprefix('sha256:')}" candidate_tag = f"candidate-{image_input['input_sha256']}"
candidate_reference = f"{IMAGE_REPOSITORY}:{candidate_tag}" candidate_reference = f"{IMAGE_REPOSITORY}:{candidate_tag}"
record(
"candidate_derived",
"pass",
candidate={
"tag": candidate_tag,
"reference": candidate_reference,
"local_build_config_digest": build_config_id,
},
)
current_phase = "remote_reconcile"
existing_digest = _registry_candidate_digest(gcloud_binary, candidate_tag)
if existing_digest is not None:
observed_digest = existing_digest
record(
"remote_reconcile",
"present",
registry={
"preexisting": True,
"action": "observed",
"observed_digest": existing_digest,
},
)
remote_image = _pull_validated_registry_candidate(
image_input,
observed,
existing_digest,
docker_binary=docker_binary,
docker_host=docker_host,
docker_config=docker_config,
authority=authority,
)
receipt = build_build_push_receipt(image_input, post_push_image=remote_image)
record("remote_readback", "reused", registry={"action": "reused"})
else:
record("remote_reconcile", "absent", registry={"preexisting": False})
_require( _require(
_local_tag_config_id( _local_tag_config_id(
docker_binary, docker_binary,
@ -2417,6 +2926,14 @@ def build_and_push_staging_image(
_target_repository_digests(pre_push_image) == [], _target_repository_digests(pre_push_image) == [],
"staging candidate already has a repository digest before push", "staging candidate already has a repository digest before push",
) )
push_attempted = True
record(
"push",
"attempted",
registry={"push_attempted": True, "action": "indeterminate"},
)
push_error: BaseException | None = None
try:
pushed = _run_docker( pushed = _run_docker(
docker_binary, docker_binary,
docker_host, docker_host,
@ -2427,31 +2944,58 @@ def build_and_push_staging_image(
authority=authority, authority=authority,
) )
_bounded_docker_output(pushed, "staging image push") _bounded_docker_output(pushed, "staging image push")
post_push_image = _inspect_local_image( except (OSError, subprocess.SubprocessError, PackageError) as exc:
docker_binary, push_error = exc
docker_host, observed_digest = _wait_registry_candidate_digest(gcloud_binary, candidate_tag)
docker_config, _require(observed_digest is not None, "staging push produced no observable immutable candidate")
candidate_reference, remote_image = _pull_validated_registry_candidate(
image_input,
observed,
observed_digest,
docker_binary=docker_binary,
docker_host=docker_host,
docker_config=docker_config,
authority=authority, authority=authority,
) )
_require( record(
validate_image_configuration(image_input, post_push_image) == observed, "remote_readback",
"staging candidate configuration drifted across push", "recovered" if push_error is not None else "pass",
registry={
"action": "reconciled_after_attempt" if push_error is not None else "fresh_push",
"observed_digest": observed_digest,
},
) )
repository_digests = _target_repository_digests(post_push_image) receipt = build_build_push_receipt(image_input, post_push_image=remote_image)
_require(len(repository_digests) == 1, "staging push did not produce one exact repository digest")
registry_reference = repository_digests[0] current_phase = "receipt"
match = IMAGE_REFERENCE.fullmatch(registry_reference)
_require(match is not None, "staging push repository digest is invalid")
receipt = build_build_push_receipt(image_input, post_push_image=post_push_image)
except (OSError, subprocess.SubprocessError, PackageError) as exc:
raise PackageError("staging image build/push failed") from exc
publish_build_push_receipt( publish_build_push_receipt(
output_receipt, output_receipt,
receipt, receipt,
image_input=image_input, image_input=image_input,
outside_context=context, outside_context=context,
) )
record("receipt", "pass", release={"build_push_receipt_present": True})
except (OSError, subprocess.SubprocessError, PackageError) as exc:
registry_updates: dict[str, Any] = {}
if observed_digest is not None:
registry_updates["observed_digest"] = observed_digest
if push_attempted and outcome["registry"]["action"] not in {"fresh_push", "reconciled_after_attempt"}:
registry_updates["action"] = "indeterminate"
try:
outcome = advance_publish_outcome(
outcome,
phase=current_phase,
event_status="fail",
status="fail",
failure_phase=current_phase,
registry=registry_updates or None,
)
publish_publish_outcome(outcome_journal, outcome, create=False)
except (OSError, PackageError) as journal_error:
raise PackageError("staging image build/push failed and outcome journaling failed") from journal_error
if current_phase == "receipt" and isinstance(exc, PackageError):
raise
raise PackageError("staging image build/push failed") from exc
return receipt return receipt
@ -3065,6 +3609,235 @@ def publish_build_push_receipt(
os.close(parent_descriptor) os.close(parent_descriptor)
def publish_publish_outcome(path: Path, value: dict[str, Any], *, create: bool) -> None:
"""Atomically create or replace the non-authority publish outcome journal."""
validate_publish_outcome(value)
_require(path.is_absolute() and path.name not in {"", ".", ".."}, "publish outcome path is invalid")
try:
payload = (json.dumps(value, allow_nan=False, indent=2, sort_keys=True) + "\n").encode("utf-8")
except (TypeError, ValueError) as exc:
raise PackageError("cannot encode publish outcome") from exc
_require(len(payload) <= MAX_PUBLISH_OUTCOME_BYTES, "publish outcome is too large")
parent_descriptor = _open_private_operator_directory(path.parent, "publish outcome parent")
staged_name = f".{path.name}.{uuid.uuid4().hex}.tmp"
descriptor: int | None = None
try:
existing = _directory_entry_stat(parent_descriptor, path.name)
if create:
_require(existing is None, "publish outcome already exists")
else:
_require(
existing is not None
and stat.S_ISREG(existing.st_mode)
and existing.st_uid == os.geteuid()
and stat.S_IMODE(existing.st_mode) == 0o600,
"publish outcome update target is unsafe",
)
flags = os.O_WRONLY | os.O_CREAT | os.O_EXCL
if hasattr(os, "O_NOFOLLOW"):
flags |= os.O_NOFOLLOW
descriptor = os.open(staged_name, flags, 0o600, dir_fd=parent_descriptor)
os.fchmod(descriptor, 0o600)
with os.fdopen(descriptor, "wb", closefd=False) as handle:
handle.write(payload)
handle.flush()
os.fsync(descriptor)
if create:
os.link(
staged_name,
path.name,
src_dir_fd=parent_descriptor,
dst_dir_fd=parent_descriptor,
follow_symlinks=False,
)
os.unlink(staged_name, dir_fd=parent_descriptor)
else:
os.replace(
staged_name,
path.name,
src_dir_fd=parent_descriptor,
dst_dir_fd=parent_descriptor,
)
observed = os.stat(path.name, dir_fd=parent_descriptor, follow_symlinks=False)
_require(
stat.S_ISREG(observed.st_mode)
and observed.st_uid == os.geteuid()
and stat.S_IMODE(observed.st_mode) == 0o600
and observed.st_size == len(payload),
"published outcome posture drifted",
)
_fsync_directory_descriptor(parent_descriptor)
except OSError as exc:
raise PackageError("cannot publish outcome journal") from exc
finally:
if descriptor is not None:
os.close(descriptor)
staged = _directory_entry_stat(parent_descriptor, staged_name)
if staged is not None:
try:
os.unlink(staged_name, dir_fd=parent_descriptor)
_fsync_directory_descriptor(parent_descriptor)
except OSError as exc:
os.close(parent_descriptor)
raise PackageError("cannot clean staged outcome journal") from exc
os.close(parent_descriptor)
def load_publish_outcome(path: Path, *, image_input: dict[str, Any] | None = None) -> dict[str, Any]:
_require(path.is_absolute() and path.name not in {"", ".", ".."}, "publish outcome path is invalid")
parent_descriptor = _open_private_operator_directory(path.parent, "publish outcome parent")
descriptor: int | None = None
try:
flags = os.O_RDONLY
if hasattr(os, "O_NOFOLLOW"):
flags |= os.O_NOFOLLOW
descriptor = os.open(path.name, flags, dir_fd=parent_descriptor)
before = os.fstat(descriptor)
_require(
stat.S_ISREG(before.st_mode)
and before.st_uid == os.geteuid()
and stat.S_IMODE(before.st_mode) == 0o600
and before.st_size <= MAX_PUBLISH_OUTCOME_BYTES,
"publish outcome file posture is unsafe",
)
chunks: list[bytes] = []
remaining = MAX_PUBLISH_OUTCOME_BYTES + 1
while remaining > 0:
chunk = os.read(descriptor, min(64 * 1024, remaining))
if not chunk:
break
chunks.append(chunk)
remaining -= len(chunk)
payload = b"".join(chunks)
after = os.fstat(descriptor)
named = os.stat(path.name, dir_fd=parent_descriptor, follow_symlinks=False)
_require(
len(payload) <= MAX_PUBLISH_OUTCOME_BYTES
and (before.st_dev, before.st_ino, before.st_size, before.st_mtime_ns, before.st_ctime_ns)
== (after.st_dev, after.st_ino, after.st_size, after.st_mtime_ns, after.st_ctime_ns)
and (after.st_dev, after.st_ino) == (named.st_dev, named.st_ino)
and len(payload) == after.st_size,
"publish outcome changed while it was read",
)
except (OSError, UnicodeError) as exc:
raise PackageError("cannot read publish outcome") from exc
finally:
if descriptor is not None:
os.close(descriptor)
os.close(parent_descriptor)
try:
value = _strict_json_loads(payload.decode("utf-8"), "publish outcome")
except UnicodeError as exc:
raise PackageError("cannot decode publish outcome") from exc
_require(isinstance(value, dict), "publish outcome must be a JSON object")
validate_publish_outcome(value, image_input=image_input)
return value
def complete_publish_outcome(
*,
image_input_path: Path,
outcome_journal: Path,
build_push_receipt_path: Path,
release_path: Path,
build_outcome: str,
finalize_outcome: str,
cleanup_outcome: str,
) -> dict[str, Any]:
"""Finalize the mutation journal without masking an earlier workflow failure."""
allowed_step_outcomes = {"success", "failure", "cancelled", "skipped"}
_require(
{build_outcome, finalize_outcome, cleanup_outcome} <= allowed_step_outcomes,
"workflow step outcome is invalid",
)
image_input = load_json(image_input_path, "image input")
outcome = load_publish_outcome(outcome_journal, image_input=image_input)
_require(outcome["status"] != "pass", "publish outcome is already complete")
release_updates = {
"build_push_receipt_present": False,
"bundle_present": False,
"release_sha256": None,
}
evidence_failure: str | None = None
receipt: dict[str, Any] | None = None
release: dict[str, Any] | None = None
if build_push_receipt_path.is_file() and not build_push_receipt_path.is_symlink():
try:
receipt = load_private_json(build_push_receipt_path, "build/push receipt")
validate_build_push_receipt(receipt, image_input=image_input)
_require(
receipt["registry"]["manifest_digest"] == outcome["registry"]["observed_digest"],
"build/push receipt differs from publish outcome",
)
release_updates["build_push_receipt_present"] = True
except PackageError:
receipt = None
evidence_failure = "receipt_validation"
if release_path.is_file() and not release_path.is_symlink():
try:
release = load_json(release_path, "release descriptor")
validate_release_descriptor(release)
_require(
receipt is not None
and release["build_push_receipt"] == receipt
and release["image_input"] == image_input
and release["image"]["digest"] == outcome["registry"]["observed_digest"],
"release descriptor differs from publish outcome",
)
release_updates.update(
{
"bundle_present": True,
"release_sha256": release["release_sha256"],
}
)
except PackageError:
release = None
evidence_failure = evidence_failure or "release_validation"
if build_outcome == "success" and release_updates["build_push_receipt_present"] is not True:
evidence_failure = evidence_failure or "receipt_missing"
if finalize_outcome == "success" and release_updates["bundle_present"] is not True:
evidence_failure = evidence_failure or "release_missing"
failure_phase = outcome["failure_phase"]
if failure_phase is None and build_outcome != "success":
failure_phase = {
"failure": "build_push",
"cancelled": "workflow_cancelled",
"skipped": "pre_publish",
}[build_outcome]
if failure_phase is None and finalize_outcome != "success":
failure_phase = "finalize"
if failure_phase is None and evidence_failure is not None:
failure_phase = evidence_failure
if failure_phase is None and cleanup_outcome != "success":
failure_phase = "cleanup"
passing = (
failure_phase is None
and build_outcome == "success"
and finalize_outcome == "success"
and cleanup_outcome == "success"
and release_updates["build_push_receipt_present"] is True
and release_updates["bundle_present"] is True
and _is_sha256(release_updates["release_sha256"])
)
completed = advance_publish_outcome(
outcome,
phase="complete",
event_status="pass" if passing else "fail",
status="pass" if passing else "fail",
failure_phase=None if passing else failure_phase or "completion",
cleanup={"docker_config": "pass" if cleanup_outcome == "success" else "fail"},
release=release_updates,
)
publish_publish_outcome(outcome_journal, completed, create=False)
return completed
def _remove_published_bundle(output_bundle: Path, expected_identity: tuple[int, int]) -> None: def _remove_published_bundle(output_bundle: Path, expected_identity: tuple[int, int]) -> None:
try: try:
observed = output_bundle.stat(follow_symlinks=False) observed = output_bundle.stat(follow_symlinks=False)
@ -3289,8 +4062,21 @@ def _parse_args(argv: list[str] | None = None) -> argparse.Namespace:
build_push.add_argument("--docker-binary", type=Path, required=True) build_push.add_argument("--docker-binary", type=Path, required=True)
build_push.add_argument("--docker-host", required=True) build_push.add_argument("--docker-host", required=True)
build_push.add_argument("--docker-config", type=Path, required=True) build_push.add_argument("--docker-config", type=Path, required=True)
build_push.add_argument("--gcloud-binary", type=Path, required=True)
build_push.add_argument("--output-receipt", type=Path, required=True) build_push.add_argument("--output-receipt", type=Path, required=True)
build_push.add_argument("--outcome-journal", type=Path, required=True)
build_push.add_argument("--execute-staging-push", action="store_true") build_push.add_argument("--execute-staging-push", action="store_true")
init_outcome = subparsers.add_parser("init-outcome")
init_outcome.add_argument("--image-input", type=Path, required=True)
init_outcome.add_argument("--output-journal", type=Path, required=True)
complete_outcome = subparsers.add_parser("complete-outcome")
complete_outcome.add_argument("--image-input", type=Path, required=True)
complete_outcome.add_argument("--outcome-journal", type=Path, required=True)
complete_outcome.add_argument("--build-push-receipt", type=Path, required=True)
complete_outcome.add_argument("--release", type=Path, required=True)
complete_outcome.add_argument("--build-outcome", required=True)
complete_outcome.add_argument("--finalize-outcome", required=True)
complete_outcome.add_argument("--cleanup-outcome", required=True)
finalize = subparsers.add_parser("finalize") finalize = subparsers.add_parser("finalize")
finalize.add_argument("--image-input", type=Path, required=True) finalize.add_argument("--image-input", type=Path, required=True)
finalize.add_argument("--build-push-receipt", type=Path, required=True) finalize.add_argument("--build-push-receipt", type=Path, required=True)
@ -3375,9 +4161,38 @@ def main(argv: list[str] | None = None) -> int:
docker_binary=args.docker_binary, docker_binary=args.docker_binary,
docker_host=args.docker_host, docker_host=args.docker_host,
docker_config=args.docker_config, docker_config=args.docker_config,
gcloud_binary=args.gcloud_binary,
output_receipt=args.output_receipt.absolute(), output_receipt=args.output_receipt.absolute(),
outcome_journal=args.outcome_journal.absolute(),
execute_staging_push=args.execute_staging_push, execute_staging_push=args.execute_staging_push,
) )
elif args.command == "init-outcome":
image_input = load_json(args.image_input, "image input")
result = build_initial_publish_outcome(image_input)
publish_publish_outcome(args.output_journal.absolute(), result, create=True)
elif args.command == "complete-outcome":
result = complete_publish_outcome(
image_input_path=args.image_input,
outcome_journal=args.outcome_journal.absolute(),
build_push_receipt_path=args.build_push_receipt.absolute(),
release_path=args.release.absolute(),
build_outcome=args.build_outcome,
finalize_outcome=args.finalize_outcome,
cleanup_outcome=args.cleanup_outcome,
)
if result["status"] != "pass":
print(
json.dumps(
{
"status": "fail",
"schema": result["schema"],
"failure_phase": result["failure_phase"],
},
sort_keys=True,
),
file=sys.stderr,
)
return 65
elif args.command == "finalize": elif args.command == "finalize":
image_input = load_json(args.image_input, "image input") image_input = load_json(args.image_input, "image input")
build_push_receipt = load_private_json( build_push_receipt = load_private_json(

View file

@ -19,6 +19,9 @@ from ops import gcp_leoclean_nosend_package as package
ROOT = Path(__file__).resolve().parents[1] ROOT = Path(__file__).resolve().parents[1]
REVISION = package._git_commit(ROOT) REVISION = package._git_commit(ROOT)
IMAGE_DIGEST = "2" * 64 IMAGE_DIGEST = "2" * 64
LOCAL_CONFIG_DIGEST = "sha256:" + "3" * 64
REMOTE_CONFIG_DIGEST = "sha256:" + "6" * 64
ROOTFS_LAYERS = ["sha256:" + "7" * 64, "sha256:" + "8" * 64]
CLAIM_CEILING = "Offline package proof only; live GCP identity, database access, restart, and parity remain unproven." CLAIM_CEILING = "Offline package proof only; live GCP identity, database access, restart, and parity remain unproven."
@ -394,7 +397,8 @@ def docker_inspection_fixture(
image_input: dict[str, object], image_input: dict[str, object],
reference: str, reference: str,
*, *,
config_digest: str = "sha256:" + "3" * 64, config_digest: str = LOCAL_CONFIG_DIGEST,
rootfs_layers: list[str] | None = None,
) -> dict[str, object]: ) -> dict[str, object]:
runtime = copy.deepcopy(package.IMAGE_RUNTIME_CONFIG) runtime = copy.deepcopy(package.IMAGE_RUNTIME_CONFIG)
healthcheck = runtime["healthcheck"] healthcheck = runtime["healthcheck"]
@ -404,6 +408,7 @@ def docker_inspection_fixture(
"Architecture": "amd64", "Architecture": "amd64",
"Os": "linux", "Os": "linux",
"Variant": None, "Variant": None,
"RootFS": {"Type": "layers", "Layers": list(rootfs_layers or ROOTFS_LAYERS)},
"Config": { "Config": {
"Labels": package.expected_image_labels(image_input), "Labels": package.expected_image_labels(image_input),
"User": runtime["user"], "User": runtime["user"],
@ -447,6 +452,32 @@ def docker_authority(tmp_path: Path) -> tuple[Path, str, Path]:
return docker_binary, "unix:///tmp/livingip-docker.sock", docker_config return docker_binary, "unix:///tmp/livingip-docker.sock", docker_config
def publish_outcome_authority(
tmp_path: Path,
image_input: dict[str, object],
) -> tuple[Path, Path]:
gcloud_binary = tmp_path / "bin" / "gcloud"
gcloud_binary.parent.mkdir(exist_ok=True)
gcloud_binary.write_text("#!/bin/sh\nexit 0\n", encoding="utf-8")
gcloud_binary.chmod(0o755)
outcome_journal = tmp_path / "publish-outcome.json"
package.publish_publish_outcome(
outcome_journal,
package.build_initial_publish_outcome(image_input),
create=True,
)
return gcloud_binary, outcome_journal
def mock_fresh_registry_reconcile(
monkeypatch: pytest.MonkeyPatch,
*,
manifest_digest: str = "sha256:" + IMAGE_DIGEST,
) -> None:
monkeypatch.setattr(package, "_registry_candidate_digest", lambda *_args: None)
monkeypatch.setattr(package, "_wait_registry_candidate_digest", lambda *_args: manifest_digest)
def docker_inventory_fixture( def docker_inventory_fixture(
reference: str, reference: str,
*, *,
@ -505,19 +536,28 @@ def mocked_build_push_docker(
*, *,
post_push_references: list[str] | None = None, post_push_references: list[str] | None = None,
fail_after_side_effect: str | None = None, fail_after_side_effect: str | None = None,
remote_config_digest: str = LOCAL_CONFIG_DIGEST,
remote_rootfs_layers: list[str] | None = None,
remote_verification_valid: bool = True,
) -> tuple[object, dict[str, object], list[tuple[list[str], dict[str, object]]]]: ) -> tuple[object, dict[str, object], list[tuple[list[str], dict[str, object]]]]:
config_digest = "sha256:" + "3" * 64 config_digest = LOCAL_CONFIG_DIGEST
exact_reference = f"{package.IMAGE_REPOSITORY}@sha256:{IMAGE_DIGEST}" exact_reference = f"{package.IMAGE_REPOSITORY}@sha256:{IMAGE_DIGEST}"
references = list(post_push_references if post_push_references is not None else [exact_reference]) references = list(post_push_references if post_push_references is not None else [exact_reference])
build_tag = f"input-{image_input['input_sha256']}" build_tag = f"input-{image_input['input_sha256']}"
build_reference = f"{package.LOCAL_BUILD_REPOSITORY}:{build_tag}" build_reference = f"{package.LOCAL_BUILD_REPOSITORY}:{build_tag}"
candidate_tag = f"candidate-{config_digest.removeprefix('sha256:')}" candidate_tag = f"candidate-{image_input['input_sha256']}"
candidate_reference = f"{package.IMAGE_REPOSITORY}:{candidate_tag}" candidate_reference = f"{package.IMAGE_REPOSITORY}:{candidate_tag}"
state: dict[str, object] = {"build": False, "candidate": False, "digests": False, "pushes": 0} state: dict[str, object] = {"build": False, "candidate": False, "digests": False, "pushes": 0}
calls: list[tuple[list[str], dict[str, object]]] = [] calls: list[tuple[list[str], dict[str, object]]] = []
def inspection(reference: str) -> bytes: def inspection(reference: str) -> bytes:
value = docker_inspection_fixture(image_input, reference, config_digest=config_digest) is_remote = "@sha256:" in reference
value = docker_inspection_fixture(
image_input,
reference,
config_digest=remote_config_digest if is_remote else config_digest,
rootfs_layers=remote_rootfs_layers if is_remote else ROOTFS_LAYERS,
)
value["RepoDigests"] = references if state["digests"] else [] value["RepoDigests"] = references if state["digests"] else []
return json.dumps([value]).encode() return json.dumps([value]).encode()
@ -576,6 +616,52 @@ def mocked_build_push_docker(
stdout=("sha256:" + "9" * 64 + "\n").encode(), stdout=("sha256:" + "9" * 64 + "\n").encode(),
stderr=b"", stderr=b"",
) )
if operation[0] == "pull":
assert operation[1:4] == ["--quiet", "--platform", package.PLATFORM]
assert operation[4] in references
state["digests"] = True
return subprocess.CompletedProcess(arguments, 0, stdout=b"pulled\n", stderr=b"")
if operation[0] == "run":
assert operation == [
"run",
"--rm",
"--pull=never",
"--platform",
package.PLATFORM,
"--network=none",
"--read-only",
"--cap-drop=ALL",
"--security-opt=no-new-privileges:true",
"--pids-limit=64",
f"--user={package.RUNTIME_UID}:{package.RUNTIME_GID}",
exact_reference,
"verify-build",
image_input["runtime"]["teleo_git_head"],
image_input["runtime"]["artifact_sha256"],
image_input["identity"]["bundle_sha256"],
image_input["input_sha256"],
]
verification = (
{
"schema": "livingip.leocleanNoSendContainerVerification.v1",
"status": "pass",
"teleo_git_head": image_input["runtime"]["teleo_git_head"],
"artifact_sha256": image_input["runtime"]["artifact_sha256"],
"identity_sha256": image_input["identity"]["bundle_sha256"],
"input_sha256": image_input["input_sha256"],
"python_version": package.PYTHON_VERSION,
"postgres_version": package.POSTGRES_VERSION,
"cloudsql_ca_sha256": package.CA_SHA256,
}
if remote_verification_valid
else {"status": "fail"}
)
return subprocess.CompletedProcess(
arguments,
0,
stdout=(json.dumps(verification) + "\n").encode(),
stderr=b"",
)
if operation[:2] == ["image", "inspect"]: if operation[:2] == ["image", "inspect"]:
reference = operation[2] reference = operation[2]
present = ( present = (
@ -602,6 +688,98 @@ def mocked_build_push_docker(
return fake_run, state, calls return fake_run, state, calls
def test_registry_candidate_lookup_uses_exact_resource_shape_and_command(
tmp_path: Path,
monkeypatch: pytest.MonkeyPatch,
) -> None:
candidate_tag = "candidate-" + "a" * 64
digest = "b" * 64
expected_tag = f"{package.ARTIFACT_PACKAGE_RESOURCE}/tags/{candidate_tag}"
fixture = [
{
"tag": expected_tag,
"version": f"{package.ARTIFACT_PACKAGE_RESOURCE}/versions/sha256:{digest}",
}
]
observed: dict[str, object] = {}
def fake_gcloud(
binary: Path,
arguments: list[str],
*,
timeout: int = 60,
) -> subprocess.CompletedProcess[bytes]:
observed.update({"binary": binary, "arguments": arguments, "timeout": timeout})
return subprocess.CompletedProcess(arguments, 0, stdout=json.dumps(fixture).encode(), stderr=b"")
monkeypatch.setattr(package, "_run_gcloud", fake_gcloud)
gcloud_binary = tmp_path / "gcloud"
assert package._registry_candidate_digest(gcloud_binary, candidate_tag) == f"sha256:{digest}"
assert observed == {
"binary": gcloud_binary,
"arguments": [
"artifacts",
"docker",
"tags",
"list",
package.IMAGE_REPOSITORY,
"--project",
package.PROJECT,
f"--filter=tag={expected_tag}",
"--format=json(tag,version)",
"--limit=2",
],
"timeout": 60,
}
assert "--location" not in observed["arguments"]
def test_registry_candidate_lookup_rejects_mixed_exact_and_unexpected_rows(
tmp_path: Path,
monkeypatch: pytest.MonkeyPatch,
) -> None:
candidate_tag = "candidate-" + "a" * 64
digest = "b" * 64
expected_tag = f"{package.ARTIFACT_PACKAGE_RESOURCE}/tags/{candidate_tag}"
rows = [
{
"tag": expected_tag,
"version": f"{package.ARTIFACT_PACKAGE_RESOURCE}/versions/sha256:{digest}",
},
{
"tag": f"{package.ARTIFACT_PACKAGE_RESOURCE}/tags/candidate-{'c' * 64}",
"version": f"{package.ARTIFACT_PACKAGE_RESOURCE}/versions/sha256:{'d' * 64}",
},
]
monkeypatch.setattr(
package,
"_run_gcloud",
lambda *_args, **_kwargs: subprocess.CompletedProcess(
[],
0,
stdout=json.dumps(rows).encode(),
stderr=b"",
),
)
with pytest.raises(package.PackageError, match="unexpected tag"):
package._registry_candidate_digest(tmp_path / "gcloud", candidate_tag)
def test_publish_outcome_loader_handles_short_reads(
tmp_path: Path,
monkeypatch: pytest.MonkeyPatch,
) -> None:
image_input = image_input_fixture(tmp_path)
_gcloud_binary, outcome_journal = publish_outcome_authority(tmp_path, image_input)
expected = package.build_initial_publish_outcome(image_input)
original_read = os.read
monkeypatch.setattr(package.os, "read", lambda descriptor, size: original_read(descriptor, min(size, 7)))
assert package.load_publish_outcome(outcome_journal, image_input=image_input) == expected
def test_image_input_binds_exact_runtime_identity_and_staging_target(tmp_path: Path) -> None: def test_image_input_binds_exact_runtime_identity_and_staging_target(tmp_path: Path) -> None:
value = image_input_fixture(tmp_path) value = image_input_fixture(tmp_path)
package.validate_image_input(value) package.validate_image_input(value)
@ -1065,6 +1243,7 @@ def test_build_push_derives_receipt_and_retains_all_daemon_references(
repo_root.mkdir() repo_root.mkdir()
output = tmp_path / "build-push-receipt.json" output = tmp_path / "build-push-receipt.json"
docker_binary, docker_host, docker_config = docker_authority(tmp_path) docker_binary, docker_host, docker_config = docker_authority(tmp_path)
gcloud_binary, outcome_journal = publish_outcome_authority(tmp_path, image_input)
fake_run, state, calls = mocked_build_push_docker(image_input) fake_run, state, calls = mocked_build_push_docker(image_input)
reviewed: list[tuple[Path, Path, dict[str, object]]] = [] reviewed: list[tuple[Path, Path, dict[str, object]]] = []
monkeypatch.setattr(package, "validate_prepared_image_context", lambda _context: (image_input, build_args)) monkeypatch.setattr(package, "validate_prepared_image_context", lambda _context: (image_input, build_args))
@ -1074,6 +1253,7 @@ def test_build_push_derives_receipt_and_retains_all_daemon_references(
lambda root, observed_context, value: reviewed.append((root, observed_context, value)), lambda root, observed_context, value: reviewed.append((root, observed_context, value)),
) )
monkeypatch.setattr(package.subprocess, "run", fake_run) monkeypatch.setattr(package.subprocess, "run", fake_run)
mock_fresh_registry_reconcile(monkeypatch)
monkeypatch.setenv("DOCKER_TLS_VERIFY", "1") monkeypatch.setenv("DOCKER_TLS_VERIFY", "1")
monkeypatch.setenv("BUILDKIT_PROGRESS", "plain") monkeypatch.setenv("BUILDKIT_PROGRESS", "plain")
monkeypatch.setenv("BUILDX_BUILDER", "unreviewed") monkeypatch.setenv("BUILDX_BUILDER", "unreviewed")
@ -1084,13 +1264,24 @@ def test_build_push_derives_receipt_and_retains_all_daemon_references(
docker_binary=docker_binary, docker_binary=docker_binary,
docker_host=docker_host, docker_host=docker_host,
docker_config=docker_config, docker_config=docker_config,
gcloud_binary=gcloud_binary,
output_receipt=output, output_receipt=output,
outcome_journal=outcome_journal,
execute_staging_push=True, execute_staging_push=True,
) )
assert json.loads(output.read_text(encoding="utf-8")) == receipt assert json.loads(output.read_text(encoding="utf-8")) == receipt
assert receipt["registry"]["manifest_digest"] == f"sha256:{IMAGE_DIGEST}" assert receipt["registry"]["manifest_digest"] == f"sha256:{IMAGE_DIGEST}"
assert receipt["registry"]["manifest_digest"] != "sha256:" + "9" * 64 assert receipt["registry"]["manifest_digest"] != "sha256:" + "9" * 64
outcome = package.load_publish_outcome(outcome_journal, image_input=image_input)
assert outcome["candidate"]["tag"] == f"candidate-{image_input['input_sha256']}"
assert outcome["registry"] == {
"repository": package.IMAGE_REPOSITORY,
"preexisting": False,
"push_attempted": True,
"action": "fresh_push",
"observed_digest": f"sha256:{IMAGE_DIGEST}",
}
assert reviewed == [(repo_root, context, image_input), (repo_root, context, image_input)] assert reviewed == [(repo_root, context, image_input), (repo_root, context, image_input)]
assert state == {"build": True, "candidate": True, "digests": True, "pushes": 1} assert state == {"build": True, "candidate": True, "digests": True, "pushes": 1}
build = next(operation for operation, _kwargs in calls if operation and operation[0] == "build") build = next(operation for operation, _kwargs in calls if operation and operation[0] == "build")
@ -1134,7 +1325,9 @@ def test_build_push_requires_explicit_execution_flag_before_validation(tmp_path:
docker_binary=tmp_path / "docker", docker_binary=tmp_path / "docker",
docker_host="unix:///tmp/docker.sock", docker_host="unix:///tmp/docker.sock",
docker_config=tmp_path, docker_config=tmp_path,
gcloud_binary=tmp_path / "gcloud",
output_receipt=tmp_path / "receipt.json", output_receipt=tmp_path / "receipt.json",
outcome_journal=tmp_path / "outcome.json",
execute_staging_push=False, execute_staging_push=False,
) )
@ -1156,6 +1349,7 @@ def test_build_push_rejects_writable_docker_config_before_docker(
repo_root.mkdir() repo_root.mkdir()
output = tmp_path / "writable-config-build-push-receipt.json" output = tmp_path / "writable-config-build-push-receipt.json"
docker_binary, docker_host, docker_config = docker_authority(tmp_path) docker_binary, docker_host, docker_config = docker_authority(tmp_path)
gcloud_binary, outcome_journal = publish_outcome_authority(tmp_path, image_input)
docker_config.chmod(0o777) docker_config.chmod(0o777)
docker_calls: list[list[str]] = [] docker_calls: list[list[str]] = []
monkeypatch.setattr(package, "validate_prepared_image_context", lambda _context: (image_input, build_args)) monkeypatch.setattr(package, "validate_prepared_image_context", lambda _context: (image_input, build_args))
@ -1173,7 +1367,9 @@ def test_build_push_rejects_writable_docker_config_before_docker(
docker_binary=docker_binary, docker_binary=docker_binary,
docker_host=docker_host, docker_host=docker_host,
docker_config=docker_config, docker_config=docker_config,
gcloud_binary=gcloud_binary,
output_receipt=output, output_receipt=output,
outcome_journal=outcome_journal,
execute_staging_push=True, execute_staging_push=True,
) )
@ -1282,6 +1478,7 @@ def test_build_push_rejects_replaced_docker_config_before_mutation(
repo_root.mkdir() repo_root.mkdir()
output = tmp_path / "replaced-config-build-push-receipt.json" output = tmp_path / "replaced-config-build-push-receipt.json"
docker_binary, docker_host, docker_config = docker_authority(tmp_path) docker_binary, docker_host, docker_config = docker_authority(tmp_path)
gcloud_binary, outcome_journal = publish_outcome_authority(tmp_path, image_input)
retained_config = tmp_path / "retained-docker-config" retained_config = tmp_path / "retained-docker-config"
docker_calls: list[list[str]] = [] docker_calls: list[list[str]] = []
monkeypatch.setattr(package, "validate_prepared_image_context", lambda _context: (image_input, build_args)) monkeypatch.setattr(package, "validate_prepared_image_context", lambda _context: (image_input, build_args))
@ -1306,7 +1503,9 @@ def test_build_push_rejects_replaced_docker_config_before_mutation(
docker_binary=docker_binary, docker_binary=docker_binary,
docker_host=docker_host, docker_host=docker_host,
docker_config=docker_config, docker_config=docker_config,
gcloud_binary=gcloud_binary,
output_receipt=output, output_receipt=output,
outcome_journal=outcome_journal,
execute_staging_push=True, execute_staging_push=True,
) )
@ -1335,6 +1534,7 @@ def test_build_push_rejects_receipt_parent_symlink_alias_before_docker(
repo_root = tmp_path / "repo" repo_root = tmp_path / "repo"
repo_root.mkdir() repo_root.mkdir()
docker_binary, docker_host, docker_config = docker_authority(tmp_path) docker_binary, docker_host, docker_config = docker_authority(tmp_path)
gcloud_binary, outcome_journal = publish_outcome_authority(tmp_path, image_input)
docker_calls: list[list[str]] = [] docker_calls: list[list[str]] = []
monkeypatch.setattr(package, "validate_prepared_image_context", lambda _context: (image_input, build_args)) monkeypatch.setattr(package, "validate_prepared_image_context", lambda _context: (image_input, build_args))
monkeypatch.setattr(package, "validate_reviewed_context_source", lambda *_args: None) monkeypatch.setattr(package, "validate_reviewed_context_source", lambda *_args: None)
@ -1351,7 +1551,9 @@ def test_build_push_rejects_receipt_parent_symlink_alias_before_docker(
docker_binary=docker_binary, docker_binary=docker_binary,
docker_host=docker_host, docker_host=docker_host,
docker_config=docker_config, docker_config=docker_config,
gcloud_binary=gcloud_binary,
output_receipt=output, output_receipt=output,
outcome_journal=outcome_journal,
execute_staging_push=True, execute_staging_push=True,
) )
@ -1380,11 +1582,13 @@ def test_build_push_rechecks_receipt_context_separation_at_publication(
retained_parent = tmp_path / "retained-receipts" retained_parent = tmp_path / "retained-receipts"
output = output_parent / "receipt.json" output = output_parent / "receipt.json"
docker_binary, docker_host, docker_config = docker_authority(tmp_path) docker_binary, docker_host, docker_config = docker_authority(tmp_path)
gcloud_binary, outcome_journal = publish_outcome_authority(tmp_path, image_input)
fake_run, state, calls = mocked_build_push_docker(image_input) fake_run, state, calls = mocked_build_push_docker(image_input)
original_publish = package.publish_build_push_receipt original_publish = package.publish_build_push_receipt
monkeypatch.setattr(package, "validate_prepared_image_context", lambda _context: (image_input, build_args)) monkeypatch.setattr(package, "validate_prepared_image_context", lambda _context: (image_input, build_args))
monkeypatch.setattr(package, "validate_reviewed_context_source", lambda *_args: None) monkeypatch.setattr(package, "validate_reviewed_context_source", lambda *_args: None)
monkeypatch.setattr(package.subprocess, "run", fake_run) monkeypatch.setattr(package.subprocess, "run", fake_run)
mock_fresh_registry_reconcile(monkeypatch)
def alias_parent_before_publish(*args: object, **kwargs: object) -> None: def alias_parent_before_publish(*args: object, **kwargs: object) -> None:
output_parent.rename(retained_parent) output_parent.rename(retained_parent)
@ -1400,7 +1604,9 @@ def test_build_push_rechecks_receipt_context_separation_at_publication(
docker_binary=docker_binary, docker_binary=docker_binary,
docker_host=docker_host, docker_host=docker_host,
docker_config=docker_config, docker_config=docker_config,
gcloud_binary=gcloud_binary,
output_receipt=output, output_receipt=output,
outcome_journal=outcome_journal,
execute_staging_push=True, execute_staging_push=True,
) )
@ -1437,6 +1643,7 @@ def test_build_push_fails_closed_without_one_exact_post_push_digest(
repo_root = tmp_path / "repo" repo_root = tmp_path / "repo"
repo_root.mkdir() repo_root.mkdir()
docker_binary, docker_host, docker_config = docker_authority(tmp_path) docker_binary, docker_host, docker_config = docker_authority(tmp_path)
gcloud_binary, outcome_journal = publish_outcome_authority(tmp_path, image_input)
fake_run, state, _calls = mocked_build_push_docker( fake_run, state, _calls = mocked_build_push_docker(
image_input, image_input,
post_push_references=references, post_push_references=references,
@ -1444,6 +1651,12 @@ def test_build_push_fails_closed_without_one_exact_post_push_digest(
monkeypatch.setattr(package, "validate_prepared_image_context", lambda _context: (image_input, build_args)) monkeypatch.setattr(package, "validate_prepared_image_context", lambda _context: (image_input, build_args))
monkeypatch.setattr(package, "validate_reviewed_context_source", lambda *_args: None) monkeypatch.setattr(package, "validate_reviewed_context_source", lambda *_args: None)
monkeypatch.setattr(package.subprocess, "run", fake_run) monkeypatch.setattr(package.subprocess, "run", fake_run)
monkeypatch.setattr(package, "_registry_candidate_digest", lambda *_args: None)
monkeypatch.setattr(
package,
"_wait_registry_candidate_digest",
lambda *_args: f"sha256:{IMAGE_DIGEST}" if references else None,
)
output = tmp_path / "failed-build-push-receipt.json" output = tmp_path / "failed-build-push-receipt.json"
with pytest.raises(package.PackageError, match="build/push failed"): with pytest.raises(package.PackageError, match="build/push failed"):
@ -1453,7 +1666,9 @@ def test_build_push_fails_closed_without_one_exact_post_push_digest(
docker_binary=docker_binary, docker_binary=docker_binary,
docker_host=docker_host, docker_host=docker_host,
docker_config=docker_config, docker_config=docker_config,
gcloud_binary=gcloud_binary,
output_receipt=output, output_receipt=output,
outcome_journal=outcome_journal,
execute_staging_push=True, execute_staging_push=True,
) )
@ -1462,7 +1677,7 @@ def test_build_push_fails_closed_without_one_exact_post_push_digest(
assert state["candidate"] is True assert state["candidate"] is True
@pytest.mark.parametrize("failure", ["build", "tag", "push"]) @pytest.mark.parametrize("failure", ["build", "tag"])
def test_build_push_retains_daemon_references_when_cli_fails_after_side_effect( def test_build_push_retains_daemon_references_when_cli_fails_after_side_effect(
tmp_path: Path, tmp_path: Path,
monkeypatch: pytest.MonkeyPatch, monkeypatch: pytest.MonkeyPatch,
@ -1480,6 +1695,7 @@ def test_build_push_retains_daemon_references_when_cli_fails_after_side_effect(
repo_root = tmp_path / "repo" repo_root = tmp_path / "repo"
repo_root.mkdir() repo_root.mkdir()
docker_binary, docker_host, docker_config = docker_authority(tmp_path) docker_binary, docker_host, docker_config = docker_authority(tmp_path)
gcloud_binary, outcome_journal = publish_outcome_authority(tmp_path, image_input)
fake_run, state, calls = mocked_build_push_docker( fake_run, state, calls = mocked_build_push_docker(
image_input, image_input,
fail_after_side_effect=failure, fail_after_side_effect=failure,
@ -1487,6 +1703,7 @@ def test_build_push_retains_daemon_references_when_cli_fails_after_side_effect(
monkeypatch.setattr(package, "validate_prepared_image_context", lambda _context: (image_input, build_args)) monkeypatch.setattr(package, "validate_prepared_image_context", lambda _context: (image_input, build_args))
monkeypatch.setattr(package, "validate_reviewed_context_source", lambda *_args: None) monkeypatch.setattr(package, "validate_reviewed_context_source", lambda *_args: None)
monkeypatch.setattr(package.subprocess, "run", fake_run) monkeypatch.setattr(package.subprocess, "run", fake_run)
mock_fresh_registry_reconcile(monkeypatch)
output = tmp_path / "failed-side-effect-receipt.json" output = tmp_path / "failed-side-effect-receipt.json"
with pytest.raises(package.PackageError, match="build/push failed") as raised: with pytest.raises(package.PackageError, match="build/push failed") as raised:
@ -1496,7 +1713,9 @@ def test_build_push_retains_daemon_references_when_cli_fails_after_side_effect(
docker_binary=docker_binary, docker_binary=docker_binary,
docker_host=docker_host, docker_host=docker_host,
docker_config=docker_config, docker_config=docker_config,
gcloud_binary=gcloud_binary,
output_receipt=output, output_receipt=output,
outcome_journal=outcome_journal,
execute_staging_push=True, execute_staging_push=True,
) )
@ -1504,14 +1723,188 @@ def test_build_push_retains_daemon_references_when_cli_fails_after_side_effect(
assert not output.exists() assert not output.exists()
assert state == { assert state == {
"build": True, "build": True,
"candidate": failure in {"tag", "push"}, "candidate": failure == "tag",
"digests": failure == "push", "digests": False,
"pushes": 1 if failure == "push" else 0, "pushes": 0,
} }
removals = [operation for operation, _kwargs in calls if operation[:2] == ["image", "rm"]] removals = [operation for operation, _kwargs in calls if operation[:2] == ["image", "rm"]]
assert removals == [] assert removals == []
def test_build_push_reuses_verified_candidate_despite_nondeterministic_rootfs(
tmp_path: Path,
monkeypatch: pytest.MonkeyPatch,
) -> None:
image_input = image_input_fixture(tmp_path)
build_args = {
"ARTIFACT_SHA256": image_input["runtime"]["artifact_sha256"],
"IDENTITY_SHA256": image_input["identity"]["bundle_sha256"],
"INPUT_SHA256": image_input["input_sha256"],
"TELEO_REVISION": image_input["runtime"]["teleo_git_head"],
}
context = tmp_path / "context"
context.mkdir()
repo_root = tmp_path / "repo"
repo_root.mkdir()
output = tmp_path / "reused-build-push-receipt.json"
docker_binary, docker_host, docker_config = docker_authority(tmp_path)
gcloud_binary, outcome_journal = publish_outcome_authority(tmp_path, image_input)
fake_run, state, calls = mocked_build_push_docker(
image_input,
remote_config_digest=REMOTE_CONFIG_DIGEST,
remote_rootfs_layers=["sha256:" + "9" * 64],
)
monkeypatch.setattr(package, "validate_prepared_image_context", lambda _context: (image_input, build_args))
monkeypatch.setattr(package, "validate_reviewed_context_source", lambda *_args: None)
monkeypatch.setattr(package.subprocess, "run", fake_run)
monkeypatch.setattr(
package,
"_registry_candidate_digest",
lambda *_args: f"sha256:{IMAGE_DIGEST}",
)
receipt = package.build_and_push_staging_image(
repo_root,
context,
docker_binary=docker_binary,
docker_host=docker_host,
docker_config=docker_config,
gcloud_binary=gcloud_binary,
output_receipt=output,
outcome_journal=outcome_journal,
execute_staging_push=True,
)
assert receipt["local_image"]["config_digest"] == REMOTE_CONFIG_DIGEST
assert state == {"build": True, "candidate": False, "digests": True, "pushes": 0}
assert not any(operation[:2] == ["image", "push"] for operation, _kwargs in calls)
assert any(operation and operation[0] == "pull" for operation, _kwargs in calls)
assert any(operation and operation[0] == "run" for operation, _kwargs in calls)
outcome = package.load_publish_outcome(outcome_journal, image_input=image_input)
assert outcome["candidate"] == {
"tag": f"candidate-{image_input['input_sha256']}",
"reference": f"{package.IMAGE_REPOSITORY}:candidate-{image_input['input_sha256']}",
"local_build_config_digest": LOCAL_CONFIG_DIGEST,
}
assert outcome["registry"] == {
"repository": package.IMAGE_REPOSITORY,
"preexisting": True,
"push_attempted": False,
"action": "reused",
"observed_digest": f"sha256:{IMAGE_DIGEST}",
}
def test_build_push_rejects_preexisting_failed_runtime_verification_without_claiming_reuse(
tmp_path: Path,
monkeypatch: pytest.MonkeyPatch,
) -> None:
image_input = image_input_fixture(tmp_path)
build_args = {
"ARTIFACT_SHA256": image_input["runtime"]["artifact_sha256"],
"IDENTITY_SHA256": image_input["identity"]["bundle_sha256"],
"INPUT_SHA256": image_input["input_sha256"],
"TELEO_REVISION": image_input["runtime"]["teleo_git_head"],
}
context = tmp_path / "context"
context.mkdir()
repo_root = tmp_path / "repo"
repo_root.mkdir()
output = tmp_path / "mismatched-build-push-receipt.json"
docker_binary, docker_host, docker_config = docker_authority(tmp_path)
gcloud_binary, outcome_journal = publish_outcome_authority(tmp_path, image_input)
fake_run, state, calls = mocked_build_push_docker(
image_input,
remote_config_digest=REMOTE_CONFIG_DIGEST,
remote_verification_valid=False,
)
monkeypatch.setattr(package, "validate_prepared_image_context", lambda _context: (image_input, build_args))
monkeypatch.setattr(package, "validate_reviewed_context_source", lambda *_args: None)
monkeypatch.setattr(package.subprocess, "run", fake_run)
monkeypatch.setattr(
package,
"_registry_candidate_digest",
lambda *_args: f"sha256:{IMAGE_DIGEST}",
)
with pytest.raises(package.PackageError, match="build/push failed"):
package.build_and_push_staging_image(
repo_root,
context,
docker_binary=docker_binary,
docker_host=docker_host,
docker_config=docker_config,
gcloud_binary=gcloud_binary,
output_receipt=output,
outcome_journal=outcome_journal,
execute_staging_push=True,
)
assert not output.exists()
assert state == {"build": True, "candidate": False, "digests": True, "pushes": 0}
assert not any(operation[:2] == ["image", "push"] for operation, _kwargs in calls)
outcome = package.load_publish_outcome(outcome_journal, image_input=image_input)
assert outcome["status"] == "fail"
assert outcome["registry"]["preexisting"] is True
assert outcome["registry"]["observed_digest"] == f"sha256:{IMAGE_DIGEST}"
assert outcome["registry"]["action"] == "observed"
assert all(event["status"] != "reused" for event in outcome["events"])
def test_post_push_validation_failure_retains_mutation_evidence(
tmp_path: Path,
monkeypatch: pytest.MonkeyPatch,
) -> None:
image_input = image_input_fixture(tmp_path)
build_args = {
"ARTIFACT_SHA256": image_input["runtime"]["artifact_sha256"],
"IDENTITY_SHA256": image_input["identity"]["bundle_sha256"],
"INPUT_SHA256": image_input["input_sha256"],
"TELEO_REVISION": image_input["runtime"]["teleo_git_head"],
}
context = tmp_path / "context"
context.mkdir()
repo_root = tmp_path / "repo"
repo_root.mkdir()
output = tmp_path / "post-push-failure-receipt.json"
docker_binary, docker_host, docker_config = docker_authority(tmp_path)
gcloud_binary, outcome_journal = publish_outcome_authority(tmp_path, image_input)
fake_run, state, calls = mocked_build_push_docker(
image_input,
remote_verification_valid=False,
)
monkeypatch.setattr(package, "validate_prepared_image_context", lambda _context: (image_input, build_args))
monkeypatch.setattr(package, "validate_reviewed_context_source", lambda *_args: None)
monkeypatch.setattr(package.subprocess, "run", fake_run)
mock_fresh_registry_reconcile(monkeypatch)
with pytest.raises(package.PackageError, match="build/push failed"):
package.build_and_push_staging_image(
repo_root,
context,
docker_binary=docker_binary,
docker_host=docker_host,
docker_config=docker_config,
gcloud_binary=gcloud_binary,
output_receipt=output,
outcome_journal=outcome_journal,
execute_staging_push=True,
)
assert not output.exists()
assert state == {"build": True, "candidate": True, "digests": True, "pushes": 1}
assert any(operation[:2] == ["image", "push"] for operation, _kwargs in calls)
outcome = package.load_publish_outcome(outcome_journal, image_input=image_input)
assert outcome["status"] == "fail"
assert outcome["registry"] == {
"repository": package.IMAGE_REPOSITORY,
"preexisting": False,
"push_attempted": True,
"action": "indeterminate",
"observed_digest": f"sha256:{IMAGE_DIGEST}",
}
def test_build_push_receipt_publication_is_atomic_repeatable_and_no_clobber(tmp_path: Path) -> None: def test_build_push_receipt_publication_is_atomic_repeatable_and_no_clobber(tmp_path: Path) -> None:
image_input = image_input_fixture(tmp_path) image_input = image_input_fixture(tmp_path)
receipt = build_push_receipt_fixture(image_input) receipt = build_push_receipt_fixture(image_input)

View file

@ -0,0 +1,180 @@
from pathlib import Path
import yaml
from ops import gcp_leoclean_nosend_package as package
WORKFLOW = Path(".github/workflows/gcp-leoclean-nosend-release.yml")
def load_workflow() -> dict[str, object]:
return yaml.load(WORKFLOW.read_text(encoding="utf-8"), Loader=yaml.BaseLoader)
def workflow_step(name: str) -> dict[str, object]:
workflow = load_workflow()
jobs = workflow["jobs"]
assert isinstance(jobs, dict)
publish = jobs["publish"]
assert isinstance(publish, dict)
steps = publish["steps"]
assert isinstance(steps, list)
return next(step for step in steps if step.get("name") == name)
def test_release_workflow_is_manual_main_only_and_serialized() -> None:
workflow = load_workflow()
assert set(workflow["on"]) == {"workflow_dispatch"}
assert workflow["permissions"] == {"contents": "read", "id-token": "write"}
assert workflow["concurrency"] == {
"group": "gcp-leoclean-nosend-release-main",
"cancel-in-progress": "false",
}
guard = workflow_step("Require manual main dispatch")["run"]
assert 'test "${GITHUB_EVENT_NAME}" = "workflow_dispatch"' in guard
assert 'test "${GITHUB_REF}" = "refs/heads/main"' in guard
assert 'test "${GITHUB_REF_TYPE}" = "branch"' in guard
assert 'test "${GITHUB_REPOSITORY}" = "living-ip/teleo-infrastructure"' in guard
assert '"living-ip/teleo-infrastructure/.github/workflows/gcp-leoclean-nosend-release.yml@refs/heads/main"' in guard
def test_release_workflow_checks_out_exact_clean_revision_without_persisted_token() -> None:
step = workflow_step("Check out exact dispatched revision")
assert step["uses"] == "actions/checkout@11d5960a326750d5838078e36cf38b85af677262"
assert step["with"] == {
"fetch-depth": "0",
"persist-credentials": "false",
"ref": "${{ github.sha }}",
}
revision_check = workflow_step("Verify exact clean main revision")["run"]
assert 'test "$(git rev-parse --verify HEAD)" = "${GITHUB_SHA}"' in revision_check
assert 'test "$(git rev-parse --verify refs/remotes/origin/main^{commit})" = "${GITHUB_SHA}"' in revision_check
assert "git status --porcelain=v1 --untracked-files=all" in revision_check
def test_release_workflow_smokes_before_requesting_cloud_identity() -> None:
workflow = WORKFLOW.read_text(encoding="utf-8")
smoke = workflow.index("Smoke exact no-send OCI surface")
prepare = workflow.index("Prepare exact candidate context")
initialize = workflow.index("Initialize pre-mutation publish outcome")
auth = workflow.index("Authenticate to Google Cloud through WIF")
push = workflow.index("Build and push one immutable candidate")
finalize = workflow.index("Finalize and validate release v3 bundle")
cleanup = workflow.index("Remove ephemeral Docker authentication")
complete = workflow.index("Finalize publish outcome")
upload = workflow.index("Upload exact available build and release evidence")
assert smoke < prepare < initialize < auth < push < finalize < cleanup < complete < upload
assert "scripts/run_leoclean_nosend_runtime_canary.sh" in workflow
assert "scripts/run_gcp_leoclean_nosend_oci_smoke.py" in workflow
assert 'assert smoke["input_sha256"] == image_input["input_sha256"]' in workflow
assert 'assert smoke["cleanup"]["status"] == "pass"' in workflow
def test_release_workflow_uses_exact_wif_artifact_builder_without_repository_secrets() -> None:
workflow = load_workflow()
text = WORKFLOW.read_text(encoding="utf-8")
env = workflow["env"]
assert env["PROJECT_ID"] == "teleo-501523"
assert env["REGION"] == "europe-west6"
assert env["ARTIFACT_REPOSITORY"] == "teleo"
assert env["IMAGE_REPOSITORY"] == package.IMAGE_REPOSITORY
assert env["WORKLOAD_IDENTITY_PROVIDER"] == (
"projects/785938879453/locations/global/workloadIdentityPools/github-actions/providers/living-ip-github"
)
assert env["ARTIFACT_SERVICE_ACCOUNT"] == "sa-artifact-builder@teleo-501523.iam.gserviceaccount.com"
assert "${{ secrets." not in text
assert "password" not in text.casefold()
auth = workflow_step("Authenticate to Google Cloud through WIF")
assert auth["uses"] == "google-github-actions/auth@c200f3691d83b41bf9bbd8638997a462592937ed"
assert auth["with"] == {
"workload_identity_provider": "${{ env.WORKLOAD_IDENTITY_PROVIDER }}",
"service_account": "${{ env.ARTIFACT_SERVICE_ACCOUNT }}",
"create_credentials_file": "true",
"export_environment_variables": "true",
}
def test_release_workflow_keeps_runner_temp_out_of_job_environment() -> None:
workflow = load_workflow()
jobs = workflow["jobs"]
assert isinstance(jobs, dict)
publish = jobs["publish"]
assert isinstance(publish, dict)
root_env = workflow["env"]
job_env = publish.get("env", {})
assert isinstance(root_env, dict)
assert isinstance(job_env, dict)
assert all("runner.temp" not in value for value in [*root_env.values(), *job_env.values()])
initialize = workflow_step("Initialize private release paths")["run"]
assert 'release_root="${RUNNER_TEMP}/leoclean-nosend-release"' in initialize
def test_release_workflow_requires_immutable_repository_and_package_owned_candidate_push() -> None:
workflow = WORKFLOW.read_text(encoding="utf-8")
immutable = workflow_step("Require immutable fixed Artifact Registry repository")["run"]
push = workflow_step("Build and push one immutable candidate")["run"]
assert "gcloud artifacts repositories describe" in immutable
assert 'repository["dockerConfig"]["immutableTags"] is True' in immutable
assert "ops/gcp_leoclean_nosend_package.py build-push" in push
assert "--execute-staging-push" in push
assert "--docker-binary /usr/bin/docker" in push
assert "--docker-host unix:///var/run/docker.sock" in push
assert '--gcloud-binary "$(command -v gcloud)"' in push
assert '--outcome-journal "${PUBLISH_OUTCOME}"' in push
assert "docker push" not in workflow
assert "docker tag" not in workflow
assert "candidate-" not in workflow
def test_release_workflow_always_finalizes_journal_and_uploads_only_allowlisted_evidence() -> None:
finalize = workflow_step("Finalize and validate release v3 bundle")["run"]
cleanup = workflow_step("Remove ephemeral Docker authentication")
complete = workflow_step("Finalize publish outcome")
upload = workflow_step("Upload exact available build and release evidence")
paths = upload["with"]["path"].splitlines()
assert "ops/gcp_leoclean_nosend_package.py finalize" in finalize
assert "ops/gcp_leoclean_nosend_package.py validate" in finalize
assert 'assert release["schema"] == "livingip.leocleanNoSendRelease.v3"' in finalize
assert cleanup["if"] == "always()"
assert complete["if"] == "always() && steps.init_outcome.outcome == 'success'"
assert "ops/gcp_leoclean_nosend_package.py complete-outcome" in complete["run"]
assert '--build-outcome "${{ steps.build_push.outcome }}"' in complete["run"]
assert '--finalize-outcome "${{ steps.finalize.outcome }}"' in complete["run"]
assert '--cleanup-outcome "${{ steps.cleanup.outcome }}"' in complete["run"]
assert upload["if"] == "always()"
assert paths == [
"${{ runner.temp }}/leoclean-nosend-release/evidence/runtime-receipt.json",
"${{ runner.temp }}/leoclean-nosend-release/evidence/oci-smoke.json",
"${{ runner.temp }}/leoclean-nosend-release/evidence/publish-outcome.json",
"${{ runner.temp }}/leoclean-nosend-release/receipts/build-push-receipt.json",
"${{ runner.temp }}/leoclean-nosend-release/final/release-v3/release.json",
"${{ runner.temp }}/leoclean-nosend-release/final/release-v3/leoclean-gcp-nosend.service",
]
assert upload["with"]["if-no-files-found"] == "warn"
assert upload["with"]["retention-days"] == "30"
assert "*" not in upload["with"]["path"]
assert "google-github-actions" not in upload["with"]["path"]
def test_release_workflow_has_no_deployment_or_runtime_mutation_surface() -> None:
workflow = WORKFLOW.read_text(encoding="utf-8").casefold()
forbidden = (
"--execute-restart",
"install_gcp_leoclean_nosend_release.py",
"gcloud compute",
"gcloud sql",
"gcloud secrets",
"kubectl",
"systemctl",
"terraform apply",
)
assert all(marker not in workflow for marker in forbidden)