agilentics / boiler
# App Engine standard - the INTEGRATION service.
#
# Deployed to the same project as production and alongside it, as a second
# service rather than a second project: `gcloud app deploy app-int.yaml`. The
# `default` service is untouched by that command, so production keeps serving
# whatever it was serving.
#
# What differs from app.yaml, and why each difference exists, is the whole
# content of this file. Everything not listed here is deliberately the same, so
# int is a rehearsal of production rather than a different application.
runtime: python310

# The one line that makes this a separate deployment. The hostname follows from
# it: https://int-dot-<project>.<region>.r.appspot.com
service: int

instance_class: F1

automatic_scaling:
  # No warm instance. Production keeps one because a user returning from an
  # external redirect must not meet a cold start; nobody is waiting on int, and
  # an always-on instance would bill for a service that is idle almost always.
  min_instances: 0
  # One, against a budget rather than against demand: int's share of the shared
  # Cloud SQL instance is whatever production does not account for. With the two
  # numbers below that is (2 threads + 1 overflow) x 1 worker x 1 instance = 3
  # connections. These four numbers are one decision - see app.yaml.
  max_instances: 1

entrypoint: gunicorn -c gunicorn.py main:app

# Its own secret file, not production's. Rendered by CI into the runner's
# workspace and gitignored, exactly like env_secrets.yaml - and separate so that
# int cannot pick up production's secrets by including the wrong file.
includes:
  - env_secrets_int.yaml

beta_settings:
  cloud_sql_instances: myapp-000000:us-central1:myapp-postgres

env_variables:
  # "prod" because int is served over HTTPS like everything else - anything else
  # here would hand out session cookies without the Secure flag. It does not mean
  # "the production environment".
  ENV: prod
  DB_USER: myapp
  # The only difference that matters. A separate database on the same instance,
  # created with `gcloud sql databases create myapp_int`.
  DB_NAME: myapp_int
  INSTANCE_UNIX_SOCKET: myapp-000000:us-central1:myapp-postgres

  # See max_instances above - these three numbers are one decision.
  WEB_CONCURRENCY: "1"
  WEB_THREADS: "2"

  PUBLIC_BASE_URL: https://int-dot-myapp-000000.uc.r.appspot.com

  # Deliberately absent, and each absence is a behaviour:
  #
  #   SMTP_*            mail is logged instead of sent, so int cannot email a
  #                     real person a verification or reset link.
  #   GOOGLE_CLIENT_*   no Google sign-in; the button is hidden and password
  #                     sign-in still works. A passkey or an OAuth client is
  #                     bound to the origins registered for it, so production's
  #                     client does not cover the int hostname - register
  #                     <this host>/auth/google/callback on it to switch this on.
  #
  # Add to this list as you add integrations, and say what the absence does
  # rather than leaving a reader to infer it from settings.py.

handlers:
- url: /static
  static_dir: static
- url: /.*
  secure: always
  redirect_http_response_code: 301
  script: auto