Containerize for independent deployment
All checks were successful
Docker / build-and-push (push) Successful in 46s
All checks were successful
Docker / build-and-push (push) Successful in 46s
Adds a Dockerfile, nginx config, docker-compose.yml, and a Gitea Actions workflow (mirroring goonk's), so rack builds and ships as its own image rather than being embedded in goonk-cv's build - deploys stay fully decoupled. Targets a subdomain, not a goonk.se subpath, to avoid needing path-prefix rewriting kept in sync across the build, edge proxy, and container. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
4
.dockerignore
Normal file
4
.dockerignore
Normal file
@@ -0,0 +1,4 @@
|
||||
node_modules
|
||||
dist
|
||||
.git
|
||||
*.log
|
||||
2
.env.example
Normal file
2
.env.example
Normal file
@@ -0,0 +1,2 @@
|
||||
# Copy to .env (or set in your shell) to override docker-compose.yml defaults
|
||||
HOST_PORT=5677
|
||||
61
.gitea/workflows/docker.yml
Normal file
61
.gitea/workflows/docker.yml
Normal file
@@ -0,0 +1,61 @@
|
||||
name: Docker
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build-and-push:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Derive image name and tags
|
||||
id: meta
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
# Gitea Actions aims for GitHub Actions compatibility, but runners may expose
|
||||
# slightly different env vars depending on configuration.
|
||||
SERVER_URL="${GITHUB_SERVER_URL:-${GITEA_SERVER_URL:-}}"
|
||||
REPO="${GITHUB_REPOSITORY:-${GITEA_REPOSITORY:-}}"
|
||||
SHA="${GITHUB_SHA:-${GITEA_SHA:-}}"
|
||||
|
||||
if [[ -z "${SERVER_URL}" || -z "${REPO}" || -z "${SHA}" ]]; then
|
||||
echo "Missing SERVER_URL/REPO/SHA env vars." >&2
|
||||
echo "SERVER_URL='${SERVER_URL}' REPO='${REPO}' SHA='${SHA}'" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
HOST=$(echo "${SERVER_URL}" | sed 's|https://||;s|http://||')
|
||||
IMAGE=$(echo "${HOST}/${REPO}" | tr '[:upper:]' '[:lower:]')
|
||||
SHORT_SHA=$(echo "${SHA}" | cut -c1-7)
|
||||
|
||||
echo "host=${HOST}" >> "$GITHUB_OUTPUT"
|
||||
echo "image=${IMAGE}" >> "$GITHUB_OUTPUT"
|
||||
echo "short_sha=${SHORT_SHA}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Log in to Gitea container registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ steps.meta.outputs.host }}
|
||||
username: ${{ github.actor }}
|
||||
# Create a Gitea PAT with packages:write scope and add it as a
|
||||
# repository secret named TKNTKN (Settings → Secrets)
|
||||
password: ${{ secrets.TKNTKN }}
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
file: Dockerfile
|
||||
push: true
|
||||
tags: |
|
||||
${{ steps.meta.outputs.image }}:latest
|
||||
${{ steps.meta.outputs.image }}:${{ steps.meta.outputs.short_sha }}
|
||||
36
CHANGELOG.md
36
CHANGELOG.md
@@ -205,3 +205,39 @@
|
||||
project (`goonk`, `adventure`, etc.), which each have their own `.git`.
|
||||
Ran `git init` here to match that convention ahead of the eventual real
|
||||
deploy.
|
||||
|
||||
## 2026-07-02 — containerize, decoupled from goonk-cv's own deploy
|
||||
|
||||
- Corrected course from the previous entry: copying rack's build into
|
||||
`goonk/public/rack/` would mean every rack change requires rebuilding
|
||||
and redeploying goonk-cv's own image — exactly the coupling explicitly
|
||||
ruled out ("I don't necessarily want to redeploy goonk-cv when updating
|
||||
this"). `goonk-cv` is built as its own Docker image via Gitea Actions
|
||||
and pulled to host independently of any other project's repo, so rack
|
||||
needed the same shape, not a file dropped into goonk's build.
|
||||
- Added `Dockerfile` + `nginx/default.conf` (multi-stage: `node:24-alpine`
|
||||
build → `nginx:alpine` static serve), mirroring `goonk`'s own Dockerfile
|
||||
structure exactly, minus the `/admin` and `/api` proxy blocks it needs
|
||||
and rack doesn't (no backend). Verified locally: `docker build` succeeds
|
||||
and the resulting container serves the app correctly on a test port.
|
||||
- Added `.gitea/workflows/docker.yml`, copied from `goonk`'s workflow
|
||||
(which already derives the registry image name from the repo path, so
|
||||
no rack-specific edits were needed beyond dropping the `-api` build
|
||||
step goonk has and rack doesn't). Requires a `TKNTKN` repo secret (Gitea
|
||||
PAT, `packages:write`) to be added manually in this repo's settings —
|
||||
not something settable from here.
|
||||
- Added `docker-compose.yml` (host-side reference, `image:
|
||||
repo.explewd.com/explewd/rack:latest`) and `.env.example` for the
|
||||
`HOST_PORT` override, matching `goonk`'s compose shape.
|
||||
- Decision: **subdomain, not `goonk.se/rack`.** Discussed both — a subpath
|
||||
deployment needs the `--base=/rack/` build *and* the edge proxy to
|
||||
forward `/rack/...` through unstripped *and* the container to serve
|
||||
files at that same sub-path, three things that have to stay in sync. A
|
||||
subdomain (`rack.explewd.com`) needs one DNS record and one new
|
||||
`server_name`/proxy-host entry, with the app built completely vanilla —
|
||||
same decoupled-deploy story, far less to keep synchronized. Kept
|
||||
`npm run build:subpath` in `package.json` in case the subpath route is
|
||||
wanted later, but it's not part of the current deploy path.
|
||||
- Host port: `5677`, matching the existing dev-server port for
|
||||
memorability, mapped via `docker-compose.yml`'s `HOST_PORT` (defaults to
|
||||
`5677`, override via `.env`).
|
||||
|
||||
13
Dockerfile
Normal file
13
Dockerfile
Normal file
@@ -0,0 +1,13 @@
|
||||
# Build static site
|
||||
FROM node:24-alpine AS build
|
||||
WORKDIR /app
|
||||
COPY package.json package-lock.json* ./
|
||||
RUN npm ci || npm install
|
||||
COPY . .
|
||||
RUN npm run build
|
||||
|
||||
# Serve static output
|
||||
FROM nginx:alpine
|
||||
COPY --from=build /app/dist/ /usr/share/nginx/html/
|
||||
COPY nginx/default.conf /etc/nginx/conf.d/default.conf
|
||||
EXPOSE 80
|
||||
44
README.md
44
README.md
@@ -85,26 +85,40 @@ uninstall) re-render immediately via a direct callback instead of waiting
|
||||
on that interval, and it's skipped entirely while a `<select>` has focus so
|
||||
the service-picker dropdown doesn't get yanked shut mid-render.
|
||||
|
||||
## Deploying as a subdirectory (e.g. goonk.se/rack)
|
||||
## Deployment
|
||||
|
||||
`npm run build` emits root-relative asset URLs (`/assets/...`), which only
|
||||
works if rack is served from the domain root. To deploy it under a
|
||||
subpath instead, use:
|
||||
rack is its own container — deployed independently of `goonk`/`goonk-cv`,
|
||||
same pattern as every other project on this host: a `Dockerfile` builds
|
||||
the static site and serves it via nginx, `.gitea/workflows/docker.yml`
|
||||
builds and pushes that image to the Gitea registry on every push to
|
||||
`main`, and `docker-compose.yml` is the host-side reference for running
|
||||
it. The CI workflow needs a repo secret named `TKNTKN` (a Gitea PAT with
|
||||
`packages:write`) — same requirement as `goonk`'s own workflow, set under
|
||||
Settings → Secrets.
|
||||
|
||||
Served from its own subdomain (e.g. `rack.explewd.com`), not a subpath of
|
||||
goonk.se — that keeps the build completely vanilla (root-relative asset
|
||||
URLs work as-is, no `--base` flag needed) and means the edge nginx just
|
||||
needs one new `server_name` block or NPM proxy host pointing at whichever
|
||||
port `docker-compose.yml`'s `HOST_PORT` exposes (`5677` by default — no
|
||||
`--base=/rack/`/path-prefix plumbing required, unlike a subpath
|
||||
deployment would need).
|
||||
|
||||
```bash
|
||||
npm run build:subpath # vite build --base=/rack/
|
||||
docker build -t rack .
|
||||
docker run -d -p 5677:80 rack # or: docker compose up -d
|
||||
```
|
||||
|
||||
That rewrites `dist/index.html`'s asset URLs to `/rack/assets/...`. Drop
|
||||
the resulting `dist/` contents into the host site wherever it serves
|
||||
static files under `/rack/` — for the `goonk` Astro site specifically,
|
||||
that's `goonk/public/rack/` (Astro copies `public/` verbatim into its own
|
||||
`dist/` at build time, and its nginx config already does a
|
||||
`try_files $uri $uri/ /index.html` fallback, so no nginx changes are
|
||||
needed - existing `public/waste`, `public/pitwall` follow the same
|
||||
one-subdir-per-project convention). `rack`'s own `localStorage` key
|
||||
(`rack-save-v1`) is namespaced, so it won't collide with anything else
|
||||
served from the same origin.
|
||||
`rack`'s own `localStorage` key (`rack-save-v1`) is namespaced regardless
|
||||
of URL shape, so it won't collide with anything else on the same origin.
|
||||
|
||||
If a subpath deployment (`goonk.se/rack`) is wanted later instead, use
|
||||
`npm run build:subpath` (`vite build --base=/rack/`) — it rewrites
|
||||
`dist/index.html`'s asset URLs to `/rack/assets/...`. That requires the
|
||||
edge proxy to forward the `/rack/...` path through *unchanged* (not
|
||||
strip the prefix) and the container to serve files at that same
|
||||
sub-path — more moving parts to keep in sync than the subdomain route
|
||||
above, which is why subdomain is the current choice.
|
||||
|
||||
## Tests
|
||||
|
||||
|
||||
8
docker-compose.yml
Normal file
8
docker-compose.yml
Normal file
@@ -0,0 +1,8 @@
|
||||
services:
|
||||
web:
|
||||
image: repo.explewd.com/explewd/rack:latest
|
||||
container_name: rack-web
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "${HOST_PORT:-5677}:80"
|
||||
pull_policy: always
|
||||
12
nginx/default.conf
Normal file
12
nginx/default.conf
Normal file
@@ -0,0 +1,12 @@
|
||||
server {
|
||||
listen 80;
|
||||
server_name _;
|
||||
|
||||
root /usr/share/nginx/html;
|
||||
index index.html;
|
||||
|
||||
# Single static page, no client-side router - just serve what's asked for.
|
||||
location / {
|
||||
try_files $uri $uri/ =404;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user