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
2 changes: 1 addition & 1 deletion .github/request-simulation-model-versions.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ set -euo pipefail
# versions are optional compatibility checks that should resolve to the same
# gateway app as the .py bundle route.

GATEWAY_URL="${SIMULATION_API_URL:-https://policyengine--policyengine-simulation-gateway-web-app.modal.run}"
GATEWAY_URL="${SIMULATION_ENTRYPOINT_URL:-https://policyengine--policyengine-simulation-gateway-web-app.modal.run}"

usage() {
echo "Usage: $0 -py <policyengine_version> [-us <us_version>] [-uk <uk_version>]"
Expand Down
46 changes: 46 additions & 0 deletions .github/scripts/capture_cloud_run_service_state.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#!/usr/bin/env bash

# Capture the stable URL and sole 100%-serving revision before a candidate
# deployment. Promotion consumes this revision as its optimistic concurrency
# guard; rollback consumes it as the exact restoration target.

set -euo pipefail

source .github/scripts/cloud_run_env.sh
cloud_run_set_defaults

cloud_run_require_env \
CLOUD_RUN_PROJECT \
CLOUD_RUN_REGION \
CLOUD_RUN_SERVICE

if [[ "${CLOUD_RUN_DRY_RUN:-0}" == "1" ]]; then
echo "stable_url=https://${CLOUD_RUN_SERVICE}-dry-run.a.run.app"
echo "revision=${CLOUD_RUN_SERVICE}-00001-dry"
exit 0
fi

gcloud_bin="${GCLOUD_BIN:-gcloud}"
service_json="$("${gcloud_bin}" run services describe "${CLOUD_RUN_SERVICE}" \
--project "${CLOUD_RUN_PROJECT}" \
--region "${CLOUD_RUN_REGION}" \
--platform managed \
--format=json)"

stable_url="$(jq -er '
.status.url
| select(type == "string" and length > 0)
' <<<"${service_json}")"
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}")"

printf 'stable_url=%s\nrevision=%s\n' "${stable_url}" "${revision}"
5 changes: 3 additions & 2 deletions .github/scripts/deploy_cloud_run_candidate.sh
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,15 @@ env_vars=(
"POLICYENGINE_DB_INSTANCE_CONNECTION_NAME=${CLOUD_RUN_CLOUD_SQL_INSTANCE}"
"POLICYENGINE_DB_USER=${POLICYENGINE_DB_USER:-policyengine}"
"POLICYENGINE_DB_NAME=${POLICYENGINE_DB_NAME:-policyengine}"
"SIMULATION_API_URL=${SIMULATION_API_URL}"
"SIMULATION_ENTRYPOINT_URL=${SIMULATION_ENTRYPOINT_URL}"
"OLD_SIMULATION_GATEWAY_URL=${OLD_SIMULATION_GATEWAY_URL}"
"GATEWAY_AUTH_REQUIRED=1"
"GATEWAY_AUTH_ISSUER=${GATEWAY_AUTH_ISSUER}"
"GATEWAY_AUTH_AUDIENCE=${GATEWAY_AUTH_AUDIENCE}"
"GATEWAY_AUTH_CLIENT_ID=${GATEWAY_AUTH_CLIENT_ID}"
"GATEWAY_AUTH_CLIENT_SECRET_RESOURCE=${GATEWAY_AUTH_CLIENT_SECRET_RESOURCE}"
"API_HOST_BACKEND=cloud_run"
"SIM_FRONT_DOOR=old_gateway_direct"
"SIM_ENTRYPOINT=${SIM_ENTRYPOINT:-old_gateway_direct}"
"SIM_COMPUTE_ECONOMY=old_gateway"
"CLOUD_RUN_REVISION_TAG=${CLOUD_RUN_TAG}"
"WEB_CONCURRENCY=${CLOUD_RUN_WEB_CONCURRENCY}"
Expand Down
17 changes: 0 additions & 17 deletions .github/scripts/get_cloud_run_service_url.sh

This file was deleted.

31 changes: 0 additions & 31 deletions .github/scripts/get_cloud_run_tag_url.sh

This file was deleted.

18 changes: 0 additions & 18 deletions .github/scripts/promote_cloud_run_tag.sh

This file was deleted.

95 changes: 95 additions & 0 deletions .github/scripts/resolve_cloud_run_candidate_state.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
#!/usr/bin/env bash

# Resolve a no-traffic tag once, then pin all later testing and promotion to
# the exact ready revision and immutable image represented by that tag.

set -euo pipefail

source .github/scripts/cloud_run_env.sh
cloud_run_set_defaults

cloud_run_require_env \
CLOUD_RUN_PROJECT \
CLOUD_RUN_REGION \
CLOUD_RUN_SERVICE \
CLOUD_RUN_TAG

if [[ "${CLOUD_RUN_DRY_RUN:-0}" == "1" ]]; then
echo "url=https://${CLOUD_RUN_TAG}---${CLOUD_RUN_SERVICE}-dry-run.a.run.app"
echo "revision=${CLOUD_RUN_SERVICE}-00002-dry"
echo "image=${CLOUD_RUN_IMAGE_URI%@*}@sha256:dry-run"
exit 0
fi

gcloud_bin="${GCLOUD_BIN:-gcloud}"
service_json="$("${gcloud_bin}" run services describe "${CLOUD_RUN_SERVICE}" \
--project "${CLOUD_RUN_PROJECT}" \
--region "${CLOUD_RUN_REGION}" \
--platform managed \
--format=json)"
candidate_json="$(jq -cer --arg tag "${CLOUD_RUN_TAG}" '
[
.status.traffic[]?
| select(.tag == $tag)
| {
url: (
.url
| select(type == "string" and length > 0)
),
revision: (
.revisionName
| select(type == "string" and length > 0)
)
}
]
| if length == 1 then .[0]
else error("candidate tag must resolve to exactly one traffic target")
end
' <<<"${service_json}")"
url="$(jq -er '.url' <<<"${candidate_json}")"
revision="$(jq -er '.revision' <<<"${candidate_json}")"

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

if [[ "${revision_service}" != "${CLOUD_RUN_SERVICE}" ]]; then
printf 'Revision %s belongs to %s, not %s\n' \
"${revision}" "${revision_service:-an unknown service}" \
"${CLOUD_RUN_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' "${revision}" >&2
exit 2
fi

image="$(jq -er '
(.status.imageDigest // .spec.containers[0].image)
| select(type == "string" and contains("@sha256:"))
' <<<"${revision_json}")"

if [[ -n "${CLOUD_RUN_EXPECTED_REVISION:-}" \
&& "${revision}" != "${CLOUD_RUN_EXPECTED_REVISION}" ]]; then
printf 'Candidate tag %s moved: expected revision %s, found %s\n' \
"${CLOUD_RUN_TAG}" "${CLOUD_RUN_EXPECTED_REVISION}" "${revision}" >&2
exit 2
fi

if [[ -n "${CLOUD_RUN_EXPECTED_IMAGE:-}" \
&& "${image}" != "${CLOUD_RUN_EXPECTED_IMAGE}" ]]; then
printf 'Candidate image changed: expected %s, found %s\n' \
"${CLOUD_RUN_EXPECTED_IMAGE}" "${image}" >&2
exit 2
fi

printf 'url=%s\nrevision=%s\nimage=%s\n' "${url}" "${revision}" "${image}"
113 changes: 113 additions & 0 deletions .github/scripts/set_cloud_run_revision.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
#!/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. Rollback uses the same script
# with the previous and candidate revisions swapped.

set -euo pipefail

source .github/scripts/cloud_run_env.sh
cloud_run_set_defaults

cloud_run_require_env \
CLOUD_RUN_PROJECT \
CLOUD_RUN_REGION \
CLOUD_RUN_SERVICE \
CLOUD_RUN_TARGET_REVISION \
CLOUD_RUN_EXPECTED_CURRENT_REVISION

target_revision="${CLOUD_RUN_TARGET_REVISION}"
expected_current_revision="${CLOUD_RUN_EXPECTED_CURRENT_REVISION}"

for revision in "${target_revision}" "${expected_current_revision}"; do
case "${revision}" in
[Ll][Aa][Tt][Ee][Ss][Tt])
echo "Cloud Run traffic targets must be exact; LATEST is not allowed" >&2
exit 2
;;
esac
done

gcloud_bin="${GCLOUD_BIN:-gcloud}"

if [[ "${CLOUD_RUN_DRY_RUN:-0}" == "1" ]]; then
cloud_run_run "${gcloud_bin}" run services update-traffic \
"${CLOUD_RUN_SERVICE}" \
--project "${CLOUD_RUN_PROJECT}" \
--region "${CLOUD_RUN_REGION}" \
--platform managed \
--to-revisions "${target_revision}=100"
exit 0
fi

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 "${CLOUD_RUN_SERVICE}" \
--project "${CLOUD_RUN_PROJECT}" \
--region "${CLOUD_RUN_REGION}" \
--platform managed \
--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 "${CLOUD_RUN_PROJECT}" \
--region "${CLOUD_RUN_REGION}" \
--platform managed \
--format=json)"
revision_service="$(jq -r \
'.metadata.labels["serving.knative.dev/service"] // empty' \
<<<"${revision_json}")"

if [[ "${revision_service}" != "${CLOUD_RUN_SERVICE}" ]]; then
printf 'Revision %s belongs to %s, not %s\n' \
"${target_revision}" "${revision_service:-an unknown service}" \
"${CLOUD_RUN_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 "${CLOUD_RUN_SERVICE}" \
--project "${CLOUD_RUN_PROJECT}" \
--region "${CLOUD_RUN_REGION}" \
--platform managed \
--to-revisions "${target_revision}=100"

updated_service_json="$("${gcloud_bin}" run services describe \
"${CLOUD_RUN_SERVICE}" \
--project "${CLOUD_RUN_PROJECT}" \
--region "${CLOUD_RUN_REGION}" \
--platform managed \
--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
4 changes: 3 additions & 1 deletion .github/scripts/validate_app_engine_deploy_env.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
set -euo pipefail

required=(
SIMULATION_API_URL
SIMULATION_ENTRYPOINT_URL
OLD_SIMULATION_GATEWAY_URL
SIM_ENTRYPOINT
GATEWAY_AUTH_ISSUER
GATEWAY_AUTH_AUDIENCE
GATEWAY_AUTH_CLIENT_ID
Expand Down
3 changes: 2 additions & 1 deletion .github/scripts/validate_cloud_run_deploy_env.sh
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ cloud_run_require_env \
CLOUD_RUN_ANTHROPIC_API_KEY_SECRET \
CLOUD_RUN_OPENAI_API_KEY_SECRET \
CLOUD_RUN_HUGGING_FACE_TOKEN_SECRET \
SIMULATION_API_URL \
SIMULATION_ENTRYPOINT_URL \
OLD_SIMULATION_GATEWAY_URL \
GATEWAY_AUTH_ISSUER \
GATEWAY_AUTH_AUDIENCE \
GATEWAY_AUTH_CLIENT_ID \
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ jobs:
run: sudo apt-get install -y jq
- name: Check simulation API supports updated PolicyEngine bundle
env:
SIMULATION_API_URL: ${{ secrets.SIMULATION_API_URL }}
SIMULATION_ENTRYPOINT_URL: ${{ secrets.SIMULATION_ENTRYPOINT_URL }}
run: bash .github/check-policyengine-bundle-supported.sh --if-changed-from-base "${{ github.base_ref }}"

lint:
Expand Down
Loading
Loading