chore(deploy): SITE_URL defaults to the public address in the image; docs for the sitemap and feeds

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Lucas WintherandClaude Opus 5.5 committed 2026-11-11 10:28:30 +01:00
1 parent ab611c2de8
commit 77dd15ac03
8 files changed
+27 -3

No files matched your search

+2 -1
View File
@@ -22,7 +22,8 @@ ENV APP_VERSION=$APP_VERSION \
PORT=8080 \
POLL_INTERVAL=1h \
SOURCES_INTERVAL=6h \
CONTENT_BRANCH=main
CONTENT_BRANCH=main \
SITE_URL=https://lucaswinther.info
USER bun
VOLUME /data
+1
View File
@@ -38,6 +38,7 @@ services:
CONTENT_REPO: /content
POLL_INTERVAL: 30s
SOURCES_FIXTURES: /sources
SITE_URL: http://localhost:${SITE_PORT:-8080}
ports:
- "${SITE_PORT:-8080}:8080"
volumes:
+2
View File
@@ -36,6 +36,8 @@ services:
TRAKT_USER: lucasw89
LISTENBRAINZ_USER: stereotypicalcat
HARDCOVER_TOKEN: ${HARDCOVER_TOKEN:?Hardcover API token required}
# SITE_URL (canonical URLs, link previews, /sitemap.xml, the feeds) defaults to https://lucaswinther.info in
# the image; set it only to serve the site under another address.
volumes:
- ./data:/data
healthcheck:
+8
View File
@@ -167,6 +167,14 @@ An off-the-clock post whose slug is `issues`, or equals an item's id, is left ou
(the page belongs to the issues or the item); change its slug in Ghost. Post HTML is cleaned: scripts and
embeds become links, and only images hosted on the Ghost site itself are shown inline.
## Link previews and feeds
Nothing new to write: a shared link's preview uses the page's existing title and description. A project's
`image` and an item's first image in `media` become the preview picture (`alt` included); other pages show none.
The feeds (`/feed.xml`, `/work/feed.xml`, `/off-the-clock/feed.xml`) list projects, posts and items with their
title, date and a short summary (a project's `summary`, a post's excerpt, an item's `line`) and the AI level where
there is one. Drafts never appear. All of this needs the image's `SITE_URL` (`docs/DEPLOY.md`).
## What the validator checks
`contract` is 1 or 2; IDs match `a-z0-9-`; dates are real calendar days as `YYYY-MM-DD`; required fields exist;
+9
View File
@@ -45,10 +45,19 @@ both stacks to `site.yml` and the payload table in `server/README.md` like the o
| `HARDCOVER_TOKEN` | secret `hardcover_token` |
| `LISTENBRAINZ_USER` | `stereotypicalcat` (not a secret; without it the Listening cell is left out) |
| `VIRTUAL_HOST`, `LETSENCRYPT_HOST` | `lucaswinther.info` |
| `SITE_URL` | `https://lucaswinther.info`, the image's default: nothing to set. The public origin (no path) for canonical URLs, link previews, `/sitemap.xml` and the feeds. Set it if the site moves; unset (empty) leaves all of those out. An invalid value fails the build (shown in `/status`). |
`SOURCES_FIXTURES=<dir>` (used by the smoke test and `compose.local.yml`) reads the fixture JSON in
`fixtures/sources` instead of calling the services. Do not set it in production.
### Sitemap and feeds
With `SITE_URL` set, each build writes `/sitemap.xml` and three Atom feeds: `/feed.xml` (everything),
`/work/feed.xml` and `/off-the-clock/feed.xml`; every page links its feed in the head. There is no `robots.txt` in
the build because Cloudflare serves a managed one. Tell search engines about the sitemap once: submit
`https://lucaswinther.info/sitemap.xml` in Google Search Console (and Bing Webmaster Tools), or add the line
`Sitemap: https://lucaswinther.info/sitemap.xml` to the robots.txt in Cloudflare if it allows custom lines.
## Prerequisites
- **Bot user and token**: a Gitea user that is a read-only collaborator on `portfolio-next-content`
+2
View File
@@ -30,6 +30,8 @@ Small, specific, independently actionable. Remove only when actually done.
- [ ] The strip's Listening cell replaced the newest pinned song (owner: "for now, then we can look at alternatives later"). The pinned song's note ("why ↘") now has no spot in the strip; look at alternatives.
- [ ] Revisit the start page's "or wander through everything at once" pill (and its G shortcut). Hidden for now on the owner's request: `SHOW_EVERYTHING_PILL` in `src/site/pages/index.astro`. Its CSS, the seam positioning in `src/site/scripts/gate.ts` and the seam check in `e2e/gate.spec.ts` are kept; `/everything/` is still reachable from `/simple`.
- [ ] Personal wall: a chip filter is not reflected in the URL, so a filtered view can't be shared.
- [ ] Link previews: a site-wide preview image for pages without a picture of their own (the start page, `/work/`, posts, the wall, issues). A design call for the owner (spec 007 left it out); until then those pages share a plain `summary` card with no image.
- [ ] Feeds: no visible link to `/feed.xml`, `/work/feed.xml` or `/off-the-clock/feed.xml` yet, only the `rel="alternate"` links in each head (spec 007). Where a visible one would go, if anywhere, is the owner's design call.
- [ ] Film soundtrack, alternative design: mockup B (the soundtrack as a record tucked behind the poster, slides out on hover; `mockups/shots/spec-002-r1-cards.png`). The owner kept it as an idea for later or as an alternative to the tray.
- [ ] Single-cover comic, alternative design: mockup G (ink-framed cover beside the text). J (cover leaning on the paper with the one-liner) "could be interesting but doesn't work yet". I (cover plus two panels) only with real, different page images — today's panel strip already does that when an item has three images.
- [ ] Media images ship as-is: the build copies `media/` verbatim and cards use plain `<img>` (no `astro:assets`). Resize/convert at build (e.g. `sharp`, a new runtime dependency: ask the owner) or keep resizing by hand in the content repo.
+1 -1
View File
@@ -35,7 +35,7 @@ Commands: `bun run typecheck`, `bun test`, `bunx playwright test` (`--workers=2`
`.xml` files, no alternate links. Watch fail; wire `Base.astro`, the layouts, the pages and
`src/site/pages/[...file].xml.ts`; `test/site/fixture-sites.ts` sets `SITE_URL` for the main fixture. Docs:
ARCHITECTURE, DECISIONS. Commit.
- [ ] 5. Deploy: `Dockerfile` ENV `SITE_URL`, `deploy/compose.yml` comment, `scripts/smoke-test.sh` and
- [x] 5. Deploy: `Dockerfile` ENV `SITE_URL`, `deploy/compose.yml` comment, `scripts/smoke-test.sh` and
`deploy/compose.local.yml` use a synthetic address; `docs/DEPLOY.md` (`SITE_URL`, sitemap note), `docs/TODO.md`
(preview image, visible feed link). Commit.
- [ ] 6. Verify: build the real content into `.tmp/spec007-after` with and without `SITE_URL`, diff every page's
+2 -1
View File
@@ -22,7 +22,7 @@ expected=$(git -C "$work/content" rev-parse HEAD)
# drops -c / GIT_CONFIG_* settings before upload-pack, so safe.directory must come from a config file.
printf '[safe]\n\tdirectory = *\n' > "$work/smoke.gitconfig"
chmod -R a+rX "$work"
docker create --name "$name" -e CONTENT_REPO=/content -e SOURCES_FIXTURES=/sources \
docker create --name "$name" -e CONTENT_REPO=/content -e SOURCES_FIXTURES=/sources -e SITE_URL=https://example.invalid \
-e GIT_CONFIG_GLOBAL=/smoke.gitconfig "$image" >/dev/null
tar -C "$work" --owner=0 --group=0 -c content sources smoke.gitconfig | docker cp -q - "$name:/"
docker start "$name" >/dev/null
@@ -45,5 +45,6 @@ for path in / /work/ /off-the-clock/; do
get "$path" >/dev/null || { echo "smoke: $path did not return 200"; docker logs "$name"; exit 1; }
done
get / | grep -q 'Example Person' || { echo "smoke: / did not render the fixture content"; exit 1; }
get /sitemap.xml | grep -q 'https://example.invalid/work/' || { echo "smoke: /sitemap.xml missing or without SITE_URL"; exit 1; }
get /status | grep -q '"sources": *{' || { echo "smoke: /status has no sources object"; get /status; exit 1; }
echo "smoke: ok ($expected)"