skill(astro-wiki-deploy): full deploy flow + 6 pitfalls from live deployment

Captures the wiki.clawdie.si deployment experience (26.jun.2026):

Pitfalls documented:
1. const declarations outside function scope fail in Astro SSR
2. YAML frontmatter with unquoted colons breaks content parsing
3. SL content auto-generates stale routes (explicit sl/ routes needed)
4. Stale dist cache preserves old route generation (rm -rf dist)
5. import.meta.url path resolution fails in Astro SSR
6. H1 fallback for pages without YAML frontmatter
7. Nginx SSL cert placeholder (referenced, in nginx skill)

Full deploy flow: stage → jail → build → deploy → verify.
First-time setup instructions for CMS jail.
This commit is contained in:
Sam & Claude 2026-06-26 12:36:42 +02:00
parent 834197e2ae
commit 1793ea0a75

View file

@ -0,0 +1,195 @@
---
name: astro-wiki-deploy
description: Deploy the Colibri wiki (astro/wiki/) to wiki.clawdie.si via the CMS jail. Plain Astro — no Starlight. Covers content staging, build, deploy, and the 6 pitfalls discovered during the initial deployment (26.jun.2026).
---
# Astro Wiki Deploy
Deploy the plain-Astro Colibri wiki to `https://wiki.clawdie.si`.
## Architecture
```
colibri/
docs/wiki/ ← canonical content (23 EN + 23 SL .md files)
astro/wiki/ ← Astro build pipeline (minimal, no Starlight)
src/pages/
index.astro — EN landing (flat list)
[...slug].astro — EN dynamic route (reads src/content/*.md)
sl/index.astro — SL landing
sl/[...slug].astro — SL dynamic route (reads src/content/sl/*.md)
```
CMS jail builds the site, host nginx serves it. Same pattern as docs.clawdie.si.
## Full deploy flow
```sh
# 1. Stage content into Astro project (source of truth → build input)
cd /home/clawdie/ai/colibri
rm -rf astro/wiki/src/content
cp -r docs/wiki astro/wiki/src/content
# 2. Copy source into CMS jail
sudo cp -r astro/wiki/src \
/usr/local/bastille/jails/cms/root/usr/home/clawdie/clawdie-wiki/
# 3. Build inside jail (rm -rf dist for clean state — see pitfall #4)
sudo bastille cmd cms sh -c '
cd /usr/home/clawdie/clawdie-wiki &&
rm -rf dist node_modules/.astro &&
ASTRO_SITE_URL="https://wiki.clawdie.si" npm run build'
# 4. Deploy to jail webroot
sudo bastille cmd cms sh -c '
rm -rf /usr/local/www/wiki.clawdie.si &&
mkdir -p /usr/local/www/wiki.clawdie.si &&
cp -r /usr/home/clawdie/clawdie-wiki/dist/* /usr/local/www/wiki.clawdie.si/'
# 5. Cross jail boundary to host webroot (tar is the reliable bridge)
sudo bastille cmd cms sh -c \
'tar -czf /tmp/wiki-dist.tar.gz -C /usr/local/www/wiki.clawdie.si .'
sudo cp /usr/local/bastille/jails/cms/root/tmp/wiki-dist.tar.gz /tmp/
sudo rm -rf /usr/local/www/wiki.clawdie.si
sudo mkdir -p /usr/local/www/wiki.clawdie.si
sudo tar -xzf /tmp/wiki-dist.tar.gz -C /usr/local/www/wiki.clawdie.si/
sudo chown -R clawdie:clawdie /usr/local/www/wiki.clawdie.si
# 6. Verify
curl -sk --resolve wiki.clawdie.si:443:127.0.0.1 https://wiki.clawdie.si/ | head -5
curl -sk --resolve wiki.clawdie.si:443:127.0.0.1 https://wiki.clawdie.si/sl/ | head -5
```
## First-time setup (CMS jail)
```sh
# Stage the Astro project into the jail once
sudo mkdir -p /usr/local/bastille/jails/cms/root/usr/home/clawdie/clawdie-wiki
sudo cp -r astro/wiki/package.json astro/wiki/astro.config.mjs \
/usr/local/bastille/jails/cms/root/usr/home/clawdie/clawdie-wiki/
# Install dependencies inside jail
sudo bastille cmd cms sh -c 'cd /usr/home/clawdie/clawdie-wiki && npm install'
```
## Pitfalls
### 1. `const` declarations outside function scope fail in Astro SSR
**Symptom:** `WIKI_DIR is not defined` at build time, but the variable is
clearly declared.
**Cause:** Astro's frontmatter script compilation scopes top-level `const`
differently in SSR context. Variables declared outside `getStaticPaths()` or
the template function may not be accessible.
**Fix:** Move all `const` declarations that are needed in `getStaticPaths()`
inside the function body. For variables needed in the template (after
frontmatter), declare them at module level but only those that Astro's
compiler can reach — simple string literals and path.resolve calls work.
```js
// ❌ BROKEN — WIKI_DIR is undefined in compiled output
const WIKI_DIR = path.resolve("src/content");
export function getStaticPaths() { ... }
// ✅ WORKS — declared inside function scope
export function getStaticPaths() {
const WIKI_DIR = path.resolve("src/content");
...
}
```
### 2. YAML frontmatter with unquoted colons breaks content parsing
**Symptom:** `bad indentation of a mapping entry` at line N:XX during build.
**Cause:** Slovenian (and English) titles/descriptions often contain colons
(`:`). YAML interprets unquoted colons as key-value separators. This breaks
Astro's content collection parsing.
**Fix:** Quote ALL frontmatter values that contain colons, double-quotes,
or special characters:
```yaml
# ❌ BROKEN
title: Agentska oprema: zot + Colibri
# ✅ WORKS
title: "Agentska vprega: pi, zot & Colibri"
```
Also: descriptions with nested double-quotes need single-quote wrappers:
```yaml
# ❌ BROKEN — inner quotes terminate the string
description: "Sprememba ni "končana" brez..."
# ✅ WORKS — single-quote wrapper, inner double-quotes preserved
description: 'Sprememba ni "končana" brez...'
```
### 3. SL content auto-generates stale routes
**Symptom:** `src/pages/sl/index.astro` appears in the build output with
hardcoded paths like `scandir '/usr/docs/wiki'` even after removing
`src/content/sl/`.
**Cause:** Astro's content collection auto-generation creates `sl/` routes
from `src/content/sl/`. These auto-generated routes use different path
resolution and can persist in the dist cache between builds.
**Fix:**
1. Create explicit `src/pages/sl/index.astro` and `src/pages/sl/[...slug].astro`
routes that read from `src/content/sl/` directly.
2. Remove any auto-generated `sl/` directory from `src/pages/sl/` if it
appears.
3. ALWAYS `rm -rf dist node_modules/.astro` between builds (see pitfall #4).
### 4. Stale dist cache preserves old route generation
**Symptom:** Fixed source code, but the same error persists after rebuild.
**Cause:** Astro caches compiled routes in `dist/` and `.astro/`. A previous
failed build can leave stale compiled `.mjs` files that shadow the fixed
source.
**Fix:** Always clean before rebuilding:
```sh
rm -rf dist node_modules/.astro && npm run build
```
### 5. `import.meta.url` path resolution fails in Astro SSR
**Symptom:** Paths resolved via `fileURLToPath(import.meta.url)` point to
compiled `.mjs` files in `dist/`, not the source `.astro` files.
**Cause:** Astro compiles `.astro` to `.mjs` and runs from `dist/`. The
`import.meta.url` in the compiled module points to the compiled path, not
the project root.
**Fix:** Use `path.resolve("src/content")``process.cwd()` during build
is the project root (`astro/wiki/`), so relative paths work correctly.
Do not use `import.meta.url` for content path resolution.
### 6. H1 fallback for pages without YAML frontmatter
**Symptom:** Page `<title>` shows the slug (e.g., "agent-harness") instead
of the actual heading ("Agent harness: pi, zot & Colibri").
**Cause:** The title extraction only checks `frontmatter.title`, falling back
to the URL slug. Pages with only a markdown H1 (`# Title`) and no YAML
frontmatter get slug-based titles.
**Fix:** Add H1 regex extraction as a third fallback:
```js
const title = (frontmatter.title || content.match(/^#\s+(.+)$/m)?.[1] || slug)
.replace(/^["']|["']$/g, "");
```
### 7. Nginx: missing SSL cert blocks startup (see nginx skill)
Before the first deploy, nginx needs a placeholder cert to start. See the
**nginx** skill §"Adding a new public static HTTPS site — full flow" and
§Troubleshooting "BIO_new_file() failed". The wiki deployment follows the
same pattern: placeholder cert → ACME challenge → real cert.