diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..3be2309 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,4 @@ +node_modules +dist +.git +*.log diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..a074849 --- /dev/null +++ b/.env.example @@ -0,0 +1,2 @@ +# Copy to .env (or set in your shell) to override docker-compose.yml defaults +HOST_PORT=5677 diff --git a/.gitea/workflows/docker.yml b/.gitea/workflows/docker.yml new file mode 100644 index 0000000..86173e6 --- /dev/null +++ b/.gitea/workflows/docker.yml @@ -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 }} diff --git a/CHANGELOG.md b/CHANGELOG.md index b974afe..6c0fa8d 100644 --- a/CHANGELOG.md +++ b/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`). diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..2828d1a --- /dev/null +++ b/Dockerfile @@ -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 diff --git a/README.md b/README.md index eebb336..964ee7e 100644 --- a/README.md +++ b/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 `