diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 473b2e3..a2b8669 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -89,12 +89,12 @@ repos: args: ['-config-file', '.github/actionlint.yaml'] files: ^\.forgejo/workflows/ - # Documentation - check for duplicate card titles + # Documentation - check for duplicate filenames (required for Quartz shortest path resolution) - repo: local hooks: - - id: doc-card-titles - name: doc-card-titles - entry: mise run doc-card-titles + - id: doc-filenames + name: doc-filenames + entry: mise run doc-filenames language: system files: ^docs/.*\.md$ pass_filenames: false diff --git a/docs/changelog.d/fix-wiki-links.doc.md b/docs/changelog.d/fix-wiki-links.doc.md new file mode 100644 index 0000000..d0042ba --- /dev/null +++ b/docs/changelog.d/fix-wiki-links.doc.md @@ -0,0 +1 @@ +Fix wiki-links to use filename-based resolution with Quartz shortest path mode diff --git a/docs/index.md b/docs/index.md index b3c9324..61f97e9 100644 --- a/docs/index.md +++ b/docs/index.md @@ -8,4 +8,4 @@ Welcome to the BlumeOps documentation. ## Sections -- [[Reference]] - Technical reference cards for services, infrastructure, and operations +- [[reference/index|Reference]] - Technical reference cards for services, infrastructure, and operations diff --git a/docs/reference/index.md b/docs/reference/index.md index cbce315..413fe26 100644 --- a/docs/reference/index.md +++ b/docs/reference/index.md @@ -14,54 +14,54 @@ Individual service reference cards with URLs and configuration details. | Service | Description | Location | |---------|-------------|----------| -| [[Grafana Alloy|Alloy]] | Observability collector (metrics & logs) | indri + k8s | -| [[ArgoCD]] | GitOps continuous delivery | k8s | -| [[Borgmatic]] | Backup system | indri | -| [[1Password]] | Secrets management | cloud + k8s | -| [[Forgejo]] | Git forge & CI/CD | indri | -| [[Grafana]] | Dashboards & visualization | k8s | -| [[Immich]] | Photo management | k8s | -| [[Jellyfin]] | Media server | indri | -| [[Kiwix]] | Offline Wikipedia & ZIM archives | k8s | -| [[Loki]] | Log aggregation | k8s | -| [[Miniflux]] | RSS feed reader | k8s | -| [[Navidrome]] | Music streaming | k8s | -| [[PostgreSQL]] | Database cluster | k8s | -| [[Prometheus]] | Metrics collection | k8s | -| [[TeslaMate]] | Tesla data logger | k8s | -| [[Transmission]] | BitTorrent daemon | k8s | -| [[Zot]] | Container registry | indri | +| [[alloy|Alloy]] | Observability collector (metrics & logs) | indri + k8s | +| [[argocd|ArgoCD]] | GitOps continuous delivery | k8s | +| [[borgmatic|Borgmatic]] | Backup system | indri | +| [[1password|1Password]] | Secrets management | cloud + k8s | +| [[forgejo|Forgejo]] | Git forge & CI/CD | indri | +| [[grafana|Grafana]] | Dashboards & visualization | k8s | +| [[immich|Immich]] | Photo management | k8s | +| [[jellyfin|Jellyfin]] | Media server | indri | +| [[kiwix|Kiwix]] | Offline Wikipedia & ZIM archives | k8s | +| [[loki|Loki]] | Log aggregation | k8s | +| [[miniflux|Miniflux]] | RSS feed reader | k8s | +| [[navidrome|Navidrome]] | Music streaming | k8s | +| [[postgresql|PostgreSQL]] | Database cluster | k8s | +| [[prometheus|Prometheus]] | Metrics collection | k8s | +| [[teslamate|TeslaMate]] | Tesla data logger | k8s | +| [[transmission|Transmission]] | BitTorrent daemon | k8s | +| [[zot|Zot]] | Container registry | indri | ## Infrastructure Host inventory and network configuration. -- [[Host Inventory|Hosts]] - Device inventory -- [[Indri]] - Primary server -- [[Gilbert]] - Development workstation -- [[Tailscale]] - ACLs, groups, tags -- [[Service Routing|Routing]] - DNS domains, port mappings +- [[hosts|Hosts]] - Device inventory +- [[indri|Indri]] - Primary server +- [[gilbert|Gilbert]] - Development workstation +- [[tailscale|Tailscale]] - ACLs, groups, tags +- [[routing|Routing]] - DNS domains, port mappings ## Kubernetes Cluster configuration and application registry. -- [[Kubernetes Cluster|Cluster]] - Minikube specs, storage, networking -- [[ArgoCD Applications|Apps]] - ArgoCD application registry -- [[External Secrets]] - Secrets management +- [[cluster|Cluster]] - Minikube specs, storage, networking +- [[apps|Apps]] - ArgoCD application registry +- [[external-secrets|External Secrets]] - Secrets management ## Storage Network storage and backup configuration. -- [[Sifaka NAS|Sifaka]] - Synology NAS configuration -- [[PostgreSQL Storage]] - Database cluster -- [[Backup Policy|Backups]] - Backup policy and schedule +- [[sifaka|Sifaka]] - Synology NAS configuration +- [[postgresql-storage|PostgreSQL Storage]] - Database cluster +- [[backups|Backups]] - Backup policy and schedule ## Operations Operational concerns and their components. -- [[Observability]] - Metrics, logs, dashboards -- [[Backup]] - Data protection -- [[Disaster Recovery]] - Recovery procedures (TBD) +- [[observability|Observability]] - Metrics, logs, dashboards +- [[backup|Backup]] - Data protection +- [[disaster-recovery|Disaster Recovery]] - Recovery procedures (TBD) diff --git a/docs/reference/infrastructure/gilbert.md b/docs/reference/infrastructure/gilbert.md index 7f08799..95b32d2 100644 --- a/docs/reference/infrastructure/gilbert.md +++ b/docs/reference/infrastructure/gilbert.md @@ -23,5 +23,5 @@ Managed via `Brewfile` and `mise.toml` in the blumeops repo. ## Related -- [[Indri]] - Server accessed from gilbert -- [[Kubernetes Cluster|Cluster]] - Remote k8s access +- [[indri|Indri]] - Server accessed from gilbert +- [[cluster|Cluster]] - Remote k8s access diff --git a/docs/reference/infrastructure/hosts.md b/docs/reference/infrastructure/hosts.md index bc11b36..a0e2475 100644 --- a/docs/reference/infrastructure/hosts.md +++ b/docs/reference/infrastructure/hosts.md @@ -12,14 +12,14 @@ All devices connected via [Tailscale](https://login.tailscale.com/) tailnet `tai | Host | Description | Card | |------|-------------|------| -| **Indri** | Mac Mini M1, 2020 - Primary server | [[Indri|Details]] | -| **Gilbert** | MacBook Air M4, 2025 - Workstation | [[Gilbert|Details]] | -| **[[Sifaka NAS|Sifaka]]** | Synology NAS - Storage & backups | [[Sifaka NAS|Details]] | +| **Indri** | Mac Mini M1, 2020 - Primary server | [[indri|Details]] | +| **Gilbert** | MacBook Air M4, 2025 - Workstation | [[gilbert|Details]] | +| **[[sifaka|Sifaka]]** | Synology NAS - Storage & backups | [[sifaka|Details]] | | **Mouse** | MacBook Air M2 - Allison's laptop | - | | **UniFi** | UniFi Express 7 - Home WiFi | - | | **Dwarf** | iPad Air - Employer-provided, off tailnet | - | ## Related -- [[Tailscale]] - Network configuration -- [[Service Routing|Routing]] - Service URLs +- [[tailscale|Tailscale]] - Network configuration +- [[routing|Routing]] - Service URLs diff --git a/docs/reference/infrastructure/indri.md b/docs/reference/infrastructure/indri.md index b93277e..475ab41 100644 --- a/docs/reference/infrastructure/indri.md +++ b/docs/reference/infrastructure/indri.md @@ -22,17 +22,17 @@ Primary BlumeOps server. Mac Mini M1 (2020). ## Services Hosted **Native (via Ansible):** -- [[Forgejo]] - Git forge -- [[Zot]] - Container registry -- [[Jellyfin]] - Media server -- [[Borgmatic]] - Backup system -- [[Grafana Alloy|Alloy]] - Metrics/logs collector +- [[forgejo|Forgejo]] - Git forge +- [[zot|Zot]] - Container registry +- [[jellyfin|Jellyfin]] - Media server +- [[borgmatic|Borgmatic]] - Backup system +- [[alloy|Alloy]] - Metrics/logs collector - Caddy - Reverse proxy for `*.ops.eblu.me` **Kubernetes (via minikube):** -- [[ArgoCD Applications|All k8s applications]] +- [[apps|All k8s applications]] ## Related -- [[Service Routing|Routing]] - Port mappings -- [[Kubernetes Cluster|Cluster]] - Minikube details +- [[routing|Routing]] - Port mappings +- [[cluster|Cluster]] - Minikube details diff --git a/docs/reference/infrastructure/routing.md b/docs/reference/infrastructure/routing.md index 476b9b3..972aafa 100644 --- a/docs/reference/infrastructure/routing.md +++ b/docs/reference/infrastructure/routing.md @@ -25,20 +25,20 @@ DNS points to indri's Tailscale IP (100.98.163.89). TLS via Let's Encrypt (ACME | Service | URL | Description | |---------|-----|-------------| | Homepage | https://go.ops.eblu.me | Service dashboard | -| [[Forgejo]] | https://forge.ops.eblu.me | Git hosting (SSH: 2222) | -| [[Zot]] | https://registry.ops.eblu.me | Container registry | -| [[Grafana]] | https://grafana.ops.eblu.me | Dashboards | -| [[ArgoCD]] | https://argocd.ops.eblu.me | GitOps CD | -| [[Prometheus]] | https://prometheus.ops.eblu.me | Metrics | -| [[Loki]] | https://loki.ops.eblu.me | Logs | -| [[Miniflux]] | https://feed.ops.eblu.me | RSS reader | -| [[Kiwix]] | https://kiwix.ops.eblu.me | Offline Wikipedia | -| [[Transmission]] | https://torrent.ops.eblu.me | BitTorrent | -| [[TeslaMate]] | https://tesla.ops.eblu.me | Tesla logger | -| [[Navidrome]] | https://dj.ops.eblu.me | Music streaming | -| [[Jellyfin]] | https://jellyfin.ops.eblu.me | Media server | -| [[PostgreSQL]] | pg.ops.eblu.me:5432 | Database | -| [[Sifaka NAS|Sifaka]] | https://nas.ops.eblu.me | NAS dashboard | +| [[forgejo|Forgejo]] | https://forge.ops.eblu.me | Git hosting (SSH: 2222) | +| [[zot|Zot]] | https://registry.ops.eblu.me | Container registry | +| [[grafana|Grafana]] | https://grafana.ops.eblu.me | Dashboards | +| [[argocd|ArgoCD]] | https://argocd.ops.eblu.me | GitOps CD | +| [[prometheus|Prometheus]] | https://prometheus.ops.eblu.me | Metrics | +| [[loki|Loki]] | https://loki.ops.eblu.me | Logs | +| [[miniflux|Miniflux]] | https://feed.ops.eblu.me | RSS reader | +| [[kiwix|Kiwix]] | https://kiwix.ops.eblu.me | Offline Wikipedia | +| [[transmission|Transmission]] | https://torrent.ops.eblu.me | BitTorrent | +| [[teslamate|TeslaMate]] | https://tesla.ops.eblu.me | Tesla logger | +| [[navidrome|Navidrome]] | https://dj.ops.eblu.me | Music streaming | +| [[jellyfin|Jellyfin]] | https://jellyfin.ops.eblu.me | Media server | +| [[postgresql|PostgreSQL]] | pg.ops.eblu.me:5432 | Database | +| [[sifaka|Sifaka]] | https://nas.ops.eblu.me | NAS dashboard | ## Tailscale-Only Services @@ -61,5 +61,5 @@ DNS points to indri's Tailscale IP (100.98.163.89). TLS via Let's Encrypt (ACME ## Related -- [[Tailscale]] - ACL configuration -- [[Indri]] - Where services run +- [[tailscale|Tailscale]] - ACL configuration +- [[indri|Indri]] - Where services run diff --git a/docs/reference/infrastructure/tailscale.md b/docs/reference/infrastructure/tailscale.md index 9e89e71..8786512 100644 --- a/docs/reference/infrastructure/tailscale.md +++ b/docs/reference/infrastructure/tailscale.md @@ -17,7 +17,7 @@ ACLs managed via Pulumi in `pulumi/policy.hujson`. | Group | Members | Purpose | |-------|---------|---------| -| `group:allisonflix` | admin, member | [[Jellyfin]] media access | +| `group:allisonflix` | admin, member | [[jellyfin|Jellyfin]] media access | ## Device Tags @@ -58,5 +58,5 @@ Pulumi uses OAuth client from 1Password (blumeops vault): ## Related -- [[Service Routing|Routing]] - Service URLs -- [[Host Inventory|Hosts]] - Device inventory +- [[routing|Routing]] - Service URLs +- [[hosts|Hosts]] - Device inventory diff --git a/docs/reference/kubernetes/apps.md b/docs/reference/kubernetes/apps.md index 029601f..b844f54 100644 --- a/docs/reference/kubernetes/apps.md +++ b/docs/reference/kubernetes/apps.md @@ -7,33 +7,33 @@ tags: # ArgoCD Applications -Registry of all applications deployed via [[ArgoCD]]. +Registry of all applications deployed via [[argocd|ArgoCD]]. ## Application Registry | App | Namespace | Path/Source | Service | |-----|-----------|-------------|---------| | `apps` | argocd | `argocd/apps/` | App-of-apps root | -| `argocd` | argocd | `argocd/manifests/argocd/` | [[ArgoCD]] | +| `argocd` | argocd | `argocd/manifests/argocd/` | [[argocd|ArgoCD]] | | `tailscale-operator` | tailscale | `argocd/manifests/tailscale-operator/` | Tailscale k8s operator | -| `1password-connect` | 1password | `argocd/manifests/1password-connect/` | [[1Password]] | -| `external-secrets` | external-secrets | Helm chart | [[1Password]] | -| `external-secrets-config` | external-secrets | `argocd/manifests/external-secrets-config/` | [[1Password]] | +| `1password-connect` | 1password | `argocd/manifests/1password-connect/` | [[1password|1Password]] | +| `external-secrets` | external-secrets | Helm chart | [[1password|1Password]] | +| `external-secrets-config` | external-secrets | `argocd/manifests/external-secrets-config/` | [[1password|1Password]] | | `cloudnative-pg` | cnpg-system | Helm chart (forge mirror) | PostgreSQL operator | -| `blumeops-pg` | databases | `argocd/manifests/databases/` | [[PostgreSQL]] | -| `prometheus` | monitoring | `argocd/manifests/prometheus/` | [[Prometheus]] | -| `loki` | monitoring | `argocd/manifests/loki/` | [[Loki]] | -| `grafana` | monitoring | Helm chart (forge mirror) | [[Grafana]] | -| `grafana-config` | monitoring | `argocd/manifests/grafana-config/` | [[Grafana]] | -| `immich` | immich | Helm chart | [[Immich]] | -| `alloy-k8s` | alloy | `argocd/manifests/alloy-k8s/` | [[Grafana Alloy|Alloy]] | +| `blumeops-pg` | databases | `argocd/manifests/databases/` | [[postgresql|PostgreSQL]] | +| `prometheus` | monitoring | `argocd/manifests/prometheus/` | [[prometheus|Prometheus]] | +| `loki` | monitoring | `argocd/manifests/loki/` | [[loki|Loki]] | +| `grafana` | monitoring | Helm chart (forge mirror) | [[grafana|Grafana]] | +| `grafana-config` | monitoring | `argocd/manifests/grafana-config/` | [[grafana|Grafana]] | +| `immich` | immich | Helm chart | [[immich|Immich]] | +| `alloy-k8s` | alloy | `argocd/manifests/alloy-k8s/` | [[alloy|Alloy]] | | `kube-state-metrics` | monitoring | `argocd/manifests/kube-state-metrics/` | K8s metrics | -| `miniflux` | miniflux | `argocd/manifests/miniflux/` | [[Miniflux]] | -| `kiwix` | kiwix | `argocd/manifests/kiwix/` | [[Kiwix]] | -| `torrent` | torrent | `argocd/manifests/torrent/` | [[Transmission]] | -| `navidrome` | navidrome | `argocd/manifests/navidrome/` | [[Navidrome]] | -| `teslamate` | teslamate | `argocd/manifests/teslamate/` | [[TeslaMate]] | -| `forgejo-runner` | forgejo-runner | `argocd/manifests/forgejo-runner/` | [[Forgejo]] CI | +| `miniflux` | miniflux | `argocd/manifests/miniflux/` | [[miniflux|Miniflux]] | +| `kiwix` | kiwix | `argocd/manifests/kiwix/` | [[kiwix|Kiwix]] | +| `torrent` | torrent | `argocd/manifests/torrent/` | [[transmission|Transmission]] | +| `navidrome` | navidrome | `argocd/manifests/navidrome/` | [[navidrome|Navidrome]] | +| `teslamate` | teslamate | `argocd/manifests/teslamate/` | [[teslamate|TeslaMate]] | +| `forgejo-runner` | forgejo-runner | `argocd/manifests/forgejo-runner/` | [[forgejo|Forgejo]] CI | ## Sync Policies @@ -44,5 +44,5 @@ Registry of all applications deployed via [[ArgoCD]]. ## Related -- [[ArgoCD]] - GitOps platform details -- [[Kubernetes Cluster|Cluster]] - Kubernetes infrastructure +- [[argocd|ArgoCD]] - GitOps platform details +- [[cluster|Cluster]] - Kubernetes infrastructure diff --git a/docs/reference/kubernetes/cluster.md b/docs/reference/kubernetes/cluster.md index 31fcc35..1aceb3f 100644 --- a/docs/reference/kubernetes/cluster.md +++ b/docs/reference/kubernetes/cluster.md @@ -6,7 +6,7 @@ tags: # Kubernetes Cluster -Single-node Minikube cluster running on [[Indri]]. +Single-node Minikube cluster running on [[indri|Indri]]. ## Cluster Specifications @@ -24,16 +24,16 @@ Single-node Minikube cluster running on [[Indri]]. ## Volume Mounting -Pods mount NFS directly from [[Sifaka NAS|Sifaka]]. Docker NATs outbound traffic through indri's LAN IP (192.168.1.50), allowing access to Sifaka's NFS exports. +Pods mount NFS directly from [[sifaka|Sifaka]]. Docker NATs outbound traffic through indri's LAN IP (192.168.1.50), allowing access to Sifaka's NFS exports. ## Registry Mirror -Containerd uses [[Zot]] as a pull-through cache at `host.minikube.internal:5050`. +Containerd uses [[zot|Zot]] as a pull-through cache at `host.minikube.internal:5050`. Mirrors configured: `registry.ops.eblu.me`, `docker.io`, `ghcr.io`, `quay.io` ## Related -- [[ArgoCD Applications|Apps]] - ArgoCD applications -- [[ArgoCD]] - GitOps deployment -- [[Zot]] - Registry mirror +- [[apps|Apps]] - ArgoCD applications +- [[argocd|ArgoCD]] - GitOps deployment +- [[zot|Zot]] - Registry mirror diff --git a/docs/reference/kubernetes/external-secrets.md b/docs/reference/kubernetes/external-secrets.md index 2b814e8..95da276 100644 --- a/docs/reference/kubernetes/external-secrets.md +++ b/docs/reference/kubernetes/external-secrets.md @@ -7,4 +7,4 @@ tags: # External Secrets -See [[1Password]] in Services. +See [[1password|1Password]] in Services. diff --git a/docs/reference/operations/backup.md b/docs/reference/operations/backup.md index fa0c627..a67a3ba 100644 --- a/docs/reference/operations/backup.md +++ b/docs/reference/operations/backup.md @@ -10,6 +10,6 @@ Daily automated backups of BlumeOps data. ## Components -- [[Borgmatic]] - Backup orchestration -- [[Sifaka NAS|Sifaka]] - Backup target (NAS) -- [[Backup Policy]] - What gets backed up and retention +- [[borgmatic|Borgmatic]] - Backup orchestration +- [[sifaka|Sifaka]] - Backup target (NAS) +- [[backups|Backup Policy]] - What gets backed up and retention diff --git a/docs/reference/operations/disaster-recovery.md b/docs/reference/operations/disaster-recovery.md index e32be67..9038201 100644 --- a/docs/reference/operations/disaster-recovery.md +++ b/docs/reference/operations/disaster-recovery.md @@ -8,12 +8,12 @@ tags: TBD. Current state: -- [[Borgmatic]] provides daily backups to [[Sifaka NAS|Sifaka]] +- [[borgmatic|Borgmatic]] provides daily backups to [[sifaka|Sifaka]] - Infrastructure can be rebootstrapped using the blumeops repo - Detailed DR procedures not yet documented ## Components -- [[Borgmatic]] - Backup restoration -- [[1Password]] - Credential recovery -- [[Forgejo]] - Source of truth for infrastructure code +- [[borgmatic|Borgmatic]] - Backup restoration +- [[1password|1Password]] - Credential recovery +- [[forgejo|Forgejo]] - Source of truth for infrastructure code diff --git a/docs/reference/operations/observability.md b/docs/reference/operations/observability.md index 1e79c07..3d7da3e 100644 --- a/docs/reference/operations/observability.md +++ b/docs/reference/operations/observability.md @@ -10,7 +10,7 @@ Metrics, logs, and dashboards for BlumeOps infrastructure. ## Components -- [[Prometheus]] - Metrics storage and querying -- [[Loki]] - Log aggregation -- [[Grafana Alloy|Alloy]] - Metrics and log collection -- [[Grafana]] - Dashboards and visualization +- [[prometheus|Prometheus]] - Metrics storage and querying +- [[loki|Loki]] - Log aggregation +- [[alloy|Alloy]] - Metrics and log collection +- [[grafana|Grafana]] - Dashboards and visualization diff --git a/docs/reference/services/1password.md b/docs/reference/services/1password.md index 2db7f59..fff91ba 100644 --- a/docs/reference/services/1password.md +++ b/docs/reference/services/1password.md @@ -36,5 +36,5 @@ Services reference 1Password items via `ExternalSecret` manifests. ## Related -- [[ArgoCD]] - Uses secrets for git access -- [[PostgreSQL]] - Database credentials +- [[argocd|ArgoCD]] - Uses secrets for git access +- [[postgresql|PostgreSQL]] - Database credentials diff --git a/docs/reference/services/argocd.md b/docs/reference/services/argocd.md index b944d7f..f327b99 100644 --- a/docs/reference/services/argocd.md +++ b/docs/reference/services/argocd.md @@ -7,7 +7,7 @@ tags: # ArgoCD -GitOps continuous delivery platform for the [[Kubernetes Cluster|Kubernetes cluster]]. +GitOps continuous delivery platform for the [[cluster|Kubernetes cluster]]. ## Quick Reference @@ -33,5 +33,5 @@ GitOps continuous delivery platform for the [[Kubernetes Cluster|Kubernetes clus ## Related -- [[ArgoCD Applications|Apps]] - Full application registry -- [[Forgejo]] - Git source +- [[apps|Apps]] - Full application registry +- [[forgejo|Forgejo]] - Git source diff --git a/docs/reference/services/borgmatic.md b/docs/reference/services/borgmatic.md index 2c26647..639c2e4 100644 --- a/docs/reference/services/borgmatic.md +++ b/docs/reference/services/borgmatic.md @@ -16,7 +16,7 @@ Daily backup system using Borg backup, running on indri. | **Install** | mise (pipx) | | **Config** | `~/.config/borgmatic/config.yaml` | | **Schedule** | Daily at 2:00 AM | -| **Repository** | `/Volumes/backups/borg/` on [[Sifaka NAS|Sifaka]] | +| **Repository** | `/Volumes/backups/borg/` on [[sifaka|Sifaka]] | ## What Gets Backed Up @@ -55,6 +55,6 @@ Dashboard: "Borgmatic Backups" in [[grafana|Grafana]] ## Related -- [[Backup Policy|Backups]] - Full backup policy -- [[Sifaka NAS|Sifaka]] - Backup target +- [[backups|Backups]] - Full backup policy +- [[sifaka|Sifaka]] - Backup target - [[postgresql|PostgreSQL]] - Database backups diff --git a/docs/reference/services/forgejo.md b/docs/reference/services/forgejo.md index ff5a0d0..fd48051 100644 --- a/docs/reference/services/forgejo.md +++ b/docs/reference/services/forgejo.md @@ -44,5 +44,5 @@ Managed via 1Password: `lfs-jwt-secret`, `internal-token`, `oauth2-jwt-secret`, ## Related -- [[ArgoCD]] - Uses Forgejo as git source -- [[Zot]] - Container registry for built images +- [[argocd|ArgoCD]] - Uses Forgejo as git source +- [[zot|Zot]] - Container registry for built images diff --git a/docs/reference/services/immich.md b/docs/reference/services/immich.md index df76d37..d77122b 100644 --- a/docs/reference/services/immich.md +++ b/docs/reference/services/immich.md @@ -16,11 +16,11 @@ Self-hosted photo and video management. | **URL** | https://photos.ops.eblu.me | | **Namespace** | `immich` | | **Deployment** | Helm chart (k8s) | -| **Database** | [[PostgreSQL]] (CNPG) | -| **Storage** | [[Sifaka NAS|Sifaka]] photos volume | +| **Database** | [[postgresql|PostgreSQL]] (CNPG) | +| **Storage** | [[sifaka|Sifaka]] photos volume | ## Related -- [[PostgreSQL]] - Database backend -- [[Sifaka NAS|Sifaka]] - Photo storage -- [[Jellyfin]] - Video streaming (separate service) +- [[postgresql|PostgreSQL]] - Database backend +- [[sifaka|Sifaka]] - Photo storage +- [[jellyfin|Jellyfin]] - Video streaming (separate service) diff --git a/docs/reference/services/jellyfin.md b/docs/reference/services/jellyfin.md index 0eb13ce..74be14b 100644 --- a/docs/reference/services/jellyfin.md +++ b/docs/reference/services/jellyfin.md @@ -48,4 +48,4 @@ Dashboard > Playback: ## Related - [[navidrome|Navidrome]] - Music streaming -- [[Sifaka NAS|Sifaka]] - Media storage +- [[sifaka|Sifaka]] - Media storage diff --git a/docs/reference/services/kiwix.md b/docs/reference/services/kiwix.md index 5e2bb7a..f98835c 100644 --- a/docs/reference/services/kiwix.md +++ b/docs/reference/services/kiwix.md @@ -17,7 +17,7 @@ Offline Wikipedia and ZIM archive server. | **Tailscale URL** | https://kiwix.tail8d86e.ts.net | | **Namespace** | `kiwix` | | **Image** | `ghcr.io/kiwix/kiwix-serve:3.8.1` | -| **Storage** | NFS from [[Sifaka NAS|Sifaka]] (`/volume1/torrents`) | +| **Storage** | NFS from [[sifaka|Sifaka]] (`/volume1/torrents`) | ## Architecture @@ -49,4 +49,4 @@ Full list: `argocd/manifests/kiwix/configmap-zim-torrents.yaml` ## Related - [[transmission|Transmission]] - Downloads ZIM files -- [[Sifaka NAS|Sifaka]] - ZIM storage +- [[sifaka|Sifaka]] - ZIM storage diff --git a/docs/reference/services/miniflux.md b/docs/reference/services/miniflux.md index b99a8b0..8c303b1 100644 --- a/docs/reference/services/miniflux.md +++ b/docs/reference/services/miniflux.md @@ -17,7 +17,7 @@ Minimalist RSS/Atom feed reader. | **Tailscale URL** | https://feed.tail8d86e.ts.net | | **Namespace** | `miniflux` | | **Image** | `ghcr.io/miniflux/miniflux:latest` | -| **Database** | [[PostgreSQL]] | +| **Database** | [[postgresql|PostgreSQL]] | ## Features @@ -33,9 +33,9 @@ Uses CloudNativePG cluster at `pg.ops.eblu.me`. Database user password stored in ## Backup -Feed subscriptions and read state backed up via [[Borgmatic]] PostgreSQL hook. +Feed subscriptions and read state backed up via [[borgmatic|Borgmatic]] PostgreSQL hook. ## Related -- [[PostgreSQL]] - Database backend -- [[Borgmatic]] - Data backup +- [[postgresql|PostgreSQL]] - Database backend +- [[borgmatic|Borgmatic]] - Data backup diff --git a/docs/reference/services/navidrome.md b/docs/reference/services/navidrome.md index e6c08db..0c5ba87 100644 --- a/docs/reference/services/navidrome.md +++ b/docs/reference/services/navidrome.md @@ -38,5 +38,5 @@ The `/data` directory contains SQLite database, configuration, and cache. ## Related -- [[Jellyfin]] - Video streaming -- [[Sifaka NAS|Sifaka]] - Music storage +- [[jellyfin|Jellyfin]] - Video streaming +- [[sifaka|Sifaka]] - Music storage diff --git a/docs/reference/services/postgresql.md b/docs/reference/services/postgresql.md index e71bf3e..a9a7eb1 100644 --- a/docs/reference/services/postgresql.md +++ b/docs/reference/services/postgresql.md @@ -23,8 +23,8 @@ Database cluster via CloudNativePG operator. | Database | Owner | Purpose | |----------|-------|---------| -| miniflux | miniflux | [[Miniflux]] feed data | -| teslamate | teslamate | [[TeslaMate]] vehicle data | +| miniflux | miniflux | [[miniflux|Miniflux]] feed data | +| teslamate | teslamate | [[teslamate|TeslaMate]] vehicle data | ## Users @@ -34,11 +34,11 @@ Database cluster via CloudNativePG operator. | miniflux | app owner | Owns miniflux database | | teslamate | superuser | TeslaMate (needs extensions) | | eblume | superuser | Admin access | -| borgmatic | pg_read_all_data | [[Borgmatic|Backup]] access | +| borgmatic | pg_read_all_data | [[borgmatic|Backup]] access | ## Backup -Backed up via [[Borgmatic]] `postgresql_databases` hook. Streams `pg_dump` directly to Borg (no intermediate files, no downtime). See [[Backup]] for overall backup policy. +Backed up via [[borgmatic|Borgmatic]] `postgresql_databases` hook. Streams `pg_dump` directly to Borg (no intermediate files, no downtime). See [[backup|Backup]] for overall backup policy. ## Credentials @@ -54,6 +54,6 @@ Backed up via [[Borgmatic]] `postgresql_databases` hook. Streams `pg_dump` direc ## Related -- [[Miniflux]] - Feed reader database -- [[TeslaMate]] - Vehicle data database -- [[Borgmatic]] - Database backup +- [[miniflux|Miniflux]] - Feed reader database +- [[teslamate|TeslaMate]] - Vehicle data database +- [[borgmatic|Borgmatic]] - Database backup diff --git a/docs/reference/services/prometheus.md b/docs/reference/services/prometheus.md index 0966c2c..8b2093e 100644 --- a/docs/reference/services/prometheus.md +++ b/docs/reference/services/prometheus.md @@ -23,19 +23,19 @@ Metrics storage and querying for BlumeOps infrastructure. ## Data Sources ### Remote Write (from Alloy) -- Indri system metrics via [[Grafana Alloy|Alloy]] remote_write +- Indri system metrics via [[alloy|Alloy]] remote_write - Textfile metrics: minikube, borgmatic, zot, jellyfin ### Scrape Targets | Target | Metrics | |--------|---------| -| `sifaka:9100` | [[Sifaka NAS|Sifaka]] NAS (node_exporter) | -| `cnpg-metrics.tail8d86e.ts.net:9187` | [[PostgreSQL|CloudNativePG]] metrics | +| `sifaka:9100` | [[sifaka|Sifaka]] NAS (node_exporter) | +| `cnpg-metrics.tail8d86e.ts.net:9187` | [[postgresql|CloudNativePG]] metrics | | `kube-state-metrics.monitoring.svc:8080` | Kubernetes resource metrics | ## Related -- [[Grafana Alloy|Alloy]] - Metrics collector -- [[Grafana]] - Visualization -- [[Loki]] - Logs counterpart +- [[alloy|Alloy]] - Metrics collector +- [[grafana|Grafana]] - Visualization +- [[loki|Loki]] - Logs counterpart diff --git a/docs/reference/services/transmission.md b/docs/reference/services/transmission.md index 80fd138..7ba1110 100644 --- a/docs/reference/services/transmission.md +++ b/docs/reference/services/transmission.md @@ -17,7 +17,7 @@ BitTorrent daemon, primarily for downloading ZIM archives for [[kiwix|Kiwix]]. | **Tailscale URL** | https://torrent.tail8d86e.ts.net | | **Namespace** | `torrent` | | **Image** | `lscr.io/linuxserver/transmission:latest` | -| **Storage** | NFS PVC from [[Sifaka NAS|Sifaka]] | +| **Storage** | NFS PVC from [[sifaka|Sifaka]] | ## Storage Layout @@ -50,4 +50,4 @@ Web UI shows: active/seeding/paused counts, speeds, disk usage. ## Related - [[kiwix|Kiwix]] - ZIM archive consumer -- [[Sifaka NAS|Sifaka]] - Download storage +- [[sifaka|Sifaka]] - Download storage diff --git a/docs/reference/services/zot.md b/docs/reference/services/zot.md index ecab117..b369c08 100644 --- a/docs/reference/services/zot.md +++ b/docs/reference/services/zot.md @@ -30,7 +30,7 @@ OCI-native container registry providing pull-through cache and private image sto ## Pull-Through Cache -When [[Kubernetes Cluster|minikube]] pulls an image, containerd checks zot first. If cached, returns immediately. If not, zot fetches from upstream, caches it, then returns. +When [[cluster|minikube]] pulls an image, containerd checks zot first. If cached, returns immediately. If not, zot fetches from upstream, caches it, then returns. ## Security Model @@ -38,5 +38,5 @@ Network access only (no authentication). Defense is the Tailscale ACL boundary. ## Related -- [[Forgejo]] - Container build CI -- [[Kubernetes Cluster|Cluster]] - Registry consumer +- [[forgejo|Forgejo]] - Container build CI +- [[cluster|Cluster]] - Registry consumer diff --git a/docs/reference/storage/backups.md b/docs/reference/storage/backups.md index ded19c8..c937714 100644 --- a/docs/reference/storage/backups.md +++ b/docs/reference/storage/backups.md @@ -7,13 +7,13 @@ tags: # Backup Policy -Daily automated backups from [[Indri]] to [[Sifaka NAS|Sifaka]] NAS. +Daily automated backups from [[indri|Indri]] to [[sifaka|Sifaka]] NAS. ## Schedule | Time | Frequency | System | |------|-----------|--------| -| 2:00 AM | Daily | [[Borgmatic]] | +| 2:00 AM | Daily | [[borgmatic|Borgmatic]] | ## What Gets Backed Up @@ -31,8 +31,8 @@ Daily automated backups from [[Indri]] to [[Sifaka NAS|Sifaka]] NAS. | Database | Host | Method | |----------|------|--------| -| miniflux | [[PostgreSQL|pg.ops.eblu.me]] | pg_dump stream | -| teslamate | [[PostgreSQL|pg.ops.eblu.me]] | pg_dump stream | +| miniflux | [[postgresql|pg.ops.eblu.me]] | pg_dump stream | +| teslamate | [[postgresql|pg.ops.eblu.me]] | pg_dump stream | ## What Is NOT Backed Up @@ -53,19 +53,19 @@ Daily automated backups from [[Indri]] to [[Sifaka NAS|Sifaka]] NAS. ## Backup Target -Repository: `/Volumes/backups/borg/` on [[Sifaka NAS|Sifaka]] +Repository: `/Volumes/backups/borg/` on [[sifaka|Sifaka]] ## Monitoring -Metrics exposed to [[Prometheus]]: +Metrics exposed to [[prometheus|Prometheus]]: - `borgmatic_up` - Repository accessible - `borgmatic_last_archive_timestamp` - Last backup time - `borgmatic_repo_deduplicated_size_bytes` - Disk usage -Dashboard: "Borgmatic Backups" in [[Grafana]] +Dashboard: "Borgmatic Backups" in [[grafana|Grafana]] ## Related -- [[Borgmatic]] - Backup system details -- [[Sifaka NAS|Sifaka]] - Backup storage -- [[PostgreSQL]] - Database backups +- [[borgmatic|Borgmatic]] - Backup system details +- [[sifaka|Sifaka]] - Backup storage +- [[postgresql|PostgreSQL]] - Database backups diff --git a/docs/reference/storage/postgresql.md b/docs/reference/storage/postgresql-storage.md similarity index 67% rename from docs/reference/storage/postgresql.md rename to docs/reference/storage/postgresql-storage.md index 8742569..7127d7d 100644 --- a/docs/reference/storage/postgresql.md +++ b/docs/reference/storage/postgresql-storage.md @@ -7,4 +7,4 @@ tags: # PostgreSQL Storage -See [[PostgreSQL]] in Services. +See [[postgresql|PostgreSQL]] in Services. diff --git a/docs/reference/storage/sifaka.md b/docs/reference/storage/sifaka.md index 38249cb..c3b7e50 100644 --- a/docs/reference/storage/sifaka.md +++ b/docs/reference/storage/sifaka.md @@ -21,11 +21,11 @@ Synology NAS providing network storage and backup target. | Share | Path | Purpose | Consumers | |-------|------|---------|-----------| -| backups | `/volume1/backups` | Borg backup repository | [[Borgmatic]] | -| torrents | `/volume1/torrents` | ZIM downloads | [[Kiwix]], [[Transmission]] | -| music | `/volume1/music` | Music library | [[Navidrome]] | -| allisonflix | `/volume1/allisonflix` | Video library | [[Jellyfin]] | -| photos | `/volume1/photos` | Photo library | [[Immich]] | +| backups | `/volume1/backups` | Borg backup repository | [[borgmatic|Borgmatic]] | +| torrents | `/volume1/torrents` | ZIM downloads | [[kiwix|Kiwix]], [[transmission|Transmission]] | +| music | `/volume1/music` | Music library | [[navidrome|Navidrome]] | +| allisonflix | `/volume1/allisonflix` | Video library | [[jellyfin|Jellyfin]] | +| photos | `/volume1/photos` | Photo library | [[immich|Immich]] | ## NFS Exports @@ -37,7 +37,7 @@ Synology NAS providing network storage and backup target. ## Monitoring -Node exporter running in Docker container, scraped by [[Prometheus]] at `sifaka:9100`. +Node exporter running in Docker container, scraped by [[prometheus|Prometheus]] at `sifaka:9100`. ## Tailscale @@ -46,14 +46,14 @@ Node exporter running in Docker container, scraped by [[Prometheus]] at `sifaka: ## Backup -Sifaka is the **target** for [[Backup|backups]], not a backup source. [[Borgmatic]] sends backups TO sifaka, not OF sifaka. +Sifaka is the **target** for [[backup|backups]], not a backup source. [[borgmatic|Borgmatic]] sends backups TO sifaka, not OF sifaka. Data protection for sifaka itself currently relies on the Synology RAID 5 configuration, which provides single-disk fault tolerance. Future plans include offsite duplication for additional resiliency. ## Related -- [[Backup Policy|Backups]] - Backup policy -- [[Borgmatic]] - Backup system -- [[Immich]] - Photo consumer -- [[Jellyfin]] - Media consumer -- [[Navidrome]] - Music consumer +- [[backups|Backups]] - Backup policy +- [[borgmatic|Borgmatic]] - Backup system +- [[immich|Immich]] - Photo consumer +- [[jellyfin|Jellyfin]] - Media consumer +- [[navidrome|Navidrome]] - Music consumer diff --git a/docs/zk/argocd.md b/docs/zk/argocd-log.md similarity index 99% rename from docs/zk/argocd.md rename to docs/zk/argocd-log.md index 7a1f325..47facc5 100644 --- a/docs/zk/argocd.md +++ b/docs/zk/argocd-log.md @@ -1,5 +1,5 @@ --- -id: argocd +id: argocd-log aliases: - argo-cd tags: diff --git a/docs/zk/borgmatic.md b/docs/zk/borgmatic-log.md similarity index 99% rename from docs/zk/borgmatic.md rename to docs/zk/borgmatic-log.md index 87ccf05..3481f4f 100644 --- a/docs/zk/borgmatic.md +++ b/docs/zk/borgmatic-log.md @@ -1,5 +1,5 @@ --- -id: borgmatic +id: borgmatic-log aliases: - borg-backup tags: diff --git a/docs/zk/external-secrets.md b/docs/zk/external-secrets-log.md similarity index 98% rename from docs/zk/external-secrets.md rename to docs/zk/external-secrets-log.md index 3eccda4..310932c 100644 --- a/docs/zk/external-secrets.md +++ b/docs/zk/external-secrets-log.md @@ -1,5 +1,5 @@ --- -id: external-secrets +id: external-secrets-log aliases: - eso - external-secrets-operator diff --git a/docs/zk/grafana.md b/docs/zk/grafana-log.md similarity index 99% rename from docs/zk/grafana.md rename to docs/zk/grafana-log.md index 1d9ff9c..c01402a 100644 --- a/docs/zk/grafana.md +++ b/docs/zk/grafana-log.md @@ -1,5 +1,5 @@ --- -id: grafana +id: grafana-log tags: - blumeops --- diff --git a/docs/zk/indri.md b/docs/zk/indri-log.md similarity index 99% rename from docs/zk/indri.md rename to docs/zk/indri-log.md index 6f4047d..a3a86c4 100644 --- a/docs/zk/indri.md +++ b/docs/zk/indri-log.md @@ -1,5 +1,5 @@ --- -id: indri +id: indri-log aliases: - mac-mini tags: diff --git a/docs/zk/jellyfin.md b/docs/zk/jellyfin-log.md similarity index 99% rename from docs/zk/jellyfin.md rename to docs/zk/jellyfin-log.md index 132b55b..f3e9899 100644 --- a/docs/zk/jellyfin.md +++ b/docs/zk/jellyfin-log.md @@ -1,5 +1,5 @@ --- -id: jellyfin +id: jellyfin-log tags: - blumeops --- diff --git a/docs/zk/kiwix.md b/docs/zk/kiwix-log.md similarity index 99% rename from docs/zk/kiwix.md rename to docs/zk/kiwix-log.md index 5e19bb5..44ef9de 100644 --- a/docs/zk/kiwix.md +++ b/docs/zk/kiwix-log.md @@ -1,5 +1,5 @@ --- -id: kiwix +id: kiwix-log tags: - blumeops --- diff --git a/docs/zk/miniflux.md b/docs/zk/miniflux-log.md similarity index 99% rename from docs/zk/miniflux.md rename to docs/zk/miniflux-log.md index a516d6a..9005106 100644 --- a/docs/zk/miniflux.md +++ b/docs/zk/miniflux-log.md @@ -1,5 +1,5 @@ --- -id: miniflux +id: miniflux-log aliases: - feed - rss diff --git a/docs/zk/navidrome.md b/docs/zk/navidrome-log.md similarity index 99% rename from docs/zk/navidrome.md rename to docs/zk/navidrome-log.md index ef66d5b..5e0623e 100644 --- a/docs/zk/navidrome.md +++ b/docs/zk/navidrome-log.md @@ -1,5 +1,5 @@ --- -id: navidrome +id: navidrome-log aliases: - DJ tags: diff --git a/docs/zk/postgresql.md b/docs/zk/postgresql-log.md similarity index 99% rename from docs/zk/postgresql.md rename to docs/zk/postgresql-log.md index f8d35d1..a1959c8 100644 --- a/docs/zk/postgresql.md +++ b/docs/zk/postgresql-log.md @@ -1,5 +1,5 @@ --- -id: postgresql +id: postgresql-log aliases: - postgres - pg diff --git a/docs/zk/teslamate.md b/docs/zk/teslamate-log.md similarity index 99% rename from docs/zk/teslamate.md rename to docs/zk/teslamate-log.md index 8684a36..211a0ff 100644 --- a/docs/zk/teslamate.md +++ b/docs/zk/teslamate-log.md @@ -1,5 +1,5 @@ --- -id: teslamate +id: teslamate-log aliases: - tesla tags: diff --git a/docs/zk/transmission.md b/docs/zk/transmission-log.md similarity index 99% rename from docs/zk/transmission.md rename to docs/zk/transmission-log.md index 9a46834..794bb53 100644 --- a/docs/zk/transmission.md +++ b/docs/zk/transmission-log.md @@ -1,5 +1,5 @@ --- -id: transmission +id: transmission-log tags: - blumeops --- diff --git a/docs/zk/zot.md b/docs/zk/zot-log.md similarity index 99% rename from docs/zk/zot.md rename to docs/zk/zot-log.md index 113d2fe..ea62e1d 100644 --- a/docs/zk/zot.md +++ b/docs/zk/zot-log.md @@ -1,5 +1,5 @@ --- -id: zot +id: zot-log aliases: - container-registry tags: diff --git a/mise-tasks/doc-card-titles b/mise-tasks/doc-card-titles deleted file mode 100755 index c791f1b..0000000 --- a/mise-tasks/doc-card-titles +++ /dev/null @@ -1,154 +0,0 @@ -#!/usr/bin/env -S uv run --script -# /// script -# requires-python = ">=3.12" -# dependencies = ["pyyaml>=6.0", "rich>=13.0.0"] -# /// -#MISE description="List all doc card titles and detect duplicates" -"""List all documentation card titles/IDs and detect duplicates. - -This script scans all markdown files in the docs/ directory (excluding -changelog.d/), extracts frontmatter titles, IDs, and aliases, and reports -any duplicates or conflicts that could cause wiki-link resolution issues. - -Usage: mise run doc-card-titles -""" - -import sys -from collections import defaultdict -from pathlib import Path - -import yaml -from rich.console import Console -from rich.table import Table - -DOCS_DIR = Path(__file__).parent.parent / "docs" - - -def extract_frontmatter(file_path: Path) -> dict | None: - """Extract YAML frontmatter from a markdown file.""" - content = file_path.read_text() - if not content.startswith("---"): - return None - - # Find the closing --- - end_idx = content.find("---", 3) - if end_idx == -1: - return None - - frontmatter_text = content[3:end_idx].strip() - try: - return yaml.safe_load(frontmatter_text) or {} - except yaml.YAMLError: - return None - - -def main() -> int: - console = Console() - - # Collect all titles/IDs and their source files - # Key: identifier (title, id, or alias), Value: list of (file_path, identifier_type) - identifiers: dict[str, list[tuple[str, str]]] = defaultdict(list) - - # Scan all markdown files - for md_file in sorted(DOCS_DIR.rglob("*.md")): - # Skip changelog fragments - if "changelog.d" in md_file.parts: - continue - - rel_path = md_file.relative_to(DOCS_DIR) - frontmatter = extract_frontmatter(md_file) - - if not frontmatter: - continue - - # Extract title (used by reference docs) - title = frontmatter.get("title") - if title: - identifiers[title].append((str(rel_path), "title")) - - # Extract id (used by zk cards) - card_id = frontmatter.get("id") - if card_id: - identifiers[card_id].append((str(rel_path), "id")) - - # Extract aliases - aliases = frontmatter.get("aliases", []) - if aliases: - for alias in aliases: - identifiers[alias].append((str(rel_path), "alias")) - - # Separate into duplicates and unique - duplicates: dict[str, list[tuple[str, str]]] = {} - unique: dict[str, tuple[str, str]] = {} - - for identifier, sources in identifiers.items(): - if len(sources) > 1: - duplicates[identifier] = sources - else: - unique[identifier] = sources[0] - - # Print results - console.print("[bold]Doc Card Title Inventory[/bold]") - console.print() - - # Duplicates table (if any) - if duplicates: - console.print("[bold red]Duplicates Found[/bold red]") - dup_table = Table(show_header=True, header_style="bold") - dup_table.add_column("Identifier") - dup_table.add_column("Type") - dup_table.add_column("File") - - for identifier in sorted(duplicates.keys()): - sources = duplicates[identifier] - first = True - for file_path, id_type in sources: - dup_table.add_row( - identifier if first else "", - id_type, - file_path, - ) - first = False - - console.print(dup_table) - console.print() - - # All identifiers table - console.print("[bold]All Identifiers[/bold]") - all_table = Table(show_header=True, header_style="bold") - all_table.add_column("Identifier") - all_table.add_column("Type") - all_table.add_column("File") - all_table.add_column("Status") - - for identifier in sorted(identifiers.keys()): - sources = identifiers[identifier] - is_dup = identifier in duplicates - first = True - for file_path, id_type in sources: - status = "[red]DUPLICATE[/red]" if is_dup else "[green]OK[/green]" - all_table.add_row( - identifier if first else "", - id_type, - file_path, - status if first else "", - ) - first = False - - console.print(all_table) - - # Summary - console.print() - console.print(f"Total identifiers: {len(identifiers)}") - console.print(f"Duplicates: {len(duplicates)}") - - if duplicates: - console.print() - console.print("[bold red]Action required:[/bold red] Resolve duplicates to ensure wiki-links work correctly.") - return 1 - - return 0 - - -if __name__ == "__main__": - sys.exit(main()) diff --git a/mise-tasks/doc-filenames b/mise-tasks/doc-filenames new file mode 100755 index 0000000..9eb678a --- /dev/null +++ b/mise-tasks/doc-filenames @@ -0,0 +1,162 @@ +#!/usr/bin/env -S uv run --script +# /// script +# requires-python = ">=3.12" +# dependencies = ["pyyaml>=6.0", "rich>=13.0.0"] +# /// +#MISE description="List all doc card filenames and detect duplicates" +"""List all documentation card filenames and detect duplicates. + +This script scans all markdown files in the docs/ directory (excluding +changelog.d/), and reports any duplicate filenames that could cause +wiki-link resolution issues with Quartz's "shortest" path mode. + +With shortest mode, [[filename]] resolves to the file with that name, +so filenames must be unique across the entire docs directory. + +Usage: mise run doc-card-titles +""" + +import sys +from collections import defaultdict +from pathlib import Path + +import yaml +from rich.console import Console +from rich.table import Table + +DOCS_DIR = Path(__file__).parent.parent / "docs" + + +def extract_frontmatter(file_path: Path) -> dict | None: + """Extract YAML frontmatter from a markdown file.""" + content = file_path.read_text() + if not content.startswith("---"): + return None + + # Find the closing --- + end_idx = content.find("---", 3) + if end_idx == -1: + return None + + frontmatter_text = content[3:end_idx].strip() + try: + return yaml.safe_load(frontmatter_text) or {} + except yaml.YAMLError: + return None + + +def main() -> int: + console = Console() + + # Collect all filenames (without extension) and their full paths + # Key: filename (stem), Value: list of full relative paths + filenames: dict[str, list[str]] = defaultdict(list) + + # Also collect id/aliases from zk cards for reference + zk_identifiers: dict[str, list[tuple[str, str]]] = defaultdict(list) + + # Scan all markdown files + for md_file in sorted(DOCS_DIR.rglob("*.md")): + # Skip changelog fragments + if "changelog.d" in md_file.parts: + continue + + rel_path = str(md_file.relative_to(DOCS_DIR)) + filename = md_file.stem # filename without .md extension + + filenames[filename].append(rel_path) + + # For zk cards, also track id and aliases + if "zk" in md_file.parts: + frontmatter = extract_frontmatter(md_file) + if frontmatter: + card_id = frontmatter.get("id") + if card_id: + zk_identifiers[card_id].append((rel_path, "id")) + aliases = frontmatter.get("aliases", []) + for alias in aliases: + zk_identifiers[alias].append((rel_path, "alias")) + + # Find duplicate filenames (excluding "index" which is expected in multiple dirs) + duplicates = { + name: paths + for name, paths in filenames.items() + if len(paths) > 1 and name != "index" + } + + # Print results + console.print("[bold]Doc Card Filename Inventory[/bold]") + console.print() + console.print("With Quartz 'shortest' path mode, wiki-links like [[filename]]") + console.print("resolve by filename, so filenames must be unique.") + console.print() + + # Duplicates table (if any) + if duplicates: + console.print("[bold red]Duplicate Filenames Found[/bold red]") + dup_table = Table(show_header=True, header_style="bold") + dup_table.add_column("Filename") + dup_table.add_column("Paths") + + for filename in sorted(duplicates.keys()): + paths = duplicates[filename] + dup_table.add_row(filename, "\n".join(paths)) + + console.print(dup_table) + console.print() + + # All filenames table + console.print("[bold]All Filenames[/bold]") + all_table = Table(show_header=True, header_style="bold") + all_table.add_column("Filename") + all_table.add_column("Path") + all_table.add_column("Status") + + for filename in sorted(filenames.keys()): + paths = filenames[filename] + is_dup = filename in duplicates + status = "[red]DUPLICATE[/red]" if is_dup else "[green]OK[/green]" + all_table.add_row(filename, paths[0], status) + for extra_path in paths[1:]: + all_table.add_row("", extra_path, "") + + console.print(all_table) + + # ZK identifiers (for reference) + if zk_identifiers: + console.print() + console.print("[bold]ZK Card Identifiers (id/aliases)[/bold]") + zk_table = Table(show_header=True, header_style="bold") + zk_table.add_column("Identifier") + zk_table.add_column("Type") + zk_table.add_column("File") + + for identifier in sorted(zk_identifiers.keys()): + sources = zk_identifiers[identifier] + first = True + for file_path, id_type in sources: + zk_table.add_row( + identifier if first else "", + id_type, + file_path, + ) + first = False + + console.print(zk_table) + + # Summary + console.print() + console.print(f"Total files: {sum(len(p) for p in filenames.values())}") + console.print(f"Unique filenames: {len(filenames)}") + console.print(f"Duplicate filenames: {len(duplicates)}") + + if duplicates: + console.print() + console.print("[bold red]Action required:[/bold red] Rename files to ensure unique filenames for wiki-link resolution.") + return 1 + + return 0 + + +if __name__ == "__main__": + sys.exit(main())