Skip to content

Commit f5899cd

Browse files
committed
Use Secret Manager for Cloud Run runtime secrets
1 parent f13cbd5 commit f5899cd

7 files changed

Lines changed: 236 additions & 27 deletions

.github/scripts/cloud_run_env.sh

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@ cloud_run_set_defaults() {
1313
CLOUD_RUN_MIN_INSTANCES="${CLOUD_RUN_MIN_INSTANCES:-0}"
1414
CLOUD_RUN_MAX_INSTANCES="${CLOUD_RUN_MAX_INSTANCES:-1}"
1515
CLOUD_RUN_PORT="${CLOUD_RUN_PORT:-8080}"
16+
CLOUD_RUN_POLICYENGINE_DB_PASSWORD_SECRET="${CLOUD_RUN_POLICYENGINE_DB_PASSWORD_SECRET:-policyengine-api-prod-db-password:latest}"
17+
CLOUD_RUN_GITHUB_MICRODATA_TOKEN_SECRET="${CLOUD_RUN_GITHUB_MICRODATA_TOKEN_SECRET:-policyengine-api-prod-github-microdata-token:latest}"
18+
CLOUD_RUN_ANTHROPIC_API_KEY_SECRET="${CLOUD_RUN_ANTHROPIC_API_KEY_SECRET:-policyengine-api-prod-anthropic-api-key:latest}"
19+
CLOUD_RUN_OPENAI_API_KEY_SECRET="${CLOUD_RUN_OPENAI_API_KEY_SECRET:-policyengine-api-prod-openai-api-key:latest}"
20+
CLOUD_RUN_HUGGING_FACE_TOKEN_SECRET="${CLOUD_RUN_HUGGING_FACE_TOKEN_SECRET:-policyengine-api-prod-hugging-face-token:latest}"
1621

1722
local sha
1823
sha="${GITHUB_SHA:-local}"
@@ -35,6 +40,11 @@ cloud_run_set_defaults() {
3540
export CLOUD_RUN_MIN_INSTANCES
3641
export CLOUD_RUN_MAX_INSTANCES
3742
export CLOUD_RUN_PORT
43+
export CLOUD_RUN_POLICYENGINE_DB_PASSWORD_SECRET
44+
export CLOUD_RUN_GITHUB_MICRODATA_TOKEN_SECRET
45+
export CLOUD_RUN_ANTHROPIC_API_KEY_SECRET
46+
export CLOUD_RUN_OPENAI_API_KEY_SECRET
47+
export CLOUD_RUN_HUGGING_FACE_TOKEN_SECRET
3848
export CLOUD_RUN_IMAGE_TAG
3949
export CLOUD_RUN_IMAGE_URI
4050
export CLOUD_RUN_TAG

.github/scripts/deploy_cloud_run_candidate.sh

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,6 @@ env_vars=(
1111
"POLICYENGINE_DB_INSTANCE_CONNECTION_NAME=${CLOUD_RUN_CLOUD_SQL_INSTANCE}"
1212
"POLICYENGINE_DB_USER=${POLICYENGINE_DB_USER:-policyengine}"
1313
"POLICYENGINE_DB_NAME=${POLICYENGINE_DB_NAME:-policyengine}"
14-
"POLICYENGINE_DB_PASSWORD=${POLICYENGINE_DB_PASSWORD}"
15-
"POLICYENGINE_GITHUB_MICRODATA_AUTH_TOKEN=${POLICYENGINE_GITHUB_MICRODATA_AUTH_TOKEN}"
16-
"ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY}"
17-
"OPENAI_API_KEY=${OPENAI_API_KEY}"
18-
"HUGGING_FACE_TOKEN=${HUGGING_FACE_TOKEN}"
1914
"SIMULATION_API_URL=${SIMULATION_API_URL}"
2015
"GATEWAY_AUTH_REQUIRED=1"
2116
"GATEWAY_AUTH_ISSUER=${GATEWAY_AUTH_ISSUER}"
@@ -28,7 +23,16 @@ env_vars=(
2823
"CLOUD_RUN_REVISION_TAG=${CLOUD_RUN_TAG}"
2924
)
3025

26+
secret_vars=(
27+
"POLICYENGINE_DB_PASSWORD=${CLOUD_RUN_POLICYENGINE_DB_PASSWORD_SECRET}"
28+
"POLICYENGINE_GITHUB_MICRODATA_AUTH_TOKEN=${CLOUD_RUN_GITHUB_MICRODATA_TOKEN_SECRET}"
29+
"ANTHROPIC_API_KEY=${CLOUD_RUN_ANTHROPIC_API_KEY_SECRET}"
30+
"OPENAI_API_KEY=${CLOUD_RUN_OPENAI_API_KEY_SECRET}"
31+
"HUGGING_FACE_TOKEN=${CLOUD_RUN_HUGGING_FACE_TOKEN_SECRET}"
32+
)
33+
3134
set_env_vars="$(IFS='|'; echo "^|^${env_vars[*]}")"
35+
set_secret_vars="$(IFS='|'; echo "^|^${secret_vars[*]}")"
3236

3337
cloud_run_run gcloud run deploy "${CLOUD_RUN_SERVICE}" \
3438
--project "${CLOUD_RUN_PROJECT}" \
@@ -47,4 +51,5 @@ cloud_run_run gcloud run deploy "${CLOUD_RUN_SERVICE}" \
4751
--timeout "${CLOUD_RUN_TIMEOUT}" \
4852
--min-instances "${CLOUD_RUN_MIN_INSTANCES}" \
4953
--max-instances "${CLOUD_RUN_MAX_INSTANCES}" \
50-
--set-env-vars "${set_env_vars}"
54+
--set-env-vars "${set_env_vars}" \
55+
--set-secrets "${set_secret_vars}"

.github/scripts/validate_cloud_run_deploy_env.sh

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,11 @@ cloud_run_require_env \
1414
CLOUD_RUN_TAG \
1515
CLOUD_RUN_RUNTIME_SERVICE_ACCOUNT \
1616
CLOUD_RUN_CLOUD_SQL_INSTANCE \
17-
POLICYENGINE_DB_PASSWORD \
18-
POLICYENGINE_GITHUB_MICRODATA_AUTH_TOKEN \
19-
ANTHROPIC_API_KEY \
20-
OPENAI_API_KEY \
21-
HUGGING_FACE_TOKEN \
17+
CLOUD_RUN_POLICYENGINE_DB_PASSWORD_SECRET \
18+
CLOUD_RUN_GITHUB_MICRODATA_TOKEN_SECRET \
19+
CLOUD_RUN_ANTHROPIC_API_KEY_SECRET \
20+
CLOUD_RUN_OPENAI_API_KEY_SECRET \
21+
CLOUD_RUN_HUGGING_FACE_TOKEN_SECRET \
2222
SIMULATION_API_URL \
2323
GATEWAY_AUTH_ISSUER \
2424
GATEWAY_AUTH_AUDIENCE \

.github/workflows/push.yml

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -253,11 +253,6 @@ jobs:
253253
CLOUD_RUN_RUNTIME_SERVICE_ACCOUNT: ${{ secrets.GCP_CLOUD_RUN_RUNTIME_SERVICE_ACCOUNT }}
254254
POLICYENGINE_DB_USER: ${{ vars.POLICYENGINE_DB_USER }}
255255
POLICYENGINE_DB_NAME: ${{ vars.POLICYENGINE_DB_NAME }}
256-
POLICYENGINE_DB_PASSWORD: ${{ secrets.POLICYENGINE_DB_PASSWORD }}
257-
POLICYENGINE_GITHUB_MICRODATA_AUTH_TOKEN: ${{ secrets.POLICYENGINE_GITHUB_MICRODATA_AUTH_TOKEN }}
258-
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
259-
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
260-
HUGGING_FACE_TOKEN: ${{ secrets.HUGGING_FACE_TOKEN }}
261256
SIMULATION_API_URL: ${{ secrets.SIMULATION_API_URL }}
262257
GATEWAY_AUTH_ISSUER: ${{ secrets.GATEWAY_AUTH_ISSUER }}
263258
GATEWAY_AUTH_AUDIENCE: ${{ secrets.GATEWAY_AUTH_AUDIENCE }}
@@ -491,11 +486,6 @@ jobs:
491486
CLOUD_RUN_RUNTIME_SERVICE_ACCOUNT: ${{ secrets.GCP_CLOUD_RUN_RUNTIME_SERVICE_ACCOUNT }}
492487
POLICYENGINE_DB_USER: ${{ vars.POLICYENGINE_DB_USER }}
493488
POLICYENGINE_DB_NAME: ${{ vars.POLICYENGINE_DB_NAME }}
494-
POLICYENGINE_DB_PASSWORD: ${{ secrets.POLICYENGINE_DB_PASSWORD }}
495-
POLICYENGINE_GITHUB_MICRODATA_AUTH_TOKEN: ${{ secrets.POLICYENGINE_GITHUB_MICRODATA_AUTH_TOKEN }}
496-
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
497-
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
498-
HUGGING_FACE_TOKEN: ${{ secrets.HUGGING_FACE_TOKEN }}
499489
SIMULATION_API_URL: ${{ secrets.SIMULATION_API_URL }}
500490
GATEWAY_AUTH_ISSUER: ${{ secrets.GATEWAY_AUTH_ISSUER }}
501491
GATEWAY_AUTH_AUDIENCE: ${{ secrets.GATEWAY_AUTH_AUDIENCE }}
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
name: Sync Cloud Run secrets
2+
3+
on:
4+
workflow_dispatch:
5+
6+
concurrency:
7+
group: cloud-run-secret-sync
8+
9+
jobs:
10+
sync-cloud-run-secrets:
11+
name: Sync GitHub secrets to Secret Manager
12+
runs-on: ubuntu-latest
13+
environment: production
14+
permissions:
15+
contents: read
16+
id-token: write
17+
steps:
18+
- name: Require master branch
19+
if: github.ref != 'refs/heads/master'
20+
run: |
21+
echo "::error::Cloud Run secret sync must run from master."
22+
exit 1
23+
- name: Checkout repo
24+
uses: actions/checkout@v4
25+
- name: GCP authentication
26+
uses: "google-github-actions/auth@v2"
27+
with:
28+
workload_identity_provider: "${{ secrets.GCP_WORKLOAD_IDENTITY_PROVIDER }}"
29+
service_account: "${{ secrets.GCP_DEPLOY_SERVICE_ACCOUNT }}"
30+
- name: Set up GCloud
31+
uses: "google-github-actions/setup-gcloud@v2"
32+
- name: Sync runtime secrets
33+
env:
34+
CLOUD_RUN_PROJECT: policyengine-api
35+
CLOUD_RUN_RUNTIME_SERVICE_ACCOUNT: ${{ secrets.GCP_CLOUD_RUN_RUNTIME_SERVICE_ACCOUNT }}
36+
POLICYENGINE_DB_PASSWORD: ${{ secrets.POLICYENGINE_DB_PASSWORD }}
37+
POLICYENGINE_GITHUB_MICRODATA_AUTH_TOKEN: ${{ secrets.POLICYENGINE_GITHUB_MICRODATA_AUTH_TOKEN }}
38+
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
39+
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
40+
HUGGING_FACE_TOKEN: ${{ secrets.HUGGING_FACE_TOKEN }}
41+
run: |
42+
set -euo pipefail
43+
set +x
44+
45+
require_env() {
46+
local env_name="$1"
47+
if [[ -z "${!env_name:-}" ]]; then
48+
echo "::error::Missing required workflow environment ${env_name}."
49+
exit 1
50+
fi
51+
}
52+
53+
sync_secret() {
54+
local env_name="$1"
55+
local secret_name="$2"
56+
local secret_value="${!env_name:-}"
57+
58+
if [[ -z "${secret_value}" ]]; then
59+
echo "::error::Missing required GitHub secret ${env_name}."
60+
exit 1
61+
fi
62+
63+
if ! gcloud secrets describe "${secret_name}" \
64+
--project "${CLOUD_RUN_PROJECT}" >/dev/null 2>&1; then
65+
gcloud secrets create "${secret_name}" \
66+
--project "${CLOUD_RUN_PROJECT}" \
67+
--replication-policy automatic
68+
fi
69+
70+
printf '%s' "${secret_value}" | gcloud secrets versions add \
71+
"${secret_name}" \
72+
--project "${CLOUD_RUN_PROJECT}" \
73+
--data-file=- >/dev/null
74+
75+
gcloud secrets add-iam-policy-binding "${secret_name}" \
76+
--project "${CLOUD_RUN_PROJECT}" \
77+
--member "serviceAccount:${CLOUD_RUN_RUNTIME_SERVICE_ACCOUNT}" \
78+
--role roles/secretmanager.secretAccessor >/dev/null
79+
80+
echo "Synced ${env_name} to Secret Manager secret ${secret_name}."
81+
}
82+
83+
require_env CLOUD_RUN_RUNTIME_SERVICE_ACCOUNT
84+
85+
sync_secret POLICYENGINE_DB_PASSWORD policyengine-api-prod-db-password
86+
sync_secret POLICYENGINE_GITHUB_MICRODATA_AUTH_TOKEN policyengine-api-prod-github-microdata-token
87+
sync_secret ANTHROPIC_API_KEY policyengine-api-prod-anthropic-api-key
88+
sync_secret OPENAI_API_KEY policyengine-api-prod-openai-api-key
89+
sync_secret HUGGING_FACE_TOKEN policyengine-api-prod-hugging-face-token

docs/migration-pr3-cloud-run-candidate-runbook.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ does not migrate the public App Engine/custom API URL.
1111
production tracks, then promoted to the Cloud Run service URL after tests.
1212
- Runtime environment configuration for the production Cloud SQL instance and
1313
the existing simulation gateway.
14+
- Secret Manager-backed Cloud Run runtime credentials, synced manually from
15+
existing GitHub Actions secrets.
1416
- A dedicated Cloud Run runtime service account, separate from the GitHub deploy
1517
service account used to run `gcloud`.
1618
- The same live staging integration suite against both the App Engine staging
@@ -29,6 +31,8 @@ does not migrate the public App Engine/custom API URL.
2931
- No native FastAPI route migration beyond `/health`.
3032
- No Supabase, Alembic, SQLAlchemy model, or Modal compute migration.
3133
- No managed Redis, Redis Memorystore, or API v2-alpha-style cache replacement.
34+
- No App Engine secret-handling migration; App Engine deploys still use the
35+
existing transitional bundle path.
3236
- No App Engine retirement.
3337

3438
## Resource Defaults
@@ -42,6 +46,12 @@ does not migrate the public App Engine/custom API URL.
4246
- Cloud SQL instance: `policyengine-api:us-central1:policyengine-api-data`
4347
- Staging revision tag: `stage3-staging-${GITHUB_RUN_NUMBER}-${GITHUB_SHA::7}`
4448
- Production revision tag: `stage3-prod-${GITHUB_RUN_NUMBER}-${GITHUB_SHA::7}`
49+
- Secret Manager secrets:
50+
- `policyengine-api-prod-db-password`
51+
- `policyengine-api-prod-github-microdata-token`
52+
- `policyengine-api-prod-anthropic-api-key`
53+
- `policyengine-api-prod-openai-api-key`
54+
- `policyengine-api-prod-hugging-face-token`
4555

4656
## Required Runtime IAM
4757

@@ -53,11 +63,38 @@ The runtime service account must be:
5363

5464
- granted Cloud SQL client access for
5565
`policyengine-api:us-central1:policyengine-api-data`;
66+
- allowed to read the five Cloud Run runtime secrets listed above;
5667
- allowed to read the Secret Manager secret referenced by
5768
`GATEWAY_AUTH_CLIENT_SECRET_RESOURCE`;
5869
- allowed as a service account user for the GitHub deploy service account, so the
5970
workflow can deploy revisions using that runtime identity.
6071

72+
The manual `Sync Cloud Run secrets` workflow authenticates through Workload
73+
Identity Federation as `${{ secrets.GCP_DEPLOY_SERVICE_ACCOUNT }}`. That deploy
74+
service account must be able to create the five secrets if missing, add secret
75+
versions, and grant the Cloud Run runtime service account Secret Manager access
76+
on those secrets.
77+
78+
## Secret Sync
79+
80+
Run `.github/workflows/sync-cloud-run-secrets.yml` manually from `master` before
81+
the first Cloud Run deployment that uses Secret Manager references, and again
82+
whenever one of the source GitHub secrets is rotated.
83+
84+
The workflow copies these existing GitHub secrets into Secret Manager:
85+
86+
- `POLICYENGINE_DB_PASSWORD`
87+
- `POLICYENGINE_GITHUB_MICRODATA_AUTH_TOKEN`
88+
- `ANTHROPIC_API_KEY`
89+
- `OPENAI_API_KEY`
90+
- `HUGGING_FACE_TOKEN`
91+
92+
The workflow writes secret payloads to `gcloud secrets versions add` through
93+
stdin and does not print secret values. GitHub Actions remains the temporary
94+
source of truth in PR 3. The long-term target is to create, rotate, and manage
95+
these credentials directly in Secret Manager, with GitHub Actions only deploying
96+
Secret Manager references.
97+
6198
## Post-Merge Flow
6299

63100
The `Push` workflow now uses two deployment tracks.

tests/unit/test_cloud_run_deploy_scripts.py

Lines changed: 84 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,22 @@
1111
DEDICATED_CLOUD_RUN_RUNTIME_SERVICE_ACCOUNT = (
1212
"policyengine-api-cr-runtime@policyengine-api.iam.gserviceaccount.com"
1313
)
14+
CLOUD_RUN_SECRET_MAPPINGS = {
15+
"POLICYENGINE_DB_PASSWORD": "policyengine-api-prod-db-password:latest",
16+
"POLICYENGINE_GITHUB_MICRODATA_AUTH_TOKEN": (
17+
"policyengine-api-prod-github-microdata-token:latest"
18+
),
19+
"ANTHROPIC_API_KEY": "policyengine-api-prod-anthropic-api-key:latest",
20+
"OPENAI_API_KEY": "policyengine-api-prod-openai-api-key:latest",
21+
"HUGGING_FACE_TOKEN": "policyengine-api-prod-hugging-face-token:latest",
22+
}
23+
RAW_CLOUD_RUN_SECRET_VALUES = (
24+
"raw-db-secret-value",
25+
"raw-github-secret-value",
26+
"raw-anthropic-secret-value",
27+
"raw-openai-secret-value",
28+
"raw-hf-secret-value",
29+
)
1430

1531

1632
def _script_env(**overrides: str) -> dict[str, str]:
@@ -25,11 +41,11 @@ def _script_env(**overrides: str) -> dict[str, str]:
2541

2642
def _required_runtime_env() -> dict[str, str]:
2743
return {
28-
"POLICYENGINE_DB_PASSWORD": "db-password",
29-
"POLICYENGINE_GITHUB_MICRODATA_AUTH_TOKEN": "github-token",
30-
"ANTHROPIC_API_KEY": "anthropic-key",
31-
"OPENAI_API_KEY": "openai-key",
32-
"HUGGING_FACE_TOKEN": "hf-token",
44+
"POLICYENGINE_DB_PASSWORD": "raw-db-secret-value",
45+
"POLICYENGINE_GITHUB_MICRODATA_AUTH_TOKEN": ("raw-github-secret-value"),
46+
"ANTHROPIC_API_KEY": "raw-anthropic-secret-value",
47+
"OPENAI_API_KEY": "raw-openai-secret-value",
48+
"HUGGING_FACE_TOKEN": "raw-hf-secret-value",
3349
"SIMULATION_API_URL": "https://simulation.example.test",
3450
"GATEWAY_AUTH_ISSUER": "https://issuer.example.test",
3551
"GATEWAY_AUTH_AUDIENCE": "simulation-gateway",
@@ -55,6 +71,12 @@ def _push_workflow() -> str:
5571
return (REPO / ".github/workflows/push.yml").read_text(encoding="utf-8")
5672

5773

74+
def _sync_secrets_workflow() -> str:
75+
return (REPO / ".github/workflows/sync-cloud-run-secrets.yml").read_text(
76+
encoding="utf-8"
77+
)
78+
79+
5880
def _workflow_job_block(workflow: str, job_name: str) -> str:
5981
match = re.search(
6082
rf"^ {re.escape(job_name)}:\n(?P<body>.*?)(?=^ [a-zA-Z0-9_-]+:|\Z)",
@@ -118,8 +140,9 @@ def test_validate_cloud_run_deploy_env_reports_missing_runtime_config():
118140

119141
assert result.returncode == 1
120142
assert "Missing required Cloud Run deployment configuration" in result.stderr
121-
assert "POLICYENGINE_DB_PASSWORD" in result.stderr
143+
assert "SIMULATION_API_URL" in result.stderr
122144
assert "GATEWAY_AUTH_CLIENT_SECRET_RESOURCE" in result.stderr
145+
assert "POLICYENGINE_DB_PASSWORD" not in result.stderr
123146

124147

125148
def test_build_cloud_run_image_dry_run_uses_cloud_run_dockerfile():
@@ -168,6 +191,11 @@ def test_deploy_cloud_run_candidate_dry_run_never_shifts_traffic():
168191
f"POLICYENGINE_DB_INSTANCE_CONNECTION_NAME={PRODUCTION_CLOUD_SQL_INSTANCE}"
169192
in result.stdout
170193
)
194+
assert "--set-secrets" in result.stdout
195+
for env_name, secret_ref in CLOUD_RUN_SECRET_MAPPINGS.items():
196+
assert f"{env_name}={secret_ref}" in result.stdout
197+
for raw_secret_value in RAW_CLOUD_RUN_SECRET_VALUES:
198+
assert raw_secret_value not in result.stdout
171199
assert "CLOUD_RUN_INTERNAL_PROBES" not in result.stdout
172200
assert "--to-latest" not in result.stdout
173201
assert "update-traffic" not in result.stdout
@@ -278,6 +306,56 @@ def test_push_workflow_uses_dedicated_cloud_run_runtime_service_account():
278306
assert deploy_account_secret not in cloud_run_production
279307

280308

309+
def test_push_workflow_does_not_pass_raw_secrets_to_cloud_run_deploy_jobs():
310+
workflow = _push_workflow()
311+
cloud_run_staging = _workflow_job_block(workflow, "deploy-cloud-run-staging")
312+
cloud_run_production = _workflow_job_block(workflow, "deploy-cloud-run-candidate")
313+
raw_secret_envs = (
314+
"POLICYENGINE_DB_PASSWORD: ${{ secrets.POLICYENGINE_DB_PASSWORD }}",
315+
(
316+
"POLICYENGINE_GITHUB_MICRODATA_AUTH_TOKEN: "
317+
"${{ secrets.POLICYENGINE_GITHUB_MICRODATA_AUTH_TOKEN }}"
318+
),
319+
"ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}",
320+
"OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}",
321+
"HUGGING_FACE_TOKEN: ${{ secrets.HUGGING_FACE_TOKEN }}",
322+
)
323+
324+
for raw_secret_env in raw_secret_envs:
325+
assert raw_secret_env not in cloud_run_staging
326+
assert raw_secret_env not in cloud_run_production
327+
328+
329+
def test_sync_cloud_run_secrets_workflow_is_manual_and_environment_gated():
330+
workflow = _sync_secrets_workflow()
331+
332+
assert "workflow_dispatch:" in workflow
333+
assert "pull_request:" not in workflow
334+
assert "push:" not in workflow
335+
assert "environment: production" in workflow
336+
assert "id-token: write" in workflow
337+
assert "github.ref != 'refs/heads/master'" in workflow
338+
assert "google-github-actions/auth@v2" in workflow
339+
assert (
340+
'workload_identity_provider: "${{ secrets.GCP_WORKLOAD_IDENTITY_PROVIDER }}"'
341+
in workflow
342+
)
343+
assert 'service_account: "${{ secrets.GCP_DEPLOY_SERVICE_ACCOUNT }}"' in workflow
344+
345+
346+
def test_sync_cloud_run_secrets_workflow_writes_expected_secret_versions():
347+
workflow = _sync_secrets_workflow()
348+
349+
assert "set +x" in workflow
350+
assert "--data-file=-" in workflow
351+
assert "gcloud secrets add-iam-policy-binding" in workflow
352+
assert "roles/secretmanager.secretAccessor" in workflow
353+
for env_name, secret_ref in CLOUD_RUN_SECRET_MAPPINGS.items():
354+
secret_name = secret_ref.removesuffix(":latest")
355+
assert f"{env_name}: ${{{{ secrets.{env_name} }}}}" in workflow
356+
assert f"sync_secret {env_name} {secret_name}" in workflow
357+
358+
281359
def test_push_workflow_promotes_production_cloud_run_after_candidate_smoke():
282360
workflow = _push_workflow()
283361
cloud_run_production = _workflow_job_block(workflow, "deploy-cloud-run-candidate")

0 commit comments

Comments
 (0)