Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions .github/scripts/cloud-run-simulation-entry-smoke.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#!/usr/bin/env bash

set -euo pipefail

base_url="${1:?Simulation Entrypoint base URL is required}"
base_url="${base_url%/}"
expected_revision="${2:-}"

health_headers="$(mktemp)"
health_body="$(mktemp)"
trap 'rm -f "${health_headers}" "${health_body}"' EXIT

curl --fail --silent --show-error \
--dump-header "${health_headers}" \
--output "${health_body}" \
"${base_url}/health"
jq -e '.status == "healthy"' "${health_body}" >/dev/null

if [ -n "${expected_revision}" ]; then
actual_revision="$(
awk '
tolower($1) == "x-policyengine-simulation-revision:" {
gsub("\r", "", $2)
print $2
}
' "${health_headers}" |
tail -n 1
)"
if [ "${actual_revision}" != "${expected_revision}" ]; then
printf 'Expected revision %s at %s, received %s\n' \
"${expected_revision}" "${base_url}" "${actual_revision:-no revision header}" >&2
exit 1
fi
fi

curl --fail --silent --show-error "${base_url}/ready" |
jq -e '.status == "ready"' >/dev/null
curl --fail --silent --show-error "${base_url}/versions" >/dev/null
curl --fail --silent --show-error \
--request POST \
--header "Content-Type: application/json" \
--data '{"value": 1}' \
"${base_url}/ping" |
jq -e '.incremented == 2' >/dev/null
70 changes: 0 additions & 70 deletions .github/scripts/modal-deploy-app.sh

This file was deleted.

86 changes: 86 additions & 0 deletions .github/scripts/set-cloud-run-simulation-entry-revision.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
#!/usr/bin/env bash

# Assign all stable Cloud Run service traffic to one exact, ready revision.
# The expected-current guard prevents this workflow from overwriting a traffic
# change made after its candidate deployment. The same command supports
# rollback by swapping the target and expected-current revisions.

set -euo pipefail

gcloud_bin="${GCLOUD_BIN:-gcloud}"
project_id="${SIMULATION_ENTRYPOINT_GCP_PROJECT_ID:?SIMULATION_ENTRYPOINT_GCP_PROJECT_ID is required}"
region="${SIMULATION_ENTRYPOINT_GCP_REGION:-us-central1}"
service="${SIMULATION_ENTRYPOINT_SERVICE:?SIMULATION_ENTRYPOINT_SERVICE is required}"
target_revision="${SIMULATION_ENTRYPOINT_TARGET_REVISION:?SIMULATION_ENTRYPOINT_TARGET_REVISION is required}"
expected_current_revision="${SIMULATION_ENTRYPOINT_EXPECTED_CURRENT_REVISION:?SIMULATION_ENTRYPOINT_EXPECTED_CURRENT_REVISION is required}"

for revision in "${target_revision}" "${expected_current_revision}"; do
if [ "${revision}" = "LATEST" ] || [ "${revision}" = "latest" ]; then
printf 'Traffic revisions must be exact; LATEST is not allowed\n' >&2
exit 2
fi
done

active_revision() {
jq -er '
[
.status.traffic[]?
| select((.percent // 0) == 100)
| .revisionName
| select(type == "string" and length > 0)
]
| if length == 1 then .[0]
else error("service must have exactly one revision at 100 percent")
end
'
}

service_json="$("${gcloud_bin}" run services describe "${service}" \
--project "${project_id}" \
--region "${region}" \
--format=json)"
current_revision="$(active_revision <<<"${service_json}")"

if [ "${current_revision}" != "${expected_current_revision}" ]; then
printf 'Stable traffic changed after deployment: expected %s, found %s\n' \
"${expected_current_revision}" "${current_revision}" >&2
exit 2
fi

revision_json="$("${gcloud_bin}" run revisions describe "${target_revision}" \
--project "${project_id}" \
--region "${region}" \
--format=json)"
revision_service="$(jq -r \
'.metadata.labels["serving.knative.dev/service"] // empty' \
<<<"${revision_json}")"

if [ "${revision_service}" != "${service}" ]; then
printf 'Revision %s belongs to %s, not %s\n' \
"${target_revision}" "${revision_service:-an unknown service}" "${service}" >&2
exit 2
fi

if ! jq -e \
'.status.conditions[]? | select(.type == "Ready" and .status == "True")' \
>/dev/null <<<"${revision_json}"; then
printf 'Revision %s is not Ready\n' "${target_revision}" >&2
exit 2
fi

"${gcloud_bin}" run services update-traffic "${service}" \
--project "${project_id}" \
--region "${region}" \
--to-revisions "${target_revision}=100"

updated_service_json="$("${gcloud_bin}" run services describe "${service}" \
--project "${project_id}" \
--region "${region}" \
--format=json)"
updated_revision="$(active_revision <<<"${updated_service_json}")"

if [ "${updated_revision}" != "${target_revision}" ]; then
printf 'Traffic update did not activate %s; found %s\n' \
"${target_revision}" "${updated_revision}" >&2
exit 2
fi
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#!/bin/bash
# Generate deployment summary for GitHub Actions
# Usage: ./modal-deployment-summary.sh <beta-result> <beta-url> <prod-result> <prod-url>
# Usage: ./simulation-deployment-summary.sh <beta-result> <beta-candidate-url> <prod-result> <prod-stable-url>

set -euo pipefail

Expand All @@ -10,13 +10,13 @@ PROD_RESULT="${3:-skipped}"
PROD_URL="${4:-}"

{
echo "## Modal Deployment Summary"
echo "## Simulation API Deployment Summary"
echo ""

case "$BETA_RESULT" in
success)
echo "✅ **Beta deployment**: Success"
[ -n "$BETA_URL" ] && echo " - URL: $BETA_URL"
[ -n "$BETA_URL" ] && echo " - Candidate URL: $BETA_URL"
;;
skipped)
echo "⏭️ **Beta deployment**: Skipped"
Expand All @@ -30,14 +30,14 @@ PROD_URL="${4:-}"

case "$PROD_RESULT" in
success)
echo "✅ **Production deployment**: Success"
[ -n "$PROD_URL" ] && echo " - URL: $PROD_URL"
echo "✅ **Prod deployment**: Success"
[ -n "$PROD_URL" ] && echo " - Stable URL: $PROD_URL"
;;
skipped)
echo "⏭️ **Production deployment**: Skipped"
echo "⏭️ **Prod deployment**: Skipped"
;;
*)
echo "❌ **Production deployment**: $PROD_RESULT"
echo "❌ **Prod deployment**: $PROD_RESULT"
;;
esac
} >> "$GITHUB_STEP_SUMMARY"
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#!/bin/bash
# Run simulation integration tests
# Usage: ./modal-run-integ-tests.sh <environment> <base-url> [us-version] [uk-version]
# Run integration tests through the deployed Simulation Entrypoint.
# Usage: ./simulation-run-integ-tests.sh <environment> <base-url> [us-version] [uk-version]
# Environment: beta runs all tests, prod excludes beta_only tests

set -euo pipefail
Expand All @@ -17,15 +17,15 @@ truthy() {
esac
}

GATEWAY_AUTH_VARS=(
GATEWAY_AUTH_ISSUER
GATEWAY_AUTH_AUDIENCE
GATEWAY_AUTH_CLIENT_ID
GATEWAY_AUTH_CLIENT_SECRET
TEST_AUTH_VARS=(
SIMULATION_TEST_AUTH_ISSUER
SIMULATION_TEST_AUTH_AUDIENCE
SIMULATION_TEST_AUTH_CLIENT_ID
SIMULATION_TEST_AUTH_CLIENT_SECRET
)
present=()
missing=()
for var in "${GATEWAY_AUTH_VARS[@]}"; do
for var in "${TEST_AUTH_VARS[@]}"; do
if [ -n "${!var:-}" ]; then
present+=("$var")
else
Expand All @@ -34,36 +34,36 @@ for var in "${GATEWAY_AUTH_VARS[@]}"; do
done

if [ ${#present[@]} -gt 0 ] && [ ${#missing[@]} -gt 0 ]; then
echo "Gateway auth integration-test config is partial." >&2
echo "Simulation integration-test auth config is partial." >&2
echo " Present: ${present[*]-}" >&2
echo " Missing: ${missing[*]-}" >&2
exit 1
fi

SHOULD_MINT_TOKEN=0
if truthy "${GATEWAY_AUTH_REQUIRED:-}"; then
if truthy "${SIMULATION_TEST_AUTH_REQUIRED:-}"; then
if [ ${#missing[@]} -gt 0 ]; then
echo "GATEWAY_AUTH_REQUIRED is enabled but integration-test auth secrets are missing." >&2
echo "SIMULATION_TEST_AUTH_REQUIRED is enabled but auth secrets are missing." >&2
echo " Missing: ${missing[*]-}" >&2
exit 1
fi
SHOULD_MINT_TOKEN=1
elif [ ${#present[@]} -eq ${#GATEWAY_AUTH_VARS[@]} ]; then
elif [ ${#present[@]} -eq ${#TEST_AUTH_VARS[@]} ]; then
SHOULD_MINT_TOKEN=1
fi

ACCESS_TOKEN=""
if [ "$SHOULD_MINT_TOKEN" -eq 1 ]; then
ISSUER="${GATEWAY_AUTH_ISSUER%/}"
ISSUER="${SIMULATION_TEST_AUTH_ISSUER%/}"
TOKEN_URL="$ISSUER/oauth/token"

# Build the token-request JSON with Python so that any ", \, or newline in
# the client secret is encoded correctly (Auth0-generated secrets are random
# strings that routinely contain characters that break a shell heredoc).
TOKEN_REQUEST_JSON=$(
CLIENT_ID="$GATEWAY_AUTH_CLIENT_ID" \
CLIENT_SECRET="$GATEWAY_AUTH_CLIENT_SECRET" \
AUDIENCE="$GATEWAY_AUTH_AUDIENCE" \
CLIENT_ID="$SIMULATION_TEST_AUTH_CLIENT_ID" \
CLIENT_SECRET="$SIMULATION_TEST_AUTH_CLIENT_SECRET" \
AUDIENCE="$SIMULATION_TEST_AUTH_AUDIENCE" \
python3 -c '
import json, os
print(json.dumps({
Expand Down Expand Up @@ -100,12 +100,12 @@ print(token)
fi

cd projects/policyengine-apis-integ
uv sync --extra test
uv sync --extra test --frozen

export simulation_integ_test_base_url="$BASE_URL"

if [ -n "${GATEWAY_AUTH_REQUIRED:-}" ]; then
export simulation_integ_test_gateway_auth_required="$GATEWAY_AUTH_REQUIRED"
if [ -n "${SIMULATION_TEST_AUTH_REQUIRED:-}" ]; then
export simulation_integ_test_gateway_auth_required="$SIMULATION_TEST_AUTH_REQUIRED"
fi

if [ -n "$ACCESS_TOKEN" ]; then
Expand Down
Loading