# Deployment Handover How the migrated Hugo site is served, and how to adjust the server-side deploy workflow (Gitea Actions + webhook) for the new two-stage build. ## Serving topology (unchanged) The site is served as before. The host's system nginx (Certbot-managed TLS at `djosen.one`) proxies `location /` → `127.0.0.1:8080`, where the Docker `web` container listens. **Nothing changed** in the system nginx (`nginx/system-default.conf` / `nginx/reverse.conf`) or the Certbot/DNS setup during this migration. What changed is **what's inside the container**: instead of a Vite SPA (a single `index.html` plus JavaScript), the container now serves a Hugo static site — `public/` with one real `.html` page per path. ## What changed in the build The build is now **two steps**: 1. **Engine** — `gpu-bg/` Vite/Typescript build → a static JS bundle (`gpu-bg/dist/`). 2. **Site** — `hugo --minify` → `public/`. The Dockerfile runs both in stages (`node` → `hugo` → `nginx`). Key details for the server build: - **Hugo image:** the `site` stage uses `hugomods/hugo:debian-dart-sass-go-0.165.0` — an **extended Hugo** build including dart-sass. This is required because hugo-coder compiles the theme's SCSS via Hugo Pipes (`toCSS`), and Hugo 0.129+ dropped libsass in favor of dart-sass. This replaced the deprecated `klakegg/hugo` image. - **Theme submodule:** hugo-coder lives as a **git submodule** at `themes/hugo-coder`. - **Static serving only:** the container nginx no longer does SPA fallback (`try_files ... /index.html`). It serves plain static files — each page is a real `.html`, and a 404 for a missing path is legitimate and intentional. ## Mandatory server-side change (the #1 gotcha) **Your clone/checkout must initialize submodules before building.** The `themes/hugo-coder` submodule must be pulled in before `docker build`; otherwise `hugo` fails with `theme not found` and the build breaks. - **Gitea Actions `deploy.yml`:** make the checkout recursive. With `actions/checkout`, set: ```yaml - uses: actions/checkout@v3 with: submodules: recursive ``` - **Plain webhook / shell:** use a recursive clone, or run the update before building: ```bash git clone --recursive ... # or, on an existing clone: git pull git submodule update --init --recursive ``` Nothing else changes server-side: registry credentials, the SSH/webhook target, and the `docker compose up -d` deploy command are all the same. The image is slightly larger to build (node and Hugo-extended layers are discarded; the final nginx stage stays small). ## Testing ### Local (before pushing) ```bash git submodule update --init --recursive cd gpu-bg && npm install && npm run build && cd .. hugo # builds public/ docker compose build # 3-stage build docker compose up -d # maps 127.0.0.1:8080:8080 ``` ### Smoke test (after `up -d`) ```bash curl -sf http://127.0.0.1:8080/ | grep -q 'id="gpu"' && echo "canvas OK" curl -sf http://127.0.0.1:8080/gpu/engine.js -o /dev/null && echo "engine bundle OK" curl -sf http://127.0.0.1:8080/about/ -o /dev/null && echo "about OK" curl -sf http://127.0.0.1:8080/projects/ -o /dev/null && echo "projects OK" curl -sf http://127.0.0.1:8080/resume/ -o /dev/null && echo "resume OK" curl -sf http://127.0.0.1:8080/posts/ -o /dev/null && echo "posts OK" curl -s http://127.0.0.1:8080/nonexistent -o /dev/null -w "%{http_code}\n" # expect 404 ``` ### Browser check (manual — WebGPU is a real-GPU feature) - Homepage: emerald spiral animates behind the dark glass panel; text readable. - With WebGPU disabled, the WebGL2 fallback still animates. - With no WebGL at all, the page still shows its content; the canvas simply doesn't render. ### After deploy (remote) - `curl -s https://djosen.one/health` → `ok` (system nginx health endpoint, unchanged). - Verify `/`, `/about/`, `/projects/`, `/resume/`, `/posts/` over HTTPS. - Confirm the spiral actually renders in a real browser — the one thing `curl` cannot check. ## Troubleshooting ### Deploy status stuck/red on Gitea after a migration or restart If the Actions/deploy status shows broken or stops updating (commits push but no run is registered), the per-repo git hooks may be stale or missing. Fix by resynchronizing hooks from the Gitea admin CLI (this regenerates `pre-receive`, `update`, `post-receive` and their `.d/gitea` delegates): ```bash docker exec -u git gitea gitea admin regenerate hooks ``` For the full documented recovery, the Gitea FAQ steps are: "Sync missed branches from git data to databases", "Sync tags from git data to database", and "Resynchronize pre-receive, update and post-receive hooks of all repositories" (the last one is what the command above does). After regenerating, push a commit to refresh the status. ## Rollback The previous site is preserved in the git tag `pre-hugo` and the `archive/` folder. To roll back: `git checkout pre-hugo` and redeploy with the old single-stage Dockerfile (also recoverable from git history). No system nginx or DNS changes were made in this migration, so rollback is purely a container-image swap.