From 71122971504f92d0931500776136123c3f441521 Mon Sep 17 00:00:00 2001 From: Erich Blume Date: Tue, 3 Feb 2026 07:57:47 -0800 Subject: [PATCH 01/12] Move zk cards to docs/zk/ for documentation restructuring Phase 1 of docs restructuring: move all zettelkasten cards to a temporary docs/zk/ subdirectory while preparing to build a new Diataxis-based documentation structure. - Move all existing markdown files from docs/ to docs/zk/ - Update zk-docs mise task to look in the new location - Add docs/README.md explaining the restructuring plan Co-Authored-By: Claude Opus 4.5 --- docs/README.md | 55 +++++++++++++++++++++++++++++++ docs/{ => zk}/1767747119-YCPO.md | 0 docs/{ => zk}/1768246525-RVRY.md | 0 docs/{ => zk}/1768283761-TRXN.md | 0 docs/{ => zk}/1768457769-LOCK.md | 0 docs/{ => zk}/1768506761-GHUW.md | 0 docs/{ => zk}/1768506761-XGYX.md | 0 docs/{ => zk}/argocd.md | 0 docs/{ => zk}/borgmatic.md | 0 docs/{ => zk}/external-secrets.md | 0 docs/{ => zk}/grafana.md | 0 docs/{ => zk}/indri.md | 0 docs/{ => zk}/jellyfin.md | 0 docs/{ => zk}/kiwix.md | 0 docs/{ => zk}/miniflux.md | 0 docs/{ => zk}/minikube.md | 0 docs/{ => zk}/navidrome.md | 0 docs/{ => zk}/postgresql.md | 0 docs/{ => zk}/pulumi.md | 0 docs/{ => zk}/teslamate.md | 0 docs/{ => zk}/transmission.md | 0 docs/{ => zk}/zot.md | 0 mise-tasks/zk-docs | 2 +- 23 files changed, 56 insertions(+), 1 deletion(-) create mode 100644 docs/README.md rename docs/{ => zk}/1767747119-YCPO.md (100%) rename docs/{ => zk}/1768246525-RVRY.md (100%) rename docs/{ => zk}/1768283761-TRXN.md (100%) rename docs/{ => zk}/1768457769-LOCK.md (100%) rename docs/{ => zk}/1768506761-GHUW.md (100%) rename docs/{ => zk}/1768506761-XGYX.md (100%) rename docs/{ => zk}/argocd.md (100%) rename docs/{ => zk}/borgmatic.md (100%) rename docs/{ => zk}/external-secrets.md (100%) rename docs/{ => zk}/grafana.md (100%) rename docs/{ => zk}/indri.md (100%) rename docs/{ => zk}/jellyfin.md (100%) rename docs/{ => zk}/kiwix.md (100%) rename docs/{ => zk}/miniflux.md (100%) rename docs/{ => zk}/minikube.md (100%) rename docs/{ => zk}/navidrome.md (100%) rename docs/{ => zk}/postgresql.md (100%) rename docs/{ => zk}/pulumi.md (100%) rename docs/{ => zk}/teslamate.md (100%) rename docs/{ => zk}/transmission.md (100%) rename docs/{ => zk}/zot.md (100%) diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..1d46f18 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,55 @@ +# Blumeops Documentation + +This directory contains documentation for blumeops, Erich Blume's personal infrastructure GitOps repository. + +## Documentation Restructuring (In Progress) + +The documentation is being restructured to follow the [Diataxis](https://diataxis.fr/) documentation framework while serving multiple audiences: + +### Target Audiences + +1. **Erich (owner)** - A knowledge graph/zettelkasten for quickly recalling important facts about blumeops infrastructure and operations. + +2. **Claude/AI agents** - Memory and context enrichment for AI-assisted operations and development. + +3. **New external readers** - People who want to understand "what is blumeops?" at a high level. + +4. **Potential operators/contributors** - External readers who want to help operate, modify, or answer questions about blumeops, or onboard as a member. + +5. **Replicators** - People who want to duplicate this approach for their own personal infrastructure operations. + +### Planned Structure (Diataxis) + +The final documentation structure will include: + +- **Tutorials** - Learning-oriented guides for getting started +- **How-to Guides** - Task-oriented instructions for specific operations +- **Reference** - Information-oriented technical descriptions +- **Explanation** - Understanding-oriented discussion of concepts and decisions + +### Current State + +The restructuring is happening in phases: + +- **Phase 1** (current): Move existing zettelkasten cards to `docs/zk/` as a temporary holding area +- **Future phases**: Build out the new Diataxis-based structure, migrate relevant content from zk cards, and eventually deprecate or integrate the zk format + +### Directory Layout + +``` +docs/ +├── README.md # This file +└── zk/ # Zettelkasten cards (temporary location during restructuring) + ├── 1767747119-YCPO.md # Main blumeops overview card + └── ... # Service-specific cards and notes +``` + +## Viewing the ZK Cards + +To view all blumeops zettelkasten cards: + +```fish +mise run zk-docs +``` + +This displays all cards tagged with `blumeops`, starting with the main overview card. diff --git a/docs/1767747119-YCPO.md b/docs/zk/1767747119-YCPO.md similarity index 100% rename from docs/1767747119-YCPO.md rename to docs/zk/1767747119-YCPO.md diff --git a/docs/1768246525-RVRY.md b/docs/zk/1768246525-RVRY.md similarity index 100% rename from docs/1768246525-RVRY.md rename to docs/zk/1768246525-RVRY.md diff --git a/docs/1768283761-TRXN.md b/docs/zk/1768283761-TRXN.md similarity index 100% rename from docs/1768283761-TRXN.md rename to docs/zk/1768283761-TRXN.md diff --git a/docs/1768457769-LOCK.md b/docs/zk/1768457769-LOCK.md similarity index 100% rename from docs/1768457769-LOCK.md rename to docs/zk/1768457769-LOCK.md diff --git a/docs/1768506761-GHUW.md b/docs/zk/1768506761-GHUW.md similarity index 100% rename from docs/1768506761-GHUW.md rename to docs/zk/1768506761-GHUW.md diff --git a/docs/1768506761-XGYX.md b/docs/zk/1768506761-XGYX.md similarity index 100% rename from docs/1768506761-XGYX.md rename to docs/zk/1768506761-XGYX.md diff --git a/docs/argocd.md b/docs/zk/argocd.md similarity index 100% rename from docs/argocd.md rename to docs/zk/argocd.md diff --git a/docs/borgmatic.md b/docs/zk/borgmatic.md similarity index 100% rename from docs/borgmatic.md rename to docs/zk/borgmatic.md diff --git a/docs/external-secrets.md b/docs/zk/external-secrets.md similarity index 100% rename from docs/external-secrets.md rename to docs/zk/external-secrets.md diff --git a/docs/grafana.md b/docs/zk/grafana.md similarity index 100% rename from docs/grafana.md rename to docs/zk/grafana.md diff --git a/docs/indri.md b/docs/zk/indri.md similarity index 100% rename from docs/indri.md rename to docs/zk/indri.md diff --git a/docs/jellyfin.md b/docs/zk/jellyfin.md similarity index 100% rename from docs/jellyfin.md rename to docs/zk/jellyfin.md diff --git a/docs/kiwix.md b/docs/zk/kiwix.md similarity index 100% rename from docs/kiwix.md rename to docs/zk/kiwix.md diff --git a/docs/miniflux.md b/docs/zk/miniflux.md similarity index 100% rename from docs/miniflux.md rename to docs/zk/miniflux.md diff --git a/docs/minikube.md b/docs/zk/minikube.md similarity index 100% rename from docs/minikube.md rename to docs/zk/minikube.md diff --git a/docs/navidrome.md b/docs/zk/navidrome.md similarity index 100% rename from docs/navidrome.md rename to docs/zk/navidrome.md diff --git a/docs/postgresql.md b/docs/zk/postgresql.md similarity index 100% rename from docs/postgresql.md rename to docs/zk/postgresql.md diff --git a/docs/pulumi.md b/docs/zk/pulumi.md similarity index 100% rename from docs/pulumi.md rename to docs/zk/pulumi.md diff --git a/docs/teslamate.md b/docs/zk/teslamate.md similarity index 100% rename from docs/teslamate.md rename to docs/zk/teslamate.md diff --git a/docs/transmission.md b/docs/zk/transmission.md similarity index 100% rename from docs/transmission.md rename to docs/zk/transmission.md diff --git a/docs/zot.md b/docs/zk/zot.md similarity index 100% rename from docs/zot.md rename to docs/zk/zot.md diff --git a/mise-tasks/zk-docs b/mise-tasks/zk-docs index 32de565..2ce9bb3 100755 --- a/mise-tasks/zk-docs +++ b/mise-tasks/zk-docs @@ -4,7 +4,7 @@ set -euo pipefail # Blumeops docs now live in the repo itself (symlinked into zk) -DOCS_DIR="$(cd "$(dirname "$0")/.." && pwd)/docs" +DOCS_DIR="$(cd "$(dirname "$0")/.." && pwd)/docs/zk" MAIN_CARD="$DOCS_DIR/1767747119-YCPO.md" # Find all files tagged with blumeops (excluding main card) -- 2.50.1 (Apple Git-155) From fb8f98a47d1705b153d82f520d663b79bf65c88b Mon Sep 17 00:00:00 2001 From: Erich Blume Date: Tue, 3 Feb 2026 08:02:36 -0800 Subject: [PATCH 02/12] Update main README to reflect docs restructuring Point to docs/README.md for the restructuring plan and clarify that zk cards now live in docs/zk/. Co-Authored-By: Claude Opus 4.5 --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 370bb0e..c41fc18 100644 --- a/README.md +++ b/README.md @@ -80,10 +80,11 @@ This repo uses [Forgejo Actions](https://forgejo.org/docs/latest/user/actions/) ## Documentation -Documentation lives in `docs/` and covers infrastructure, services, and operational runbooks. View all docs with: +Documentation lives in `docs/` and is being restructured to follow the [Diataxis](https://diataxis.fr/) framework. See [`docs/README.md`](docs/README.md) for the restructuring plan and current status. +**Quick reference (zettelkasten cards):** ```bash mise run zk-docs ``` -The docs use [Obsidian](https://obsidian.md) wiki-link syntax (`[[link]]`) for cross-references. Edit with any markdown editor, or use [obsidian.nvim](https://github.com/obsidian-nvim/obsidian.nvim) for enhanced navigation and completion. +The zk cards in `docs/zk/` use [Obsidian](https://obsidian.md) wiki-link syntax (`[[link]]`) for cross-references. Edit with any markdown editor, or use [obsidian.nvim](https://github.com/obsidian-nvim/obsidian.nvim) for enhanced navigation and completion. -- 2.50.1 (Apple Git-155) From 38dae76cb97a95c91eb9b9a511cf9993e84ede88 Mon Sep 17 00:00:00 2001 From: Erich Blume Date: Tue, 3 Feb 2026 08:08:46 -0800 Subject: [PATCH 03/12] Expand docs/README.md with full restructuring plan Add comprehensive phased plan for documentation restructuring: - Phase 1: Foundation (current) - zk migration, tooling selection - Phase 2: Tutorials - learning-oriented content - Phase 3: How-to Guides - task-oriented instructions - Phase 4: Reference - technical descriptions - Phase 5: Explanation - concepts and decisions - Phase 6: Integration & Cleanup Also includes: - Tooling evaluation table (Quartz, MkDocs, mdBook, Hugo, Docusaurus) - Requirements (Obsidian compatibility, wiki-links, HTML/PDF output) - Note on project naming (BlumeOps / blumeops / Blue Mops) Co-Authored-By: Claude Opus 4.5 --- docs/README.md | 104 ++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 85 insertions(+), 19 deletions(-) diff --git a/docs/README.md b/docs/README.md index 1d46f18..8bc69a4 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,52 +1,118 @@ -# Blumeops Documentation +# BlumeOps Documentation -This directory contains documentation for blumeops, Erich Blume's personal infrastructure GitOps repository. +> **Note on naming**: The project is properly stylized as **BlumeOps**, though "blumeops" and "Blue Mops" are also commonly used interchangeably. + +This directory contains documentation for BlumeOps, Erich Blume's personal infrastructure GitOps repository. ## Documentation Restructuring (In Progress) -The documentation is being restructured to follow the [Diataxis](https://diataxis.fr/) documentation framework while serving multiple audiences: +The documentation is being restructured to follow the [Diataxis](https://diataxis.fr/) documentation framework while serving multiple audiences. ### Target Audiences -1. **Erich (owner)** - A knowledge graph/zettelkasten for quickly recalling important facts about blumeops infrastructure and operations. +1. **Erich (owner)** - A knowledge graph/zettelkasten for quickly recalling important facts about BlumeOps infrastructure and operations. 2. **Claude/AI agents** - Memory and context enrichment for AI-assisted operations and development. -3. **New external readers** - People who want to understand "what is blumeops?" at a high level. +3. **New external readers** - People who want to understand "what is BlumeOps?" at a high level. -4. **Potential operators/contributors** - External readers who want to help operate, modify, or answer questions about blumeops, or onboard as a member. +4. **Potential operators/contributors** - External readers who want to help operate, modify, or answer questions about BlumeOps, or onboard as a member. 5. **Replicators** - People who want to duplicate this approach for their own personal infrastructure operations. -### Planned Structure (Diataxis) +### Requirements -The final documentation structure will include: +- **Source format**: Markdown with optional YAML frontmatter +- **Editing**: Compatible with [Obsidian](https://obsidian.md) and [obsidian.nvim](https://github.com/obsidian-nvim/obsidian.nvim) +- **Cross-references**: Wiki-link syntax (`[[link]]`) for internal links +- **Output formats**: HTML (for web hosting) and PDF (for offline reference) +- **Changelog**: Track significant documentation changes -- **Tutorials** - Learning-oriented guides for getting started -- **How-to Guides** - Task-oriented instructions for specific operations -- **Reference** - Information-oriented technical descriptions -- **Explanation** - Understanding-oriented discussion of concepts and decisions +### Tooling Options (To Evaluate) -### Current State +Several markdown-to-HTML/PDF tools to consider: -The restructuring is happening in phases: +| Tool | Language | Obsidian Compatibility | Notes | +|------|----------|------------------------|-------| +| [Quartz](https://quartz.jzhao.xyz/) | TypeScript | Excellent (designed for Obsidian) | Wiki-links, backlinks, graph view | +| [MkDocs](https://www.mkdocs.org/) + [Material](https://squidfunk.github.io/mkdocs-material/) | Python | Good (with plugins) | Very popular, great search | +| [mdBook](https://rust-lang.github.io/mdBook/) | Rust | Limited | Simple, fast, used by Rust docs | +| [Hugo](https://gohugo.io/) | Go | Moderate | Fast builds, flexible | +| [Docusaurus](https://docusaurus.io/) | React | Limited | Feature-rich, heavier | -- **Phase 1** (current): Move existing zettelkasten cards to `docs/zk/` as a temporary holding area -- **Future phases**: Build out the new Diataxis-based structure, migrate relevant content from zk cards, and eventually deprecate or integrate the zk format +**Leaning toward**: Quartz (best Obsidian compatibility) or MkDocs Material (most mature ecosystem). -### Directory Layout +## Restructuring Phases + +### Phase 1: Foundation (Current) +- [x] Move existing zk cards to `docs/zk/` +- [x] Update `zk-docs` mise task for new path +- [x] Create this README with restructuring plan +- [ ] Evaluate and select documentation build tooling +- [ ] Set up basic build pipeline (mise task or CI) +- [ ] Create `CHANGELOG.md` + +### Phase 2: Tutorials +Learning-oriented content for getting started. + +- [ ] Create `tutorials/` directory +- [ ] "Getting Started with BlumeOps" - What this is and how to explore it +- [ ] "Setting Up a Similar Environment" - For replicators +- [ ] "Your First Contribution" - For potential contributors + +### Phase 3: How-to Guides +Task-oriented instructions for specific operations. + +- [ ] Create `how-to/` directory +- [ ] Migrate operational content from zk cards +- [ ] "How to deploy a new Kubernetes service" +- [ ] "How to add a new Ansible role" +- [ ] "How to update Tailscale ACLs" +- [ ] "How to troubleshoot common issues" + +### Phase 4: Reference +Information-oriented technical descriptions. + +- [ ] Create `reference/` directory +- [ ] Service reference pages (migrate from zk cards) +- [ ] Infrastructure inventory +- [ ] Configuration reference +- [ ] API/CLI reference for mise tasks + +### Phase 5: Explanation +Understanding-oriented discussion of concepts and decisions. + +- [ ] Create `explanation/` directory +- [ ] "Why GitOps?" - Philosophy and approach +- [ ] "Architecture Overview" - How everything fits together +- [ ] "Security Model" - Tailscale, secrets management, etc. +- [ ] "Decision Log" - ADRs (Architecture Decision Records) + +### Phase 6: Integration & Cleanup +- [ ] Migrate remaining useful content from `docs/zk/` +- [ ] Decide fate of zk cards (archive, delete, or keep as separate knowledge base) +- [ ] Update CLAUDE.md to reference new doc structure +- [ ] Set up automated doc builds (CI/CD) +- [ ] Consider hosting docs publicly (GitHub Pages, etc.) + +## Current Directory Layout ``` docs/ ├── README.md # This file -└── zk/ # Zettelkasten cards (temporary location during restructuring) +├── CHANGELOG.md # Documentation changelog (planned) +├── tutorials/ # Learning-oriented (planned) +├── how-to/ # Task-oriented (planned) +├── reference/ # Information-oriented (planned) +├── explanation/ # Understanding-oriented (planned) +└── zk/ # Zettelkasten cards (temporary) ├── 1767747119-YCPO.md # Main blumeops overview card └── ... # Service-specific cards and notes ``` ## Viewing the ZK Cards -To view all blumeops zettelkasten cards: +To view all BlumeOps zettelkasten cards: ```fish mise run zk-docs -- 2.50.1 (Apple Git-155) From 0080d1c54a8f4c11b38f7cf0557063a3e757dc92 Mon Sep 17 00:00:00 2001 From: Erich Blume Date: Tue, 3 Feb 2026 08:38:12 -0800 Subject: [PATCH 04/12] Add Quartz documentation build infrastructure Phase 1a infrastructure for building and hosting BlumeOps docs: - Add quartz.config.ts and quartz.layout.ts for Quartz configuration - Add containers/quartz/ with nginx-based static site server that downloads release bundles on startup via DOCS_RELEASE_URL env var - Add .forgejo/workflows/build-blumeops.yaml workflow (manual trigger) that builds Quartz site and creates Forgejo release with tarball - Update docs/README.md with finalized tooling choice and split Phase 1 into 1a (CI) and 1b (CD/hosting) The architecture separates content versioning from infrastructure: - Releases are versioned BlumeOps releases (v1.0.0, etc.) - Doc tarballs are attached as release assets - The quartz container is a generic static site server Co-Authored-By: Claude Opus 4.5 --- .forgejo/workflows/build-blumeops.yaml | 146 +++++++++++++++++++++++++ containers/quartz/Dockerfile | 52 +++++++++ containers/quartz/start.sh | 31 ++++++ docs/README.md | 37 ++++--- quartz.config.ts | 91 +++++++++++++++ quartz.layout.ts | 54 +++++++++ 6 files changed, 395 insertions(+), 16 deletions(-) create mode 100644 .forgejo/workflows/build-blumeops.yaml create mode 100644 containers/quartz/Dockerfile create mode 100644 containers/quartz/start.sh create mode 100644 quartz.config.ts create mode 100644 quartz.layout.ts diff --git a/.forgejo/workflows/build-blumeops.yaml b/.forgejo/workflows/build-blumeops.yaml new file mode 100644 index 0000000..6d0b110 --- /dev/null +++ b/.forgejo/workflows/build-blumeops.yaml @@ -0,0 +1,146 @@ +# BlumeOps Release Workflow +# +# Creates a versioned release of BlumeOps with all build artifacts. +# Currently includes: +# - Documentation site (Quartz static build) +# +# Future additions may include other release artifacts. +# +# Usage: +# 1. Go to Actions > Build BlumeOps > Run workflow +# 2. Enter a version tag (e.g., v1.2.0) +# 3. The workflow creates a release with attached artifacts +# +# Documentation asset URL: +# https://forge.ops.eblu.me/eblume/blumeops/releases/download//docs-.tar.gz + +name: Build BlumeOps + +on: + workflow_dispatch: + inputs: + version: + description: 'Version tag (e.g., v1.2.0)' + required: true + type: string + +jobs: + build: + runs-on: k8s + steps: + - name: Validate version + run: | + VERSION="${{ inputs.version }}" + if [[ ! "$VERSION" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + echo "Error: Version must be in format vX.Y.Z (e.g., v1.0.0)" + exit 1 + fi + echo "Building BlumeOps release: $VERSION" + + - name: Checkout + uses: actions/checkout@v4 + + - name: Build Quartz site + run: | + VERSION="${{ inputs.version }}" + echo "Node version: $(node --version)" + echo "NPM version: $(npm --version)" + + echo "Setting up Quartz..." + # Clone Quartz to a temp directory + git clone --depth 1 https://github.com/jackyzha0/quartz.git /tmp/quartz + cd /tmp/quartz + + # Install dependencies + npm ci + + # Copy our configuration + cp "$GITHUB_WORKSPACE/quartz.config.ts" . + cp "$GITHUB_WORKSPACE/quartz.layout.ts" . + + # Copy docs as content + rm -rf content + cp -r "$GITHUB_WORKSPACE/docs" content + + # Create index page + cat > content/index.md << 'EOF' + --- + title: BlumeOps Documentation + --- + + Welcome to the BlumeOps documentation. + + [[README|Documentation Home]] + EOF + + # Build + echo "Building static site..." + npx quartz build + + # Create tarball + TARBALL="docs-${VERSION}.tar.gz" + echo "Creating tarball: $TARBALL" + tar -czf "$GITHUB_WORKSPACE/$TARBALL" -C public . + + echo "Build complete!" + ls -lh "$GITHUB_WORKSPACE/$TARBALL" + + - name: Create release + run: | + VERSION="${{ inputs.version }}" + TARBALL="docs-${VERSION}.tar.gz" + + echo "Creating release $VERSION..." + + # Use Forgejo API to create release + RELEASE_DATA=$(cat < /etc/nginx/conf.d/default.conf << 'EOF' +server { + listen 80; + server_name _; + root /usr/share/nginx/html; + index index.html; + + # Enable gzip compression + gzip on; + gzip_types text/plain text/css application/json application/javascript text/xml application/xml text/javascript; + + # Cache static assets + location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ { + expires 1y; + add_header Cache-Control "public, immutable"; + } + + # SPA fallback - serve index.html for client-side routing + location / { + try_files $uri $uri/ $uri.html /index.html; + } + + # Health check endpoint + location /healthz { + access_log off; + return 200 "ok\n"; + add_header Content-Type text/plain; + } +} +EOF + +EXPOSE 80 + +CMD ["/start.sh"] diff --git a/containers/quartz/start.sh b/containers/quartz/start.sh new file mode 100644 index 0000000..778eeb1 --- /dev/null +++ b/containers/quartz/start.sh @@ -0,0 +1,31 @@ +#!/bin/sh +set -e + +HTML_DIR="/usr/share/nginx/html" + +# Check for required environment variable +if [ -z "$DOCS_RELEASE_URL" ]; then + echo "Error: DOCS_RELEASE_URL environment variable is required" + echo "Set it to the URL of the static site tarball to serve" + exit 1 +fi + +echo "Downloading docs from: $DOCS_RELEASE_URL" + +# Download the tarball +if ! curl -fsSL "$DOCS_RELEASE_URL" -o /tmp/docs.tar.gz; then + echo "Error: Failed to download docs from $DOCS_RELEASE_URL" + exit 1 +fi + +# Clear existing content and extract +rm -rf "${HTML_DIR:?}"/* +echo "Extracting docs to $HTML_DIR" +tar -xzf /tmp/docs.tar.gz -C "$HTML_DIR" +rm /tmp/docs.tar.gz + +echo "Docs extracted successfully" +echo "Starting nginx..." + +# Start nginx in foreground +exec nginx -g "daemon off;" diff --git a/docs/README.md b/docs/README.md index 8bc69a4..01d1b66 100644 --- a/docs/README.md +++ b/docs/README.md @@ -28,30 +28,36 @@ The documentation is being restructured to follow the [Diataxis](https://diataxi - **Output formats**: HTML (for web hosting) and PDF (for offline reference) - **Changelog**: Track significant documentation changes -### Tooling Options (To Evaluate) +### Tooling -Several markdown-to-HTML/PDF tools to consider: +**Selected**: [Quartz](https://quartz.jzhao.xyz/) - A TypeScript-based static site generator designed for Obsidian vaults. Features wiki-link support, backlinks, graph view, and excellent Obsidian compatibility. -| Tool | Language | Obsidian Compatibility | Notes | -|------|----------|------------------------|-------| -| [Quartz](https://quartz.jzhao.xyz/) | TypeScript | Excellent (designed for Obsidian) | Wiki-links, backlinks, graph view | -| [MkDocs](https://www.mkdocs.org/) + [Material](https://squidfunk.github.io/mkdocs-material/) | Python | Good (with plugins) | Very popular, great search | -| [mdBook](https://rust-lang.github.io/mdBook/) | Rust | Limited | Simple, fast, used by Rust docs | -| [Hugo](https://gohugo.io/) | Go | Moderate | Fast builds, flexible | -| [Docusaurus](https://docusaurus.io/) | React | Limited | Feature-rich, heavier | - -**Leaning toward**: Quartz (best Obsidian compatibility) or MkDocs Material (most mature ecosystem). +**Architecture**: +- **Source**: Markdown files in `docs/` with optional YAML frontmatter +- **Build**: Quartz builds static HTML/CSS/JS via Forgejo workflow +- **Release**: Built assets published as Forgejo release attachments +- **Hosting**: `quartz` container downloads release bundle on startup and serves via nginx +- **URL**: `docs.ops.eblu.me` (planned) ## Restructuring Phases -### Phase 1: Foundation (Current) +### Phase 1a: Foundation & CI (Current) - [x] Move existing zk cards to `docs/zk/` - [x] Update `zk-docs` mise task for new path - [x] Create this README with restructuring plan -- [ ] Evaluate and select documentation build tooling -- [ ] Set up basic build pipeline (mise task or CI) +- [x] Select documentation tooling (Quartz) +- [x] Create Quartz configuration (`quartz.config.ts`, `quartz.layout.ts`) +- [x] Create `quartz` container for serving static sites +- [x] Create `build-blumeops` workflow for building releases +- [ ] Test the build workflow and verify release creation - [ ] Create `CHANGELOG.md` +### Phase 1b: CD & Hosting +- [ ] Create ArgoCD manifests for `quartz` deployment +- [ ] Add `docs.ops.eblu.me` to Caddy reverse proxy +- [ ] Configure deployment to pull from release URL +- [ ] Test end-to-end: commit -> build -> release -> deploy + ### Phase 2: Tutorials Learning-oriented content for getting started. @@ -92,8 +98,7 @@ Understanding-oriented discussion of concepts and decisions. - [ ] Migrate remaining useful content from `docs/zk/` - [ ] Decide fate of zk cards (archive, delete, or keep as separate knowledge base) - [ ] Update CLAUDE.md to reference new doc structure -- [ ] Set up automated doc builds (CI/CD) -- [ ] Consider hosting docs publicly (GitHub Pages, etc.) +- [ ] Mirror docs to GitHub Pages for public access (optional) ## Current Directory Layout diff --git a/quartz.config.ts b/quartz.config.ts new file mode 100644 index 0000000..bfdaa22 --- /dev/null +++ b/quartz.config.ts @@ -0,0 +1,91 @@ +import { QuartzConfig } from "./quartz/cfg" +import * as Plugin from "./quartz/plugins" + +/** + * Quartz configuration for BlumeOps documentation + * See https://quartz.jzhao.xyz/configuration + */ +const config: QuartzConfig = { + configuration: { + pageTitle: "BlumeOps Docs", + pageTitleSuffix: "", + enableSPA: true, + enablePopovers: true, + analytics: null, + locale: "en-US", + baseUrl: "docs.ops.eblu.me", + ignorePatterns: ["private", "templates", ".obsidian"], + defaultDateType: "modified", + theme: { + fontOrigin: "googleFonts", + cdnCaching: true, + typography: { + header: "Schibsted Grotesk", + body: "Source Sans Pro", + code: "IBM Plex Mono", + }, + colors: { + lightMode: { + light: "#faf8f8", + lightgray: "#e5e5e5", + gray: "#b8b8b8", + darkgray: "#4e4e4e", + dark: "#2b2b2b", + secondary: "#284b63", + tertiary: "#84a59d", + highlight: "rgba(143, 159, 169, 0.15)", + textHighlight: "#fff23688", + }, + darkMode: { + light: "#161618", + lightgray: "#393639", + gray: "#646464", + darkgray: "#d4d4d4", + dark: "#ebebec", + secondary: "#7b97aa", + tertiary: "#84a59d", + highlight: "rgba(143, 159, 169, 0.15)", + textHighlight: "#fff23688", + }, + }, + }, + }, + plugins: { + transformers: [ + Plugin.FrontMatter(), + Plugin.CreatedModifiedDate({ + priority: ["frontmatter", "git", "filesystem"], + }), + Plugin.SyntaxHighlighting({ + theme: { + light: "github-light", + dark: "github-dark", + }, + keepBackground: false, + }), + Plugin.ObsidianFlavoredMarkdown({ enableInHtmlEmbed: false }), + Plugin.GitHubFlavoredMarkdown(), + Plugin.TableOfContents(), + Plugin.CrawlLinks({ markdownLinkResolution: "shortest" }), + Plugin.Description(), + Plugin.Latex({ renderEngine: "katex" }), + ], + filters: [Plugin.RemoveDrafts()], + emitters: [ + Plugin.AliasRedirects(), + Plugin.ComponentResources(), + Plugin.ContentPage(), + Plugin.FolderPage(), + Plugin.TagPage(), + Plugin.ContentIndex({ + enableSiteMap: true, + enableRSS: true, + }), + Plugin.Assets(), + Plugin.Static(), + Plugin.NotFoundPage(), + ], + }, +} + +export default config diff --git a/quartz.layout.ts b/quartz.layout.ts new file mode 100644 index 0000000..c3f7e6d --- /dev/null +++ b/quartz.layout.ts @@ -0,0 +1,54 @@ +import { PageLayout, SharedLayout } from "./quartz/cfg" +import * as Component from "./quartz/components" + +/** + * Quartz layout configuration for BlumeOps documentation + * See https://quartz.jzhao.xyz/layout + */ + +// Components shared across all pages +export const sharedPageComponents: SharedLayout = { + head: Component.Head(), + header: [], + afterBody: [], + footer: Component.Footer({ + links: { + "GitHub": "https://github.com/eblume/blumeops", + "Forge": "https://forge.ops.eblu.me/eblume/blumeops", + }, + }), +} + +// Components for pages that list posts (folder pages, tag pages) +export const defaultContentPageLayout: PageLayout = { + beforeBody: [ + Component.Breadcrumbs(), + Component.ArticleTitle(), + Component.ContentMeta(), + Component.TagList(), + ], + left: [ + Component.PageTitle(), + Component.MobileOnly(Component.Spacer()), + Component.Search(), + Component.Darkmode(), + Component.DesktopOnly(Component.Explorer()), + ], + right: [ + Component.Graph(), + Component.DesktopOnly(Component.TableOfContents()), + Component.Backlinks(), + ], +} + +export const defaultListPageLayout: PageLayout = { + beforeBody: [Component.Breadcrumbs(), Component.ArticleTitle(), Component.ContentMeta()], + left: [ + Component.PageTitle(), + Component.MobileOnly(Component.Spacer()), + Component.Search(), + Component.Darkmode(), + Component.DesktopOnly(Component.Explorer()), + ], + right: [], +} -- 2.50.1 (Apple Git-155) From fe161e547c8a749e0b2ee7693fd198af0691299c Mon Sep 17 00:00:00 2001 From: Erich Blume Date: Tue, 3 Feb 2026 08:40:21 -0800 Subject: [PATCH 05/12] Move Quartz config files to docs/ Keep repo root cleaner by storing quartz.config.ts and quartz.layout.ts in docs/ alongside the content they configure. Updated workflow to copy from the new location. Co-Authored-By: Claude Opus 4.5 --- .forgejo/workflows/build-blumeops.yaml | 6 +++--- quartz.config.ts => docs/quartz.config.ts | 0 quartz.layout.ts => docs/quartz.layout.ts | 0 3 files changed, 3 insertions(+), 3 deletions(-) rename quartz.config.ts => docs/quartz.config.ts (100%) rename quartz.layout.ts => docs/quartz.layout.ts (100%) diff --git a/.forgejo/workflows/build-blumeops.yaml b/.forgejo/workflows/build-blumeops.yaml index 6d0b110..d115d01 100644 --- a/.forgejo/workflows/build-blumeops.yaml +++ b/.forgejo/workflows/build-blumeops.yaml @@ -54,9 +54,9 @@ jobs: # Install dependencies npm ci - # Copy our configuration - cp "$GITHUB_WORKSPACE/quartz.config.ts" . - cp "$GITHUB_WORKSPACE/quartz.layout.ts" . + # Copy our configuration (lives in docs/ to keep repo root clean) + cp "$GITHUB_WORKSPACE/docs/quartz.config.ts" . + cp "$GITHUB_WORKSPACE/docs/quartz.layout.ts" . # Copy docs as content rm -rf content diff --git a/quartz.config.ts b/docs/quartz.config.ts similarity index 100% rename from quartz.config.ts rename to docs/quartz.config.ts diff --git a/quartz.layout.ts b/docs/quartz.layout.ts similarity index 100% rename from quartz.layout.ts rename to docs/quartz.layout.ts -- 2.50.1 (Apple Git-155) From b3b0e110c83cfcaf1367d647f366055a5f2a649e Mon Sep 17 00:00:00 2001 From: Erich Blume Date: Tue, 3 Feb 2026 08:52:13 -0800 Subject: [PATCH 06/12] Add docs/index.md instead of generating in workflow Keep the index page as a normal doc file that can be edited, rather than generating it during the build workflow. Co-Authored-By: Claude Opus 4.5 --- .forgejo/workflows/build-blumeops.yaml | 13 +------------ docs/index.md | 7 +++++++ 2 files changed, 8 insertions(+), 12 deletions(-) create mode 100644 docs/index.md diff --git a/.forgejo/workflows/build-blumeops.yaml b/.forgejo/workflows/build-blumeops.yaml index d115d01..52d98c0 100644 --- a/.forgejo/workflows/build-blumeops.yaml +++ b/.forgejo/workflows/build-blumeops.yaml @@ -58,21 +58,10 @@ jobs: cp "$GITHUB_WORKSPACE/docs/quartz.config.ts" . cp "$GITHUB_WORKSPACE/docs/quartz.layout.ts" . - # Copy docs as content + # Copy docs as content (includes index.md) rm -rf content cp -r "$GITHUB_WORKSPACE/docs" content - # Create index page - cat > content/index.md << 'EOF' - --- - title: BlumeOps Documentation - --- - - Welcome to the BlumeOps documentation. - - [[README|Documentation Home]] - EOF - # Build echo "Building static site..." npx quartz build diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..409a10e --- /dev/null +++ b/docs/index.md @@ -0,0 +1,7 @@ +--- +title: BlumeOps Documentation +--- + +Welcome to the BlumeOps documentation. + +[[README|Documentation Home]] -- 2.50.1 (Apple Git-155) From 456296a990ec00029679a474d5312608d2834cc9 Mon Sep 17 00:00:00 2001 From: Erich Blume Date: Tue, 3 Feb 2026 08:58:09 -0800 Subject: [PATCH 07/12] Use local Quartz mirror instead of GitHub Clone from forge.ops.eblu.me to avoid external dependencies during builds. Co-Authored-By: Claude Opus 4.5 --- .forgejo/workflows/build-blumeops.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.forgejo/workflows/build-blumeops.yaml b/.forgejo/workflows/build-blumeops.yaml index 52d98c0..4f13779 100644 --- a/.forgejo/workflows/build-blumeops.yaml +++ b/.forgejo/workflows/build-blumeops.yaml @@ -47,8 +47,8 @@ jobs: echo "NPM version: $(npm --version)" echo "Setting up Quartz..." - # Clone Quartz to a temp directory - git clone --depth 1 https://github.com/jackyzha0/quartz.git /tmp/quartz + # Clone Quartz from local mirror + git clone --depth 1 https://forge.ops.eblu.me/eblume/quartz.git /tmp/quartz cd /tmp/quartz # Install dependencies -- 2.50.1 (Apple Git-155) From 3e6245da282a6f37c168afc9cc4d35d8671c7a6b Mon Sep 17 00:00:00 2001 From: Erich Blume Date: Tue, 3 Feb 2026 09:01:48 -0800 Subject: [PATCH 08/12] Add caching for Quartz setup in build workflow Cache the Quartz clone and node_modules to speed up subsequent builds. Only the content copy and build steps run on cache hit. Co-Authored-By: Claude Opus 4.5 --- .forgejo/workflows/build-blumeops.yaml | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/.forgejo/workflows/build-blumeops.yaml b/.forgejo/workflows/build-blumeops.yaml index 4f13779..dacf93a 100644 --- a/.forgejo/workflows/build-blumeops.yaml +++ b/.forgejo/workflows/build-blumeops.yaml @@ -40,13 +40,22 @@ jobs: - name: Checkout uses: actions/checkout@v4 - - name: Build Quartz site + # Cache Quartz installation (clone + node_modules) + # Key includes Quartz version; bump suffix to invalidate cache + - name: Restore Quartz cache + id: cache-quartz + uses: actions/cache@v4 + with: + path: /tmp/quartz + key: quartz-v4.0.8-1 + + - name: Setup Quartz + if: steps.cache-quartz.outputs.cache-hit != 'true' run: | - VERSION="${{ inputs.version }}" + echo "Cache miss - setting up Quartz..." echo "Node version: $(node --version)" echo "NPM version: $(npm --version)" - echo "Setting up Quartz..." # Clone Quartz from local mirror git clone --depth 1 https://forge.ops.eblu.me/eblume/quartz.git /tmp/quartz cd /tmp/quartz @@ -54,6 +63,13 @@ jobs: # Install dependencies npm ci + echo "Quartz setup complete" + + - name: Build docs + run: | + VERSION="${{ inputs.version }}" + cd /tmp/quartz + # Copy our configuration (lives in docs/ to keep repo root clean) cp "$GITHUB_WORKSPACE/docs/quartz.config.ts" . cp "$GITHUB_WORKSPACE/docs/quartz.layout.ts" . -- 2.50.1 (Apple Git-155) From e626425d343325afa0ff722a4385d6865830f402 Mon Sep 17 00:00:00 2001 From: Erich Blume Date: Tue, 3 Feb 2026 09:03:53 -0800 Subject: [PATCH 09/12] Simplify Quartz cache key naming Use quartz-1, quartz-2, etc. for cache invalidation. The actual Quartz version is determined by the mirror repo, not this key. Co-Authored-By: Claude Opus 4.5 --- .forgejo/workflows/build-blumeops.yaml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.forgejo/workflows/build-blumeops.yaml b/.forgejo/workflows/build-blumeops.yaml index dacf93a..3222010 100644 --- a/.forgejo/workflows/build-blumeops.yaml +++ b/.forgejo/workflows/build-blumeops.yaml @@ -41,13 +41,14 @@ jobs: uses: actions/checkout@v4 # Cache Quartz installation (clone + node_modules) - # Key includes Quartz version; bump suffix to invalidate cache + # Quartz version is determined by the mirror (forge.ops.eblu.me/eblume/quartz) + # Bump the cache key number to invalidate (e.g., after mirror update) - name: Restore Quartz cache id: cache-quartz uses: actions/cache@v4 with: path: /tmp/quartz - key: quartz-v4.0.8-1 + key: quartz-1 - name: Setup Quartz if: steps.cache-quartz.outputs.cache-hit != 'true' -- 2.50.1 (Apple Git-155) From 9b87542cae59d354d51c6b9d5976d19634e89c94 Mon Sep 17 00:00:00 2001 From: Erich Blume Date: Tue, 3 Feb 2026 09:04:47 -0800 Subject: [PATCH 10/12] Remove Quartz caching, rebuild every time Simpler is better - just clone and npm ci on each build. Co-Authored-By: Claude Opus 4.5 --- .forgejo/workflows/build-blumeops.yaml | 22 ++-------------------- 1 file changed, 2 insertions(+), 20 deletions(-) diff --git a/.forgejo/workflows/build-blumeops.yaml b/.forgejo/workflows/build-blumeops.yaml index 3222010..ab11c17 100644 --- a/.forgejo/workflows/build-blumeops.yaml +++ b/.forgejo/workflows/build-blumeops.yaml @@ -40,20 +40,9 @@ jobs: - name: Checkout uses: actions/checkout@v4 - # Cache Quartz installation (clone + node_modules) - # Quartz version is determined by the mirror (forge.ops.eblu.me/eblume/quartz) - # Bump the cache key number to invalidate (e.g., after mirror update) - - name: Restore Quartz cache - id: cache-quartz - uses: actions/cache@v4 - with: - path: /tmp/quartz - key: quartz-1 - - - name: Setup Quartz - if: steps.cache-quartz.outputs.cache-hit != 'true' + - name: Build docs run: | - echo "Cache miss - setting up Quartz..." + VERSION="${{ inputs.version }}" echo "Node version: $(node --version)" echo "NPM version: $(npm --version)" @@ -64,13 +53,6 @@ jobs: # Install dependencies npm ci - echo "Quartz setup complete" - - - name: Build docs - run: | - VERSION="${{ inputs.version }}" - cd /tmp/quartz - # Copy our configuration (lives in docs/ to keep repo root clean) cp "$GITHUB_WORKSPACE/docs/quartz.config.ts" . cp "$GITHUB_WORKSPACE/docs/quartz.layout.ts" . -- 2.50.1 (Apple Git-155) From b45e2cb8622605d943df9417cb843b746e8bd62c Mon Sep 17 00:00:00 2001 From: Erich Blume Date: Tue, 3 Feb 2026 09:10:18 -0800 Subject: [PATCH 11/12] Add auto-increment patch version to build workflow Leave version input empty to auto-increment patch (v_._.+1) from the latest release. First release starts at v1.0.0. Co-Authored-By: Claude Opus 4.5 --- .forgejo/workflows/build-blumeops.yaml | 45 +++++++++++++++++++------- 1 file changed, 34 insertions(+), 11 deletions(-) diff --git a/.forgejo/workflows/build-blumeops.yaml b/.forgejo/workflows/build-blumeops.yaml index ab11c17..35668bc 100644 --- a/.forgejo/workflows/build-blumeops.yaml +++ b/.forgejo/workflows/build-blumeops.yaml @@ -8,7 +8,7 @@ # # Usage: # 1. Go to Actions > Build BlumeOps > Run workflow -# 2. Enter a version tag (e.g., v1.2.0) +# 2. Enter a version tag (e.g., v1.2.0) or leave empty to auto-increment patch # 3. The workflow creates a release with attached artifacts # # Documentation asset URL: @@ -20,21 +20,44 @@ on: workflow_dispatch: inputs: version: - description: 'Version tag (e.g., v1.2.0)' - required: true + description: 'Version (e.g., v1.2.0) or empty to auto-increment patch (v_._.+1)' + required: false + default: '' type: string jobs: build: runs-on: k8s steps: - - name: Validate version + - name: Resolve version + id: version run: | - VERSION="${{ inputs.version }}" - if [[ ! "$VERSION" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then - echo "Error: Version must be in format vX.Y.Z (e.g., v1.0.0)" - exit 1 + INPUT_VERSION="${{ inputs.version }}" + + if [ -n "$INPUT_VERSION" ]; then + # User provided a version - validate it + if [[ ! "$INPUT_VERSION" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + echo "Error: Version must be in format vX.Y.Z (e.g., v1.0.0)" + exit 1 + fi + VERSION="$INPUT_VERSION" + echo "Using provided version: $VERSION" + else + # Auto-increment patch version from latest release + echo "Fetching latest release..." + LATEST=$(curl -sf "https://forge.ops.eblu.me/api/v1/repos/eblume/blumeops/releases/latest" | jq -r '.tag_name // empty') + + if [ -z "$LATEST" ]; then + VERSION="v1.0.0" + echo "No previous releases found, starting at: $VERSION" + else + # Parse vX.Y.Z and increment patch + VERSION=$(echo "$LATEST" | awk -F. '{print $1"."$2"."$3+1}') + echo "Auto-incremented from $LATEST to: $VERSION" + fi fi + + echo "version=$VERSION" >> "$GITHUB_OUTPUT" echo "Building BlumeOps release: $VERSION" - name: Checkout @@ -42,7 +65,7 @@ jobs: - name: Build docs run: | - VERSION="${{ inputs.version }}" + VERSION="${{ steps.version.outputs.version }}" echo "Node version: $(node --version)" echo "NPM version: $(npm --version)" @@ -75,7 +98,7 @@ jobs: - name: Create release run: | - VERSION="${{ inputs.version }}" + VERSION="${{ steps.version.outputs.version }}" TARBALL="docs-${VERSION}.tar.gz" echo "Creating release $VERSION..." @@ -121,7 +144,7 @@ jobs: - name: Summary run: | - VERSION="${{ inputs.version }}" + VERSION="${{ steps.version.outputs.version }}" TARBALL="docs-${VERSION}.tar.gz" echo "================================================" echo "BlumeOps Release: $VERSION" -- 2.50.1 (Apple Git-155) From 3ba211cf26fa745791eab60d0056fee1cb5240f2 Mon Sep 17 00:00:00 2001 From: Erich Blume Date: Tue, 3 Feb 2026 09:11:27 -0800 Subject: [PATCH 12/12] Check for existing release before building Fail early with clear error if the specified version already exists. Co-Authored-By: Claude Opus 4.5 --- .forgejo/workflows/build-blumeops.yaml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.forgejo/workflows/build-blumeops.yaml b/.forgejo/workflows/build-blumeops.yaml index 35668bc..6b39c3c 100644 --- a/.forgejo/workflows/build-blumeops.yaml +++ b/.forgejo/workflows/build-blumeops.yaml @@ -57,6 +57,13 @@ jobs: fi fi + # Check if this version already exists + if curl -sf "https://forge.ops.eblu.me/api/v1/repos/eblume/blumeops/releases/tags/$VERSION" > /dev/null 2>&1; then + echo "Error: Release $VERSION already exists" + echo "See: https://forge.ops.eblu.me/eblume/blumeops/releases/tag/$VERSION" + exit 1 + fi + echo "version=$VERSION" >> "$GITHUB_OUTPUT" echo "Building BlumeOps release: $VERSION" -- 2.50.1 (Apple Git-155)