From 409934a730a0e61ab41fc6a5a1400ab8089e02b4 Mon Sep 17 00:00:00 2001 From: Jun-te Kim Date: Mon, 6 Jul 2026 12:54:03 +0000 Subject: [PATCH 1/4] Fix fastapi lambda exceeding AWS's 250MB unzipped size limit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit dev deploys have failed since PR #1231 (2026-06-15) with InvalidParameterValueException: Unzipped size must be smaller than 262144000 bytes. The lambda zip is built from the whole repo root (deployment/terraform/modules/lambda_with_api_gateway) and only excluded tests/deployment/pycache — so .git (206MB packed), the per-schema corpus.jsonl fixtures under backend/epc_api/json_samples/ (~50MB), and the sap10_calculator PDF specs (~13MB) were all being zipped up even though none of them are read at runtime. Also add scripts/check_lambda_zip_size.py, which mirrors the terraform module's pip install + zip-with-excludes behaviour to report the projected unzipped size without mutating the repo, and wire it into deploy_terraform.yml as a fast_api_lambda_zip_size_check job that gates the real deploy so this fails fast with a clear message instead of surfacing as an opaque Terraform/AWS error. Co-Authored-By: Claude Sonnet 5 --- .github/workflows/deploy_terraform.yml | 28 +++- .../lambda_with_api_gateway/variables.tf | 5 +- scripts/check_lambda_zip_size.py | 149 ++++++++++++++++++ 3 files changed, 180 insertions(+), 2 deletions(-) create mode 100755 scripts/check_lambda_zip_size.py diff --git a/.github/workflows/deploy_terraform.yml b/.github/workflows/deploy_terraform.yml index 5e469dc66..8f2d405f4 100644 --- a/.github/workflows/deploy_terraform.yml +++ b/.github/workflows/deploy_terraform.yml @@ -534,11 +534,37 @@ jobs: TF_VAR_social_housing_wave_3_sharepoint_id: ${{ secrets.SOCIAL_HOUSING_WAVE_3_SHAREPOINT_ID }} + # ============================================================ + # Check FastAPI Lambda package will fit AWS's unzipped size limit + # ============================================================ + 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 scripts/check_lambda_zip_size.py \ + --source-dir . \ + --requirements backend/app/requirements/requirements.txt \ + --exclude "**/__pycache__/**" \ + --exclude "**/*.pyc" \ + --exclude "**/.pytest_cache/**" \ + --exclude "**/tests/**" \ + --exclude "**/deployment/**" \ + --exclude "**/.git/**" \ + --exclude "**/json_samples/**" \ + --exclude "**/docs/specs/**" + # ============================================================ # Deploy FastAPI Lambda # ============================================================ fast_api_lambda: - needs: [determine_stage, ara_engine_lambda, categorisation_lambda, postcodeSplitter_lambda, bulk_address2uprn_combiner_lambda, bulkUploadFinaliser_lambda] + needs: [determine_stage, ara_engine_lambda, categorisation_lambda, postcodeSplitter_lambda, bulk_address2uprn_combiner_lambda, bulkUploadFinaliser_lambda, fast_api_lambda_zip_size_check] uses: ./.github/workflows/_deploy_lambda.yml with: lambda_name: ara_fast_api diff --git a/deployment/terraform/modules/lambda_with_api_gateway/variables.tf b/deployment/terraform/modules/lambda_with_api_gateway/variables.tf index b5d0515a0..7576a4bf0 100644 --- a/deployment/terraform/modules/lambda_with_api_gateway/variables.tf +++ b/deployment/terraform/modules/lambda_with_api_gateway/variables.tf @@ -11,7 +11,10 @@ variable "zip_excludes" { "**/*.pyc", "**/.pytest_cache/**", "**/tests/**", - "**/deployment/**" + "**/deployment/**", + "**/.git/**", + "**/json_samples/**", + "**/docs/specs/**" ] } diff --git a/scripts/check_lambda_zip_size.py b/scripts/check_lambda_zip_size.py new file mode 100755 index 000000000..160ac4b63 --- /dev/null +++ b/scripts/check_lambda_zip_size.py @@ -0,0 +1,149 @@ +#!/usr/bin/env python3 +"""Estimate the unzipped size of a Terraform-packaged Lambda before deploying. + +Mirrors what `deployment/terraform/modules/lambda_with_api_gateway/main.tf` does: +`pip install -r -t ...` followed by zipping +`source_dir` minus `zip_excludes`. AWS Lambda's hard limit is 262144000 bytes +(250 MiB) unzipped. We install the dependencies into a scratch directory +instead of the real source tree so this can run locally or in CI without +mutating the repo. + +Usage: + python3 scripts/check_lambda_zip_size.py \ + --source-dir . \ + --requirements backend/app/requirements/requirements.txt \ + --exclude "**/tests/**" --exclude "**/.git/**" ... + +Exits non-zero if the projected unzipped size exceeds --limit (or exceeds +--warn-pct of the limit, when --strict is not passed the warning is just +printed). +""" +from __future__ import annotations + +import argparse +import fnmatch +import subprocess +import sys +import tempfile +from pathlib import Path + +LAMBDA_UNZIPPED_LIMIT_BYTES = 262_144_000 + + +def doublestar_match(pattern_parts: list[str], path_parts: list[str]) -> bool: + if not pattern_parts: + return not path_parts + head, rest = pattern_parts[0], pattern_parts[1:] + if head == "**": + if doublestar_match(rest, path_parts): + return True + if path_parts and doublestar_match(pattern_parts, path_parts[1:]): + return True + return False + if not path_parts: + return False + if fnmatch.fnmatchcase(path_parts[0], head): + return doublestar_match(rest, path_parts[1:]) + return False + + +def is_excluded(rel_path: Path, excludes: list[str]) -> bool: + parts = rel_path.as_posix().split("/") + return any(doublestar_match(pattern.split("/"), parts) for pattern in excludes) + + +def sum_dir_size(root: Path, excludes: list[str] | None = None) -> int: + total = 0 + for path in root.rglob("*"): + if path.is_dir(): + continue + rel = path.relative_to(root) + if excludes and is_excluded(rel, excludes): + continue + total += path.stat().st_size + return total + + +def install_dependencies(requirements: Path, target: Path) -> int: + subprocess.run( + [ + "pip", + "install", + "-r", + str(requirements), + "-t", + str(target), + "--platform", + "manylinux2014_x86_64", + "--implementation", + "cp", + "--python-version", + "3.11", + "--only-binary=:all:", + ], + check=True, + ) + return sum_dir_size(target) + + +def human(n: int) -> str: + return f"{n:,} bytes ({n / 1024 / 1024:.1f} MiB)" + + +def main() -> int: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--source-dir", type=Path, default=Path(".")) + parser.add_argument("--requirements", type=Path, default=None) + parser.add_argument("--exclude", action="append", default=[], dest="excludes") + parser.add_argument("--limit", type=int, default=LAMBDA_UNZIPPED_LIMIT_BYTES) + parser.add_argument( + "--warn-pct", + type=float, + default=85.0, + help="Print a warning once projected size crosses this %% of the limit.", + ) + parser.add_argument( + "--strict", + action="store_true", + help="Exit non-zero on the warning threshold too, not just the hard limit.", + ) + args = parser.parse_args() + + source_size = sum_dir_size(args.source_dir, args.excludes) + + deps_size = 0 + if args.requirements is not None: + with tempfile.TemporaryDirectory(prefix="lambda-deps-") as tmp: + deps_size = install_dependencies(args.requirements, Path(tmp)) + + total = source_size + deps_size + pct = total / args.limit * 100 + + print(f"source files (post-exclude): {human(source_size)}") + if args.requirements is not None: + print(f"pip dependencies: {human(deps_size)}") + print(f"projected unzipped total: {human(total)} [{pct:.1f}% of limit]") + print(f"AWS limit: {human(args.limit)}") + + if total > args.limit: + print( + f"\nFAIL: projected unzipped size exceeds the Lambda limit by " + f"{human(total - args.limit)}.", + file=sys.stderr, + ) + return 1 + + if pct >= args.warn_pct: + msg = ( + f"\nWARNING: projected unzipped size is at {pct:.1f}% of the " + f"{human(args.limit)} limit." + ) + print(msg, file=sys.stderr) + if args.strict: + return 1 + + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) From d5f1c61689ef513df7ab2c97aa3d5db039b11ad0 Mon Sep 17 00:00:00 2001 From: Jun-te Kim Date: Mon, 6 Jul 2026 13:00:59 +0000 Subject: [PATCH 2/4] Move zip-size checker next to the fastapi requirements it checks Relocate scripts/check_lambda_zip_size.py to backend/app/requirements/, right beside the requirements.txt it's built to measure, since it's specific to the fastapi lambda rather than a generic repo script. While moving it, also make the excludes list self-updating: the script now reads zip_excludes straight out of deployment/terraform/modules/lambda_with_api_gateway/variables.tf by default instead of duplicating it as CLI flags, so the CI check and the actual Terraform packaging can't drift apart. requirements.txt defaults to the sibling file too, so both the CI step and local runs now collapse to a bare `check_lambda_zip_size.py` invocation. Co-Authored-By: Claude Sonnet 5 --- .github/workflows/deploy_terraform.yml | 13 +--- .../requirements}/check_lambda_zip_size.py | 68 +++++++++++++++---- 2 files changed, 55 insertions(+), 26 deletions(-) rename {scripts => backend/app/requirements}/check_lambda_zip_size.py (63%) diff --git a/.github/workflows/deploy_terraform.yml b/.github/workflows/deploy_terraform.yml index 8f2d405f4..9907ebee3 100644 --- a/.github/workflows/deploy_terraform.yml +++ b/.github/workflows/deploy_terraform.yml @@ -547,18 +547,7 @@ jobs: python-version: "3.11" - name: Check projected unzipped Lambda size - run: | - python3 scripts/check_lambda_zip_size.py \ - --source-dir . \ - --requirements backend/app/requirements/requirements.txt \ - --exclude "**/__pycache__/**" \ - --exclude "**/*.pyc" \ - --exclude "**/.pytest_cache/**" \ - --exclude "**/tests/**" \ - --exclude "**/deployment/**" \ - --exclude "**/.git/**" \ - --exclude "**/json_samples/**" \ - --exclude "**/docs/specs/**" + run: python3 backend/app/requirements/check_lambda_zip_size.py # ============================================================ # Deploy FastAPI Lambda diff --git a/scripts/check_lambda_zip_size.py b/backend/app/requirements/check_lambda_zip_size.py similarity index 63% rename from scripts/check_lambda_zip_size.py rename to backend/app/requirements/check_lambda_zip_size.py index 160ac4b63..2ccd9a07a 100755 --- a/scripts/check_lambda_zip_size.py +++ b/backend/app/requirements/check_lambda_zip_size.py @@ -1,18 +1,22 @@ #!/usr/bin/env python3 -"""Estimate the unzipped size of a Terraform-packaged Lambda before deploying. +"""Estimate the unzipped size of the fastapi Lambda before deploying. -Mirrors what `deployment/terraform/modules/lambda_with_api_gateway/main.tf` does: -`pip install -r -t ...` followed by zipping +Mirrors what `deployment/terraform/modules/lambda_with_api_gateway/main.tf` does +for the `ara_fast_api` lambda (`deployment/terraform/lambda/fast-api`): +`pip install -r requirements.txt -t ...` followed by zipping `source_dir` minus `zip_excludes`. AWS Lambda's hard limit is 262144000 bytes (250 MiB) unzipped. We install the dependencies into a scratch directory instead of the real source tree so this can run locally or in CI without mutating the repo. -Usage: - python3 scripts/check_lambda_zip_size.py \ - --source-dir . \ - --requirements backend/app/requirements/requirements.txt \ - --exclude "**/tests/**" --exclude "**/.git/**" ... +Usage (from the repo root): + python3 backend/app/requirements/check_lambda_zip_size.py --source-dir . + +--requirements defaults to the requirements.txt next to this script, and +--exclude defaults to whatever `zip_excludes` is set to in +`deployment/terraform/modules/lambda_with_api_gateway/variables.tf` — read +straight from that file, so this can never silently drift from what +Terraform actually excludes. Pass --exclude explicitly to override. Exits non-zero if the projected unzipped size exceeds --limit (or exceeds --warn-pct of the limit, when --strict is not passed the warning is just @@ -22,6 +26,7 @@ from __future__ import annotations import argparse import fnmatch +import re import subprocess import sys import tempfile @@ -29,6 +34,24 @@ from pathlib import Path LAMBDA_UNZIPPED_LIMIT_BYTES = 262_144_000 +REPO_ROOT = Path(__file__).resolve().parents[3] +DEFAULT_REQUIREMENTS = Path(__file__).resolve().parent / "requirements.txt" +DEFAULT_TF_VARIABLES_FILE = ( + REPO_ROOT / "deployment/terraform/modules/lambda_with_api_gateway/variables.tf" +) + + +def parse_zip_excludes_from_tf(tf_variables_file: Path) -> list[str]: + text = tf_variables_file.read_text() + match = re.search( + r'variable\s+"zip_excludes"\s*{.*?default\s*=\s*\[(.*?)\]', text, re.S + ) + if not match: + raise ValueError( + f'could not find variable "zip_excludes" default in {tf_variables_file}' + ) + return re.findall(r'"([^"]+)"', match.group(1)) + def doublestar_match(pattern_parts: list[str], path_parts: list[str]) -> bool: if not pattern_parts: @@ -92,9 +115,21 @@ def human(n: int) -> str: def main() -> int: parser = argparse.ArgumentParser(description=__doc__) - parser.add_argument("--source-dir", type=Path, default=Path(".")) - parser.add_argument("--requirements", type=Path, default=None) - parser.add_argument("--exclude", action="append", default=[], dest="excludes") + parser.add_argument("--source-dir", type=Path, default=REPO_ROOT) + parser.add_argument("--requirements", type=Path, default=DEFAULT_REQUIREMENTS) + parser.add_argument( + "--skip-deps", + action="store_true", + help="Skip the pip install step and only measure source files.", + ) + parser.add_argument( + "--exclude", + action="append", + default=None, + dest="excludes", + help="Repeatable. Defaults to zip_excludes read from --tf-variables-file.", + ) + parser.add_argument("--tf-variables-file", type=Path, default=DEFAULT_TF_VARIABLES_FILE) parser.add_argument("--limit", type=int, default=LAMBDA_UNZIPPED_LIMIT_BYTES) parser.add_argument( "--warn-pct", @@ -109,10 +144,15 @@ def main() -> int: ) args = parser.parse_args() - source_size = sum_dir_size(args.source_dir, args.excludes) + excludes = args.excludes + if excludes is None: + excludes = parse_zip_excludes_from_tf(args.tf_variables_file) + print(f"excludes (from {args.tf_variables_file.relative_to(REPO_ROOT)}): {excludes}") + + source_size = sum_dir_size(args.source_dir, excludes) deps_size = 0 - if args.requirements is not None: + if not args.skip_deps: with tempfile.TemporaryDirectory(prefix="lambda-deps-") as tmp: deps_size = install_dependencies(args.requirements, Path(tmp)) @@ -120,7 +160,7 @@ def main() -> int: pct = total / args.limit * 100 print(f"source files (post-exclude): {human(source_size)}") - if args.requirements is not None: + if not args.skip_deps: print(f"pip dependencies: {human(deps_size)}") print(f"projected unzipped total: {human(total)} [{pct:.1f}% of limit]") print(f"AWS limit: {human(args.limit)}") From e816972c4597e4e314c9df26aad126b872bbf7a4 Mon Sep 17 00:00:00 2001 From: Jun-te Kim Date: Mon, 6 Jul 2026 13:28:24 +0000 Subject: [PATCH 3/4] Move the fastapi Lambda zip-size gate into the PR test suite MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit deploy_terraform.yml only triggers on push to dev/prod — by the time it runs, the change has already merged to main and rolled into dev. That's exactly how the size regression sat undetected for weeks: it only ever surfaced deep inside terraform apply on dev. Drop the fast_api_lambda_zip_size_check job from deploy_terraform.yml and instead add tests/test_lambda_zip_size.py, which runs backend/app/requirements/check_lambda_zip_size.py as part of the existing pytest tests/ suite. ddd_tests.yml already runs that suite on `pull_request: branches: ["**"]`, so this now fails the PR before anything merges to main, rather than failing the dev deploy after. Co-Authored-By: Claude Sonnet 5 --- .github/workflows/deploy_terraform.yml | 17 +---------- tests/test_lambda_zip_size.py | 42 ++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 16 deletions(-) create mode 100644 tests/test_lambda_zip_size.py diff --git a/.github/workflows/deploy_terraform.yml b/.github/workflows/deploy_terraform.yml index 9907ebee3..5e469dc66 100644 --- a/.github/workflows/deploy_terraform.yml +++ b/.github/workflows/deploy_terraform.yml @@ -534,26 +534,11 @@ jobs: TF_VAR_social_housing_wave_3_sharepoint_id: ${{ secrets.SOCIAL_HOUSING_WAVE_3_SHAREPOINT_ID }} - # ============================================================ - # Check FastAPI Lambda package will fit AWS's unzipped size limit - # ============================================================ - 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 - # ============================================================ # Deploy FastAPI Lambda # ============================================================ fast_api_lambda: - needs: [determine_stage, ara_engine_lambda, categorisation_lambda, postcodeSplitter_lambda, bulk_address2uprn_combiner_lambda, bulkUploadFinaliser_lambda, fast_api_lambda_zip_size_check] + needs: [determine_stage, ara_engine_lambda, categorisation_lambda, postcodeSplitter_lambda, bulk_address2uprn_combiner_lambda, bulkUploadFinaliser_lambda] uses: ./.github/workflows/_deploy_lambda.yml with: lambda_name: ara_fast_api diff --git a/tests/test_lambda_zip_size.py b/tests/test_lambda_zip_size.py new file mode 100644 index 000000000..a9cc89bca --- /dev/null +++ b/tests/test_lambda_zip_size.py @@ -0,0 +1,42 @@ +"""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 + ) From adb1bf4b56c73f81043cc0bc62466cd26ad292ec Mon Sep 17 00:00:00 2001 From: Jun-te Kim Date: Mon, 6 Jul 2026 13:43:36 +0000 Subject: [PATCH 4/4] Log GITHUB_SHA on /health; move zip-size gate to its own PR workflow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit /health already returned GITHUB_SHA in its response but never logged it, and the fastapi lambda's Terraform environment never actually set GITHUB_SHA — so every health check response contained "unknown" in production. Wire var.github_sha through the fastapi lambda module (default "unknown" for local/other invocations) via a new TF_VAR_github_sha env var set from `github.sha` in _deploy_lambda.yml's Terraform Plan step, and log it on every /health call so a request in CloudWatch can be tied back to the deploy that served it. Also fix the zip-size gate added for PR #1469: putting it in tests/test_lambda_zip_size.py (run via the Docker-based ddd_tests.yml suite) broke CI, because Dockerfile.test's build context excludes deployment/* (.dockerignore), so check_lambda_zip_size.py couldn't find variables.tf to read zip_excludes from inside that container. Move the check to its own lightweight workflow, check_lambda_zip_size.yml, triggered on pull_request into main — a plain checkout (no Docker build) has the full repo, so the check works, runs fast, and still gates merges to main before a regression can roll into the dev deploy. Co-Authored-By: Claude Sonnet 5 --- .github/workflows/_deploy_lambda.yml | 1 + .github/workflows/check_lambda_zip_size.yml | 23 ++++++++++ backend/app/main.py | 4 +- deployment/terraform/lambda/fast-api/main.tf | 11 ++--- .../terraform/lambda/fast-api/variables.tf | 8 +++- tests/test_lambda_zip_size.py | 42 ------------------- 6 files changed, 40 insertions(+), 49 deletions(-) create mode 100644 .github/workflows/check_lambda_zip_size.yml delete mode 100644 tests/test_lambda_zip_size.py 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 - )