diff --git a/.github/workflows/_deploy_lambda.yml b/.github/workflows/_deploy_lambda.yml index bce793dcb..9f6b07beb 100644 --- a/.github/workflows/_deploy_lambda.yml +++ b/.github/workflows/_deploy_lambda.yml @@ -148,6 +148,7 @@ jobs: - name: Terraform Plan working-directory: ${{ inputs.lambda_path }} env: + TF_VAR_github_sha: ${{ github.sha }} TF_VAR_db_host: ${{ secrets.TF_VAR_db_host }} TF_VAR_db_name: ${{ secrets.TF_VAR_db_name }} TF_VAR_db_port: ${{ secrets.TF_VAR_db_port }} diff --git a/.github/workflows/check_lambda_zip_size.yml b/.github/workflows/check_lambda_zip_size.yml new file mode 100644 index 000000000..c65e9cfd2 --- /dev/null +++ b/.github/workflows/check_lambda_zip_size.yml @@ -0,0 +1,23 @@ +name: Check FastAPI Lambda package size + +# Runs on PRs targeting main so a Lambda-size regression fails the PR before +# it can merge to main and roll into the dev deploy — deploy_terraform.yml +# only triggers on push to dev/prod, which is too late (see PR #1469: this +# broke dev for weeks before anyone noticed). +on: + pull_request: + branches: + - main + +jobs: + fast_api_lambda_zip_size_check: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Check projected unzipped Lambda size + run: python3 backend/app/requirements/check_lambda_zip_size.py diff --git a/backend/app/main.py b/backend/app/main.py index 2904fb978..0dd9e489b 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -53,7 +53,9 @@ async def log_requests(request: Request, call_next): @app.get("/health") async def health(): - return {"status": "ok", "sha": os.getenv("GITHUB_SHA", "unknown")} + sha = os.getenv("GITHUB_SHA", "unknown") + logger.info(f"Health check OK - deployed sha={sha}") + return {"status": "ok", "sha": sha} app.include_router(tasks_router.router, prefix="/v1") diff --git a/deployment/terraform/lambda/fast-api/main.tf b/deployment/terraform/lambda/fast-api/main.tf index dea9b7d92..04d05af72 100644 --- a/deployment/terraform/lambda/fast-api/main.tf +++ b/deployment/terraform/lambda/fast-api/main.tf @@ -86,6 +86,7 @@ module "fastapi" { environment = { ENVIRONMENT = var.stage + GITHUB_SHA = var.github_sha API_KEY = var.api_key SECRET_KEY = var.secret_key # DOMAIN_NAME = var.domain_name @@ -110,11 +111,11 @@ module "fastapi" { CARBON_BASELINE_PREDICTIONS_BUCKET = data.terraform_remote_state.shared.outputs.retrofit_carbon_baseline_predictions_bucket_name HEAT_BASELINE_PREDICTIONS_BUCKET = data.terraform_remote_state.shared.outputs.retrofit_heat_baseline_predictions_bucket_name - ENGINE_SQS_URL = data.terraform_remote_state.engine.outputs.ara_engine_queue_url - CATEGORISATION_SQS_URL = data.terraform_remote_state.categorisation.outputs.categorisation_queue_url - POSTCODE_SPLITTER_SQS_URL = data.terraform_remote_state.postcode_splitter.outputs.postcode_splitter_queue_url - COMBINER_SQS_URL = data.terraform_remote_state.bulk_address2uprn_combiner.outputs.bulk_address2uprn_combiner_queue_url - FINALISER_SQS_URL = data.terraform_remote_state.bulk_upload_finaliser.outputs.bulk_upload_finaliser_queue_url + ENGINE_SQS_URL = data.terraform_remote_state.engine.outputs.ara_engine_queue_url + CATEGORISATION_SQS_URL = data.terraform_remote_state.categorisation.outputs.categorisation_queue_url + POSTCODE_SPLITTER_SQS_URL = data.terraform_remote_state.postcode_splitter.outputs.postcode_splitter_queue_url + COMBINER_SQS_URL = data.terraform_remote_state.bulk_address2uprn_combiner.outputs.bulk_address2uprn_combiner_queue_url + FINALISER_SQS_URL = data.terraform_remote_state.bulk_upload_finaliser.outputs.bulk_upload_finaliser_queue_url } } diff --git a/deployment/terraform/lambda/fast-api/variables.tf b/deployment/terraform/lambda/fast-api/variables.tf index a31575902..c421d2f57 100644 --- a/deployment/terraform/lambda/fast-api/variables.tf +++ b/deployment/terraform/lambda/fast-api/variables.tf @@ -41,4 +41,10 @@ variable "epc_auth_token" { variable "google_solar_api_key" { type = string sensitive = true -} \ No newline at end of file +} + +variable "github_sha" { + type = string + description = "Commit SHA being deployed, surfaced via /health so cloud logs can be tied back to a deploy." + default = "unknown" +} diff --git a/tests/test_lambda_zip_size.py b/tests/test_lambda_zip_size.py deleted file mode 100644 index a9cc89bca..000000000 --- a/tests/test_lambda_zip_size.py +++ /dev/null @@ -1,42 +0,0 @@ -"""Guard against the fastapi Lambda's unzipped package exceeding AWS's -262144000 byte (250 MiB) limit. - -dev deployed with `InvalidParameterValueException: Unzipped size must be -smaller than 262144000 bytes` for several weeks (2026-06-15 to 2026-07-03) -before anyone noticed, because the failure only ever surfaced deep inside -`terraform apply` on a push to dev/prod — well after a PR to main had merged. -This runs the same check as part of the PR-gated test suite so a regression -(e.g. a large fixture or dependency landing outside the Lambda's zip_excludes) -fails the PR instead of the dev deploy. - -Runs the real `check_lambda_zip_size.py` script as a subprocess (not an -in-process import) so this exercises exactly what a human or CI would run -locally: `python3 backend/app/requirements/check_lambda_zip_size.py`. -""" -from __future__ import annotations - -import subprocess -import sys -from pathlib import Path - -REPO_ROOT = Path(__file__).resolve().parents[1] -CHECK_SCRIPT = REPO_ROOT / "backend/app/requirements/check_lambda_zip_size.py" - - -def test_fastapi_lambda_unzipped_size_within_aws_limit() -> None: - result = subprocess.run( - [sys.executable, str(CHECK_SCRIPT)], - cwd=REPO_ROOT, - capture_output=True, - text=True, - ) - print(result.stdout) - assert result.returncode == 0, ( - "fastapi Lambda's projected unzipped size exceeds (or is dangerously " - "close to) AWS's 250 MiB limit — see output above. If this is a real " - "new dependency/fixture, either trim it or extend `zip_excludes` in " - "deployment/terraform/modules/lambda_with_api_gateway/variables.tf " - "(only if it's not needed by the Lambda at runtime).\n" - + result.stdout - + result.stderr - )