Adopt Dagger CI for docs build (Phase 2) (#157)
## Summary Migrates the docs build pipeline to Dagger (Phase 2 of the Dagger CI adoption plan). - **Backfill `date-modified` frontmatter** on all 80 docs — Dagger's `--src=.` excludes `.git`, so Quartz can't use git history for page dates. Frontmatter dates work with or without git. - **New `docs-check-frontmatter` mise task + pre-commit hook** — validates all docs have `title`, `tags`, and `date-modified` - **New Dagger functions** — `build_changelog` (towncrier in Python container) and `build_docs` (chains changelog → Quartz build in Node container, returns tarball) - **Simplified CI workflow** — the ~44-line inline Quartz build (clone, npm ci, build, tar, cleanup) is replaced by `dagger call build-docs`. Changelog step remains local on the runner since towncrier needs to modify the host working tree for the git commit. ### Design decisions - **Towncrier runs twice in CI**: once inside Dagger (for the docs tarball) and once on the runner (for the git commit). This is intentional — Dagger's directory export is additive and can't delete the consumed changelog fragments from the host. - **Artifact hosting stays on Forgejo Releases** (not migrated to Forgejo Packages as the plan doc originally suggested). That migration can happen independently. - **`date-modified` frontmatter** preserved even though `build_changelog` installs git — the git there is only for towncrier's `git add` call, not for history. The local iteration story (`dagger call build-docs --src=. --version=dev` with uncommitted changes) depends on frontmatter dates. ### Local iteration ```bash dagger call build-docs --src=. --version=dev export --path=./docs-dev.tar.gz tar tf docs-dev.tar.gz | head -20 ``` ## Deployment and Testing - [x] `dagger call build-docs --src=. --version=dev` produces valid 1.1MB tarball (149 HTML pages) - [x] Pre-commit hooks pass (including new `docs-check-frontmatter`) - [ ] Full `workflow_dispatch` run after merge 🤖 Generated with [Claude Code](https://claude.com/claude-code) Reviewed-on: https://forge.ops.eblu.me/eblume/blumeops/pulls/157
This commit is contained in:
parent
738b39a321
commit
b197bd5f58
85 changed files with 272 additions and 55 deletions
|
|
@ -1,5 +1,5 @@
|
||||||
import dagger
|
import dagger
|
||||||
from dagger import function, object_type
|
from dagger import dag, function, object_type
|
||||||
|
|
||||||
|
|
||||||
@object_type
|
@object_type
|
||||||
|
|
@ -22,3 +22,65 @@ class BlumeopsCi:
|
||||||
ctr = self.build(src, container_name)
|
ctr = self.build(src, container_name)
|
||||||
ref = f"{registry}/blumeops/{container_name}:{version}"
|
ref = f"{registry}/blumeops/{container_name}:{version}"
|
||||||
return await ctr.publish(ref)
|
return await ctr.publish(ref)
|
||||||
|
|
||||||
|
@function
|
||||||
|
async def build_changelog(
|
||||||
|
self, src: dagger.Directory, version: str
|
||||||
|
) -> dagger.Directory:
|
||||||
|
"""Run towncrier to build changelog, return modified source tree."""
|
||||||
|
return await (
|
||||||
|
dag.container()
|
||||||
|
.from_("python:3.12-slim")
|
||||||
|
# git is required because towncrier stages CHANGELOG.md via git add
|
||||||
|
.with_exec(["apt-get", "update", "-qq"])
|
||||||
|
.with_exec(["apt-get", "install", "-y", "-qq", "git"])
|
||||||
|
.with_exec(["pip", "install", "towncrier"])
|
||||||
|
.with_directory("/workspace", src)
|
||||||
|
.with_workdir("/workspace")
|
||||||
|
.with_exec(["git", "init"])
|
||||||
|
.with_exec(["towncrier", "build", "--version", version, "--yes"])
|
||||||
|
.directory("/workspace")
|
||||||
|
)
|
||||||
|
|
||||||
|
@function
|
||||||
|
async def build_docs(self, src: dagger.Directory, version: str) -> dagger.File:
|
||||||
|
"""Build changelog then Quartz site. Returns docs tarball."""
|
||||||
|
updated_src = await self.build_changelog(src, version)
|
||||||
|
return await (
|
||||||
|
dag.container()
|
||||||
|
.from_("node:20-slim")
|
||||||
|
.with_exec(["apt-get", "update", "-qq"])
|
||||||
|
.with_exec(["apt-get", "install", "-y", "-qq", "git"])
|
||||||
|
.with_directory("/workspace", updated_src)
|
||||||
|
.with_workdir("/workspace")
|
||||||
|
.with_exec(
|
||||||
|
[
|
||||||
|
"git",
|
||||||
|
"clone",
|
||||||
|
"--depth=1",
|
||||||
|
"https://github.com/jackyzha0/quartz.git",
|
||||||
|
"/tmp/quartz",
|
||||||
|
]
|
||||||
|
)
|
||||||
|
.with_exec(
|
||||||
|
[
|
||||||
|
"sh",
|
||||||
|
"-c",
|
||||||
|
"cp -r /tmp/quartz/quartz /tmp/quartz/package*.json "
|
||||||
|
"/tmp/quartz/tsconfig.json .",
|
||||||
|
]
|
||||||
|
)
|
||||||
|
.with_exec(["npm", "ci"])
|
||||||
|
.with_exec(["cp", "docs/quartz.config.ts", "."])
|
||||||
|
.with_exec(["cp", "docs/quartz.layout.ts", "."])
|
||||||
|
.with_exec(["cp", "CHANGELOG.md", "docs/"])
|
||||||
|
.with_exec(["npx", "quartz", "build", "-d", "docs"])
|
||||||
|
.with_exec(
|
||||||
|
[
|
||||||
|
"sh",
|
||||||
|
"-c",
|
||||||
|
f"tar -czf /docs-{version}.tar.gz -C public .",
|
||||||
|
]
|
||||||
|
)
|
||||||
|
.file(f"/docs-{version}.tar.gz")
|
||||||
|
)
|
||||||
|
|
|
||||||
|
|
@ -106,14 +106,43 @@ jobs:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
# Need full history for git operations
|
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Ensure Dagger CLI
|
||||||
|
run: |
|
||||||
|
# Bootstrap: install dagger if not already in the runner image.
|
||||||
|
# Remove once all runners include dagger (Phase 3).
|
||||||
|
if ! command -v dagger &>/dev/null; then
|
||||||
|
echo "Dagger not found, installing..."
|
||||||
|
curl -fsSL https://dl.dagger.io/dagger/install.sh | DAGGER_VERSION=0.19.11 sh
|
||||||
|
mv ./bin/dagger /usr/local/bin/dagger && rmdir ./bin
|
||||||
|
fi
|
||||||
|
dagger version
|
||||||
|
|
||||||
|
- name: Build docs
|
||||||
|
run: |
|
||||||
|
VERSION="${{ steps.version.outputs.version }}"
|
||||||
|
TARBALL="docs-${VERSION}.tar.gz"
|
||||||
|
echo "Building docs via Dagger..."
|
||||||
|
# build-docs calls build_changelog internally (towncrier runs inside
|
||||||
|
# the Dagger container). The host working tree is not modified — only
|
||||||
|
# the tarball is exported. Towncrier runs a second time on the runner
|
||||||
|
# in the next step so that CHANGELOG.md and fragment deletion are
|
||||||
|
# captured in the git commit.
|
||||||
|
dagger call build-docs --src=. --version="$VERSION" \
|
||||||
|
export --path="./$TARBALL"
|
||||||
|
echo "Build complete!"
|
||||||
|
ls -lh "$TARBALL"
|
||||||
|
|
||||||
- name: Build changelog
|
- name: Build changelog
|
||||||
id: changelog
|
id: changelog
|
||||||
run: |
|
run: |
|
||||||
VERSION="${{ steps.version.outputs.version }}"
|
VERSION="${{ steps.version.outputs.version }}"
|
||||||
|
|
||||||
|
# Run towncrier on the runner (not in Dagger) so that CHANGELOG.md
|
||||||
|
# updates and fragment deletions appear in the working tree for the
|
||||||
|
# git commit step. This is intentionally a second towncrier run —
|
||||||
|
# the first happened inside the Dagger build-docs container above.
|
||||||
# Check if there are any changelog fragments
|
# Check if there are any changelog fragments
|
||||||
FRAGMENTS=$(find docs/changelog.d -name "*.md" -not -name ".gitkeep" 2>/dev/null | wc -l)
|
FRAGMENTS=$(find docs/changelog.d -name "*.md" -not -name ".gitkeep" 2>/dev/null | wc -l)
|
||||||
|
|
||||||
|
|
@ -123,7 +152,6 @@ jobs:
|
||||||
echo "changelog_updated=true" >> "$GITHUB_OUTPUT"
|
echo "changelog_updated=true" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
# Extract the changelog section for this release to include in release body
|
# Extract the changelog section for this release to include in release body
|
||||||
# The section starts with "## [$VERSION]" and ends before the next "## [" or EOF
|
|
||||||
RELEASE_NOTES=$(awk -v ver="$VERSION" '
|
RELEASE_NOTES=$(awk -v ver="$VERSION" '
|
||||||
/^## \[/ {
|
/^## \[/ {
|
||||||
if (found) exit
|
if (found) exit
|
||||||
|
|
@ -132,7 +160,6 @@ jobs:
|
||||||
found {print}
|
found {print}
|
||||||
' CHANGELOG.md | tail -n +2)
|
' CHANGELOG.md | tail -n +2)
|
||||||
|
|
||||||
# Save release notes to a file for later use (handles multiline content)
|
|
||||||
echo "$RELEASE_NOTES" > /tmp/release_notes.md
|
echo "$RELEASE_NOTES" > /tmp/release_notes.md
|
||||||
echo "Release notes extracted for $VERSION"
|
echo "Release notes extracted for $VERSION"
|
||||||
else
|
else
|
||||||
|
|
@ -141,51 +168,6 @@ jobs:
|
||||||
echo "" > /tmp/release_notes.md
|
echo "" > /tmp/release_notes.md
|
||||||
fi
|
fi
|
||||||
|
|
||||||
- name: Build docs
|
|
||||||
run: |
|
|
||||||
VERSION="${{ steps.version.outputs.version }}"
|
|
||||||
echo "Node version: $(node --version)"
|
|
||||||
echo "NPM version: $(npm --version)"
|
|
||||||
|
|
||||||
# Clone Quartz to temp location
|
|
||||||
git clone --depth 1 https://github.com/jackyzha0/quartz.git /tmp/quartz
|
|
||||||
|
|
||||||
# Copy Quartz build system into blumeops workspace
|
|
||||||
# This allows building from within the repo so git can find file history
|
|
||||||
cp -r /tmp/quartz/quartz "$GITHUB_WORKSPACE/"
|
|
||||||
cp /tmp/quartz/package.json "$GITHUB_WORKSPACE/"
|
|
||||||
cp /tmp/quartz/package-lock.json "$GITHUB_WORKSPACE/"
|
|
||||||
cp /tmp/quartz/tsconfig.json "$GITHUB_WORKSPACE/"
|
|
||||||
|
|
||||||
cd "$GITHUB_WORKSPACE"
|
|
||||||
|
|
||||||
# Install dependencies
|
|
||||||
npm ci
|
|
||||||
|
|
||||||
# Copy our configuration to workspace root
|
|
||||||
cp docs/quartz.config.ts .
|
|
||||||
cp docs/quartz.layout.ts .
|
|
||||||
|
|
||||||
# Copy CHANGELOG.md into docs so it's part of the content
|
|
||||||
cp CHANGELOG.md docs/
|
|
||||||
|
|
||||||
# Build using -d docs so git can find file history at correct paths
|
|
||||||
echo "Building static site..."
|
|
||||||
npx quartz build -d docs
|
|
||||||
|
|
||||||
# Create tarball
|
|
||||||
TARBALL="docs-${VERSION}.tar.gz"
|
|
||||||
echo "Creating tarball: $TARBALL"
|
|
||||||
tar -czf "$TARBALL" -C public .
|
|
||||||
|
|
||||||
echo "Build complete!"
|
|
||||||
ls -lh "$TARBALL"
|
|
||||||
|
|
||||||
# Clean up Quartz build artifacts (keep tarball)
|
|
||||||
rm -rf quartz public node_modules
|
|
||||||
rm -f package.json package-lock.json tsconfig.json quartz.config.ts quartz.layout.ts
|
|
||||||
rm -f docs/CHANGELOG.md # Remove copied file
|
|
||||||
|
|
||||||
- name: Create release
|
- name: Create release
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
|
||||||
|
|
@ -110,3 +110,9 @@ repos:
|
||||||
language: system
|
language: system
|
||||||
files: ^docs/.*\.md$
|
files: ^docs/.*\.md$
|
||||||
pass_filenames: false
|
pass_filenames: false
|
||||||
|
- id: docs-check-frontmatter
|
||||||
|
name: docs-check-frontmatter
|
||||||
|
entry: mise run docs-check-frontmatter
|
||||||
|
language: system
|
||||||
|
files: ^docs/.*\.md$
|
||||||
|
pass_filenames: false
|
||||||
|
|
|
||||||
1
docs/changelog.d/dagger-phase2-docs.feature.md
Normal file
1
docs/changelog.d/dagger-phase2-docs.feature.md
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Migrate docs build pipeline to Dagger (Phase 2): `dagger call build-docs --src=. --version=dev` now runs the full Quartz build locally, identically to CI. Adds `date-modified` frontmatter to all docs and a `docs-check-frontmatter` pre-commit hook.
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
title: Architecture
|
title: Architecture
|
||||||
|
date-modified: 2026-02-09
|
||||||
last-reviewed: 2026-02-09
|
last-reviewed: 2026-02-09
|
||||||
tags:
|
tags:
|
||||||
- explanation
|
- explanation
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
title: Explanation
|
title: Explanation
|
||||||
|
date-modified: 2026-02-10
|
||||||
last-reviewed: 2026-02-10
|
last-reviewed: 2026-02-10
|
||||||
tags:
|
tags:
|
||||||
- explanation
|
- explanation
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
title: Security Model
|
title: Security Model
|
||||||
|
date-modified: 2026-02-11
|
||||||
last-reviewed: 2026-02-11
|
last-reviewed: 2026-02-11
|
||||||
tags:
|
tags:
|
||||||
- explanation
|
- explanation
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
title: Why GitOps
|
title: Why GitOps
|
||||||
|
date-modified: 2026-02-07
|
||||||
tags:
|
tags:
|
||||||
- explanation
|
- explanation
|
||||||
- philosophy
|
- philosophy
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
title: Add Ansible Role
|
title: Add Ansible Role
|
||||||
|
date-modified: 2026-02-07
|
||||||
tags:
|
tags:
|
||||||
- how-to
|
- how-to
|
||||||
- ansible
|
- ansible
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
title: Deploy K8s Service
|
title: Deploy K8s Service
|
||||||
|
date-modified: 2026-02-07
|
||||||
tags:
|
tags:
|
||||||
- how-to
|
- how-to
|
||||||
- kubernetes
|
- kubernetes
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
title: Expose a Service Publicly
|
title: Expose a Service Publicly
|
||||||
|
date-modified: 2026-02-08
|
||||||
tags:
|
tags:
|
||||||
- how-to
|
- how-to
|
||||||
- fly-io
|
- fly-io
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
title: Gandi Operations
|
title: Gandi Operations
|
||||||
|
date-modified: 2026-02-08
|
||||||
tags:
|
tags:
|
||||||
- how-to
|
- how-to
|
||||||
- dns
|
- dns
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
title: How-To
|
title: How-To
|
||||||
|
date-modified: 2026-02-11
|
||||||
tags:
|
tags:
|
||||||
- how-to
|
- how-to
|
||||||
---
|
---
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
title: Review Documentation
|
title: Review Documentation
|
||||||
|
date-modified: 2026-02-09
|
||||||
tags:
|
tags:
|
||||||
- how-to
|
- how-to
|
||||||
- documentation
|
- documentation
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
title: Manage Fly.io Proxy
|
title: Manage Fly.io Proxy
|
||||||
|
date-modified: 2026-02-08
|
||||||
tags:
|
tags:
|
||||||
- how-to
|
- how-to
|
||||||
- fly-io
|
- fly-io
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
title: "Plan: Add UniFi Pulumi Stack"
|
title: "Plan: Add UniFi Pulumi Stack"
|
||||||
|
date-modified: 2026-02-11
|
||||||
tags:
|
tags:
|
||||||
- how-to
|
- how-to
|
||||||
- plans
|
- plans
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
title: "Plan: Adopt Dagger as CI/CD Build Engine"
|
title: "Plan: Adopt Dagger as CI/CD Build Engine"
|
||||||
|
date-modified: 2026-02-11
|
||||||
tags:
|
tags:
|
||||||
- how-to
|
- how-to
|
||||||
- plans
|
- plans
|
||||||
|
|
@ -9,7 +10,7 @@ tags:
|
||||||
|
|
||||||
# Plan: Adopt Dagger as CI/CD Build Engine
|
# Plan: Adopt Dagger as CI/CD Build Engine
|
||||||
|
|
||||||
> **Status:** Phase 1 implemented
|
> **Status:** Phase 2 implemented
|
||||||
|
|
||||||
## Background
|
## Background
|
||||||
|
|
||||||
|
|
@ -496,11 +497,11 @@ BuildKit caches aggressively, making repeated builds fast. Since the Forgejo run
|
||||||
- [x] Existing `mise run container-tag-and-release` workflow still works end-to-end
|
- [x] Existing `mise run container-tag-and-release` workflow still works end-to-end
|
||||||
|
|
||||||
### Phase 2 (Docs)
|
### Phase 2 (Docs)
|
||||||
- [ ] `dagger call build-docs --src=. --version=dev` produces valid tarball locally
|
- [x] `dagger call build-docs --src=. --version=dev` produces valid tarball locally
|
||||||
- [ ] Tarball contents match current Quartz build output
|
- [x] Tarball contents match current Quartz build output
|
||||||
- [ ] `dagger call release-docs` uploads to Forgejo packages successfully
|
- [ ] ~~`dagger call release-docs` uploads to Forgejo packages successfully~~ (deferred — artifact hosting stays on Forgejo Releases)
|
||||||
- [ ] Quartz container starts and serves docs from Forgejo packages URL
|
- [ ] ~~Quartz container starts and serves docs from Forgejo packages URL~~ (deferred)
|
||||||
- [ ] ArgoCD sync works from within Dagger
|
- [ ] ~~ArgoCD sync works from within Dagger~~ (deferred)
|
||||||
- [ ] Forgejo Actions workflow_dispatch completes full release cycle
|
- [ ] Forgejo Actions workflow_dispatch completes full release cycle
|
||||||
- [ ] CHANGELOG.md and fragment cleanup committed correctly
|
- [ ] CHANGELOG.md and fragment cleanup committed correctly
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
title: "Plan: Adopt OIDC Identity Provider"
|
title: "Plan: Adopt OIDC Identity Provider"
|
||||||
|
date-modified: 2026-02-11
|
||||||
tags:
|
tags:
|
||||||
- how-to
|
- how-to
|
||||||
- plans
|
- plans
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
title: "Plan: Forgejo Actions Dashboard"
|
title: "Plan: Forgejo Actions Dashboard"
|
||||||
|
date-modified: 2026-02-11
|
||||||
tags:
|
tags:
|
||||||
- how-to
|
- how-to
|
||||||
- plans
|
- plans
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
title: "Plan: Harden Zot Registry"
|
title: "Plan: Harden Zot Registry"
|
||||||
|
date-modified: 2026-02-11
|
||||||
tags:
|
tags:
|
||||||
- how-to
|
- how-to
|
||||||
- plans
|
- plans
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
title: "Plan: Migrate Forgejo from Brew to Source Build"
|
title: "Plan: Migrate Forgejo from Brew to Source Build"
|
||||||
|
date-modified: 2026-02-10
|
||||||
tags:
|
tags:
|
||||||
- how-to
|
- how-to
|
||||||
- plans
|
- plans
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
title: "Plan: Operationalize ReoLink Camera"
|
title: "Plan: Operationalize ReoLink Camera"
|
||||||
|
date-modified: 2026-02-11
|
||||||
tags:
|
tags:
|
||||||
- how-to
|
- how-to
|
||||||
- plans
|
- plans
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
title: Plans
|
title: Plans
|
||||||
|
date-modified: 2026-02-11
|
||||||
tags:
|
tags:
|
||||||
- how-to
|
- how-to
|
||||||
- plans
|
- plans
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
title: "Plan: Upstream Fork Strategy"
|
title: "Plan: Upstream Fork Strategy"
|
||||||
|
date-modified: 2026-02-11
|
||||||
tags:
|
tags:
|
||||||
- how-to
|
- how-to
|
||||||
- plans
|
- plans
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
title: Restart Indri
|
title: Restart Indri
|
||||||
|
date-modified: 2026-02-10
|
||||||
tags:
|
tags:
|
||||||
- how-to
|
- how-to
|
||||||
- operations
|
- operations
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
title: Restore 1Password Backup
|
title: Restore 1Password Backup
|
||||||
|
date-modified: 2026-02-10
|
||||||
tags:
|
tags:
|
||||||
- how-to
|
- how-to
|
||||||
- operations
|
- operations
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
title: Troubleshooting
|
title: Troubleshooting
|
||||||
|
date-modified: 2026-02-07
|
||||||
tags:
|
tags:
|
||||||
- how-to
|
- how-to
|
||||||
- operations
|
- operations
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
title: Update Documentation
|
title: Update Documentation
|
||||||
|
date-modified: 2026-02-08
|
||||||
tags:
|
tags:
|
||||||
- how-to
|
- how-to
|
||||||
- documentation
|
- documentation
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
title: Update Tailscale ACLs
|
title: Update Tailscale ACLs
|
||||||
|
date-modified: 2026-02-07
|
||||||
tags:
|
tags:
|
||||||
- how-to
|
- how-to
|
||||||
- tailscale
|
- tailscale
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
title: Use PyPI Proxy
|
title: Use PyPI Proxy
|
||||||
|
date-modified: 2026-02-07
|
||||||
tags:
|
tags:
|
||||||
- how-to
|
- how-to
|
||||||
- python
|
- python
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
title: BlumeOps
|
title: BlumeOps
|
||||||
|
date-modified: 2026-02-08
|
||||||
aliases: []
|
aliases: []
|
||||||
id: index
|
id: index
|
||||||
tags: []
|
tags: []
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
title: Roles
|
title: Roles
|
||||||
|
date-modified: 2026-02-07
|
||||||
tags:
|
tags:
|
||||||
- ansible
|
- ansible
|
||||||
- reference
|
- reference
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
title: Gandi
|
title: Gandi
|
||||||
|
date-modified: 2026-02-08
|
||||||
tags:
|
tags:
|
||||||
- infrastructure
|
- infrastructure
|
||||||
- networking
|
- networking
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
title: Gilbert
|
title: Gilbert
|
||||||
|
date-modified: 2026-02-07
|
||||||
tags:
|
tags:
|
||||||
- infrastructure
|
- infrastructure
|
||||||
- host
|
- host
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
title: Hosts
|
title: Hosts
|
||||||
|
date-modified: 2026-02-10
|
||||||
tags:
|
tags:
|
||||||
- infrastructure
|
- infrastructure
|
||||||
---
|
---
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
title: Indri
|
title: Indri
|
||||||
|
date-modified: 2026-02-09
|
||||||
tags:
|
tags:
|
||||||
- infrastructure
|
- infrastructure
|
||||||
- host
|
- host
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
title: Power
|
title: Power
|
||||||
|
date-modified: 2026-02-09
|
||||||
tags:
|
tags:
|
||||||
- infrastructure
|
- infrastructure
|
||||||
---
|
---
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
title: Routing
|
title: Routing
|
||||||
|
date-modified: 2026-02-09
|
||||||
tags:
|
tags:
|
||||||
- infrastructure
|
- infrastructure
|
||||||
- networking
|
- networking
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
title: Tailscale
|
title: Tailscale
|
||||||
|
date-modified: 2026-02-08
|
||||||
tags:
|
tags:
|
||||||
- infrastructure
|
- infrastructure
|
||||||
- networking
|
- networking
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
title: UniFi
|
title: UniFi
|
||||||
|
date-modified: 2026-02-10
|
||||||
tags:
|
tags:
|
||||||
- infrastructure
|
- infrastructure
|
||||||
- networking
|
- networking
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
title: Apps
|
title: Apps
|
||||||
|
date-modified: 2026-02-07
|
||||||
tags:
|
tags:
|
||||||
- kubernetes
|
- kubernetes
|
||||||
- argocd
|
- argocd
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
title: Cluster
|
title: Cluster
|
||||||
|
date-modified: 2026-02-07
|
||||||
tags:
|
tags:
|
||||||
- kubernetes
|
- kubernetes
|
||||||
---
|
---
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
title: External Secrets
|
title: External Secrets
|
||||||
|
date-modified: 2026-02-07
|
||||||
tags:
|
tags:
|
||||||
- kubernetes
|
- kubernetes
|
||||||
- secrets
|
- secrets
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
title: Tailscale Operator
|
title: Tailscale Operator
|
||||||
|
date-modified: 2026-02-08
|
||||||
tags:
|
tags:
|
||||||
- kubernetes
|
- kubernetes
|
||||||
- tailscale
|
- tailscale
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
title: Backup
|
title: Backup
|
||||||
|
date-modified: 2026-02-07
|
||||||
tags:
|
tags:
|
||||||
- operations
|
- operations
|
||||||
---
|
---
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
title: Disaster Recovery
|
title: Disaster Recovery
|
||||||
|
date-modified: 2026-02-10
|
||||||
tags:
|
tags:
|
||||||
- operations
|
- operations
|
||||||
---
|
---
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
title: Observability
|
title: Observability
|
||||||
|
date-modified: 2026-02-07
|
||||||
tags:
|
tags:
|
||||||
- operations
|
- operations
|
||||||
---
|
---
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
title: Reference
|
title: Reference
|
||||||
|
date-modified: 2026-02-10
|
||||||
tags:
|
tags:
|
||||||
- reference
|
- reference
|
||||||
---
|
---
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
title: 1Password
|
title: 1Password
|
||||||
|
date-modified: 2026-02-10
|
||||||
tags:
|
tags:
|
||||||
- service
|
- service
|
||||||
- secrets
|
- secrets
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
title: Alloy
|
title: Alloy
|
||||||
|
date-modified: 2026-02-08
|
||||||
tags:
|
tags:
|
||||||
- service
|
- service
|
||||||
- observability
|
- observability
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
title: ArgoCD
|
title: ArgoCD
|
||||||
|
date-modified: 2026-02-07
|
||||||
tags:
|
tags:
|
||||||
- service
|
- service
|
||||||
- gitops
|
- gitops
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
title: Automounter
|
title: Automounter
|
||||||
|
date-modified: 2026-02-07
|
||||||
tags:
|
tags:
|
||||||
- services
|
- services
|
||||||
- macos
|
- macos
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
title: Borgmatic
|
title: Borgmatic
|
||||||
|
date-modified: 2026-02-10
|
||||||
tags:
|
tags:
|
||||||
- service
|
- service
|
||||||
- backup
|
- backup
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
title: Caddy
|
title: Caddy
|
||||||
|
date-modified: 2026-02-08
|
||||||
tags:
|
tags:
|
||||||
- service
|
- service
|
||||||
- networking
|
- networking
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
title: Devpi
|
title: Devpi
|
||||||
|
date-modified: 2026-02-07
|
||||||
tags:
|
tags:
|
||||||
- service
|
- service
|
||||||
- python
|
- python
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
title: Docs
|
title: Docs
|
||||||
|
date-modified: 2026-02-08
|
||||||
tags:
|
tags:
|
||||||
- service
|
- service
|
||||||
- documentation
|
- documentation
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
title: Fly.io Proxy
|
title: Fly.io Proxy
|
||||||
|
date-modified: 2026-02-08
|
||||||
tags:
|
tags:
|
||||||
- service
|
- service
|
||||||
- networking
|
- networking
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
title: Forgejo
|
title: Forgejo
|
||||||
|
date-modified: 2026-02-08
|
||||||
tags:
|
tags:
|
||||||
- service
|
- service
|
||||||
- git
|
- git
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
title: Grafana
|
title: Grafana
|
||||||
|
date-modified: 2026-02-08
|
||||||
tags:
|
tags:
|
||||||
- service
|
- service
|
||||||
- observability
|
- observability
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
title: Immich
|
title: Immich
|
||||||
|
date-modified: 2026-02-07
|
||||||
tags:
|
tags:
|
||||||
- service
|
- service
|
||||||
- media
|
- media
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
title: Jellyfin
|
title: Jellyfin
|
||||||
|
date-modified: 2026-02-07
|
||||||
tags:
|
tags:
|
||||||
- service
|
- service
|
||||||
- media
|
- media
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
title: Kiwix
|
title: Kiwix
|
||||||
|
date-modified: 2026-02-07
|
||||||
tags:
|
tags:
|
||||||
- service
|
- service
|
||||||
- knowledge
|
- knowledge
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
title: Loki
|
title: Loki
|
||||||
|
date-modified: 2026-02-08
|
||||||
tags:
|
tags:
|
||||||
- service
|
- service
|
||||||
- observability
|
- observability
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
title: Miniflux
|
title: Miniflux
|
||||||
|
date-modified: 2026-02-07
|
||||||
tags:
|
tags:
|
||||||
- service
|
- service
|
||||||
- rss
|
- rss
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
title: Navidrome
|
title: Navidrome
|
||||||
|
date-modified: 2026-02-07
|
||||||
tags:
|
tags:
|
||||||
- service
|
- service
|
||||||
- media
|
- media
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
title: PostgreSQL
|
title: PostgreSQL
|
||||||
|
date-modified: 2026-02-07
|
||||||
tags:
|
tags:
|
||||||
- service
|
- service
|
||||||
- database
|
- database
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
title: Prometheus
|
title: Prometheus
|
||||||
|
date-modified: 2026-02-08
|
||||||
tags:
|
tags:
|
||||||
- service
|
- service
|
||||||
- observability
|
- observability
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
title: TeslaMate
|
title: TeslaMate
|
||||||
|
date-modified: 2026-02-07
|
||||||
tags:
|
tags:
|
||||||
- service
|
- service
|
||||||
- vehicle
|
- vehicle
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
title: Transmission
|
title: Transmission
|
||||||
|
date-modified: 2026-02-07
|
||||||
tags:
|
tags:
|
||||||
- service
|
- service
|
||||||
- torrent
|
- torrent
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
title: Zot
|
title: Zot
|
||||||
|
date-modified: 2026-02-07
|
||||||
tags:
|
tags:
|
||||||
- service
|
- service
|
||||||
- registry
|
- registry
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
title: Backups
|
title: Backups
|
||||||
|
date-modified: 2026-02-10
|
||||||
tags:
|
tags:
|
||||||
- storage
|
- storage
|
||||||
- backup
|
- backup
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
title: PostgreSQL Storage
|
title: PostgreSQL Storage
|
||||||
|
date-modified: 2026-02-07
|
||||||
tags:
|
tags:
|
||||||
- storage
|
- storage
|
||||||
- database
|
- database
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
title: Sifaka
|
title: Sifaka
|
||||||
|
date-modified: 2026-02-09
|
||||||
tags:
|
tags:
|
||||||
- storage
|
- storage
|
||||||
---
|
---
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
title: Adding a Service
|
title: Adding a Service
|
||||||
|
date-modified: 2026-02-07
|
||||||
tags:
|
tags:
|
||||||
- tutorials
|
- tutorials
|
||||||
- argocd
|
- argocd
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
title: AI Assistance Guide
|
title: AI Assistance Guide
|
||||||
|
date-modified: 2026-02-09
|
||||||
tags:
|
tags:
|
||||||
- tutorials
|
- tutorials
|
||||||
- ai
|
- ai
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
title: Contributing
|
title: Contributing
|
||||||
|
date-modified: 2026-02-07
|
||||||
tags:
|
tags:
|
||||||
- tutorials
|
- tutorials
|
||||||
- contributing
|
- contributing
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
title: Exploring the Docs
|
title: Exploring the Docs
|
||||||
|
date-modified: 2026-02-10
|
||||||
tags:
|
tags:
|
||||||
- tutorials
|
- tutorials
|
||||||
- getting-started
|
- getting-started
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
title: Replicating BlumeOps
|
title: Replicating BlumeOps
|
||||||
|
date-modified: 2026-02-07
|
||||||
tags:
|
tags:
|
||||||
- tutorials
|
- tutorials
|
||||||
- replication
|
- replication
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
title: ArgoCD Config
|
title: ArgoCD Config
|
||||||
|
date-modified: 2026-02-07
|
||||||
tags:
|
tags:
|
||||||
- tutorials
|
- tutorials
|
||||||
- replication
|
- replication
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
title: Core Services
|
title: Core Services
|
||||||
|
date-modified: 2026-02-07
|
||||||
tags:
|
tags:
|
||||||
- tutorials
|
- tutorials
|
||||||
- replication
|
- replication
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
title: Kubernetes Bootstrap
|
title: Kubernetes Bootstrap
|
||||||
|
date-modified: 2026-02-07
|
||||||
tags:
|
tags:
|
||||||
- tutorials
|
- tutorials
|
||||||
- replication
|
- replication
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
title: Observability Stack
|
title: Observability Stack
|
||||||
|
date-modified: 2026-02-07
|
||||||
tags:
|
tags:
|
||||||
- tutorials
|
- tutorials
|
||||||
- replication
|
- replication
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
title: Tailscale Setup
|
title: Tailscale Setup
|
||||||
|
date-modified: 2026-02-07
|
||||||
tags:
|
tags:
|
||||||
- tutorials
|
- tutorials
|
||||||
- replication
|
- replication
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
title: Tutorials
|
title: Tutorials
|
||||||
|
date-modified: 2026-02-07
|
||||||
tags:
|
tags:
|
||||||
- tutorials
|
- tutorials
|
||||||
---
|
---
|
||||||
|
|
|
||||||
86
mise-tasks/docs-check-frontmatter
Executable file
86
mise-tasks/docs-check-frontmatter
Executable file
|
|
@ -0,0 +1,86 @@
|
||||||
|
#!/usr/bin/env -S uv run --script
|
||||||
|
# /// script
|
||||||
|
# requires-python = ">=3.12"
|
||||||
|
# dependencies = ["rich>=13.0.0"]
|
||||||
|
# ///
|
||||||
|
#MISE description="Check that all docs have required frontmatter fields"
|
||||||
|
"""Validate that all documentation files have required YAML frontmatter.
|
||||||
|
|
||||||
|
Required fields: title, tags, date-modified
|
||||||
|
|
||||||
|
Scans all markdown files in docs/ (excluding changelog.d/) and checks
|
||||||
|
that each file has YAML frontmatter containing the required fields.
|
||||||
|
|
||||||
|
Usage: mise run docs-check-frontmatter
|
||||||
|
"""
|
||||||
|
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from rich.console import Console
|
||||||
|
from rich.table import Table
|
||||||
|
|
||||||
|
DOCS_DIR = Path(__file__).parent.parent / "docs"
|
||||||
|
REQUIRED_FIELDS = {"title", "tags", "date-modified"}
|
||||||
|
|
||||||
|
# Match YAML frontmatter block
|
||||||
|
FRONTMATTER_PATTERN = re.compile(r"^---\n(.*?)\n---\n", re.DOTALL)
|
||||||
|
|
||||||
|
# Match top-level YAML keys (not indented)
|
||||||
|
KEY_PATTERN = re.compile(r"^([a-zA-Z][a-zA-Z0-9_-]*):", re.MULTILINE)
|
||||||
|
|
||||||
|
|
||||||
|
def extract_frontmatter_keys(file_path: Path) -> set[str] | None:
|
||||||
|
"""Extract top-level keys from YAML frontmatter. Returns None if no frontmatter."""
|
||||||
|
content = file_path.read_text()
|
||||||
|
match = FRONTMATTER_PATTERN.match(content)
|
||||||
|
if not match:
|
||||||
|
return None
|
||||||
|
frontmatter = match.group(1)
|
||||||
|
return set(KEY_PATTERN.findall(frontmatter))
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> int:
|
||||||
|
console = Console()
|
||||||
|
console.print("[bold]Frontmatter Validation[/bold]")
|
||||||
|
console.print(f"Required fields: {', '.join(sorted(REQUIRED_FIELDS))}")
|
||||||
|
console.print()
|
||||||
|
|
||||||
|
issues: list[tuple[str, set[str]]] = []
|
||||||
|
|
||||||
|
for md_file in sorted(DOCS_DIR.rglob("*.md")):
|
||||||
|
if "changelog.d" in md_file.parts:
|
||||||
|
continue
|
||||||
|
|
||||||
|
rel_path = str(md_file.relative_to(DOCS_DIR))
|
||||||
|
keys = extract_frontmatter_keys(md_file)
|
||||||
|
|
||||||
|
if keys is None:
|
||||||
|
issues.append((rel_path, REQUIRED_FIELDS))
|
||||||
|
continue
|
||||||
|
|
||||||
|
missing = REQUIRED_FIELDS - keys
|
||||||
|
if missing:
|
||||||
|
issues.append((rel_path, missing))
|
||||||
|
|
||||||
|
if issues:
|
||||||
|
console.print("[bold red]Missing Required Frontmatter[/bold red]")
|
||||||
|
console.print()
|
||||||
|
table = Table(show_header=True, header_style="bold")
|
||||||
|
table.add_column("File")
|
||||||
|
table.add_column("Missing Fields")
|
||||||
|
|
||||||
|
for rel_path, missing in issues:
|
||||||
|
table.add_row(rel_path, ", ".join(sorted(missing)))
|
||||||
|
|
||||||
|
console.print(table)
|
||||||
|
console.print()
|
||||||
|
return 1
|
||||||
|
|
||||||
|
console.print("[bold green]All docs have required frontmatter![/bold green]")
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sys.exit(main())
|
||||||
Loading…
Add table
Add a link
Reference in a new issue