Merge pull request 'registry: move image and repo references to the Jinemi namespace' (#150) from fix/registry-namespace-admin-emi into dev
Some checks failed
Sync infra to hosts / sync-beta (push) Has been skipped
Sync infra to hosts / sync-prod (push) Has been skipped
Sync infra to hosts / sync-dev (push) Successful in 6s
Sync infra to hosts / sync-centralis (push) Has been skipped
Build + push images (Forgejo registry) / build-push (backend) (push) Failing after 19s
Build + push images (Forgejo registry) / build-push (frontend) (push) Failing after 18s
secrets-guard / encrypted (push) Successful in 4s
shell-lint / shellcheck (push) Successful in 8s
Validate observability stack / validate (push) Successful in 12s
Deploy / deploy (backend) (push) Failing after 5m19s
PR build (required check) / changes (pull_request) Successful in 7s
secrets-guard / encrypted (pull_request) Successful in 6s
shell-lint / shellcheck (pull_request) Successful in 7s
PR build (required check) / validate-observability (pull_request) Successful in 20s
PR build (required check) / build-frontend (pull_request) Successful in 23s
Deploy / deploy (frontend) (push) Failing after 10m9s
PR build (required check) / build-backend (pull_request) Successful in 4m29s
PR build (required check) / gate (pull_request) Successful in 3s

This commit is contained in:
admin_emi 2026-08-01 16:41:22 +00:00
commit 9e0128bf01
36 changed files with 84 additions and 84 deletions

View file

@ -4,8 +4,8 @@ name: Build + push images (Forgejo registry)
# They were identical apart from the domain string: the paths filter, the # They were identical apart from the domain string: the paths filter, the
# concurrency group, IMAGE_PATH, the tag key and the build context. # concurrency group, IMAGE_PATH, the tag key and the build context.
# #
# Images stay separate and independently deployable -- emi/thermograph/backend # Images stay separate and independently deployable -- jinemi/thermograph/backend
# and emi/thermograph/frontend -- exactly as before. Build-once, # and jinemi/thermograph/frontend -- exactly as before. Build-once,
# deploy-everywhere; nothing about the published artefacts changes here. # deploy-everywhere; nothing about the published artefacts changes here.
# #
# SEMVER EXCEPTION, preserved: a v*.*.* tag push carries no paths context, so a # SEMVER EXCEPTION, preserved: a v*.*.* tag push carries no paths context, so a
@ -116,7 +116,7 @@ jobs:
# independent of any permission setting). Needs a manually provisioned # independent of any permission setting). Needs a manually provisioned
# PAT with write:package scope, stored as REGISTRY_TOKEN. # PAT with write:package scope, stored as REGISTRY_TOKEN.
echo "${{ secrets.REGISTRY_TOKEN }}" | docker login "${{ steps.image.outputs.host }}" \ echo "${{ secrets.REGISTRY_TOKEN }}" | docker login "${{ steps.image.outputs.host }}" \
--username emi --password-stdin --username admin_emi --password-stdin
- name: Build - name: Build
if: steps.image.outputs.changed == 'true' if: steps.image.outputs.changed == 'true'

View file

@ -77,7 +77,7 @@ cannot describe vps2 alone, since vps2 runs beta and prod side by side.
- Each service's live tag is persisted host-side, so a single-service roll never - Each service's live tag is persisted host-side, so a single-service roll never
disturbs the sibling's running tag. disturbs the sibling's running tag.
Images are `emi/thermograph/backend` and `emi/thermograph/frontend`, tagged Images are `jinemi/thermograph/backend` and `jinemi/thermograph/frontend`, tagged
`sha-<12hex>` on every push and by semver on `v*.*.*` tags. They build and deploy `sha-<12hex>` on every push and by semver on `v*.*.*` tags. They build and deploy
**independently** — a backend change ships without a frontend deploy and vice **independently** — a backend change ships without a frontend deploy and vice
versa. That independence is the point of the FE/BE split and survived versa. That independence is the point of the FE/BE split and survived

View file

@ -25,7 +25,7 @@ Forgejo only reads root workflows, but dead copies are a trap):**
- `backend-build-push.yml` / `frontend-build-push.yml` — the old per-repo - `backend-build-push.yml` / `frontend-build-push.yml` — the old per-repo
build-push, path-filtered (`paths: ['backend/**']` etc.), image paths now build-push, path-filtered (`paths: ['backend/**']` etc.), image paths now
**explicit**: `emi/thermograph/backend|frontend` (the old **explicit**: `jinemi/thermograph/backend|frontend` (the old
`${{ github.repository }}/app` derivation collides in a monorepo). Per-domain `${{ github.repository }}/app` derivation collides in a monorepo). Per-domain
concurrency groups. Caveat: `v*.*.*` tag pushes carry no paths context, so a concurrency groups. Caveat: `v*.*.*` tag pushes carry no paths context, so a
version tag builds **both** images (intended: a release names the set). version tag builds **both** images (intended: a release names the set).
@ -65,7 +65,7 @@ Forgejo only reads root workflows, but dead copies are a trap):**
name, so no project-name concern there). name, so no project-name concern there).
**Verified here:** every workflow + compose file parses (PyYAML); all three **Verified here:** every workflow + compose file parses (PyYAML); all three
deploy scripts pass `bash -n`; no `emi/thermograph-{backend,frontend}/app` deploy scripts pass `bash -n`; no `admin_emi/thermograph-{backend,frontend}/app`
stragglers in sh/yml. **Not verified (impossible locally):** Forgejo actually stragglers in sh/yml. **Not verified (impossible locally):** Forgejo actually
honoring `on.push.paths` and `workflow_call` inputs on our version; any live honoring `on.push.paths` and `workflow_call` inputs on our version; any live
deploy. deploy.
@ -118,7 +118,7 @@ Checked every split-repo branch by dry-run merge; migrated everything real:
## Cutover runbook ## Cutover runbook
1. Create `emi/thermograph` on Forgejo (a **new** repo — do not resurrect the 1. Create `Jinemi/thermograph` on Forgejo (a **new** repo — do not resurrect the
archived monorepo) and push `main`. archived monorepo) and push `main`.
2. Actions config, once: secrets `REGISTRY_TOKEN`, `SSH_HOST/USER/KEY/PORT` 2. Actions config, once: secrets `REGISTRY_TOKEN`, `SSH_HOST/USER/KEY/PORT`
(beta), `PROD_SSH_*` (prod); variable `REGISTRY_HOST=git.thermograph.org`. (beta), `PROD_SSH_*` (prod); variable `REGISTRY_HOST=git.thermograph.org`.
@ -132,7 +132,7 @@ Checked every split-repo branch by dry-run merge; migrated everything real:
`/etc/thermograph*` files stay as-is. `/etc/thermograph*` files stay as-is.
5. LAN box: re-point `~/thermograph-dev` at the monorepo the same way. 5. LAN box: re-point `~/thermograph-dev` at the monorepo the same way.
6. First push builds the **new** image paths — old 6. First push builds the **new** image paths — old
`emi/thermograph-{backend,frontend}/app` packages stay in the registry for `admin_emi/thermograph-{backend,frontend}/app` packages stay in the registry for
rollback; GC after burn-in. deploy.sh's tag prune already targets the new rollback; GC after burn-in. deploy.sh's tag prune already targets the new
paths only. paths only.
7. One `workflow_dispatch` of `backend-deploy.yml` after its build-push 7. One `workflow_dispatch` of `backend-deploy.yml` after its build-push

View file

@ -8,8 +8,8 @@ for: **per-domain images, per-domain deploys, and an async FE/BE contract**.
| Dir | What | CI | | Dir | What | CI |
|---|---|---| |---|---|---|
| `backend/` | FastAPI graded-climate API, accounts, notifications (Discord bot, push, mail), data pipeline | `build-push` → image `emi/thermograph/backend`; `deploy` | | `backend/` | FastAPI graded-climate API, accounts, notifications (Discord bot, push, mail), data pipeline | `build-push` → image `jinemi/thermograph/backend`; `deploy` |
| `frontend/` | Public client: static JS/CSS + SSR pages | same `build-push` / `deploy` workflows, matrixed by domain; image `emi/thermograph/frontend` | | `frontend/` | Public client: static JS/CSS + SSR pages | same `build-push` / `deploy` workflows, matrixed by domain; image `jinemi/thermograph/frontend` |
| `infra/` | Compose (dev, on vps1) + two Swarm stacks co-resident on vps2 (beta, prod), deploy scripts, terraform, SOPS secrets vault, ops cron | `infra-sync` (host checkout + secrets render), `secrets-guard`, `ops-cron` | | `infra/` | Compose (dev, on vps1) + two Swarm stacks co-resident on vps2 (beta, prod), deploy scripts, terraform, SOPS secrets vault, ops cron | `infra-sync` (host checkout + secrets render), `secrets-guard`, `ops-cron` |
| `observability/` | Loki + Grafana + Alloy stack | `observability-validate` | | `observability/` | Loki + Grafana + Alloy stack | `observability-validate` |
@ -27,7 +27,7 @@ which docs in this repo are currently stale.
Every workflow in `.forgejo/workflows/` is **path-filtered to its domain**: a Every workflow in `.forgejo/workflows/` is **path-filtered to its domain**: a
push touching only `frontend/**` builds/deploys nothing else. Images stay push touching only `frontend/**` builds/deploys nothing else. Images stay
separate (`emi/thermograph/backend`, `emi/thermograph/frontend`, each tagged separate (`jinemi/thermograph/backend`, `jinemi/thermograph/frontend`, each tagged
`sha-<12hex>`), deploys stay per-service (`infra/deploy/deploy.sh `sha-<12hex>`), deploys stay per-service (`infra/deploy/deploy.sh
SERVICE=backend|frontend|all`), and the API version contract SERVICE=backend|frontend|all`), and the API version contract
(`GET /api/version`, `PAYLOAD_VER`) still lets FE and BE ship out of lockstep. (`GET /api/version`, `PAYLOAD_VER`) still lets FE and BE ship out of lockstep.

View file

@ -3,7 +3,7 @@
The Thermograph API service: grades recent local weather against ~45 years of The Thermograph API service: grades recent local weather against ~45 years of
climate history, and hosts the accounts, notification, and SSR-content climate history, and hosts the accounts, notification, and SSR-content
back-end that the rest of the split Thermograph stack (`thermograph-frontend`) back-end that the rest of the split Thermograph stack (`thermograph-frontend`)
talks to over HTTP. Split from the `emi/thermograph` monorepo — this repo owns talks to over HTTP. Split from the `Jinemi/thermograph` monorepo — this repo owns
the API/DB/accounts/notifications layer only; it renders no HTML/CSS/JS of its the API/DB/accounts/notifications layer only; it renders no HTML/CSS/JS of its
own. own.
@ -67,7 +67,7 @@ cities_flavor.json bundled reference data (generated by gen_cities.py /
`/content/...`; it negotiates compatibility via `GET /api/version`. `/content/...`; it negotiates compatibility via `GET /api/version`.
- **`thermograph-infra`** owns the deploy/Compose/Terraform layer — this repo - **`thermograph-infra`** owns the deploy/Compose/Terraform layer — this repo
only builds and publishes its own container image only builds and publishes its own container image
(`git.thermograph.org/emi/thermograph-backend/app`) and hands infra a tag to (`git.thermograph.org/admin_emi/thermograph-backend/app`) and hands infra a tag to
roll out (`SERVICE=backend` + `BACKEND_IMAGE_TAG` into infra's roll out (`SERVICE=backend` + `BACKEND_IMAGE_TAG` into infra's
`deploy/deploy.sh`). `deploy/deploy.sh`).
- **`thermograph-docs`** holds the cross-repo architecture/runbook docs; this - **`thermograph-docs`** holds the cross-repo architecture/runbook docs; this

View file

@ -22,11 +22,11 @@ services:
backend: backend:
# Defaults to a locally-built `:local` image (scripts/smoke.sh builds it). In CI, # Defaults to a locally-built `:local` image (scripts/smoke.sh builds it). In CI,
# set BACKEND_IMAGE_TAG=sha-<12hex> to smoke the exact published image. # set BACKEND_IMAGE_TAG=sha-<12hex> to smoke the exact published image.
# The path must match what build-push.yml publishes -- emi/thermograph/backend. # The path must match what build-push.yml publishes -- jinemi/thermograph/backend.
# It read emi/thermograph-backend/app (the retired split-era path) until this # It read admin_emi/thermograph-backend/app (the retired split-era path) until this
# was corrected; harmless for the default `:local` build, wrong the moment # was corrected; harmless for the default `:local` build, wrong the moment
# BACKEND_IMAGE_TAG names a real published tag. # BACKEND_IMAGE_TAG names a real published tag.
image: ${REGISTRY_HOST:-git.thermograph.org}/${BACKEND_IMAGE_PATH:-emi/thermograph/backend}:${BACKEND_IMAGE_TAG:-local} image: ${REGISTRY_HOST:-git.thermograph.org}/${BACKEND_IMAGE_PATH:-jinemi/thermograph/backend}:${BACKEND_IMAGE_TAG:-local}
depends_on: depends_on:
db: db:
condition: service_healthy condition: service_healthy

View file

@ -14,7 +14,7 @@ export POSTGRES_PASSWORD="${POSTGRES_PASSWORD:-smoke}"
# The image to smoke, pulled from the registry (no local build). CI passes the just- # The image to smoke, pulled from the registry (no local build). CI passes the just-
# pushed sha (BACKEND_IMAGE_TAG=sha-<12hex>); locally it defaults to a published tag. # pushed sha (BACKEND_IMAGE_TAG=sha-<12hex>); locally it defaults to a published tag.
export BACKEND_IMAGE_TAG="${BACKEND_IMAGE_TAG:-v0.0.2-split-ci}" export BACKEND_IMAGE_TAG="${BACKEND_IMAGE_TAG:-v0.0.2-split-ci}"
IMG="${REGISTRY_HOST:-git.thermograph.org}/${BACKEND_IMAGE_PATH:-emi/thermograph-backend/app}:${BACKEND_IMAGE_TAG}" IMG="${REGISTRY_HOST:-git.thermograph.org}/${BACKEND_IMAGE_PATH:-admin_emi/thermograph-backend/app}:${BACKEND_IMAGE_TAG}"
export SMOKE_HOST_PORT="${SMOKE_HOST_PORT:-18137}" export SMOKE_HOST_PORT="${SMOKE_HOST_PORT:-18137}"
COMPOSE=(docker compose -f docker-compose.test.yml) COMPOSE=(docker compose -f docker-compose.test.yml)
PORT_URL="http://127.0.0.1:${SMOKE_HOST_PORT}" PORT_URL="http://127.0.0.1:${SMOKE_HOST_PORT}"

View file

@ -12,8 +12,8 @@ thermograph/
├── README.md domain table + how CI stays decoupled ├── README.md domain table + how CI stays decoupled
├── .forgejo/workflows/ ALL CI. Nine files. Never add workflows elsewhere. ├── .forgejo/workflows/ ALL CI. Nine files. Never add workflows elsewhere.
├── .claude/ settings.json + enforcement hooks ├── .claude/ settings.json + enforcement hooks
├── backend/ FastAPI API + Go daemon → emi/thermograph/backend ├── backend/ FastAPI API + Go daemon → jinemi/thermograph/backend
├── frontend/ Go SSR + static assets → emi/thermograph/frontend ├── frontend/ Go SSR + static assets → jinemi/thermograph/frontend
├── infra/ compose, Swarm, deploy, secrets, terraform, ops ├── infra/ compose, Swarm, deploy, secrets, terraform, ops
├── observability/ Loki + Grafana + Alloy ├── observability/ Loki + Grafana + Alloy
└── docs/onboarding/ you are here └── docs/onboarding/ you are here

View file

@ -102,7 +102,7 @@ alembic + `/healthz` 200 + a real `/api/v2/place` 200.
Triggers on pushes to `dev`/`main`/`release` touching `backend/**` or Triggers on pushes to `dev`/`main`/`release` touching `backend/**` or
`frontend/**`, and on `v*.*.*` tags. Publishes `frontend/**`, and on `v*.*.*` tags. Publishes
`git.thermograph.org/emi/thermograph/{backend,frontend}:sha-<12hex>`. `git.thermograph.org/Jinemi/thermograph/{backend,frontend}:sha-<12hex>`.
- **Serialised** (`max-parallel: 1`): one physical runner, and two whole image - **Serialised** (`max-parallel: 1`): one physical runner, and two whole image
builds racing each other buys nothing. builds racing each other buys nothing.

View file

@ -148,7 +148,7 @@ pages nobody. **The only proof is a message actually arriving in
> ⚠️ **Merging is not deploying — and this one has already bitten.** > ⚠️ **Merging is not deploying — and this one has already bitten.**
> `/opt/observability` on vps1 and vps2 is a checkout of the **archived** > `/opt/observability` on vps1 and vps2 is a checkout of the **archived**
> `emi/thermograph-observability` repo. It can never `git pull` again; the > `admin_emi/thermograph-observability` repo. It can never `git pull` again; the
> content now lives here under `observability/`. A previous observability PR > content now lives here under `observability/`. A previous observability PR
> merged and had **zero live effect** for exactly this reason. > merged and had **zero live effect** for exactly this reason.

View file

@ -77,7 +77,7 @@ mentally translate host names.
### `backend/README.md` is written for the split-repo era ### `backend/README.md` is written for the split-repo era
It describes `thermograph-backend` as its own repo with sibling repos, uses the It describes `thermograph-backend` as its own repo with sibling repos, uses the
retired image path `git.thermograph.org/emi/thermograph-backend/app`, and says retired image path `git.thermograph.org/admin_emi/thermograph-backend/app`, and says
*"there is no Makefile in this repo yet"* — there is (`make test`, `make smoke`, *"there is no Makefile in this repo yet"* — there is (`make test`, `make smoke`,
`make clean-venv`). `make clean-venv`).
@ -106,7 +106,7 @@ it — that's the standing instruction in the root `CLAUDE.md`.
### `observability/README.md`: merging is not deploying ### `observability/README.md`: merging is not deploying
`/opt/observability` on vps1 and vps2 is a checkout of the **archived** `/opt/observability` on vps1 and vps2 is a checkout of the **archived**
`emi/thermograph-observability` repo. It can never `git pull` again. A previous `admin_emi/thermograph-observability` repo. It can never `git pull` again. A previous
observability PR merged and had **zero live effect** for exactly this reason. observability PR merged and had **zero live effect** for exactly this reason.
The README documents the hand-ship procedure; until the checkouts are re-pointed The README documents the hand-ship procedure; until the checkouts are re-pointed
at the monorepo, that's the only path. Flagged again in at the monorepo, that's the only path. Flagged again in
@ -118,7 +118,7 @@ at the monorepo, that's the only path. Flagged again in
### ~~The `docker-compose.test.yml` files pull retired image paths~~ — fixed ### ~~The `docker-compose.test.yml` files pull retired image paths~~ — fixed
**Fixed.** Both now use `emi/thermograph/backend`, the path `build-push.yml` **Fixed.** Both now use `jinemi/thermograph/backend`, the path `build-push.yml`
actually publishes. actually publishes.
The frontend harness had the worse half of this: it also pinned The frontend harness had the worse half of this: it also pinned

View file

@ -1,6 +1,6 @@
# Onboarding — becoming a full contributor to Thermograph # Onboarding — becoming a full contributor to Thermograph
This is the developer onboarding path for the `emi/thermograph` monorepo: what This is the developer onboarding path for the `Jinemi/thermograph` monorepo: what
the product is, how the code is shaped, how to run it, what will break silently the product is, how the code is shaped, how to run it, what will break silently
if you get it wrong, and how a change actually travels from your editor to if you get it wrong, and how a change actually travels from your editor to
`thermograph.org`. `thermograph.org`.
@ -35,10 +35,10 @@ readings.
- **`backend/`** — Python 3.12 / FastAPI. Owns all climate data, grading, the - **`backend/`** — Python 3.12 / FastAPI. Owns all climate data, grading, the
API, accounts, notifications, plus a Go daemon (`backend/daemon/`) that owns API, accounts, notifications, plus a Go daemon (`backend/daemon/`) that owns
the Discord gateway and recurring timers. Ships as `emi/thermograph/backend`. the Discord gateway and recurring timers. Ships as `jinemi/thermograph/backend`.
- **`frontend/`** — **Go** SSR service (`frontend/server/`) plus every static - **`frontend/`** — **Go** SSR service (`frontend/server/`) plus every static
asset. No climate data, no database, no compute. Ships as asset. No climate data, no database, no compute. Ships as
`emi/thermograph/frontend`. (The Python implementation at `frontend/*.py` is `jinemi/thermograph/frontend`. (The Python implementation at `frontend/*.py` is
the superseded original — see [traps](11-traps.md).) the superseded original — see [traps](11-traps.md).)
- **`infra/`** — compose and Swarm files, deploy scripts, the SOPS secrets - **`infra/`** — compose and Swarm files, deploy scripts, the SOPS secrets
vault, Terraform, ops query tooling. vault, Terraform, ops query tooling.

View file

@ -10,7 +10,7 @@ names.
## The live service is Go — `server/` ## The live service is Go — `server/`
`server/` (module `thermograph/frontend`, Go 1.26) is what builds, tests, ships `server/` (module `thermograph/frontend`, Go 1.26) is what builds, tests, ships
and runs. It is the only thing in the `emi/thermograph/frontend` image. and runs. It is the only thing in the `jinemi/thermograph/frontend` image.
The Python files at this level — `app.py`, `content.py`, `api_client.py`, The Python files at this level — `app.py`, `content.py`, `api_client.py`,
`format.py`, `content_loader.py`, `paths.py`, `templates/*.html.j2` — are the `format.py`, `content_loader.py`, `paths.py`, `templates/*.html.j2` — are the

View file

@ -5,7 +5,7 @@ static asset for [Thermograph](https://thermograph.org) — grades recent local
weather against ~45 years of climate history. This domain holds **no climate weather against ~45 years of climate history. This domain holds **no climate
data and does no compute**; it renders pages and serves the JS/CSS/image assets data and does no compute**; it renders pages and serves the JS/CSS/image assets
that call the backend's API from the browser. It builds its own image that call the backend's API from the browser. It builds its own image
(`emi/thermograph/frontend`) and deploys independently of the backend. (`jinemi/thermograph/frontend`) and deploys independently of the backend.
See [`CLAUDE.md`](CLAUDE.md) for the agent-facing detail on the deploy contract See [`CLAUDE.md`](CLAUDE.md) for the agent-facing detail on the deploy contract
and the cross-service contracts, and [`DESIGN.md`](DESIGN.md) for the visual and the cross-service contracts, and [`DESIGN.md`](DESIGN.md) for the visual

View file

@ -27,9 +27,9 @@ services:
# domain-keyed rule build-push.yml and deploy.yml both use -- so the harness # domain-keyed rule build-push.yml and deploy.yml both use -- so the harness
# tracks the tree instead of drifting behind a hardcoded pin. (It was pinned # tracks the tree instead of drifting behind a hardcoded pin. (It was pinned
# to the split-era v0.0.2-split-ci, under the retired # to the split-era v0.0.2-split-ci, under the retired
# emi/thermograph-backend/app path, long after both were superseded.) # admin_emi/thermograph-backend/app path, long after both were superseded.)
# Override THERMOGRAPH_BACKEND_TEST_TAG to pin a specific build. # Override THERMOGRAPH_BACKEND_TEST_TAG to pin a specific build.
image: ${REGISTRY_HOST:-git.thermograph.org}/${BACKEND_IMAGE_PATH:-emi/thermograph/backend}:${THERMOGRAPH_BACKEND_TEST_TAG:?set it, or run via scripts/backend-for-tests.sh which derives it} image: ${REGISTRY_HOST:-git.thermograph.org}/${BACKEND_IMAGE_PATH:-jinemi/thermograph/backend}:${THERMOGRAPH_BACKEND_TEST_TAG:?set it, or run via scripts/backend-for-tests.sh which derives it}
depends_on: depends_on:
db: db:
condition: service_healthy condition: service_healthy

View file

@ -55,5 +55,5 @@ binary; static assets and the YAML copy are read from disk.
go build ./... && go vet ./... && go test ./... go build ./... && go vet ./... && go test ./...
The deployed binary is `/usr/local/bin/thermograph-frontend` inside the The deployed binary is `/usr/local/bin/thermograph-frontend` inside the
`emi/thermograph/frontend` image; the image name, `frontend-*` CI workflows and `jinemi/thermograph/frontend` image; the image name, `frontend-*` CI workflows and
deploy path are unchanged from the Python service. deploy path are unchanged from the Python service.

View file

@ -58,7 +58,7 @@ THERMOGRAPH_INTERNAL_TOKEN=
# :local, which is the default here -- only set these to pull a specific # :local, which is the default here -- only set these to pull a specific
# published build instead of building locally. # published build instead of building locally.
# REGISTRY_HOST=git.thermograph.org # REGISTRY_HOST=git.thermograph.org
# BACKEND_IMAGE_PATH=emi/thermograph-backend/app # BACKEND_IMAGE_PATH=admin_emi/thermograph-backend/app
# BACKEND_IMAGE_TAG=local # BACKEND_IMAGE_TAG=local
# FRONTEND_IMAGE_PATH=emi/thermograph-frontend/app # FRONTEND_IMAGE_PATH=admin_emi/thermograph-frontend/app
# FRONTEND_IMAGE_TAG=local # FRONTEND_IMAGE_TAG=local

View file

@ -3,7 +3,7 @@
Infrastructure for [Thermograph](https://thermograph.org): Terraform host Infrastructure for [Thermograph](https://thermograph.org): Terraform host
provisioning, the SOPS+age secrets vault, WireGuard/Swarm networking, Forgejo, provisioning, the SOPS+age secrets vault, WireGuard/Swarm networking, Forgejo,
Caddy, mail, and the deploy scripts that run the already-built app images on each Caddy, mail, and the deploy scripts that run the already-built app images on each
host. This is a domain of the `emi/thermograph` monorepo — a host's checkout host. This is a domain of the `Jinemi/thermograph` monorepo — a host's checkout
(`/opt/thermograph`, `/opt/thermograph-beta`, or `/opt/thermograph-dev`) is a (`/opt/thermograph`, `/opt/thermograph-beta`, or `/opt/thermograph-dev`) is a
checkout of the whole monorepo, and `infra/` never builds app source; it only checkout of the whole monorepo, and `infra/` never builds app source; it only
runs published images. runs published images.

View file

@ -79,7 +79,7 @@ or 8 is done wrong, which is what the verification lines are for.
``` ```
ssh agent@169.58.46.181 ssh agent@169.58.46.181
sudo git clone --branch main http://10.10.0.2:3080/emi/thermograph.git /opt/thermograph-beta sudo git clone --branch main http://10.10.0.2:3080/Jinemi/thermograph.git /opt/thermograph-beta
sudo chown -R agent:agent /opt/thermograph-beta sudo chown -R agent:agent /opt/thermograph-beta
``` ```

View file

@ -13,7 +13,7 @@
# ssh deploy@vps 'SERVICE=all BACKEND_IMAGE_TAG=sha-<a> FRONTEND_IMAGE_TAG=sha-<b> /opt/thermograph/infra/deploy/deploy.sh' # ssh deploy@vps 'SERVICE=all BACKEND_IMAGE_TAG=sha-<a> FRONTEND_IMAGE_TAG=sha-<b> /opt/thermograph/infra/deploy/deploy.sh'
# #
# FE/BE CI-CD split: backend and frontend are published from separate repos as # FE/BE CI-CD split: backend and frontend are published from separate repos as
# separate images (emi/thermograph/backend, emi/thermograph/frontend), # separate images (jinemi/thermograph/backend, jinemi/thermograph/frontend),
# so a deploy targets ONE service and leaves the other's running container + # so a deploy targets ONE service and leaves the other's running container +
# tag untouched. Each service's live tag is persisted host-side in # tag untouched. Each service's live tag is persisted host-side in
# deploy/.image-tags.env (untracked -- survives the git reset below) so a # deploy/.image-tags.env (untracked -- survives the git reset below) so a
@ -282,7 +282,7 @@ esac
# host's existing cred; a genuine auth problem then fails loudly at `pull`. # host's existing cred; a genuine auth problem then fails loudly at `pull`.
if [ -n "${REGISTRY_TOKEN:-}" ]; then if [ -n "${REGISTRY_TOKEN:-}" ]; then
echo "==> Logging in to the registry ($REGISTRY_HOST)" echo "==> Logging in to the registry ($REGISTRY_HOST)"
echo "$REGISTRY_TOKEN" | docker login "$REGISTRY_HOST" --username emi --password-stdin echo "$REGISTRY_TOKEN" | docker login "$REGISTRY_HOST" --username admin_emi --password-stdin
else else
echo "==> No REGISTRY_TOKEN in env; relying on the host's existing docker login to $REGISTRY_HOST" echo "==> No REGISTRY_TOKEN in env; relying on the host's existing docker login to $REGISTRY_HOST"
fi fi
@ -328,7 +328,7 @@ case " ${TARGETS[*]} " in
# then always found no daemon binary in a Postgres image and dropped # then always found no daemon binary in a Postgres image and dropped
# daemon from EVERY backend deploy, regardless of what the real backend # daemon from EVERY backend deploy, regardless of what the real backend
# image contained -- reproduced and confirmed against beta directly. # image contained -- reproduced and confirmed against beta directly.
daemon_img="${REGISTRY_HOST:-git.thermograph.org}/${BACKEND_IMAGE_PATH:-emi/thermograph/backend}:${BACKEND_IMAGE_TAG}" daemon_img="${REGISTRY_HOST:-git.thermograph.org}/${BACKEND_IMAGE_PATH:-jinemi/thermograph/backend}:${BACKEND_IMAGE_TAG}"
if ! docker run --rm --entrypoint sh "$daemon_img" -c 'test -x /usr/local/bin/thermograph-daemon' 2>/dev/null; then if ! docker run --rm --entrypoint sh "$daemon_img" -c 'test -x /usr/local/bin/thermograph-daemon' 2>/dev/null; then
echo "==> $daemon_img predates the daemon binary; rolling without the daemon service this run" echo "==> $daemon_img predates the daemon binary; rolling without the daemon service this run"
kept=() kept=()
@ -408,8 +408,8 @@ fi
# roll never garbage-collects the image a rollback would need; docker also # roll never garbage-collects the image a rollback would need; docker also
# refuses to remove an image any container still uses. # refuses to remove an image any container still uses.
echo "==> Pruning old app-image tags" echo "==> Pruning old app-image tags"
_be_repo="${REGISTRY_HOST}/${BACKEND_IMAGE_PATH:-emi/thermograph/backend}" _be_repo="${REGISTRY_HOST}/${BACKEND_IMAGE_PATH:-jinemi/thermograph/backend}"
_fe_repo="${REGISTRY_HOST}/${FRONTEND_IMAGE_PATH:-emi/thermograph/frontend}" _fe_repo="${REGISTRY_HOST}/${FRONTEND_IMAGE_PATH:-jinemi/thermograph/frontend}"
docker images --format '{{.Repository}}:{{.Tag}}' \ docker images --format '{{.Repository}}:{{.Tag}}' \
| grep -E "^(${_be_repo}|${_fe_repo}):" \ | grep -E "^(${_be_repo}|${_fe_repo}):" \
| grep -v -e "^${_be_repo}:${BACKEND_IMAGE_TAG}$" -e "^${_fe_repo}:${FRONTEND_IMAGE_TAG}$" \ | grep -v -e "^${_be_repo}:${BACKEND_IMAGE_TAG}$" -e "^${_fe_repo}:${FRONTEND_IMAGE_TAG}$" \

View file

@ -203,14 +203,14 @@ bug hit during the frontend Go rewrite). `ci-runner` adds `docker-ce-cli` +
`git`/`python3`/`python3-yaml` for the other jobs that need them `git`/`python3`/`python3-yaml` for the other jobs that need them
(`shell-lint`, `observability-validate`). (`shell-lint`, `observability-validate`).
Current tag: `git.thermograph.org/emi/thermograph/ci-runner:v2` (`v1` is Current tag: `git.thermograph.org/jinemi/thermograph/ci-runner:v2` (`v1` is
broken — do not register any runner against it). Rebuild/push (requires a PAT broken — do not register any runner against it). Rebuild/push (requires a PAT
with `write:package` scope — the embedded git-remote token lacks it, same with `write:package` scope — the embedded git-remote token lacks it, same
requirement documented in `build-push.yml`): requirement documented in `build-push.yml`):
```bash ```bash
docker build -t git.thermograph.org/emi/thermograph/ci-runner:vN deploy/forgejo/ci-runner docker build -t git.thermograph.org/jinemi/thermograph/ci-runner:vN deploy/forgejo/ci-runner
docker push git.thermograph.org/emi/thermograph/ci-runner:vN docker push git.thermograph.org/jinemi/thermograph/ci-runner:vN
``` ```
`register-lan-runner.sh`'s `LABELS` default points at the current tag, so `register-lan-runner.sh`'s `LABELS` default points at the current tag, so

View file

@ -19,9 +19,9 @@
# class, and skips the per-job install entirely. # class, and skips the per-job install entirely.
# #
# Build/push (manual — see ../README.md for when to rebuild): # Build/push (manual — see ../README.md for when to rebuild):
# docker build -t git.thermograph.org/emi/thermograph/ci-runner:vN \ # docker build -t git.thermograph.org/jinemi/thermograph/ci-runner:vN \
# infra/deploy/forgejo/ci-runner # infra/deploy/forgejo/ci-runner
# docker push git.thermograph.org/emi/thermograph/ci-runner:vN # docker push git.thermograph.org/jinemi/thermograph/ci-runner:vN
# #
# Cutting over the live runner to a new tag means editing the `labels` array # Cutting over the live runner to a new tag means editing the `labels` array
# in ~/forgejo-runner/.runner on the runner host directly (same runner # in ~/forgejo-runner/.runner on the runner host directly (same runner

View file

@ -40,7 +40,7 @@ set -euo pipefail
FORGEJO_URL="${1:?usage: $0 <forgejo_url> <registration_token>}" FORGEJO_URL="${1:?usage: $0 <forgejo_url> <registration_token>}"
TOKEN="${2:?}" TOKEN="${2:?}"
RUNNER_DIR="${RUNNER_DIR:-$HOME/forgejo-runner}" RUNNER_DIR="${RUNNER_DIR:-$HOME/forgejo-runner}"
LABELS="${LABELS:-docker:docker://git.thermograph.org/emi/thermograph/ci-runner:v2,thermograph-lan}" LABELS="${LABELS:-docker:docker://git.thermograph.org/jinemi/thermograph/ci-runner:v2,thermograph-lan}"
echo "==> Stopping and disabling the old GitHub Actions runner service, if present" echo "==> Stopping and disabling the old GitHub Actions runner service, if present"
systemctl --user stop github-actions-runner 2>/dev/null || true systemctl --user stop github-actions-runner 2>/dev/null || true

View file

@ -31,7 +31,7 @@ documentation one.
- `git.thermograph.org` resolves to the **mesh** address from vps2 for registry - `git.thermograph.org` resolves to the **mesh** address from vps2 for registry
traffic. Job containers get this via `--add-host` in `config.yaml`; the host traffic. Job containers get this via `--add-host` in `config.yaml`; the host
daemon already has it. daemon already has it.
- The job image exists: `git.thermograph.org/emi/thermograph/ci-runner:v2`. - The job image exists: `git.thermograph.org/jinemi/thermograph/ci-runner:v2`.
Built from `../ci-runner/Dockerfile`; see that file for when to rebuild. Built from `../ci-runner/Dockerfile`; see that file for when to rebuild.
## Where it is deployed, and why not from the checkout ## Where it is deployed, and why not from the checkout
@ -81,7 +81,7 @@ docker run --rm -it \
--instance https://git.thermograph.org \ --instance https://git.thermograph.org \
--token "$REG_TOKEN" \ --token "$REG_TOKEN" \
--name thermograph-vps2 \ --name thermograph-vps2 \
--labels 'docker:docker://git.thermograph.org/emi/thermograph/ci-runner:v2' --labels 'docker:docker://git.thermograph.org/jinemi/thermograph/ci-runner:v2'
unset REG_TOKEN unset REG_TOKEN
``` ```

View file

@ -26,7 +26,7 @@ SELF_DIR=$(cd "$(dirname "$0")" && pwd)
. "$SELF_DIR/env-topology.sh" . "$SELF_DIR/env-topology.sh"
thermograph_topology dev thermograph_topology dev
REPO_URL="${REPO_URL:-http://10.10.0.2:3080/emi/thermograph.git}" REPO_URL="${REPO_URL:-http://10.10.0.2:3080/Jinemi/thermograph.git}"
APP_DIR="${APP_DIR:-$TG_APP_DIR}" APP_DIR="${APP_DIR:-$TG_APP_DIR}"
BRANCH="${BRANCH:-$TG_BRANCH}" BRANCH="${BRANCH:-$TG_BRANCH}"

View file

@ -124,8 +124,8 @@ case "$SERVICE" in
esac esac
export BACKEND_IMAGE_TAG="${BACKEND_IMAGE_TAG:-local}" export BACKEND_IMAGE_TAG="${BACKEND_IMAGE_TAG:-local}"
export FRONTEND_IMAGE_TAG="${FRONTEND_IMAGE_TAG:-local}" export FRONTEND_IMAGE_TAG="${FRONTEND_IMAGE_TAG:-local}"
BACKEND_IMAGE="$REGISTRY_HOST/${BACKEND_IMAGE_PATH:-emi/thermograph/backend}:$BACKEND_IMAGE_TAG" BACKEND_IMAGE="$REGISTRY_HOST/${BACKEND_IMAGE_PATH:-jinemi/thermograph/backend}:$BACKEND_IMAGE_TAG"
FRONTEND_IMAGE="$REGISTRY_HOST/${FRONTEND_IMAGE_PATH:-emi/thermograph/frontend}:$FRONTEND_IMAGE_TAG" FRONTEND_IMAGE="$REGISTRY_HOST/${FRONTEND_IMAGE_PATH:-jinemi/thermograph/frontend}:$FRONTEND_IMAGE_TAG"
# --- timescale image pin --------------------------------------------------------- # --- timescale image pin ---------------------------------------------------------
# Hazard #7: the db image under an existing volume must never drift. Resolve # Hazard #7: the db image under an existing volume must never drift. Resolve
@ -158,7 +158,7 @@ fi
# --- registry ------------------------------------------------------------------ # --- registry ------------------------------------------------------------------
if [ -n "${REGISTRY_TOKEN:-}" ]; then if [ -n "${REGISTRY_TOKEN:-}" ]; then
echo "$REGISTRY_TOKEN" | docker login "$REGISTRY_HOST" --username emi --password-stdin echo "$REGISTRY_TOKEN" | docker login "$REGISTRY_HOST" --username admin_emi --password-stdin
fi fi
echo "==> Pulling images" echo "==> Pulling images"
pull_ok=0 pull_ok=0
@ -290,8 +290,8 @@ FRONTEND_IMAGE_TAG=$FRONTEND_IMAGE_TAG
EOF EOF
# GC superseded app-image tags (keep the running pair), same as deploy.sh. # GC superseded app-image tags (keep the running pair), same as deploy.sh.
_be_repo="$REGISTRY_HOST/${BACKEND_IMAGE_PATH:-emi/thermograph/backend}" _be_repo="$REGISTRY_HOST/${BACKEND_IMAGE_PATH:-jinemi/thermograph/backend}"
_fe_repo="$REGISTRY_HOST/${FRONTEND_IMAGE_PATH:-emi/thermograph/frontend}" _fe_repo="$REGISTRY_HOST/${FRONTEND_IMAGE_PATH:-jinemi/thermograph/frontend}"
docker images --format '{{.Repository}}:{{.Tag}}' \ docker images --format '{{.Repository}}:{{.Tag}}' \
| grep -E "^(${_be_repo}|${_fe_repo}):" \ | grep -E "^(${_be_repo}|${_fe_repo}):" \
| grep -v -e "^${_be_repo}:${BACKEND_IMAGE_TAG}$" -e "^${_fe_repo}:${FRONTEND_IMAGE_TAG}$" \ | grep -v -e "^${_be_repo}:${BACKEND_IMAGE_TAG}$" -e "^${_fe_repo}:${FRONTEND_IMAGE_TAG}$" \

View file

@ -62,7 +62,7 @@
services: services:
beta-web: beta-web:
image: ${REGISTRY_HOST:-git.thermograph.org}/${BACKEND_IMAGE_PATH:-emi/thermograph/backend}:${BACKEND_IMAGE_TAG:?required} image: ${REGISTRY_HOST:-git.thermograph.org}/${BACKEND_IMAGE_PATH:-jinemi/thermograph/backend}:${BACKEND_IMAGE_TAG:?required}
entrypoint: ["/host/env-entrypoint.sh"] entrypoint: ["/host/env-entrypoint.sh"]
environment: environment:
# Beta's OWN role and OWN database on the shared instance. `db` resolves # Beta's OWN role and OWN database on the shared instance. `db` resolves
@ -108,7 +108,7 @@ services:
failure_action: rollback failure_action: rollback
beta-worker: beta-worker:
image: ${REGISTRY_HOST:-git.thermograph.org}/${BACKEND_IMAGE_PATH:-emi/thermograph/backend}:${BACKEND_IMAGE_TAG:?required} image: ${REGISTRY_HOST:-git.thermograph.org}/${BACKEND_IMAGE_PATH:-jinemi/thermograph/backend}:${BACKEND_IMAGE_TAG:?required}
entrypoint: ["/host/env-entrypoint.sh"] entrypoint: ["/host/env-entrypoint.sh"]
environment: environment:
THERMOGRAPH_DATABASE_URL: postgresql+asyncpg://thermograph_beta:${POSTGRES_PASSWORD}@db:5432/thermograph_beta THERMOGRAPH_DATABASE_URL: postgresql+asyncpg://thermograph_beta:${POSTGRES_PASSWORD}@db:5432/thermograph_beta
@ -144,7 +144,7 @@ services:
condition: on-failure condition: on-failure
beta-lake: beta-lake:
image: ${REGISTRY_HOST:-git.thermograph.org}/${BACKEND_IMAGE_PATH:-emi/thermograph/backend}:${BACKEND_IMAGE_TAG:?required} image: ${REGISTRY_HOST:-git.thermograph.org}/${BACKEND_IMAGE_PATH:-jinemi/thermograph/backend}:${BACKEND_IMAGE_TAG:?required}
entrypoint: ["/host/env-entrypoint.sh"] entrypoint: ["/host/env-entrypoint.sh"]
environment: environment:
THERMOGRAPH_ROLE: lake THERMOGRAPH_ROLE: lake
@ -176,7 +176,7 @@ services:
failure_action: rollback failure_action: rollback
beta-daemon: beta-daemon:
image: ${REGISTRY_HOST:-git.thermograph.org}/${BACKEND_IMAGE_PATH:-emi/thermograph/backend}:${BACKEND_IMAGE_TAG:?required} image: ${REGISTRY_HOST:-git.thermograph.org}/${BACKEND_IMAGE_PATH:-jinemi/thermograph/backend}:${BACKEND_IMAGE_TAG:?required}
# Same reasoning as prod's daemon: NOT env-entrypoint.sh, because that shim # Same reasoning as prod's daemon: NOT env-entrypoint.sh, because that shim
# execs the image's own entrypoint (Alembic + uvicorn) and migrations belong # execs the image's own entrypoint (Alembic + uvicorn) and migrations belong
# to the one-shot task. Source the host-rendered env and exec the binary. # to the one-shot task. Source the host-rendered env and exec the binary.
@ -218,7 +218,7 @@ services:
failure_action: rollback failure_action: rollback
beta-frontend: beta-frontend:
image: ${REGISTRY_HOST:-git.thermograph.org}/${FRONTEND_IMAGE_PATH:-emi/thermograph/frontend}:${FRONTEND_IMAGE_TAG:?required} image: ${REGISTRY_HOST:-git.thermograph.org}/${FRONTEND_IMAGE_PATH:-jinemi/thermograph/frontend}:${FRONTEND_IMAGE_TAG:?required}
entrypoint: ["/host/env-entrypoint.sh"] entrypoint: ["/host/env-entrypoint.sh"]
# REQUIRED: overriding `entrypoint:` with no `command:` drops the image's # REQUIRED: overriding `entrypoint:` with no `command:` drops the image's
# CMD entirely, and env-entrypoint.sh's fallback (`exec uvicorn app:app`) # CMD entirely, and env-entrypoint.sh's fallback (`exec uvicorn app:app`)

View file

@ -64,7 +64,7 @@ services:
condition: on-failure condition: on-failure
web: web:
image: ${REGISTRY_HOST:-git.thermograph.org}/${BACKEND_IMAGE_PATH:-emi/thermograph/backend}:${BACKEND_IMAGE_TAG:?required} image: ${REGISTRY_HOST:-git.thermograph.org}/${BACKEND_IMAGE_PATH:-jinemi/thermograph/backend}:${BACKEND_IMAGE_TAG:?required}
entrypoint: ["/host/env-entrypoint.sh"] entrypoint: ["/host/env-entrypoint.sh"]
environment: environment:
THERMOGRAPH_DATABASE_URL: postgresql+asyncpg://thermograph:${POSTGRES_PASSWORD}@db:5432/thermograph THERMOGRAPH_DATABASE_URL: postgresql+asyncpg://thermograph:${POSTGRES_PASSWORD}@db:5432/thermograph
@ -109,7 +109,7 @@ services:
failure_action: rollback failure_action: rollback
worker: worker:
image: ${REGISTRY_HOST:-git.thermograph.org}/${BACKEND_IMAGE_PATH:-emi/thermograph/backend}:${BACKEND_IMAGE_TAG:?required} image: ${REGISTRY_HOST:-git.thermograph.org}/${BACKEND_IMAGE_PATH:-jinemi/thermograph/backend}:${BACKEND_IMAGE_TAG:?required}
entrypoint: ["/host/env-entrypoint.sh"] entrypoint: ["/host/env-entrypoint.sh"]
environment: environment:
THERMOGRAPH_DATABASE_URL: postgresql+asyncpg://thermograph:${POSTGRES_PASSWORD}@db:5432/thermograph THERMOGRAPH_DATABASE_URL: postgresql+asyncpg://thermograph:${POSTGRES_PASSWORD}@db:5432/thermograph
@ -153,7 +153,7 @@ services:
# healthy and answers 503/404, and web falls through to NASA — the lake is # healthy and answers 503/404, and web falls through to NASA — the lake is
# an accelerator, never a point of failure. # an accelerator, never a point of failure.
lake: lake:
image: ${REGISTRY_HOST:-git.thermograph.org}/${BACKEND_IMAGE_PATH:-emi/thermograph/backend}:${BACKEND_IMAGE_TAG:?required} image: ${REGISTRY_HOST:-git.thermograph.org}/${BACKEND_IMAGE_PATH:-jinemi/thermograph/backend}:${BACKEND_IMAGE_TAG:?required}
entrypoint: ["/host/env-entrypoint.sh"] entrypoint: ["/host/env-entrypoint.sh"]
environment: environment:
THERMOGRAPH_ROLE: lake THERMOGRAPH_ROLE: lake
@ -186,7 +186,7 @@ services:
# (/usr/local/bin/thermograph-daemon, built into the backend image) and the # (/usr/local/bin/thermograph-daemon, built into the backend image) and the
# app share the /internal/* API contract, so one BACKEND_IMAGE_TAG rolls # app share the /internal/* API contract, so one BACKEND_IMAGE_TAG rolls
# them together and they can never skew. # them together and they can never skew.
image: ${REGISTRY_HOST:-git.thermograph.org}/${BACKEND_IMAGE_PATH:-emi/thermograph/backend}:${BACKEND_IMAGE_TAG:?required} image: ${REGISTRY_HOST:-git.thermograph.org}/${BACKEND_IMAGE_PATH:-jinemi/thermograph/backend}:${BACKEND_IMAGE_TAG:?required}
# Not env-entrypoint.sh: that shim's handoff execs the image's # Not env-entrypoint.sh: that shim's handoff execs the image's
# deploy/entrypoint.sh (Alembic + uvicorn) whenever it exists, which is # deploy/entrypoint.sh (Alembic + uvicorn) whenever it exists, which is
# exactly what this service must never run — migrations belong to the # exactly what this service must never run — migrations belong to the
@ -243,7 +243,7 @@ services:
failure_action: rollback failure_action: rollback
frontend: frontend:
image: ${REGISTRY_HOST:-git.thermograph.org}/${FRONTEND_IMAGE_PATH:-emi/thermograph/frontend}:${FRONTEND_IMAGE_TAG:?required} image: ${REGISTRY_HOST:-git.thermograph.org}/${FRONTEND_IMAGE_PATH:-jinemi/thermograph/frontend}:${FRONTEND_IMAGE_TAG:?required}
entrypoint: ["/host/env-entrypoint.sh"] entrypoint: ["/host/env-entrypoint.sh"]
# REQUIRED, not cosmetic: overriding `entrypoint:` with no `command:` drops # REQUIRED, not cosmetic: overriding `entrypoint:` with no `command:` drops
# the image's own CMD entirely (Docker/Swarm semantics, not merged) -- # the image's own CMD entirely (Docker/Swarm semantics, not merged) --

View file

@ -6,7 +6,7 @@
# build-push.yml -- backend = ${BACKEND_IMAGE_PATH}, frontend = # build-push.yml -- backend = ${BACKEND_IMAGE_PATH}, frontend =
# ${FRONTEND_IMAGE_PATH}, tagged independently by BACKEND_IMAGE_TAG / # ${FRONTEND_IMAGE_PATH}, tagged independently by BACKEND_IMAGE_TAG /
# FRONTEND_IMAGE_TAG. This replaces the earlier Stage-4 model where both # FRONTEND_IMAGE_TAG. This replaces the earlier Stage-4 model where both
# containers shared the single emi/thermograph/app image and # containers shared the single admin_emi/thermograph/app image and
# THERMOGRAPH_SERVICE_ROLE picked the process; the split Dockerfiles now start # THERMOGRAPH_SERVICE_ROLE picked the process; the split Dockerfiles now start
# the right process directly (frontend the thermograph-frontend Go binary, # the right process directly (frontend the thermograph-frontend Go binary,
# backend entrypoint.sh), so a backend deploy and a frontend deploy are fully # backend entrypoint.sh), so a backend deploy and a frontend deploy are fully
@ -99,10 +99,10 @@ services:
# No `build:` here -- infra holds no Dockerfile; each service's Dockerfile # No `build:` here -- infra holds no Dockerfile; each service's Dockerfile
# lives in its own app repo. deploy.sh sets BACKEND_IMAGE_TAG to the SHA # lives in its own app repo. deploy.sh sets BACKEND_IMAGE_TAG to the SHA
# build-push.yml pushed for the backend commit being deployed; local dev # build-push.yml pushed for the backend commit being deployed; local dev
# builds `emi/thermograph/backend:local` from the backend repo (see # builds `jinemi/thermograph/backend:local` from the backend repo (see
# infra Makefile) and this pulls/uses it. BACKEND_IMAGE_PATH/TAG are # infra Makefile) and this pulls/uses it. BACKEND_IMAGE_PATH/TAG are
# independent of the frontend's, so the two services deploy separately. # independent of the frontend's, so the two services deploy separately.
image: ${REGISTRY_HOST:-git.thermograph.org}/${BACKEND_IMAGE_PATH:-emi/thermograph/backend}:${BACKEND_IMAGE_TAG:-local} image: ${REGISTRY_HOST:-git.thermograph.org}/${BACKEND_IMAGE_PATH:-jinemi/thermograph/backend}:${BACKEND_IMAGE_TAG:-local}
depends_on: depends_on:
db: db:
condition: service_healthy condition: service_healthy
@ -180,7 +180,7 @@ services:
# without them the service stays healthy and every read falls through to # without them the service stays healthy and every read falls through to
# NASA. Never published on a host port: only the backend talks to it. # NASA. Never published on a host port: only the backend talks to it.
lake: lake:
image: ${REGISTRY_HOST:-git.thermograph.org}/${BACKEND_IMAGE_PATH:-emi/thermograph/backend}:${BACKEND_IMAGE_TAG:-local} image: ${REGISTRY_HOST:-git.thermograph.org}/${BACKEND_IMAGE_PATH:-jinemi/thermograph/backend}:${BACKEND_IMAGE_TAG:-local}
environment: environment:
THERMOGRAPH_ROLE: lake THERMOGRAPH_ROLE: lake
PORT: 8141 PORT: 8141
@ -207,7 +207,7 @@ services:
# share the /internal/* API contract, so they must roll together — a # share the /internal/* API contract, so they must roll together — a
# separate tag could skew them. It owns the Discord gateway websocket and # separate tag could skew them. It owns the Discord gateway websocket and
# the recurring-job timers; anything needing data calls back into backend. # the recurring-job timers; anything needing data calls back into backend.
image: ${REGISTRY_HOST:-git.thermograph.org}/${BACKEND_IMAGE_PATH:-emi/thermograph/backend}:${BACKEND_IMAGE_TAG:-local} image: ${REGISTRY_HOST:-git.thermograph.org}/${BACKEND_IMAGE_PATH:-jinemi/thermograph/backend}:${BACKEND_IMAGE_TAG:-local}
# Bypass deploy/entrypoint.sh entirely: that script runs Alembic, and the # Bypass deploy/entrypoint.sh entirely: that script runs Alembic, and the
# backend service's boot already owns migrations — two containers racing # backend service's boot already owns migrations — two containers racing
# `alembic upgrade head` against one database is a real hazard, not a # `alembic upgrade head` against one database is a real hazard, not a
@ -262,7 +262,7 @@ services:
# frontend/Dockerfile (which starts the thermograph-frontend Go binary # frontend/Dockerfile (which starts the thermograph-frontend Go binary
# directly -- no THERMOGRAPH_SERVICE_ROLE process-picking). Independent # directly -- no THERMOGRAPH_SERVICE_ROLE process-picking). Independent
# FRONTEND_IMAGE_TAG so a frontend deploy never disturbs the backend's tag. # FRONTEND_IMAGE_TAG so a frontend deploy never disturbs the backend's tag.
image: ${REGISTRY_HOST:-git.thermograph.org}/${FRONTEND_IMAGE_PATH:-emi/thermograph/frontend}:${FRONTEND_IMAGE_TAG:-local} image: ${REGISTRY_HOST:-git.thermograph.org}/${FRONTEND_IMAGE_PATH:-jinemi/thermograph/frontend}:${FRONTEND_IMAGE_TAG:-local}
# Its own register() fetches the IndexNow key from backend at boot -- must # Its own register() fetches the IndexNow key from backend at boot -- must
# wait for a real, healthy backend, not just a started container. # wait for a real, healthy backend, not just a started container.
depends_on: depends_on:

View file

@ -50,7 +50,7 @@ new network exposure and no interactive steps.
**As originally written** (pre vps1/vps2 split; kept for history — see the **As originally written** (pre vps1/vps2 split; kept for history — see the
note at the top of this document): note at the top of this document):
- Monorepo `emi/thermograph` (domain dirs: `backend/ frontend/ infra/ - Monorepo `Jinemi/thermograph` (domain dirs: `backend/ frontend/ infra/
observability/`). Ops tooling lives in `infra/ops/`. observability/`). Ops tooling lives in `infra/ops/`.
- Environments: **dev** = LAN compose stack on the dev machine; **beta** = - Environments: **dev** = LAN compose stack on the dev machine; **beta** =
`75.119.132.91`; **prod** = `169.58.46.181`. SSH as `agent` with `75.119.132.91`; **prod** = `169.58.46.181`. SSH as `agent` with

View file

@ -42,7 +42,7 @@ infra repo**, not the app repo — the "branch" column above is which app-repo
tag an environment is meant to track conceptually; the actual pinned versions tag an environment is meant to track conceptually; the actual pinned versions
are `var.hosts[*].environments[*].backend_image_tag` / `frontend_image_tag` are `var.hosts[*].environments[*].backend_image_tag` / `frontend_image_tag`
(e.g. `"sha-<12 hex>"` each — the app is two separately-published images, (e.g. `"sha-<12 hex>"` each — the app is two separately-published images,
`emi/thermograph-backend/app` and `emi/thermograph-frontend/app`), since the `admin_emi/thermograph-backend/app` and `admin_emi/thermograph-frontend/app`), since the
host has no app checkout to derive a tag from. `app_dir` is required with no host has no app checkout to derive a tag from. `app_dir` is required with no
default specifically so vps2's two environments can never collide on the same default specifically so vps2's two environments can never collide on the same
checkout path (prod: `/opt/thermograph`, beta: `/opt/thermograph-beta`). The checkout path (prod: `/opt/thermograph`, beta: `/opt/thermograph-beta`). The

View file

@ -239,7 +239,7 @@ resource "null_resource" "host" {
# then bring the stack up on the EXPLICIT per-service app image tags there's no # then bring the stack up on the EXPLICIT per-service app image tags there's no
# app checkout on this host to derive a tag from (see var.backend_image_tag / # app checkout on this host to derive a tag from (see var.backend_image_tag /
# frontend_image_tag). The compose file reads BACKEND_IMAGE_TAG / FRONTEND_IMAGE_TAG # frontend_image_tag). The compose file reads BACKEND_IMAGE_TAG / FRONTEND_IMAGE_TAG
# (for emi/thermograph-backend/app and emi/thermograph-frontend/app), so we export # (for admin_emi/thermograph-backend/app and admin_emi/thermograph-frontend/app), so we export
# those, not the old single IMAGE_PATH/IMAGE_TAG. docker runs as root (sources both # those, not the old single IMAGE_PATH/IMAGE_TAG. docker runs as root (sources both
# env files in the same shell) so it never depends on the docker group membership # env files in the same shell) so it never depends on the docker group membership
# taking effect in this session. # taking effect in this session.
@ -257,7 +257,7 @@ resource "null_resource" "host" {
render_thermograph_secrets ${var.app_dir} render_thermograph_secrets ${var.app_dir}
set -a; . /etc/thermograph-topology.env; . /etc/thermograph.env; set +a set -a; . /etc/thermograph-topology.env; . /etc/thermograph.env; set +a
export REGISTRY_HOST="git.thermograph.org" BACKEND_IMAGE_TAG="${var.backend_image_tag}" FRONTEND_IMAGE_TAG="${var.frontend_image_tag}" export REGISTRY_HOST="git.thermograph.org" BACKEND_IMAGE_TAG="${var.backend_image_tag}" FRONTEND_IMAGE_TAG="${var.frontend_image_tag}"
echo "$REGISTRY_TOKEN" | docker login "$REGISTRY_HOST" --username emi --password-stdin echo "$REGISTRY_TOKEN" | docker login "$REGISTRY_HOST" --username admin_emi --password-stdin
docker compose ${local.compose_flags} pull backend frontend docker compose ${local.compose_flags} pull backend frontend
docker compose ${local.compose_flags} up -d --remove-orphans docker compose ${local.compose_flags} up -d --remove-orphans
' '

View file

@ -31,12 +31,12 @@ variable "git_branch" {
} }
variable "backend_image_tag" { variable "backend_image_tag" {
description = "Backend image tag to pull (emi/thermograph-backend/app), e.g. \"sha-<12 hex>\" (build-push.yml's tag for the backend-repo commit) or a semver tag. The host has no app-repo checkout to derive this from, so it's always explicit." description = "Backend image tag to pull (admin_emi/thermograph-backend/app), e.g. \"sha-<12 hex>\" (build-push.yml's tag for the backend-repo commit) or a semver tag. The host has no app-repo checkout to derive this from, so it's always explicit."
type = string type = string
} }
variable "frontend_image_tag" { variable "frontend_image_tag" {
description = "Frontend image tag to pull (emi/thermograph-frontend/app), e.g. \"sha-<12 hex>\" (build-push.yml's tag for the frontend-repo commit) or a semver tag. Always explicit, same as backend_image_tag." description = "Frontend image tag to pull (admin_emi/thermograph-frontend/app), e.g. \"sha-<12 hex>\" (build-push.yml's tag for the frontend-repo commit) or a semver tag. Always explicit, same as backend_image_tag."
type = string type = string
} }

View file

@ -151,13 +151,13 @@ hosts = {
# } # }
# Optional overrides (shown with their defaults): # Optional overrides (shown with their defaults):
# repo_url = "https://git.thermograph.org/emi/thermograph-infra.git" # repo_url = "https://git.thermograph.org/admin_emi/thermograph-infra.git"
# app_port = 8137 # app_port = 8137
# frontend_port = 8080 # frontend_port = 8080
# #
# thermograph-infra is a PRIVATE repo, so a host cloning it for the first time # thermograph-infra is a PRIVATE repo, so a host cloning it for the first time
# needs read credentials embedded in repo_url, e.g. a Forgejo deploy token: # needs read credentials embedded in repo_url, e.g. a Forgejo deploy token:
# repo_url = "https://deploy:REPLACE_WITH_TOKEN@git.thermograph.org/emi/thermograph-infra.git" # repo_url = "https://deploy:REPLACE_WITH_TOKEN@git.thermograph.org/admin_emi/thermograph-infra.git"
# --------------------------------------------------------------------------------- # ---------------------------------------------------------------------------------
# Self-hosted Open-Meteo archive (only used by environments with openmeteo = true) # Self-hosted Open-Meteo archive (only used by environments with openmeteo = true)

View file

@ -54,8 +54,8 @@ variable "hosts" {
git_branch = string # this INFRA repo's branch the checkout is reset to git_branch = string # this INFRA repo's branch the checkout is reset to
# Which app images to run, e.g. "sha-<12 hex>" (each matching build-push.yml's tag # Which app images to run, e.g. "sha-<12 hex>" (each matching build-push.yml's tag
# for the commit that app repo built) or a semver tag on a release push. The app is # for the commit that app repo built) or a semver tag on a release push. The app is
# TWO separately-published images now emi/thermograph-backend/app and # TWO separately-published images now admin_emi/thermograph-backend/app and
# emi/thermograph-frontend/app pinned independently. Required, no default: the # admin_emi/thermograph-frontend/app pinned independently. Required, no default: the
# host's checkout is this infra repo, not the app repos, so there's no "current # host's checkout is this infra repo, not the app repos, so there's no "current
# commit" to derive a tag from; both must be explicit. Bump these (via a normal # commit" to derive a tag from; both must be explicit. Bump these (via a normal
# tfvars edit + apply) whenever an app repo ships a commit you want this # tfvars edit + apply) whenever an app repo ships a commit you want this
@ -127,9 +127,9 @@ variable "gcp_hosts" {
} }
variable "repo_url" { variable "repo_url" {
description = "Git remote to clone from when a host has no checkout yet. Points at THIS repo (thermograph-infra) now, not the app repo -- the app's own source is never checked out on a host; only its published registry images are pulled (see var.hosts[*].backend_image_tag / frontend_image_tag). thermograph-infra is a private repo, so this typically needs embedded read credentials, e.g. a Forgejo deploy token: \"https://<token-name>:<token>@git.thermograph.org/emi/thermograph-infra.git\"." description = "Git remote to clone from when a host has no checkout yet. Points at THIS repo (thermograph-infra) now, not the app repo -- the app's own source is never checked out on a host; only its published registry images are pulled (see var.hosts[*].backend_image_tag / frontend_image_tag). thermograph-infra is a private repo, so this typically needs embedded read credentials, e.g. a Forgejo deploy token: \"https://<token-name>:<token>@git.thermograph.org/admin_emi/thermograph-infra.git\"."
type = string type = string
default = "https://git.thermograph.org/emi/thermograph-infra.git" default = "https://git.thermograph.org/admin_emi/thermograph-infra.git"
sensitive = true sensitive = true
} }

View file

@ -62,7 +62,7 @@ persistent, queryable, fleet-wide log store and UI.
> ### ⚠️ Merging is not deploying > ### ⚠️ Merging is not deploying
> >
> `/opt/observability` on vps1 (and vps2) is a checkout of the **archived** > `/opt/observability` on vps1 (and vps2) is a checkout of the **archived**
> `emi/thermograph-observability` repo. It can never `git pull` again — the > `admin_emi/thermograph-observability` repo. It can never `git pull` again — the
> content now lives here, in the mono repo, under `observability/`. A previous > content now lives here, in the mono repo, under `observability/`. A previous
> observability PR merged and had **zero live effect** for exactly this reason. > observability PR merged and had **zero live effect** for exactly this reason.
> >