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
|
||||
from dagger import function, object_type
|
||||
from dagger import dag, function, object_type
|
||||
|
||||
|
||||
@object_type
|
||||
|
|
@ -22,3 +22,65 @@ class BlumeopsCi:
|
|||
ctr = self.build(src, container_name)
|
||||
ref = f"{registry}/blumeops/{container_name}:{version}"
|
||||
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
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
# Need full history for git operations
|
||||
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
|
||||
id: changelog
|
||||
run: |
|
||||
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
|
||||
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"
|
||||
|
||||
# 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" '
|
||||
/^## \[/ {
|
||||
if (found) exit
|
||||
|
|
@ -132,7 +160,6 @@ jobs:
|
|||
found {print}
|
||||
' 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 extracted for $VERSION"
|
||||
else
|
||||
|
|
@ -141,51 +168,6 @@ jobs:
|
|||
echo "" > /tmp/release_notes.md
|
||||
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
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
|
|
|||
|
|
@ -110,3 +110,9 @@ repos:
|
|||
language: system
|
||||
files: ^docs/.*\.md$
|
||||
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
|
||||
date-modified: 2026-02-09
|
||||
last-reviewed: 2026-02-09
|
||||
tags:
|
||||
- explanation
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
---
|
||||
title: Explanation
|
||||
date-modified: 2026-02-10
|
||||
last-reviewed: 2026-02-10
|
||||
tags:
|
||||
- explanation
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
---
|
||||
title: Security Model
|
||||
date-modified: 2026-02-11
|
||||
last-reviewed: 2026-02-11
|
||||
tags:
|
||||
- explanation
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
---
|
||||
title: Why GitOps
|
||||
date-modified: 2026-02-07
|
||||
tags:
|
||||
- explanation
|
||||
- philosophy
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
---
|
||||
title: Add Ansible Role
|
||||
date-modified: 2026-02-07
|
||||
tags:
|
||||
- how-to
|
||||
- ansible
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
---
|
||||
title: Deploy K8s Service
|
||||
date-modified: 2026-02-07
|
||||
tags:
|
||||
- how-to
|
||||
- kubernetes
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
---
|
||||
title: Expose a Service Publicly
|
||||
date-modified: 2026-02-08
|
||||
tags:
|
||||
- how-to
|
||||
- fly-io
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
---
|
||||
title: Gandi Operations
|
||||
date-modified: 2026-02-08
|
||||
tags:
|
||||
- how-to
|
||||
- dns
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
---
|
||||
title: How-To
|
||||
date-modified: 2026-02-11
|
||||
tags:
|
||||
- how-to
|
||||
---
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
---
|
||||
title: Review Documentation
|
||||
date-modified: 2026-02-09
|
||||
tags:
|
||||
- how-to
|
||||
- documentation
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
---
|
||||
title: Manage Fly.io Proxy
|
||||
date-modified: 2026-02-08
|
||||
tags:
|
||||
- how-to
|
||||
- fly-io
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
---
|
||||
title: "Plan: Add UniFi Pulumi Stack"
|
||||
date-modified: 2026-02-11
|
||||
tags:
|
||||
- how-to
|
||||
- plans
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
---
|
||||
title: "Plan: Adopt Dagger as CI/CD Build Engine"
|
||||
date-modified: 2026-02-11
|
||||
tags:
|
||||
- how-to
|
||||
- plans
|
||||
|
|
@ -9,7 +10,7 @@ tags:
|
|||
|
||||
# Plan: Adopt Dagger as CI/CD Build Engine
|
||||
|
||||
> **Status:** Phase 1 implemented
|
||||
> **Status:** Phase 2 implemented
|
||||
|
||||
## 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
|
||||
|
||||
### Phase 2 (Docs)
|
||||
- [ ] `dagger call build-docs --src=. --version=dev` produces valid tarball locally
|
||||
- [ ] Tarball contents match current Quartz build output
|
||||
- [ ] `dagger call release-docs` uploads to Forgejo packages successfully
|
||||
- [ ] Quartz container starts and serves docs from Forgejo packages URL
|
||||
- [ ] ArgoCD sync works from within Dagger
|
||||
- [x] `dagger call build-docs --src=. --version=dev` produces valid tarball locally
|
||||
- [x] Tarball contents match current Quartz build output
|
||||
- [ ] ~~`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~~ (deferred)
|
||||
- [ ] ~~ArgoCD sync works from within Dagger~~ (deferred)
|
||||
- [ ] Forgejo Actions workflow_dispatch completes full release cycle
|
||||
- [ ] CHANGELOG.md and fragment cleanup committed correctly
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
---
|
||||
title: "Plan: Adopt OIDC Identity Provider"
|
||||
date-modified: 2026-02-11
|
||||
tags:
|
||||
- how-to
|
||||
- plans
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
---
|
||||
title: "Plan: Forgejo Actions Dashboard"
|
||||
date-modified: 2026-02-11
|
||||
tags:
|
||||
- how-to
|
||||
- plans
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
---
|
||||
title: "Plan: Harden Zot Registry"
|
||||
date-modified: 2026-02-11
|
||||
tags:
|
||||
- how-to
|
||||
- plans
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
---
|
||||
title: "Plan: Migrate Forgejo from Brew to Source Build"
|
||||
date-modified: 2026-02-10
|
||||
tags:
|
||||
- how-to
|
||||
- plans
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
---
|
||||
title: "Plan: Operationalize ReoLink Camera"
|
||||
date-modified: 2026-02-11
|
||||
tags:
|
||||
- how-to
|
||||
- plans
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
---
|
||||
title: Plans
|
||||
date-modified: 2026-02-11
|
||||
tags:
|
||||
- how-to
|
||||
- plans
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
---
|
||||
title: "Plan: Upstream Fork Strategy"
|
||||
date-modified: 2026-02-11
|
||||
tags:
|
||||
- how-to
|
||||
- plans
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
---
|
||||
title: Restart Indri
|
||||
date-modified: 2026-02-10
|
||||
tags:
|
||||
- how-to
|
||||
- operations
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
---
|
||||
title: Restore 1Password Backup
|
||||
date-modified: 2026-02-10
|
||||
tags:
|
||||
- how-to
|
||||
- operations
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
---
|
||||
title: Troubleshooting
|
||||
date-modified: 2026-02-07
|
||||
tags:
|
||||
- how-to
|
||||
- operations
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
---
|
||||
title: Update Documentation
|
||||
date-modified: 2026-02-08
|
||||
tags:
|
||||
- how-to
|
||||
- documentation
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
---
|
||||
title: Update Tailscale ACLs
|
||||
date-modified: 2026-02-07
|
||||
tags:
|
||||
- how-to
|
||||
- tailscale
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
---
|
||||
title: Use PyPI Proxy
|
||||
date-modified: 2026-02-07
|
||||
tags:
|
||||
- how-to
|
||||
- python
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
---
|
||||
title: BlumeOps
|
||||
date-modified: 2026-02-08
|
||||
aliases: []
|
||||
id: index
|
||||
tags: []
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
---
|
||||
title: Roles
|
||||
date-modified: 2026-02-07
|
||||
tags:
|
||||
- ansible
|
||||
- reference
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
---
|
||||
title: Gandi
|
||||
date-modified: 2026-02-08
|
||||
tags:
|
||||
- infrastructure
|
||||
- networking
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
---
|
||||
title: Gilbert
|
||||
date-modified: 2026-02-07
|
||||
tags:
|
||||
- infrastructure
|
||||
- host
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
---
|
||||
title: Hosts
|
||||
date-modified: 2026-02-10
|
||||
tags:
|
||||
- infrastructure
|
||||
---
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
---
|
||||
title: Indri
|
||||
date-modified: 2026-02-09
|
||||
tags:
|
||||
- infrastructure
|
||||
- host
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
---
|
||||
title: Power
|
||||
date-modified: 2026-02-09
|
||||
tags:
|
||||
- infrastructure
|
||||
---
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
---
|
||||
title: Routing
|
||||
date-modified: 2026-02-09
|
||||
tags:
|
||||
- infrastructure
|
||||
- networking
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
---
|
||||
title: Tailscale
|
||||
date-modified: 2026-02-08
|
||||
tags:
|
||||
- infrastructure
|
||||
- networking
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
---
|
||||
title: UniFi
|
||||
date-modified: 2026-02-10
|
||||
tags:
|
||||
- infrastructure
|
||||
- networking
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
---
|
||||
title: Apps
|
||||
date-modified: 2026-02-07
|
||||
tags:
|
||||
- kubernetes
|
||||
- argocd
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
---
|
||||
title: Cluster
|
||||
date-modified: 2026-02-07
|
||||
tags:
|
||||
- kubernetes
|
||||
---
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
---
|
||||
title: External Secrets
|
||||
date-modified: 2026-02-07
|
||||
tags:
|
||||
- kubernetes
|
||||
- secrets
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
---
|
||||
title: Tailscale Operator
|
||||
date-modified: 2026-02-08
|
||||
tags:
|
||||
- kubernetes
|
||||
- tailscale
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
---
|
||||
title: Backup
|
||||
date-modified: 2026-02-07
|
||||
tags:
|
||||
- operations
|
||||
---
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
---
|
||||
title: Disaster Recovery
|
||||
date-modified: 2026-02-10
|
||||
tags:
|
||||
- operations
|
||||
---
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
---
|
||||
title: Observability
|
||||
date-modified: 2026-02-07
|
||||
tags:
|
||||
- operations
|
||||
---
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
---
|
||||
title: Reference
|
||||
date-modified: 2026-02-10
|
||||
tags:
|
||||
- reference
|
||||
---
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
---
|
||||
title: 1Password
|
||||
date-modified: 2026-02-10
|
||||
tags:
|
||||
- service
|
||||
- secrets
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
---
|
||||
title: Alloy
|
||||
date-modified: 2026-02-08
|
||||
tags:
|
||||
- service
|
||||
- observability
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
---
|
||||
title: ArgoCD
|
||||
date-modified: 2026-02-07
|
||||
tags:
|
||||
- service
|
||||
- gitops
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
---
|
||||
title: Automounter
|
||||
date-modified: 2026-02-07
|
||||
tags:
|
||||
- services
|
||||
- macos
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
---
|
||||
title: Borgmatic
|
||||
date-modified: 2026-02-10
|
||||
tags:
|
||||
- service
|
||||
- backup
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
---
|
||||
title: Caddy
|
||||
date-modified: 2026-02-08
|
||||
tags:
|
||||
- service
|
||||
- networking
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
---
|
||||
title: Devpi
|
||||
date-modified: 2026-02-07
|
||||
tags:
|
||||
- service
|
||||
- python
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
---
|
||||
title: Docs
|
||||
date-modified: 2026-02-08
|
||||
tags:
|
||||
- service
|
||||
- documentation
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
---
|
||||
title: Fly.io Proxy
|
||||
date-modified: 2026-02-08
|
||||
tags:
|
||||
- service
|
||||
- networking
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
---
|
||||
title: Forgejo
|
||||
date-modified: 2026-02-08
|
||||
tags:
|
||||
- service
|
||||
- git
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
---
|
||||
title: Grafana
|
||||
date-modified: 2026-02-08
|
||||
tags:
|
||||
- service
|
||||
- observability
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
---
|
||||
title: Immich
|
||||
date-modified: 2026-02-07
|
||||
tags:
|
||||
- service
|
||||
- media
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
---
|
||||
title: Jellyfin
|
||||
date-modified: 2026-02-07
|
||||
tags:
|
||||
- service
|
||||
- media
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
---
|
||||
title: Kiwix
|
||||
date-modified: 2026-02-07
|
||||
tags:
|
||||
- service
|
||||
- knowledge
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
---
|
||||
title: Loki
|
||||
date-modified: 2026-02-08
|
||||
tags:
|
||||
- service
|
||||
- observability
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
---
|
||||
title: Miniflux
|
||||
date-modified: 2026-02-07
|
||||
tags:
|
||||
- service
|
||||
- rss
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
---
|
||||
title: Navidrome
|
||||
date-modified: 2026-02-07
|
||||
tags:
|
||||
- service
|
||||
- media
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
---
|
||||
title: PostgreSQL
|
||||
date-modified: 2026-02-07
|
||||
tags:
|
||||
- service
|
||||
- database
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
---
|
||||
title: Prometheus
|
||||
date-modified: 2026-02-08
|
||||
tags:
|
||||
- service
|
||||
- observability
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
---
|
||||
title: TeslaMate
|
||||
date-modified: 2026-02-07
|
||||
tags:
|
||||
- service
|
||||
- vehicle
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
---
|
||||
title: Transmission
|
||||
date-modified: 2026-02-07
|
||||
tags:
|
||||
- service
|
||||
- torrent
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
---
|
||||
title: Zot
|
||||
date-modified: 2026-02-07
|
||||
tags:
|
||||
- service
|
||||
- registry
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
---
|
||||
title: Backups
|
||||
date-modified: 2026-02-10
|
||||
tags:
|
||||
- storage
|
||||
- backup
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
---
|
||||
title: PostgreSQL Storage
|
||||
date-modified: 2026-02-07
|
||||
tags:
|
||||
- storage
|
||||
- database
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
---
|
||||
title: Sifaka
|
||||
date-modified: 2026-02-09
|
||||
tags:
|
||||
- storage
|
||||
---
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
---
|
||||
title: Adding a Service
|
||||
date-modified: 2026-02-07
|
||||
tags:
|
||||
- tutorials
|
||||
- argocd
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
---
|
||||
title: AI Assistance Guide
|
||||
date-modified: 2026-02-09
|
||||
tags:
|
||||
- tutorials
|
||||
- ai
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
---
|
||||
title: Contributing
|
||||
date-modified: 2026-02-07
|
||||
tags:
|
||||
- tutorials
|
||||
- contributing
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
---
|
||||
title: Exploring the Docs
|
||||
date-modified: 2026-02-10
|
||||
tags:
|
||||
- tutorials
|
||||
- getting-started
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
---
|
||||
title: Replicating BlumeOps
|
||||
date-modified: 2026-02-07
|
||||
tags:
|
||||
- tutorials
|
||||
- replication
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
---
|
||||
title: ArgoCD Config
|
||||
date-modified: 2026-02-07
|
||||
tags:
|
||||
- tutorials
|
||||
- replication
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
---
|
||||
title: Core Services
|
||||
date-modified: 2026-02-07
|
||||
tags:
|
||||
- tutorials
|
||||
- replication
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
---
|
||||
title: Kubernetes Bootstrap
|
||||
date-modified: 2026-02-07
|
||||
tags:
|
||||
- tutorials
|
||||
- replication
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
---
|
||||
title: Observability Stack
|
||||
date-modified: 2026-02-07
|
||||
tags:
|
||||
- tutorials
|
||||
- replication
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
---
|
||||
title: Tailscale Setup
|
||||
date-modified: 2026-02-07
|
||||
tags:
|
||||
- tutorials
|
||||
- replication
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
---
|
||||
title: Tutorials
|
||||
date-modified: 2026-02-07
|
||||
tags:
|
||||
- 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