Skip to content

Install the Cloud SQL migration and household mirror-event workflows from uv.lock - #4080

Merged
anth-volk merged 5 commits into
masterfrom
operational-workflows-from-lock
Nov 3, 2026
Merged

anth-volk merged 5 commits into
masterfrom
operational-workflows-from-lock

Conversation

@anth-volk

@anth-volk anth-volk commented Oct 27, 2026 •

Copy link
Copy Markdown
Collaborator

Fixes #4079

Summary

The Public API's two operational database workflows now install from uv.lock instead of an unlocked pip install -e ".[dev]", and their exceptions to api.dependencies.install-from-lock are removed.

  • migrate-v1-cloud-sql.yml (job migrate, also called by push.yml before each staging and production deployment): sets up uv 0.13.4 with astral-sh/setup-uv@v6 (the version the Cloud Run image, the quality-guards job and the versioning job use), sets UV_PYTHON: "3.12", and installs with uv sync --frozen. migrate_v1_cloud_sql.sh runs its three v1_database_migration.py commands (--mode state, --mode upgrade, --mode verify-head) through uv run --frozen.
  • process-v1-household-mirror-event.yml (job process-event): the same setup-uv step, UV_PYTHON and uv sync --frozen. run_household_mirror_event_operator.sh runs process_v1_household_mirror_event.py through uv run --frozen, with the same POLICYENGINE_DB_PROXY_PORT and POLICYENGINE_DB_PASSWORD prefix.
  • Runtime dependencies only. Both jobs install without the dev extra, as the Cloud Run image (uv sync --frozen --no-dev) and seed-v2-database.yml do. Both scripts, the Alembic v1 environment and the v2 household writer they load import in a fresh environment built with uv sync --frozen and no extras.
  • rules/registry.toml: the two [[rule.exception]] entries for these workflows are removed, so the exception list only shrinks; docs/generated/rules.md is regenerated.
  • Docs: docs/migration/stage-11-v2-households.md says a local run of the operator script now needs uv.

This changes rules/registry.toml, so it needs approval from a core developer other than the author (api.process.rule-machinery-code-owned). No inline suppressions (# noqa) are added.

Environment variables

uv run passes its whole environment to the command it runs, and reads no .env file unless asked to. Everything the bare python received before still reaches the scripts. The new tests check this with a fake uv on PATH that records the environment it receives.

Variable Provider Wiring Read by
DEPLOYMENT_ENVIRONMENT workflow input deployment_environment job env: validate_database_environment.sh; the event command
POLICYENGINE_DB_INSTANCE_CONNECTION_NAME environment variable in staging-database, production-database, staging and production job env: from vars proxy start, validation, backup, the event command
PRODUCTION_POLICYENGINE_DB_INSTANCE_CONNECTION_NAME repository variable job env: from vars validation; the event command
POLICYENGINE_DB_READONLY_PASSWORD_SECRET, POLICYENGINE_DB_MIGRATION_PASSWORD_SECRET environment variables in all four environments job env: from vars the scripts' gcloud secrets versions access; validation
PRODUCTION_POLICYENGINE_DB_READONLY_PASSWORD_SECRET, PRODUCTION_POLICYENGINE_DB_MIGRATION_PASSWORD_SECRET repository variables job env: from vars validation
POLICYENGINE_DB_READONLY_PASSWORD, POLICYENGINE_DB_MIGRATION_PASSWORD Google Secret Manager, fetched by migrate_v1_cloud_sql.sh exported by the script, inherited through uv run v1_database_migration.py
STAGE7_EXISTING_DATABASE_URL, ALEMBIC_DATABASE_URL not declared by the workflow unset by migrate_v1_cloud_sql.sh before uv run v1_database_migration.py, migrations/v1/env.py (only when set)
V2_SUPABASE_PROJECT_REF environment variable in staging and production job env: from vars validation; the event command
PRODUCTION_V2_SUPABASE_PROJECT_REF repository variable job env: from vars validation; the event command
V2_SUPABASE_ENVIRONMENT workflow input deployment_environment job env: validation; the event command
V2_RUNTIME_DATABASE_URL environment secret V2_DATA_WRITE_DATABASE_URL in staging and production job env: from secrets the v2 household writer
HOUSEHOLD_MIRROR_OPERATOR_IDENTITY repository variable GCP_DB_MIGRATION_SERVICE_ACCOUNT job env: from vars operator script; the event command
ALLOWED_HOUSEHOLD_MIRROR_OPERATOR_IDENTITY environment variable HOUSEHOLD_MIRROR_OPERATOR_IDENTITY in staging and production job env: from vars operator script; the event command
POLICYENGINE_DB_PROXY_PORT, POLICYENGINE_DB_PASSWORD the operator script (the password from Google Secret Manager) command prefix on uv run, inherited by the command the event command
UV_PYTHON (new) the workflow, "3.12" job env: uv sync and the scripts' uv run
GCP_WORKLOAD_IDENTITY_PROVIDER repository secret with: on google-github-actions/auth; push.yml passes secrets: inherit authentication
GCP_DB_MIGRATION_SERVICE_ACCOUNT repository variable with: on google-github-actions/auth authentication
GOOGLE_APPLICATION_CREDENTIALS and the other credential variables google-github-actions/auth, written to GITHUB_ENV later steps gcloud, the Cloud SQL Auth Proxy
RUNNER_TEMP, GITHUB_ACTIONS, GITHUB_SHA, GITHUB_RUN_ID, GITHUB_RUN_ATTEMPT the runner default environment proxy scripts, password masking, backup description

Every secret and variable name above exists in the named place (checked with gh api). The staging variable values meet the validation scripts' conditions: the staging instance, password secrets and Supabase project differ from production, and HOUSEHOLD_MIRROR_OPERATOR_IDENTITY equals GCP_DB_MIGRATION_SERVICE_ACCOUNT. Secret values cannot be read through the API, so whether V2_DATA_WRITE_DATABASE_URL meets the v2 runtime loader's requirements (for example, not the transaction pooler port 6543) is unverified. The household workflow's first GitHub Actions run is described below.

Staging runs before merge

Both workflows were dispatched from this branch at f29abfa (with master merged in) on 2026-11-03.

  • migrate-v1-cloud-sql.yml, run 41713976356, deployment_environment=staging, github_environment=staging-database: passed. uv used CPython 3.12.15 and installed 169 packages from uv.lock. Upgrade and verify v1 database logged Detected v1 database state: head, so the run took no backup and made no upgrade, and --mode verify-head passed. It ran after release run 41713013425 had finished its own staging v1 migration, since the workflow has no concurrency group.
  • process-v1-household-mirror-event.yml, run 41714851692, deployment_environment=staging, country_id=us, legacy_household_id=0: exited 1, as expected for an ID with no retained event. Staging has no pending event: its logs for the last 60 days show 26 immediate copies, all "outcome": "ok", and the copy records don't include household IDs. This was the workflow's first GitHub Actions run.
    • What ran: the locked install (CPython 3.12.16, 169 packages), workload-identity authentication as the migration service account, the Cloud SQL Auth Proxy, the environment validation, the migration password fetch and the Cloud SQL query. The copy record reports "failure_category": "integrity" (no retained event) rather than "database", and the operator record names the staging instance policyengine-api:us-central1:policyengine-api-data-staging.
    • The printed error type is HouseholdMirrorUnavailableError, which wraps the HouseholdMirrorEventIntegrityError, not HouseholdMirrorEventIntegrityError itself as this description first said.
    • Not exercised: the v2 write, and so V2_DATA_WRITE_DATABASE_URL. Only a pending event reaches them, so they're first used when a pending staging or production event is processed.
    • The run's log records went to the job's output, not to Cloud Logging, so staging's household-copy monitoring has no record of it.

Testing

Written test-first, as two pairs of commits:

Commit Content Result
aca85c597 removes the Cloud SQL migration exception; tests require the locked install and uv run --frozen 9 failed, 45 passed; locked-installs failed
ded8e4ec4 the migration workflow and script 54 passed; locked-installs passed
873fba8fe removes the household exception; new behaviour tests for the operator script 5 failed, 56 passed; locked-installs failed
2ada2957c the household workflow and script, doc, changelog 61 passed; locked-installs passed

(Tests run: tests/unit/test_alembic_workflows.py, tests/unit/test_locked_test_environments.py, tests/unit/test_cloud_run_workflow_configuration.py and, from 873fba8fe, tests/unit/test_household_mirror_event_operator_script.py, through uv run --frozen --extra dev pytest.)

At the final commit:

  • ruff format --check . and ruff check .: passed
  • python scripts/run_quality_guards.py: every guard passed, including locked-installs and ci-scripts
  • python -m scripts.rules guard --base-ref origin/master: passed
  • python -m scripts.rules render: no diff
  • uv lock --check: passed
  • shellcheck on both changed scripts: passed
  • python -m unittest discover -s scripts/rules/tests -t .: 142 tests passed
  • Every test file that references .github/workflows or .github/scripts, plus tests/unit/test_household_event_command.py: 223 passed. tests/unit/telemetry_error_issues/test_workflow_and_wrapper.py needs the telemetry-error-issues group, as make test adds it; with it, 22 passed.

🤖 Generated with Claude Code

anth-volk and others added 4 commits October 27, 2026 21:11
Remove the migrate-v1-cloud-sql.yml exception from
api.dependencies.install-from-lock, and change the workflow tests to
require that the job installs with uv sync --frozen on Python 3.12, with
the pinned uv version, and that migrate_v1_cloud_sql.sh runs each
v1_database_migration.py command through uv run --frozen.

The migration script tests now put a fake uv on PATH. It fails unless it
receives the passwords fetched from Secret Manager and none of the
job-level database URLs the script unsets.

These tests and the locked-installs guard fail until the workflow and
script change in the next commit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The migrate job sets up uv 0.13.4 (the version the Cloud Run image and the
release job use) with astral-sh/setup-uv, sets UV_PYTHON to 3.12, and
installs the runtime dependencies with uv sync --frozen, replacing the
unlocked pip install -e ".[dev]". migrate_v1_cloud_sql.sh runs each
v1_database_migration.py command through uv run --frozen.

uv run passes the job and script environment to the command unchanged, so
the passwords the script fetches and exports still reach the migration
commands, and the unset job-level database URLs stay unset.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Remove the process-v1-household-mirror-event.yml exception from
api.dependencies.install-from-lock, the last operational job registered
as installing unlocked, and apply the locked install, Python version and
uv version checks to that job too.

Add behaviour tests for run_household_mirror_event_operator.sh, which had
only text checks. Fake gcloud and uv commands record their calls and the
environment uv receives. The success test requires the command to run
through uv run --frozen with the fetched migration password, the proxy
port and every job-level variable the event command reads. The other
tests cover an unauthorized operator, a staging run that targets the
production Cloud SQL instance, and an empty password.

The success test, the job checks and the locked-installs guard fail until
the workflow and script change in the next commit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The process-event job sets up uv 0.13.4 with astral-sh/setup-uv, sets
UV_PYTHON to 3.12, and installs the runtime dependencies with
uv sync --frozen, replacing the unlocked pip install -e ".[dev]".
run_household_mirror_event_operator.sh runs
process_v1_household_mirror_event.py through uv run --frozen, with the
same POLICYENGINE_DB_PROXY_PORT and POLICYENGINE_DB_PASSWORD prefix; uv
run passes them and the job environment to the command unchanged.

The household migration guide says a local run of the operator script
now needs uv.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@codecov

codecov Bot commented Oct 27, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 88.22%. Comparing base (42697cb) to head (f29abfa).
⚠️ Report is 3 commits behind head on master.

Additional details and impacted files
@@             Coverage Diff             @@
##           master    #4080       +/-   ##
===========================================
+ Coverage   52.87%   88.22%   +35.34%     
===========================================
  Files         273      285       +12     
  Lines       15647    16843     +1196     
  Branches     2505     2741      +236     
===========================================
+ Hits         8274    14859     +6585     
+ Misses       6680     1302     -5378     
+ Partials      693      682       -11     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@anth-volk
anth-volk deployed to staging-database November 3, 2026 20:35 — with GitHub Actions Active
@anth-volk
anth-volk marked this pull request as ready for review November 3, 2026 20:54
@anth-volk
anth-volk requested a review from a team as a code owner November 3, 2026 20:54
@anth-volk
anth-volk removed the request for review from a team November 3, 2026 20:54
@anth-volk
anth-volk merged commit 4780e28 into master Nov 3, 2026
23 of 24 checks passed
@anth-volk
anth-volk deleted the operational-workflows-from-lock branch November 3, 2026 21:17

This branch had an error being deployed

1 failed and 1 active deployments
staging — f29abfa9 Deployed Nov 3, 2026 by anth-volk via Process one retained household event #1
staging-database — f29abfa9 Deployed Nov 3, 2026 by anth-volk via Upgrade and verify v1 Cloud SQL #2
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Install the Cloud SQL migration and household mirror-event workflows from uv.lock

1 participant