Publish content to the repo’s static site. The site is built with Quartz v5, lives in garden/, and is generated from the repo’s own markdown and config files by garden/scripts/build-site-content.mjs, then deployed to GitHub Pages by .github/workflows/deploy.yml on every push to main.

⚠️ The site is PUBLIC

GitHub Pages is public even though this repo is private (project Pages without Enterprise can’t be restricted). Anything published is world-readable and Google-indexable. Before publishing new content, scan it for secrets (account IDs, emails, tokens, keys) and redact what must not leak — see the redaction step below. This is the cardinal rule: treat every addition as “about to be on the public internet.”

Mental model

repo source files          build script (first-party)        Quartz             GitHub Pages
obsidian/ blueprints/   →   garden/scripts/                →  garden/        →   public site
dotfiles/ vscode/ …        build-site-content.mjs            (build)            (on git push)
                           writes garden/content/

Two things are separate:

  • What appears on the site is decided by the build script’s mapping lists (MAPPINGS, CODE_FILES, CODE_DIRS, DOWNLOADS).
  • Deployment is automatic — once pushed, every git push to main re-runs the whole pipeline and redeploys. You never hand-build for production; you commit source files (and any script change). garden/content/ and garden/public/ are generated and git-ignored — never commit them.

First decision: is it picked up automatically?

What you’re addingAutomatic?Action
.md note, or image/PDF, inside obsidian/configs, obsidian/certifications, obsidian/studying, vscode/plugins/harp, .claude/skills, or anywhere under blueprints/✅ YesJust add the file (recursive dir walk)
.hcl under blueprints/01-terragrunt/✅ YesJust add the file (walked by extension)
A new dotfile (e.g. .zshrc) or any config you want shown as a code page⚙️ NoAdd one line to CODE_FILES — see pipeline.md
A new top-level area (a source dir not already mapped)⚙️ NoAdd one entry to MAPPINGS — see pipeline.md
A new binary to offer for download (e.g. a PDF book)⚙️ NoAdd one entry to DOWNLOADS — see pipeline.md
A new file type that isn’t markdown / image / PDF / a known config⚙️ NoExtend the script — see pipeline.md

Rule of thumb: anything inside an already-mapped directory is automatic. Only a new kind of content or a new top-level directory needs a one-line script edit.

Workflow A — automatic content (the common case)

For a new note/image/PDF in an already-mapped directory:

  1. Add the file to its source directory (e.g. write the note in obsidian/configs/...).
  2. Redact check (site is public) — scan the new file:
    grep -nEi 'AKIA[0-9A-Z]{16}|-----BEGIN|[0-9]{12}|[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}|tskey-|ghp_|xox[baprs]-' <new-file>
    
    If a real secret must stay in the source, add a REDACTIONS entry (see pipeline.md). Account IDs 123456789012/123456789012 and the personal email are already redacted.
  3. Preview locally (below) and confirm it renders.
  4. When ready, the source file is committed and pushed to main (the user’s call — don’t auto-commit) → the Action rebuilds and redeploys.

Workflow B — needs a script edit

When the table above says ⚙️: open pipeline.md, edit the relevant list at the top of garden/scripts/build-site-content.mjs (one line, copy an existing entry), then run Workflow A from step 2. Remember to commit the changed script too.

Preview locally

The build script and Quartz are two steps — run the script first, then Quartz:

node garden/scripts/build-site-content.mjs          # repo source → garden/content (with redactions)
npm --prefix garden run quartz -- build --serve     # → http://localhost:8080

While the server runs it watches garden/content/ and hot-reloads. After editing a source file, just re-run node garden/scripts/build-site-content.mjs — Quartz picks up the regenerated content automatically. To stop the server: lsof -ti tcp:8080 | xargs -r kill.

Deploy

Deployment is the push itself — no manual build. Committing and pushing is the user’s decision; never auto-commit or auto-push. When the user pushes to main (or asks you to):

  1. First confirm the local preview looks right and the verify checklist passes.
  2. Stage the source files (and the script, if you edited it). Never commit garden/content or garden/public.
  3. The push to main triggers .github/workflows/deploy.yml, which regenerates content, builds, and publishes to configs.themaybe.uk in ~1–2 min.
  4. (Optional) trigger a build without a content change from the repo’s Actions tab → “Deploy site to GitHub Pages” → Run workflow (workflow_dispatch).

Example — add an AWS note, file to live URL

A new note in obsidian/configs/ is in a mapped directory, so it’s the fully-automatic path (no script edit):

# 1. Write the note (line-1 `tags:`, H2 sections — see the format-vault skill for house style)
#    obsidian/configs/aws/cdn/CloudFront Signed URLs.md
 
# 2. Secret-scan it — the site is public
grep -nEi 'AKIA[0-9A-Z]{16}|-----BEGIN|[0-9]{12}|[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}|tskey-|ghp_' \
  "obsidian/configs/aws/cdn/CloudFront Signed URLs.md"
 
# 3. Preview locally
node garden/scripts/build-site-content.mjs
npm --prefix garden run quartz -- build --serve
#    → http://localhost:8080/configs/aws/cdn/cloudfront-signed-urls   (slug is lower-kebab)
 
# 4. Publish — when ready, the SOURCE note is committed and pushed to main.
#    Committing/pushing is the user's call (or done only when they ask) — never auto-run git.
#    The push triggers the Action, which rebuilds and deploys; no manual build needed.

Once it’s pushed, ~1–2 min later it’s live at https://configs.themaybe.uk/configs/aws/cdn/cloudfront-signed-urls, and any new tag auto-creates its /tags/<tag> page. Nothing else to register.

Verify before pushing

  • New file renders in the local preview (Workflow A step 3).
  • Secret scan clean, or the secret is covered by a REDACTIONS entry — confirm grep -r '<secret>' garden/public returns nothing.
  • Internal links/embeds resolve (Quartz marks unresolved wikilinks with class brokengrep -rl 'internal broken' garden/public should be empty).
  • If you edited the script: it’s staged for commit alongside the source.
  • Not committing generated dirs (garden/content, garden/public).

Styling tweaks

Site-wide CSS overrides go in garden/quartz/styles/custom.scss (a committed file that survives CI). Do not edit anything under garden/.quartz/plugins/ — that directory is git-ignored and regenerated by quartz plugin install on every build, so edits there are lost. See pipeline.md for the build gotchas.

Reference

  • pipeline.md — the build script’s config lists (with edit recipes), the transforms it applies, how redaction works, and the Quartz/build gotchas (gitignore + globby, the broken install-plugins npm script, YAML config, custom.scss).