diff --git a/deploy/deploy.sh b/deploy/deploy.sh index af8e041..30bc79a 100755 --- a/deploy/deploy.sh +++ b/deploy/deploy.sh @@ -41,8 +41,20 @@ echo "==> Fetching $BRANCH" git fetch --prune origin "$BRANCH" git reset --hard "origin/$BRANCH" -echo "==> Building images" -docker compose build +# Registry-pull cutover (repo-split Stage 6): pull the tag build-push.yml +# already pushed for this exact commit instead of building in place. Mirrors +# build-push.yml's own tag computation (sha-<12 hex>) so this always resolves +# to "whatever HEAD of this branch built to" -- no separate promotion step. +REGISTRY_HOST="${REGISTRY_HOST:-git.thermograph.org}" +IMAGE_PATH="${IMAGE_PATH:-emi/thermograph/app}" +export IMAGE_TAG="sha-$(git rev-parse --short=12 HEAD)" +export REGISTRY_HOST IMAGE_PATH + +echo "==> Logging in to the registry ($REGISTRY_HOST)" +echo "$REGISTRY_TOKEN" | docker login "$REGISTRY_HOST" --username emi --password-stdin + +echo "==> Pulling images ($IMAGE_TAG)" +docker compose pull backend frontend # Schema migrations run inside the backend container's entrypoint (alembic # upgrade head) before uvicorn starts, so there's no separate migrate step or diff --git a/deploy/thermograph.env.example b/deploy/thermograph.env.example index a2d5ef0..4ec27ba 100644 --- a/deploy/thermograph.env.example +++ b/deploy/thermograph.env.example @@ -9,6 +9,14 @@ # host loopback (127.0.0.1:8137) for Caddy to proxy to. Keep 8137. PORT=8137 +# --- Registry pull (deploy.sh / Terraform) --------------------------------------- +# deploy.sh logs in to git.thermograph.org's registry and `docker compose pull`s +# the image build-push.yml already pushed for the deployed commit (repo-split +# Stage 6), instead of building in place. A personal access token with at least +# read:package scope -- the same REGISTRY_TOKEN secret build-push.yml uses (that +# one needs write:package too, to push; this only needs to pull). +REGISTRY_TOKEN= + # --- PostgreSQL (docker-compose stack) ------------------------------------------ # The app and Postgres run as a docker-compose stack (see docker-compose.yml). # POSTGRES_PASSWORD is the database password: compose uses it to initialize the diff --git a/docker-compose.yml b/docker-compose.yml index 8d9ad91..f8bc13d 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -80,6 +80,13 @@ services: backend: build: . + # Named so `docker compose pull` can fetch a prebuilt tag instead of + # `--build`ing in place (repo-split Stage 6's registry-pull cutover -- + # deploy.sh/Terraform set IMAGE_TAG to the SHA build-push.yml just pushed + # for the branch being deployed). Harmless default for local/CI + # `--build` usage, which never needs to resolve this against the + # registry -- `build:` + `image:` together just tag the local build. + image: ${REGISTRY_HOST:-git.thermograph.org}/${IMAGE_PATH:-emi/thermograph/app}:${IMAGE_TAG:-local} depends_on: db: condition: service_healthy @@ -130,6 +137,9 @@ services: frontend: build: . + # Same image as backend (see backend's `image:` comment above) -- + # THERMOGRAPH_SERVICE_ROLE below is what picks the process. + image: ${REGISTRY_HOST:-git.thermograph.org}/${IMAGE_PATH:-emi/thermograph/app}:${IMAGE_TAG:-local} # Its own register() fetches the IndexNow key from backend at boot -- must # wait for a real, healthy backend, not just a started container. depends_on: diff --git a/terraform/main.tf b/terraform/main.tf index d1dd85c..57c5a7e 100644 --- a/terraform/main.tf +++ b/terraform/main.tf @@ -90,4 +90,5 @@ module "host" { discord_app_id = var.discord_app_id discord_bot_token = var.discord_bot_token discord_client_secret = var.discord_client_secret + registry_token = var.registry_token } diff --git a/terraform/modules/thermograph-host/main.tf b/terraform/modules/thermograph-host/main.tf index c39c5bb..e93f827 100644 --- a/terraform/modules/thermograph-host/main.tf +++ b/terraform/modules/thermograph-host/main.tf @@ -56,6 +56,7 @@ locals { discord_app_id = var.discord_app_id discord_bot_token = var.discord_bot_token discord_client_secret = var.discord_client_secret + registry_token = var.registry_token }) # Caddyfile is only meaningful on a host with a public domain. @@ -261,10 +262,14 @@ resource "null_resource" "host" { inline = [ <<-EOT set -eu - echo "[${var.name}] deploy: install env + docker compose up" + echo "[${var.name}] deploy: install env + docker compose pull + up" sudo install -m 0640 -o root -g root /tmp/thermograph.env /etc/thermograph.env rm -f /tmp/thermograph.env - sudo bash -c 'set -a; . /etc/thermograph.env; set +a; cd ${var.app_dir} && docker compose ${local.compose_flags} up -d --build' + sudo bash -c 'set -a; . /etc/thermograph.env; set +a; cd ${var.app_dir} && \ + export REGISTRY_HOST="git.thermograph.org" IMAGE_PATH="emi/thermograph/app" IMAGE_TAG="sha-$(git rev-parse --short=12 HEAD)" && \ + echo "$REGISTRY_TOKEN" | docker login "$REGISTRY_HOST" --username emi --password-stdin && \ + docker compose ${local.compose_flags} pull backend frontend && \ + docker compose ${local.compose_flags} up -d' EOT ] } diff --git a/terraform/modules/thermograph-host/templates/thermograph.env.tftpl b/terraform/modules/thermograph-host/templates/thermograph.env.tftpl index 076591c..f99a614 100644 --- a/terraform/modules/thermograph-host/templates/thermograph.env.tftpl +++ b/terraform/modules/thermograph-host/templates/thermograph.env.tftpl @@ -5,6 +5,11 @@ # Port uvicorn binds inside the container (compose publishes it on the host). PORT=${app_port} +# --- Registry pull (repo-split Stage 6) ------------------------------------------- +# `docker login` + `docker compose pull` credential -- read:package scope is +# enough (deploy.sh only pulls, never pushes). +REGISTRY_TOKEN=${registry_token} + # --- PostgreSQL (docker-compose stack) ------------------------------------------ # Initializes the db container AND builds the app's THERMOGRAPH_DATABASE_URL. POSTGRES_PASSWORD=${postgres_password} diff --git a/terraform/modules/thermograph-host/variables.tf b/terraform/modules/thermograph-host/variables.tf index 05be848..293466a 100644 --- a/terraform/modules/thermograph-host/variables.tf +++ b/terraform/modules/thermograph-host/variables.tf @@ -228,3 +228,8 @@ variable "discord_client_secret" { type = string sensitive = true } + +variable "registry_token" { + type = string + sensitive = true +} diff --git a/terraform/terraform.tfvars.example b/terraform/terraform.tfvars.example index 369ba66..f5fc86e 100644 --- a/terraform/terraform.tfvars.example +++ b/terraform/terraform.tfvars.example @@ -130,3 +130,9 @@ vapid_contact = "mailto:you@example.com" # discord_app_id = "" # discord_bot_token = "" # discord_client_secret = "" + +# ---- Required: registry pull credential (repo-split Stage 6) -------------------- +# A Forgejo personal access token, read:package scope, for deploy.sh's +# `docker login` + `docker compose pull` on every apply. No default -- apply +# fails loud without it rather than deploying a host that can't redeploy itself. +# registry_token = "" diff --git a/terraform/variables.tf b/terraform/variables.tf index 79f853b..948482b 100644 --- a/terraform/variables.tf +++ b/terraform/variables.tf @@ -238,6 +238,12 @@ variable "discord_bot_token" { default = "" } +variable "registry_token" { + description = "Forgejo registry pull credential (read:package scope) -- deploy.sh's docker login + docker compose pull (repo-split Stage 6). Required; no default -- deploys fail loud without it rather than silently falling back to a build." + type = string + sensitive = true +} + variable "discord_client_secret" { description = "THERMOGRAPH_DISCORD_CLIENT_SECRET — OAuth2 account linking (a credential)." type = string