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
15 changes: 11 additions & 4 deletions .github/workflows/push.yml
Original file line number Diff line number Diff line change
Expand Up @@ -89,11 +89,18 @@ jobs:
uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Setup uv
uses: astral-sh/setup-uv@v6
with:
version: "0.12.1"
- name: Install changelog tool
run: pip install towncrier
- name: Bump project version
run: python .github/bump_version.py
- name: Refresh uv lock
run: uv lock
- name: Build changelog
run: |
pip install towncrier
python .github/bump_version.py
towncrier build --yes --version "$(python -c "import re; print(re.search(r'version = \"(.+?)\"', open('pyproject.toml').read()).group(1))")"
run: towncrier build --yes --version "$(python -c "import re; print(re.search(r'version = \"(.+?)\"', open('pyproject.toml').read()).group(1))")"
- name: Preview changelog update
run: ".github/get-changelog-diff.sh"
- name: Update changelog
Expand Down
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,5 @@ format:

changelog:
python .github/bump_version.py
uv lock
towncrier build --yes --version $$(python -c "import re; print(re.search(r'version = \"(.+?)\"', open('pyproject.toml').read()).group(1))")
1 change: 1 addition & 0 deletions changelog.d/3858.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Install the Cloud Run API environment from the checked-in uv lock so image rebuilds retain the reviewed dependency versions.
4 changes: 4 additions & 0 deletions gcp/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ image to the staging and production Cloud Run services. The image runs Gunicorn
through `gcp/cloud_run/start.sh`; it does not install or launch Redis and has no
localhost cache fallback.

The image installs its Python environment with
`uv sync --frozen --no-dev --no-editable` from the checked-in `uv.lock`.
Dependency versions can therefore change only when that lock file changes.

Cloud Run injects the database password, external-service credentials,
Memorystore URL, and Memorystore CA through revision-specific Secret Manager
bindings. Required cache settings are `RUNTIME_CACHE_MODE`, `RUNTIME_CACHE_URL`,
Expand Down
12 changes: 9 additions & 3 deletions gcp/cloud_run/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,18 +1,24 @@
FROM python:3.12-slim

COPY --from=ghcr.io/astral-sh/uv:0.12.1 /uv /uvx /bin/

RUN apt-get update \
&& apt-get install -y --no-install-recommends build-essential \
&& rm -rf /var/lib/apt/lists/*

ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1 \
UV_PROJECT_ENVIRONMENT=/opt/venv \
PATH="/opt/venv/bin:${PATH}"

WORKDIR /app

COPY pyproject.toml README.md ./
COPY pyproject.toml uv.lock README.md ./
COPY policyengine_api ./policyengine_api
COPY gcp/cloud_run/start.sh ./start.sh

RUN chmod +x ./start.sh \
&& pip install --no-cache-dir --upgrade pip \
&& pip install --no-cache-dir -e .
&& uv sync --frozen --no-dev --no-editable

ENV GATEWAY_AUTH_REQUIRED=1

Expand Down
1 change: 1 addition & 0 deletions gcp/cloud_run/Dockerfile.dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

!README.md
!pyproject.toml
!uv.lock
!policyengine_api/
!policyengine_api/**
!gcp/
Expand Down
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ dependencies = [
"policyengine-ng==0.5.1",
"policyengine-il==0.1.0",
"policyengine[models]==5.2.0",
# Cloud Run installs with pip, which does not read uv.lock. Keep the
# calculator compatible with the country model in this bundle.
# Keep the calculator compatible with the country model in this bundle.
# The Cloud Run image installs this exact selection from uv.lock.
"spm-calculator==0.3.1",
"pydantic",
"pymysql",
Expand Down
22 changes: 20 additions & 2 deletions tests/unit/test_alembic_workflows.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
from __future__ import annotations

import os
from pathlib import Path
import subprocess
from pathlib import Path

import pytest


REPO = Path(__file__).resolve().parents[2]


Expand Down Expand Up @@ -105,6 +104,25 @@ def test_push_requires_schema_and_v2_integration_checks_before_versioning():
assert "github.repository == 'PolicyEngine/policyengine-uk'" not in workflow


def test_versioning_refreshes_the_lock_after_bumping_the_project_version():
workflow = _workflow("push.yml")
versioning_job = workflow[workflow.index(" versioning:") :]
versioning_job = versioning_job[: versioning_job.index("\n publish-git-tag:")]
makefile = (REPO / "Makefile").read_text(encoding="utf-8")
changelog_target = makefile[makefile.index("changelog:") :]

assert "uses: astral-sh/setup-uv@v6" in versioning_job
assert 'version: "0.12.1"' in versioning_job
assert versioning_job.index("python .github/bump_version.py") < (
versioning_job.index("uv lock")
)
assert versioning_job.index("uv lock") < versioning_job.index("towncrier build")
assert changelog_target.index("python .github/bump_version.py") < (
changelog_target.index("uv lock")
)
assert changelog_target.index("uv lock") < changelog_target.index("towncrier build")


def test_release_migration_uses_the_installed_python_environment():
workflow = _workflow("migrate-v1-cloud-sql.yml")
orchestration_script = (
Expand Down
17 changes: 16 additions & 1 deletion tests/unit/test_cloud_run_deploy_scripts.py
Original file line number Diff line number Diff line change
Expand Up @@ -526,8 +526,23 @@ def test_cloud_run_dockerfile_runs_startup_with_bash():
assert 'CMD ["/bin/sh", "/app/start.sh"]' not in dockerfile


def test_cloud_run_dockerfile_installs_the_frozen_uv_environment():
dockerfile = (REPO / "gcp/cloud_run/Dockerfile").read_text(encoding="utf-8")
dockerignore = (REPO / "gcp/cloud_run/Dockerfile.dockerignore").read_text(
encoding="utf-8"
)

assert "COPY --from=ghcr.io/astral-sh/uv:" in dockerfile
assert "UV_PROJECT_ENVIRONMENT=/opt/venv" in dockerfile
assert 'PATH="/opt/venv/bin:${PATH}"' in dockerfile
assert "COPY pyproject.toml uv.lock README.md ./" in dockerfile
assert "uv sync --frozen --no-dev --no-editable" in dockerfile
assert "pip install" not in dockerfile
assert "!uv.lock" in dockerignore


def test_active_images_pin_spm_calculator_for_the_legacy_country_bundle():
"""Both pip build paths must protect the current bundle's SPM behavior."""
"""Both deployed images must protect the current bundle's SPM behavior."""
import tomllib

project = tomllib.loads((REPO / "pyproject.toml").read_text())
Expand Down
2 changes: 1 addition & 1 deletion uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading