Update Documentation

How to publish documentation changes to https://docs.eblu.me.

Quick Release

After merging documentation changes to main:

  1. Go to Actions > Build BlumeOps > Run workflow
  2. Select version bump type (patch/minor/major) or enter a specific version
  3. The workflow builds, releases, and deploys automatically

Direct link: https://forge.eblu.me/eblume/blumeops/actions?workflow=build-blumeops.yaml

What the Workflow Does

The build-blumeops workflow (.forgejo/workflows/build-blumeops.yaml):

  1. Resolves version — Uses input or auto-increments from latest release
  2. Builds changelog — Runs towncrier on the runner to update CHANGELOG.md
  3. Builds docs — Calls dagger call build-docs (Quartz build in a container)
  4. Creates release — Uploads docs-<version>.tar.gz to Forgejo releases

The workflow ends at the release. Deploying is a manual step (docs are served natively by Caddy on indri since retire-minikube — no ArgoCD app): bump docs_version in ansible/roles/docs/defaults/main.yml, then mise run provision-indri -- --tags docs, and purge the flyio-proxy nginx cache (fly ssh console -a blumeops-proxy -C "sh -c 'rm -rf /tmp/cache && nginx -s reload'") so the new docs are served immediately.

Changelog Fragments (Towncrier)

When making changes, add a changelog fragment to docs/changelog.d/:

# Format: <identifier>.<type>.md
# Types: feature, bugfix, infra, doc, ai, misc
 
# Using branch name (preferred)
echo "Add new feature X" > docs/changelog.d/my-feature.feature.md
 
# Orphan fragment (when no branch fits)
echo "Fix bug Y" > docs/changelog.d/+fix-bug.bugfix.md

Fragments are automatically collected into CHANGELOG.md (at repo root) during release.

Fragment types:

TypeDescription
featureNew features
bugfixBug fixes
infraInfrastructure changes
docDocumentation updates
aiAI assistance changes
miscOther changes

Runner Environment

The workflow runs on the indri label, served since retire-minikube phase 6 by the host-mode forgejo-runner on indri (configure-launchd-runner):

  • Runner: native LaunchAgent on indri, managed by the forgejo_runner ansible role (no Kubernetes, no job container)
  • Toolchain: jobs run directly with indri’s mise toolchain (Node.js, uv/Python, dagger, …); the k8s compat label was dropped once workflows repo-wide moved to runs-on: indri
  • Build engine: the dagger CLI (mise-pinned in the forgejo_runner role) drives the Dagger engine container in indri’s Docker Desktop

Quartz Static Site Generator

Quartz builds the documentation into a static site with:

  • Wiki-link support ([[page]] syntax)
  • Backlinks panel showing what references each page
  • Graph view of document connections
  • Full-text search

Configuration files (in docs/):

  • quartz.config.ts - Site metadata, plugins, theme
  • quartz.layout.ts - Page layout components

Quartz is cloned fresh during each build (not vendored) to use the latest version.

Manual Build (Local)

To test docs locally without triggering a release:

# Build docs tarball (identical to CI)
dagger call build-docs --src=. --version=dev export --path=./docs-dev.tar.gz
 
# Inspect the output
tar tf docs-dev.tar.gz | head -20
 
# Debug a Quartz build failure interactively
dagger call --interactive build-docs --src=. --version=dev

Troubleshooting

Workflow fails on “Resolve version”:

  • Check if the version already exists as a release
  • Ensure version format is vX.Y.Z

Docs not updating after deploy:

  • Confirm docs_version was bumped in ansible/roles/docs/defaults/main.yml and the provision ran
  • Check the installed version sentinel: ssh indri 'cat ~/blumeops/docs/.installed-version'
  • If stale content is served publicly, purge the flyio-proxy cache (see above)

Towncrier not finding fragments:

  • Fragments must be in docs/changelog.d/
  • Must have .md extension
  • Must match pattern <name>.<type>.md
  • docs - Documentation service reference
  • dagger - Build engine reference
  • forgejo - Git forge and CI/CD
  • argocd - GitOps deployment