Commit graph

541 commits

Author SHA1 Message Date
Emi Griffith
2e753f2c6f deploy: adapt scripts + compose to the monorepo host checkout
/opt/thermograph becomes a monorepo checkout: deploy.sh gains INFRA_DIR (git
ops at the root, compose work cd'd into infra/), lock/tags/render paths move
under infra/deploy/, image-path defaults become emi/thermograph/backend|
frontend across compose, stack, and the tag-prune. docker-compose.yml pins
name: thermograph -- without it compose run from .../infra derives project
"infra" and recreates the whole stack beside the running one;
deploy-dev.sh pins COMPOSE_PROJECT_NAME=thermograph-dev (env wins over the
file key) to keep LAN dev's separate project.
2026-07-22 22:11:33 -07:00
Emi Griffith
7b2db07722 CI: port the split repos' workflows to per-domain path-filtered monorepo pipelines
One root workflow set replaces the four repos' copies (deleted -- root-only
is where Forgejo reads them, and dead copies are a trap): per-domain
build-push with explicit image paths (emi/thermograph/backend|frontend; the
old github.repository-derived path collides in a monorepo), path-filtered
per-domain beta/prod/dev deploys, a domain-input reusable build check, a
single always-reporting PR gate (path-filtered required checks deadlock
auto-merge), a new infra-sync pipeline (host checkout + secrets render on
infra/** pushes), and ports of secrets-guard / ops-cron /
observability-validate to monorepo paths.
2026-07-22 22:11:33 -07:00
emi
f0e87b78ba Merge pull request 'Merge main into dev: absorb responsiveness hardening; migrate its new tests to the unit tier' (#6) from reconcile-dev-with-main into dev 2026-07-23 05:07:43 +00:00
emi
017997a656 Merge pull request 'Merge main into dev: absorb hardening line + resolve the duplicate bot port' (#11) from reconcile-dev-with-main into dev 2026-07-23 05:07:40 +00:00
Emi Griffith
f2fd8f6835 Subtree-merge thermograph-observability (origin/main) into observability/
git-subtree-dir: observability
git-subtree-mainline: ae1d9bb534
git-subtree-split: 19e74af9ca
2026-07-22 22:01:11 -07:00
Emi Griffith
ae1d9bb534 Subtree-merge thermograph-infra (origin/main) into infra/
git-subtree-dir: infra
git-subtree-mainline: d6df04eab2
git-subtree-split: 99b4b3f78d
2026-07-22 22:01:11 -07:00
Emi Griffith
d6df04eab2 Subtree-merge thermograph-frontend (origin/main) into frontend/
git-subtree-dir: frontend
git-subtree-mainline: a4be7066e5
git-subtree-split: 3a98146da4
2026-07-22 22:01:11 -07:00
Emi Griffith
a4be7066e5 Subtree-merge thermograph-backend (origin/main) into backend/
git-subtree-dir: backend
git-subtree-mainline: 6723fc0326
git-subtree-split: 83c2e05b96
2026-07-22 22:01:11 -07:00
Emi Griffith
6723fc0326 Monorepo root: reunify the split app repos (docs stays separate) 2026-07-22 22:01:11 -07:00
Emi Griffith
9afc19eb2f Merge main into dev: absorb responsiveness hardening (#4)
# Conflicts:
#	tests/test_content.py
2026-07-22 21:56:11 -07:00
Emi Griffith
6c3d7b8215 Merge main into dev: absorb notifier/PG hardening + the hardened bot port
main received the perf hardening line (#5, #8) and its own landing of the
Discord gateway bot (#7) while dev carried a parallel port of the same bot
(#3). The two implementations are near-identical ports of the same archived
source; main's includes one extra hardening (grading calls moved off the
gateway event loop via asyncio.to_thread) and broader tests, so bot files
resolve wholesale to main's side. dev keeps its test-runner/smoke tooling and
the run-tests-in-image CI, which main lacks.

# Conflicts:
#	notifications/discord_bot.py
#	tests/notifications/test_discord_bot.py
#	web/app.py
2026-07-22 21:54:18 -07:00
emi
99b4b3f78d Mail docs: stack-mode gateway + postfix umbrella-unit gotcha (#12) 2026-07-23 04:45:15 +00:00
emi
6f75762c89 Document THERMOGRAPH_DISCORD_BOT (gateway-bot enable flag) in env example + key-gaps (#11) 2026-07-23 04:41:49 +00:00
emi
3a98146da4 Harden top pages against backend blips, pool the content-API client (#4) 2026-07-23 04:41:43 +00:00
emi
83c2e05b96 Land the Discord gateway bot (port + hardening) (#7) 2026-07-23 04:41:32 +00:00
emi
215c46cea7 Bound Postgres connections, add rate-limit/timeout guards, move revgeo off the threadpool (#8) 2026-07-23 04:41:26 +00:00
emi
d67476ebbf notifier: bound push/Discord sends, cap pass duration, fix key-gen race (#5) 2026-07-23 04:41:19 +00:00
emi
f2270100bb Stack rehearsal fixes: interpolation, network ownership, plain-CMD images (#10) 2026-07-23 04:21:10 +00:00
emi
9cd24387f2 Swarm stack for prod: autoscaled web tier (1-3), worker split, loopback LB (#9) 2026-07-23 04:13:12 +00:00
emi
d9d72e4e8d CI: run the test suite inside the built image (#3) 2026-07-23 00:59:06 +00:00
emi
90a7148165 CI: run the test suite inside the built image (#4) 2026-07-23 00:59:03 +00:00
emi
b412b7352a test: self-contained two-tier suite + pull-the-backend-image harness (#2) 2026-07-23 00:40:18 +00:00
emi
c2ce93fad6 test: reproducible local runner + image boot-smoke (#2) 2026-07-23 00:40:13 +00:00
emi
d02c0f719f render-secrets: chown rendered env to the deploy user on the sudo-install path (#7) 2026-07-23 00:38:15 +00:00
emi
97d0e53d77 Port the Discord gateway bot from the archived app repo (its PR #23) (#3) 2026-07-22 23:58:49 +00:00
emi
f5b8b26590 thermograph.env.example: document THERMOGRAPH_DISCORD_BOT (#8) 2026-07-22 23:58:02 +00:00
emi
e44d1603b6 deploy.sh: serialize concurrent deploys with flock; GC old app-image tags (#6) 2026-07-22 23:39:29 +00:00
emi
e929f4606f Docs pass after the app-repo archive; sync key-gaps skill; add CLAUDE.md (#5) 2026-07-22 23:36:21 +00:00
emi
5fd96552f7 Port mail provisioning from the archived app repo (+ mesh-listener knobs) (#4) 2026-07-22 23:31:33 +00:00
emi
8f98bab89f terraform: per-service backend/frontend image tags (two-image contract) (#2) 2026-07-22 23:27:13 +00:00
emi
dde342f01b Port the fixed ops-cron: prod backups died with the app-repo archive (#3) 2026-07-22 23:26:35 +00:00
Emi Griffith
59e7517747 deploy.sh: incoming image tag wins over .image-tags.env; health via container healthcheck
Two bugs surfaced wiring dev auto-deploy:
1. Sourcing .image-tags.env clobbered the caller's incoming *_IMAGE_TAG. The
   first backend-only deploy persists FRONTEND_IMAGE_TAG=local (sibling unknown);
   the next frontend deploy then sourced that and pulled :local -> 'manifest
   unknown'. Now the incoming env is captured before sourcing and re-applied.
2. Health-checked services via a host-port curl, but the dev overlay leaves the
   frontend port unpublished (reached via the backend proxy) -> false failure.
   Now polls each container's own HEALTHCHECK status via docker inspect.
2026-07-22 15:48:23 -07:00
Emi Griffith
418f4e0631 deploy.sh: make registry login conditional on REGISTRY_TOKEN
The CI/SSH deploy paths (deploy.yml/deploy-prod.yml over SSH, deploy-dev on the
LAN runner) don't pass REGISTRY_TOKEN and rely on the host already being
docker-logged-in; an unconditional login with an empty token aborts deploy.sh
under set -e. Skip the login when no token is set and trust the host cred;
pull still fails loudly on a real auth problem.
2026-07-22 15:27:30 -07:00
Emi Griffith
c202eb45a0 compose: mount appdata at /state (not /app/data) to stop shadowing the data/ package
The split backend's Python package data/ lives at /app/data; mounting the
appdata runtime volume there erased data/*.py and broke import at boot. Point
THERMOGRAPH_DATA_DIR + the appdata mount + the singleton lock at /state,
outside the code tree. Pairs with thermograph-backend paths.py making
DATA_DIR/LOGS_DIR env-overridable.
2026-07-22 14:46:22 -07:00
Emi Griffith
7b7727f234 paths.py: make DATA_DIR/LOGS_DIR env-overridable to avoid /app/data volume collision
After the repo split the code lives at the image root, so the Python package
data/ and the runtime data dir <root>/data are the same path (/app/data). The
deploy compose mounts the appdata volume there, shadowing data/*.py so
'import data.climate' fails at container boot. THERMOGRAPH_DATA_DIR (and
_LOGS_DIR) now let the container point runtime state at a path outside the code
tree; local dev is unchanged (defaults to <root>/data).
2026-07-22 14:45:26 -07:00
Emi Griffith
86c8e6905c Reconcile render-secrets.sh: sudo-read age key + in-place-write /etc/thermograph.env
The split branch carried the #34-era render-secrets.sh, missing two fixes
required for prod/beta deploy: (1) sudo-read the root-owned 0400 age key into
SOPS_AGE_KEY when the deploy user can't read it directly; (2) in-place-write
/etc/thermograph.env when it's group-writable (beta's non-root 'deploy' user)
instead of only install(1). Without these, deploy.sh's render fails on both
hosts. Flagged by the terraform-layer workstream.
2026-07-22 12:56:15 -07:00
Emi Griffith
75201ef533 Add LAN-dev registry-pull artifacts: docker-compose.dev.yml + deploy-dev.sh 2026-07-22 12:20:05 -07:00
Emi Griffith
517bbdf77c Add dev-branch flow: build.yml workflow_call + pr-build.yml + deploy-dev.yml (LAN dev) 2026-07-22 12:18:36 -07:00
Emi Griffith
8555e705a9 Add dev-branch flow: build.yml workflow_call + pr-build.yml + deploy-dev.yml (LAN dev) 2026-07-22 12:18:15 -07:00
Emi Griffith
92e6f6a282 Port e77460e Plain-language weather insight sentences from monorepo (drift reconciliation, frontend-only)
Ports the frontend/ portion of monorepo PR #50: a "what to expect" insight
card on the Weekly results (app.js, from data.climatology's rain_freq/
sun_freq) and a matching per-month-selection insight card on the Calendar
(calendar.js, from data.months_climate, incl. the cross-month "Nx more
likely to rain" comparison), plus the calendar.html mount point and
style.css card styling. The backend portion of #50 (climate.py/grading.py
computing rain_freq/sun_freq/months_climate) lives in the separate backend
repo and is out of scope here; this frontend code just consumes those
fields when the paired backend supports them. No fetch URLs were touched by
this change.
2026-07-22 12:10:48 -07:00
Emi Griffith
0af523bc34 Port f5fc288 Consolidate shared frontend render/fetch helpers from monorepo (drift reconciliation)
Ports monorepo PR #43: cache.js gains a shared loadView() (spinner-delay +
supersession + SWR wiring) used by app.js/score.js instead of each
duplicating that dance around getJSON, and shared.js gains tierKeySegs()/
GUIDE_LINK used by app.js/calendar.js instead of each hand-building the
tier-key markup. Resolved against this branch's uv()/API_VERSION work: every
fetch URL touched here stays routed through uv(...) (grade/score already
were) rather than reverting to a raw api/v2/... literal.
2026-07-22 12:08:57 -07:00
Emi Griffith
4fe750f10c Port e77460e Plain-language weather insight sentences (backend half) from monorepo (drift reconciliation)
The frontend/ half of this commit (calendar.js, calendar.html, app.js,
style.css) is out of scope for this repo; a separate agent handles the
split frontend repo.

Claude-Session: https://claude.ai/code/session_01RdARHDJaYC1wSQRTYM6t3Z
2026-07-22 12:08:24 -07:00
Emi Griffith
cc0b1e298f Port a0ec4bc Extract the shared graded-window tail in views.py from monorepo (drift reconciliation)
Claude-Session: https://claude.ai/code/session_01RdARHDJaYC1wSQRTYM6t3Z
2026-07-22 12:08:13 -07:00
Emi Griffith
8cec935b06 Port 983bceb Extract the grading day-of-year window helpers from monorepo (drift reconciliation)
Claude-Session: https://claude.ai/code/session_01RdARHDJaYC1wSQRTYM6t3Z
2026-07-22 12:08:03 -07:00
Emi Griffith
b1869af70a Port 3753bbc Refactor climate.py source->frame builders from monorepo (drift reconciliation)
Claude-Session: https://claude.ai/code/session_01RdARHDJaYC1wSQRTYM6t3Z
2026-07-22 12:07:54 -07:00
Emi Griffith
f830bce4d9 Port 871ab9c Route notifier output to Discord channels via the bot from monorepo (drift reconciliation)
Note: this commit's deploy/thermograph.env.example changes live at the
monorepo root (outside backend/) and are out of scope for this split repo.

Claude-Session: https://claude.ai/code/session_01RdARHDJaYC1wSQRTYM6t3Z
2026-07-22 12:07:46 -07:00
Emi Griffith
cd760f7e44 Port eb8f039 Activity-stream logging: notifier, auth, delivery, lifecycle, 500s, slow requests from monorepo (drift reconciliation)
Claude-Session: https://claude.ai/code/session_01RdARHDJaYC1wSQRTYM6t3Z
2026-07-22 12:07:35 -07:00
Emi Griffith
787461656e Port 3504dd6 Send notifier push/Discord DMs concurrently instead of one at a time from monorepo (drift reconciliation)
Claude-Session: https://claude.ai/code/session_01RdARHDJaYC1wSQRTYM6t3Z
2026-07-22 12:07:25 -07:00
Emi Griffith
83b6e2ebc0 Port a57d753 Move blocking calls off the event loop in async request handlers from monorepo (drift reconciliation)
Claude-Session: https://claude.ai/code/session_01RdARHDJaYC1wSQRTYM6t3Z
2026-07-22 12:07:15 -07:00
Emi Griffith
290187db6b Add frontend-scoped CLAUDE.md + README + DESIGN.md, relocate tools/
Claude-Session: https://claude.ai/code/session_01RdARHDJaYC1wSQRTYM6t3Z
2026-07-22 11:59:59 -07:00