5 Commits

Author SHA1 Message Date
f4e7fc7e20 Merge branch 'main' into deploy/compose-dev-prod-split 2026-07-11 23:45:28 +00:00
6d4cd91bcc deploy: split build into docker-compose.dev.yml overlay
Make the base docker-compose.yml strictly production-shaped — image: only, no
build: — so a production host can only ever pull, never accidentally build
(compose gives build precedence for `up --build`/`build`, which mixed the two
modes). Local builds move to an explicit, non-auto-loaded overlay.

  Production:   docker compose pull && docker compose up -d
  Development:  docker compose -f docker-compose.yml -f docker-compose.dev.yml up -d --build

Verified with `docker compose config`: base renders image-only (no build) for
both services; the dev overlay adds build back (app -> Dockerfile,
bot -> bot/Dockerfile). README quick-start updated to the two-file flow.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0114TpmrNW4wNXsHq5CR72jQ
2026-07-11 18:42:05 -05:00
3628268dda Merge pull request 'deploy: pull prebuilt registry images in compose (IMAGE_TAG)' (#54) from deploy/compose-use-registry-images into main
All checks were successful
Build container images / build (push) Successful in 56s
Reviewed-on: UOM/website#54
Reviewed-by: Colby Whitlock <whitlocktech@gmail.com>
2026-07-11 23:36:55 +00:00
25ff5aa836 deploy: pull prebuilt registry images in compose (IMAGE_TAG)
Point the `app` and `bot` services at the images published to the Gitea
registry by the build-images workflow, so deploys pull instead of building:

  image: gitea.whitlocktech.com/uom/website-app:${IMAGE_TAG:-latest}
  image: gitea.whitlocktech.com/uom/website-bot:${IMAGE_TAG:-latest}

`build:` is kept, so `up --build` still works locally; the server runs
`docker compose pull && up -d`. IMAGE_TAG defaults to `latest` for routine
deploys and pins to an immutable `sha-<7>` build for reproducible deploys /
rollback — no per-deploy compose edits. Documented in .env.example + README.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0114TpmrNW4wNXsHq5CR72jQ
2026-07-11 18:34:31 -05:00
042a151358 Merge pull request 'ci: build & publish app + bot images to Gitea registry on merge' (#53) from ci/gitea-actions-image-build into main
All checks were successful
Build container images / build (push) Successful in 24s
Reviewed-on: UOM/website#53
Reviewed-by: Colby Whitlock <whitlocktech@gmail.com>
2026-07-11 23:01:04 +00:00
4 changed files with 59 additions and 7 deletions

View File

@@ -1,6 +1,13 @@
# ─── UOMysticmoon — root environment (used by docker-compose) ─── # ─── UOMysticmoon — root environment (used by docker-compose) ───
# Copy to .env and fill in. NEVER commit the real .env. # Copy to .env and fill in. NEVER commit the real .env.
# Container image tag pulled by docker-compose (app + bot). Published by the
# Gitea Actions workflow on every merge to main as `latest` and `sha-<7>`.
# Leave as `latest` for routine deploys; pin to a specific build for a
# reproducible deploy or rollback, e.g. IMAGE_TAG=sha-042a151.
# Deploy: `docker compose pull && docker compose up -d`.
IMAGE_TAG=latest
# App # App
NODE_ENV=production NODE_ENV=production
PORT=3000 PORT=3000

View File

@@ -92,8 +92,10 @@ UOMSITE/
### Option A — Docker Compose (full stack) ### Option A — Docker Compose (full stack)
The simplest way to run everything. The image installs server deps, **builds the React client**, `docker-compose.yml` is **production-shaped**: it *pulls* the prebuilt `app` and `bot` images from
and Express serves it; MariaDB runs in its own container; tables + defaults + the first admin are the Gitea container registry (published by `.gitea/workflows/build-images.yml` on every merge to
`main`) — it never builds. Each image already bundles the server deps and the built React client,
which Express serves. MariaDB runs in its own container; tables + defaults + the first admin are
created automatically on first boot. created automatically on first boot.
```bash ```bash
@@ -103,7 +105,9 @@ cp .env.example .env
# JWT_SECRET (a long random string) # JWT_SECRET (a long random string)
# ADMIN_USERNAME, ADMIN_PASSWORD (your first admin login) # ADMIN_USERNAME, ADMIN_PASSWORD (your first admin login)
docker compose up -d --build docker compose pull && docker compose up -d # IMAGE_TAG defaults to `latest`
# pin a specific build (reproducible deploy / rollback):
IMAGE_TAG=sha-042a151 docker compose pull && docker compose up -d
``` ```
- App: **http://localhost:3000** (binds `0.0.0.0`) - App: **http://localhost:3000** (binds `0.0.0.0`)
@@ -111,6 +115,16 @@ docker compose up -d --build
- Logs: `docker compose logs -f app` (and `./logs/app.log` on the host) - Logs: `docker compose logs -f app` (and `./logs/app.log` on the host)
- Stop: `docker compose down` (add `-v` to also wipe the database + uploads volumes) - Stop: `docker compose down` (add `-v` to also wipe the database + uploads volumes)
**Build the images locally instead of pulling** (offline, or to test an unmerged change) — overlay
the dev file, which adds `build:` back:
```bash
docker compose -f docker-compose.yml -f docker-compose.dev.yml up -d --build
```
Keeping `build:` out of the base file means a production host can only ever pull — it can never
accidentally build.
### Option B — Local development (hot reload) ### Option B — Local development (hot reload)
Run the API and the Vite dev server separately. The Vite server proxies `/api` and `/uploads` Run the API and the Vite dev server separately. The Vite server proxies `/api` and `/uploads`

24
docker-compose.dev.yml Normal file
View File

@@ -0,0 +1,24 @@
# Development overlay — build the app + bot images locally instead of pulling the
# prebuilt ones from the Gitea registry.
#
# The base docker-compose.yml is production-shaped (image: only, no build:), so a
# production host can never accidentally build — it only pulls. Use this overlay
# EXPLICITLY for local work (it is not auto-loaded like docker-compose.override.yml
# would be):
#
# docker compose -f docker-compose.yml -f docker-compose.dev.yml up -d --build
#
# Production stays:
#
# docker compose pull && docker compose up -d
#
# The `image:` tags inherited from the base file double as the local build tags,
# so a built image and a pulled one are interchangeable.
services:
app:
build: .
bot:
build:
context: .
dockerfile: bot/Dockerfile

View File

@@ -21,7 +21,14 @@ services:
# - "3306:3306" # - "3306:3306"
app: app:
build: . # Prebuilt image from the Gitea registry (published by
# .gitea/workflows/build-images.yml on every merge to main). This file is
# production-shaped — image only, NO build: — so a production host can only
# ever pull, never accidentally build. IMAGE_TAG defaults to `latest`; pin a
# specific build for a reproducible deploy / rollback, e.g.
# IMAGE_TAG=sha-042a151 (see .env / .env.example). To build locally instead,
# overlay docker-compose.dev.yml (see README).
image: gitea.whitlocktech.com/uom/website-app:${IMAGE_TAG:-latest}
restart: unless-stopped restart: unless-stopped
env_file: .env env_file: .env
environment: environment:
@@ -44,9 +51,9 @@ services:
- "3000:3000" - "3000:3000"
bot: bot:
build: # Same as app: prebuilt bot image, pulled in production. Build locally via
context: . # docker-compose.dev.yml.
dockerfile: bot/Dockerfile image: gitea.whitlocktech.com/uom/website-bot:${IMAGE_TAG:-latest}
restart: unless-stopped restart: unless-stopped
env_file: .env env_file: .env
environment: environment: