← Tech specs
toolshed

Deployment

How the garden is built and deployed to GitHub Pages via GitHub Actions, and how the LinkedIn publish trigger works.

Published Maturity 🪴 Plant AI Co-created with AI Written by AI based on my ideas and direction.

The garden is a static Astro site deployed to GitHub Pages. Every push to main triggers a build and deploy. There are no staging environments, main is production.

Build pipeline

GitHub Actions workflow:

  1. Checkout main
  2. Install Node dependencies (npm ci)
  3. Run npm run build → outputs to dist/
  4. Upload dist/ as a Pages artifact
  5. Deploy the artifact to GitHub Pages

The Astro build is fully static: no SSR. Everything reader-facing is pre-rendered to HTML at build time.

A separate Vercel deployment handles the chatbot’s serverless API (/api/ask, /api/chat, /api/topics, /api/article/{section}/{slug}, /api/topic-meta/{slug}, /api/topic-view/{slug}, /api/prompts) at https://maaike-ai.vercel.app. The /wiki, /research/ask, and the per-page chat panel all call those endpoints cross-origin. The Vercel function lives at tools/karpathy-wiki/api/index.py, imports business logic from tools/karpathy-wiki/tools/serve.py, and is configured by tools/karpathy-wiki/vercel.json. CORS is locked to maaike.ai.

Required env vars on the Vercel project: ANTHROPIC_API_KEY (the bot itself) and LANGFUSE_PUBLIC_KEY + LANGFUSE_SECRET_KEY + LANGFUSE_HOST (tracing + prompt management; if missing, tracing silently no-ops and prompts fall back to the .md files in the repo). See the Langfuse integration entry for details.

LinkedIn publish trigger

A separate workflow (share-link.yml) handles optional LinkedIn publishing. It runs on:

  • Push to main (checking if any new posts were added)
  • Manual dispatch

The workflow uses git diff to check the status of content files between the current commit and the previous one. A file with status A (added) is a new post, it triggers LinkedIn publishing. A file with status M (modified) is an update, it skips LinkedIn.

git diff --name-status HEAD~1 HEAD -- src/content/articles/ src/content/jottings/

Only articles and jottings trigger LinkedIn. Other collections (seeds, field notes, weblinks, projects) don’t.

publish.bat

publish.bat is a Windows convenience script that wraps the git workflow:

git add -A
git commit -m "%~1"
git push

Run as: publish.bat "Commit message here". It’s a shortcut for the common case of committing everything and pushing. The Claude Code /publish skill handles the full publishing workflow (validate, OG images, explore map, then push).

Custom domain

The site deploys to maaike.ai. The CNAME file in /public/ contains the domain. GitHub Pages reads this during deploy and configures the DNS binding.

OG image generation

OG images are generated locally (not in CI) before pushing. The /publish skill runs node scripts/generate-og-images.cjs which writes PNGs to public/images/og/. These are committed and pushed as static assets, GitHub Actions doesn’t regenerate them.

No branch strategy

Everything goes directly to main. There are no feature branches, no PRs, no staging. The Claude Code session is the editorial moment: Maaike reviews diffs before committing, and commits before pushing.

For large experimental changes (redesigns, structural refactors), a git worktree is used instead, a second folder on disk mapped to a separate branch. This avoids uncommitted changes bleeding into main during long-running experiments.

Mycelium tags, relations, arguments & questions