diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fd752f9..23d482f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -54,6 +54,7 @@ jobs: tests/test_agent_routing.py \ tests/test_decision_engine_replay.py \ tests/test_evaluate_agent_routing.py \ + tests/test_gcp_artifact_workflow.py \ tests/test_gcp_cloudsql_restore_drill.py \ tests/test_gcp_cloudsql_restore_readback.py \ tests/test_gcp_iam_split_apply.py \ diff --git a/.github/workflows/gcp-artifact.yml b/.github/workflows/gcp-artifact.yml index 04dff76..a512de5 100644 --- a/.github/workflows/gcp-artifact.yml +++ b/.github/workflows/gcp-artifact.yml @@ -60,10 +60,14 @@ jobs: . docker run --rm "${image_uri}" - docker push "${image_uri}" + docker push "${image_uri}" | tee docker-push.log + image_digest="$(awk '/digest: sha256:/ {print $3}' docker-push.log | tail -1)" + test -n "${image_digest}" { echo "image_uri=${image_uri}" + echo "image_digest=${image_digest}" + echo "image_ref=${image_uri}@${image_digest}" echo "revision=${GITHUB_SHA}" } > gcp-artifact-image.txt diff --git a/.github/workflows/gcp-readiness.yml b/.github/workflows/gcp-readiness.yml index ac71aff..a299983 100644 --- a/.github/workflows/gcp-readiness.yml +++ b/.github/workflows/gcp-readiness.yml @@ -104,3 +104,13 @@ jobs: name: gcp-readiness path: ${{ env.READINESS_ARTIFACT_DIR }}/ if-no-files-found: error + + - name: Enforce readiness result + if: always() + shell: bash + run: | + code="$(cat "${READINESS_ARTIFACT_DIR}/gcp-infra-readiness.exitcode" 2>/dev/null || echo 1)" + if [ "${code}" != "0" ]; then + echo "GCP readiness checker failed with exit code ${code}. See the uploaded gcp-readiness artifact for exact failed checks." + exit "${code}" + fi diff --git a/ops/check_gcp_infra_readiness.py b/ops/check_gcp_infra_readiness.py index 49b30ee..68fdac9 100755 --- a/ops/check_gcp_infra_readiness.py +++ b/ops/check_gcp_infra_readiness.py @@ -185,6 +185,8 @@ def check_github_actions_artifact_ci() -> Check: GITHUB_ARTIFACT_SERVICE_ACCOUNT, "docker run --rm", "docker push", + "image_digest=", + "image_ref=", ] absent = [item for item in required_text if item not in text] if absent: diff --git a/tests/test_gcp_artifact_workflow.py b/tests/test_gcp_artifact_workflow.py new file mode 100644 index 0000000..56b33b5 --- /dev/null +++ b/tests/test_gcp_artifact_workflow.py @@ -0,0 +1,11 @@ +from pathlib import Path + + +def test_gcp_artifact_workflow_retains_immutable_image_digest() -> None: + workflow = Path(".github/workflows/gcp-artifact.yml").read_text() + + assert "docker push" in workflow + assert "tee docker-push.log" in workflow + assert "image_digest=" in workflow + assert "image_ref=" in workflow + assert "test -n \"${image_digest}\"" in workflow diff --git a/tests/test_gcp_readiness_workflow.py b/tests/test_gcp_readiness_workflow.py index 0454440..57772f4 100644 --- a/tests/test_gcp_readiness_workflow.py +++ b/tests/test_gcp_readiness_workflow.py @@ -9,4 +9,6 @@ def test_gcp_readiness_workflow_uses_uploadable_artifact_dir() -> None: assert "READINESS_ARTIFACT_DIR: gcp-readiness-artifacts" in workflow assert 'mkdir -p "${READINESS_ARTIFACT_DIR}"' in workflow assert "path: ${{ env.READINESS_ARTIFACT_DIR }}/" in workflow + assert "Enforce readiness result" in workflow + assert "GCP readiness checker failed" in workflow assert "path: .gcp-readiness/" not in workflow