diff --git a/docs/README.md b/docs/README.md index 2ea873c..17e36a8 100644 --- a/docs/README.md +++ b/docs/README.md @@ -2,8 +2,8 @@ > Auto-generated documentation for the homelab deployment > -> Generated on: tir 29 jul 16:25:52 CEST 2025 -> Source: /home/plasmagoat/homelab +> Generated on: $(date) +> Source: $(pwd) ## 📚 Documentation Files @@ -20,7 +20,7 @@ This documentation is automatically generated from your colmena flake configurat ## 🚀 Quick Actions ### View Current Status -```bash +\`\`\`bash # Service status across fleet (if homelab CLI is available) homelab services --global @@ -29,22 +29,22 @@ homelab backups --global # Overall status homelab status -``` +\`\`\` ### Update Documentation -```bash +\`\`\`bash # Regenerate all documentation homelab-generate-docs ./docs # Generate in different directory homelab-generate-docs /path/to/output -``` +\`\`\` ## 📋 Quick Stats - **Total Nodes**: 2 - **Homelab-Enabled Nodes**: 2 -- **Generated**: tir 29 jul 16:25:55 CEST 2025 +- **Generated**: tir 29 jul 16:57:16 CEST 2025 ## 🛠️ Management Tools @@ -60,6 +60,40 @@ homelab-generate-docs /path/to/output - `colmena apply` - Deploy configuration changes - `colmena build` - Build configurations without deploying +## 🎯 Integration with CI/CD + +### GitHub Actions Example + +```yaml +name: Generate Documentation +on: + push: + branches: [ main ] + +jobs: + docs: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: cachix/install-nix-action@v24 + - name: Generate docs + run: nix develop --command homelab-generate-docs ./docs + - name: Commit docs + run: | + git add docs/ + git commit -m "docs: update homelab documentation" || exit 0 + git push +``` + +### Manual Generation + +```bash +# From your homelab directory +nix develop +homelab-generate-docs ./docs +git add docs/ && git commit -m "Update docs" +``` + --- *This documentation reflects the live state of your homelab deployment as evaluated by colmena.* diff --git a/docs/current-deployment.md b/docs/current-deployment.md index 1767502..2f1541c 100644 --- a/docs/current-deployment.md +++ b/docs/current-deployment.md @@ -2,8 +2,8 @@ > Current homelab deployment configuration > -> Generated on: tir 29 jul 16:25:46 CEST 2025 -> Working directory: /home/plasmagoat/homelab +> Generated on: $(date) +> Working directory: $(pwd) ## Deployment Summary diff --git a/docs/fleet-overview.md b/docs/fleet-overview.md index 601ce6c..5982210 100644 --- a/docs/fleet-overview.md +++ b/docs/fleet-overview.md @@ -2,8 +2,8 @@ > Auto-generated fleet overview > -> Generated on: tir 29 jul 16:25:32 CEST 2025 -> Source: /home/plasmagoat/homelab +> Generated on: $(date) +> Source: $(pwd) ## Fleet Statistics @@ -28,12 +28,6 @@ | `photos` | 1 | minio | | `sandbox` | 0 | | -### Environment Distribution - -| Environment | Node Count | -|-------------|------------| -| production | 2 | - --- *Fleet overview generated from colmena evaluation* diff --git a/docs/nodes.md b/docs/nodes.md index 25bdbb8..87cade4 100644 --- a/docs/nodes.md +++ b/docs/nodes.md @@ -2,7 +2,7 @@ > Detailed per-node configuration > -> Generated on: tir 29 jul 16:25:40 CEST 2025 +> Generated on: $(date) ## Node: photos diff --git a/docs/services.md b/docs/services.md index e528e6f..a953b35 100644 --- a/docs/services.md +++ b/docs/services.md @@ -2,7 +2,7 @@ > Available services and their configuration options > -> Generated on: tir 29 jul 16:25:43 CEST 2025 +> Generated on: $(date) This document catalogs all available homelab services, their configuration options, and integration capabilities. @@ -10,6 +10,17 @@ This document catalogs all available homelab services, their configuration optio **Total Available Services:** 4 +## Service Integration Matrix + +| Service | Monitoring | Logging | Proxy | Auth Default | +|---------|------------|---------|-------|--------------| +| `gatus` | ❌ | ❌ | ❌ | 🌐 | +| `grafana` | ❌ | ❌ | ❌ | 🌐 | +| `minio` | ❌ | ❌ | ❌ | 🌐 | +| `prometheus` | ❌ | ❌ | ❌ | 🌐 | + +**Legend:** ✅ = Enabled by default, ❌ = Available but disabled, 🔒 = Auth required, 🌐 = Public access + ## Service Reference ### gatus @@ -18,7 +29,15 @@ This document catalogs all available homelab services, their configuration optio **Default Port:** `8080` -**Current Deployments:** 0 instance(s) +**Current Deployments:** 0 instance(s) on: + +#### Default Integration Status + +| Integration | Status | Default Configuration | +|-------------|--------|----------------------| +| 📊 Monitoring | ❌ Disabled | Available but requires `monitoring.enable = true` | +| 📝 Logging | ❌ Disabled | Available but requires `logging.enable = true` | +| 🔀 Proxy | ❌ Disabled | Available but requires `proxy.enable = true` | #### Core Configuration @@ -27,6 +46,11 @@ homelab.services.gatus = { enable = true; port = 8080; description = "Gatus Status Page"; + + # Default integrations (adjust as needed) + # monitoring.enable = true; # ❌ Disabled by default + # logging.enable = true; # ❌ Disabled by default + # proxy.enable = true; # ❌ Disabled by default }; ``` @@ -47,17 +71,6 @@ homelab.services.gatus = { }; ``` -#### Complete Example - -```nix -# Full configuration example for gatus -homelab.services.gatus = { - enable = true; - port = 8080; - description = "Gatus Status Page"; -}; -``` - --- ### grafana @@ -66,7 +79,15 @@ homelab.services.gatus = { **Default Port:** `3000` -**Current Deployments:** 0 instance(s) +**Current Deployments:** 0 instance(s) on: + +#### Default Integration Status + +| Integration | Status | Default Configuration | +|-------------|--------|----------------------| +| 📊 Monitoring | ❌ Disabled | Available but requires `monitoring.enable = true` | +| 📝 Logging | ❌ Disabled | Available but requires `logging.enable = true` | +| 🔀 Proxy | ❌ Disabled | Available but requires `proxy.enable = true` | #### Core Configuration @@ -75,17 +96,11 @@ homelab.services.grafana = { enable = true; port = 3000; description = "Grafana Metrics Dashboard"; -}; -``` -#### Complete Example - -```nix -# Full configuration example for grafana -homelab.services.grafana = { - enable = true; - port = 3000; - description = "Grafana Metrics Dashboard"; + # Default integrations (adjust as needed) + # monitoring.enable = true; # ❌ Disabled by default + # logging.enable = true; # ❌ Disabled by default + # proxy.enable = true; # ❌ Disabled by default }; ``` @@ -97,7 +112,15 @@ homelab.services.grafana = { **Default Port:** `9000` -**Current Deployments:** 1 instance(s) +**Current Deployments:** 1 instance(s) on: photos + +#### Default Integration Status + +| Integration | Status | Default Configuration | +|-------------|--------|----------------------| +| 📊 Monitoring | ❌ Disabled | Available but requires `monitoring.enable = true` | +| 📝 Logging | ❌ Disabled | Available but requires `logging.enable = true` | +| 🔀 Proxy | ❌ Disabled | Available but requires `proxy.enable = true` | #### Core Configuration @@ -106,6 +129,11 @@ homelab.services.minio = { enable = true; port = 9000; description = "minio"; + + # Default integrations (adjust as needed) + # monitoring.enable = true; # ❌ Disabled by default + # logging.enable = true; # ❌ Disabled by default + # proxy.enable = true; # ❌ Disabled by default }; ``` @@ -123,17 +151,6 @@ homelab.services.minio = { }; ``` -#### Complete Example - -```nix -# Full configuration example for minio -homelab.services.minio = { - enable = true; - port = 9000; - description = "minio"; -}; -``` - --- ### prometheus @@ -142,7 +159,15 @@ homelab.services.minio = { **Default Port:** `9090` -**Current Deployments:** 0 instance(s) +**Current Deployments:** 0 instance(s) on: + +#### Default Integration Status + +| Integration | Status | Default Configuration | +|-------------|--------|----------------------| +| 📊 Monitoring | ❌ Disabled | Available but requires `monitoring.enable = true` | +| 📝 Logging | ❌ Disabled | Available but requires `logging.enable = true` | +| 🔀 Proxy | ❌ Disabled | Available but requires `proxy.enable = true` | #### Core Configuration @@ -151,6 +176,11 @@ homelab.services.prometheus = { enable = true; port = 9090; description = "Prometheus Monitoring Server"; + + # Default integrations (adjust as needed) + # monitoring.enable = true; # ❌ Disabled by default + # logging.enable = true; # ❌ Disabled by default + # proxy.enable = true; # ❌ Disabled by default }; ``` @@ -174,28 +204,17 @@ homelab.services.prometheus = { }; ``` -#### Complete Example - -```nix -# Full configuration example for prometheus -homelab.services.prometheus = { - enable = true; - port = 9090; - description = "Prometheus Monitoring Server"; -}; -``` - --- ## Integration Summary ### Available Integration Types -| Integration | Purpose | Configuration | -|-------------|---------|---------------| -| **Monitoring** | Prometheus metrics + health checks | `monitoring.enable = true` | -| **Logging** | Centralized log collection | `logging.enable = true` | -| **Proxy** | Reverse proxy with SSL + auth | `proxy.enable = true` | +| Integration | Purpose | Default Behavior | Configuration | +|-------------|---------|------------------|---------------| +| **📊 Monitoring** | Prometheus metrics + health checks | Service-dependent | `monitoring.enable = true` | +| **📝 Logging** | Centralized log collection | Service-dependent | `logging.enable = true` | +| **🔀 Proxy** | Reverse proxy with SSL + auth | Service-dependent | `proxy.enable = true` | ### Integration Benefits @@ -203,6 +222,7 @@ homelab.services.prometheus = { - **📊 Unified Monitoring:** All metrics and health checks appear in Prometheus/Grafana - **📝 Centralized Logging:** All logs are collected and indexed in Loki - **🌐 Consistent Access:** All services get consistent subdomain access with SSL +- **🎯 Smart Defaults:** Each service comes with sensible default configurations --- diff --git a/pkgs/default.nix b/pkgs/default.nix index b589408..657eca0 100644 --- a/pkgs/default.nix +++ b/pkgs/default.nix @@ -3,5 +3,5 @@ pkgs: { # example = pkgs.callPackage ./example { }; ente-web = pkgs.callPackage ./ente-web.nix {}; - homelab-docs = pkgs.callPackage ./homelab-docs.nix {}; + homelab-docs = pkgs.callPackage ./homelab-docs {}; } diff --git a/pkgs/homelab-docs.nix b/pkgs/homelab-docs.nix deleted file mode 100644 index 3d5c13d..0000000 --- a/pkgs/homelab-docs.nix +++ /dev/null @@ -1,841 +0,0 @@ -# homelab-docs.nix - Standalone documentation generator package -{ - lib, - stdenv, - writeShellScriptBin, - jq, - nixfmt, -}: let - # Main documentation generator script - docsGenerator = writeShellScriptBin "homelab-generate-docs" '' - #!/usr/bin/env bash - set -euo pipefail - - # Colors - BLUE='\033[0;34m' - GREEN='\033[0;32m' - YELLOW='\033[1;33m' - RED='\033[0;31m' - NC='\033[0m' - - info() { echo -e "''${BLUE}$1''${NC}"; } - success() { echo -e "''${GREEN}$1''${NC}"; } - warn() { echo -e "''${YELLOW}$1''${NC}"; } - error() { echo -e "''${RED}$1''${NC}"; } - - # Configuration - DOCS_DIR="''${1:-./docs}" - - info "📚 Generating homelab documentation..." - echo " Output directory: $DOCS_DIR" - echo - - # Check if we're in a directory with a flake - if [[ ! -f flake.nix ]]; then - error "No flake.nix found in current directory" - echo "Please run this command from your homelab flake directory" - exit 1 - fi - - # Check if colmena is available - if ! command -v colmena >/dev/null 2>&1; then - error "colmena command not found." - echo "Please ensure colmena is available in your environment" - echo "Add it to your devShell or install it globally" - exit 1 - fi - - # Create docs directory - mkdir -p "$DOCS_DIR" - - # Generate fleet overview - info " 🌐 Generating fleet overview..." - homelab-docs-fleet > "$DOCS_DIR/fleet-overview.md" - - # Generate node documentation - info " 🖥️ Generating node configurations..." - homelab-docs-nodes > "$DOCS_DIR/nodes.md" - - # Generate service documentation - info " ⚙️ Generating service configurations..." - homelab-docs-services > "$DOCS_DIR/services.md" - - # Generate current deployment - info " 🏠 Generating current deployment..." - homelab-docs-deployment > "$DOCS_DIR/current-deployment.md" - - # Generate README - info " 📋 Generating README..." - homelab-docs-readme > "$DOCS_DIR/README.md" - - success "✅ Documentation generated successfully!" - echo - echo "Generated files:" - echo " 🌐 fleet-overview.md - Fleet statistics and overview" - echo " 🖥️ nodes.md - Per-node configurations" - echo " ⚙️ services.md - Service configurations" - echo " 🏠 current-deployment.md - Current deployment state" - echo " 📋 README.md - Documentation index" - echo - echo "💡 Tip: Add these files to your repository and set up GitHub Actions" - echo " to automatically regenerate documentation on changes!" - ''; - - # Fleet overview generator - fleetDocsGenerator = writeShellScriptBin "homelab-docs-fleet" '' - #!/usr/bin/env bash - set -euo pipefail - - cat << EOF - # Homelab Fleet Overview - - > Auto-generated fleet overview - > - > Generated on: $(date) - > Source: $(pwd) - - ## Fleet Statistics - - EOF - - # Get basic fleet stats - echo "### Basic Information" - echo - - fleet_stats=$(colmena eval -E '{ nodes, pkgs, lib, ... }: { - totalNodes = lib.length (lib.attrNames nodes); - nodeNames = lib.attrNames nodes; - }') - - total_nodes=$(echo "$fleet_stats" | ${jq}/bin/jq -r '.totalNodes') - node_names=$(echo "$fleet_stats" | ${jq}/bin/jq -r '.nodeNames[]' | tr '\n' ' ') - - echo "| Metric | Value |" - echo "|--------|-------|" - echo "| Total Nodes | $total_nodes |" - echo "| Node Names | $node_names |" - echo - - # Get homelab-enabled nodes - echo "### Homelab Configuration" - echo - - homelab_info=$(colmena eval -E '{ nodes, pkgs, lib, ... }: { - homelabNodes = lib.mapAttrs (name: node: { - enabled = node.config.homelab.enable or false; - hostname = node.config.homelab.hostname or null; - environment = node.config.homelab.environment or null; - }) nodes; - }') - - echo "| Node | Homelab Enabled | Hostname | Environment |" - echo "|------|----------------|----------|-------------|" - - echo "$homelab_info" | ${jq}/bin/jq -r '.homelabNodes | to_entries[] | - [.key, (.value.enabled | tostring), (.value.hostname // "N/A"), (.value.environment // "N/A")] | - @tsv' | while IFS=$'\t' read -r node enabled hostname environment; do - enabled_icon=$(if [[ "$enabled" == "true" ]]; then echo "✅"; else echo "❌"; fi) - echo "| \`$node\` | $enabled_icon | $hostname | $environment |" - done - - echo - - # Get service distribution - echo "### Service Distribution" - echo - - service_info=$(colmena eval -E '{ nodes, pkgs, lib, ... }: - lib.mapAttrs (name: node: - if (node.config.homelab.enable or false) then { - serviceCount = lib.length (lib.attrNames (lib.filterAttrs (n: v: v.enable or false) (node.config.homelab.services or {}))); - serviceNames = lib.attrNames (lib.filterAttrs (n: v: v.enable or false) (node.config.homelab.services or {})); - } else { - serviceCount = 0; - serviceNames = []; - } - ) nodes') - - echo "| Node | Service Count | Services |" - echo "|------|---------------|----------|" - - echo "$service_info" | ${jq}/bin/jq -r 'to_entries[] | - [.key, (.value.serviceCount | tostring), (.value.serviceNames | join(", "))] | - @tsv' | while IFS=$'\t' read -r node count services; do - echo "| \`$node\` | $count | $services |" - done - - echo - - # Environment distribution - echo "### Environment Distribution" - echo - - env_distribution=$(echo "$homelab_info" | ${jq}/bin/jq -r ' - [.homelabNodes | to_entries[] | select(.value.enabled == true) | .value.environment // "unknown"] | - group_by(.) | - map({environment: .[0], count: length}) | - .[]') - - if [[ -n "$env_distribution" ]]; then - echo "| Environment | Node Count |" - echo "|-------------|------------|" - - echo "$env_distribution" | ${jq}/bin/jq -r '[.environment, (.count | tostring)] | @tsv' | \ - while IFS=$'\t' read -r env count; do - echo "| $env | $count |" - done - else - echo "No homelab-enabled nodes found." - fi - - echo - echo "---" - echo - echo "*Fleet overview generated from colmena evaluation*" - ''; - - # Node documentation generator - nodeDocsGenerator = writeShellScriptBin "homelab-docs-nodes" '' - #!/usr/bin/env bash - set -euo pipefail - - cat << EOF - # Node Configurations - - > Detailed per-node configuration - > - > Generated on: $(date) - - EOF - - # Get all node information - node_info=$(colmena eval -E '{ nodes, pkgs, lib, ... }: - lib.mapAttrs (name: node: { - # Basic system info - nixosVersion = node.config.system.nixos.version; - hostName = node.config.networking.hostName; - system = node.config.nixpkgs.system; - - # Homelab config (safe extraction) - homelab = if (node.config.homelab.enable or false) then { - enabled = true; - hostname = node.config.homelab.hostname or null; - domain = node.config.homelab.domain or null; - externalDomain = node.config.homelab.externalDomain or null; - environment = node.config.homelab.environment or null; - location = node.config.homelab.location or null; - tags = node.config.homelab.tags or []; - } else { - enabled = false; - }; - - # Services (safe extraction) - services = if (node.config.homelab.enable or false) then - lib.mapAttrs (svcName: svc: { - enabled = svc.enable or false; - port = svc.port or null; - description = svc.description or svcName; - tags = svc.tags or []; - }) (node.config.homelab.services or {}) - else {}; - }) nodes') - - echo "$node_info" | ${jq}/bin/jq -r 'to_entries[] | .key' | while read -r node; do - echo "## Node: $node" - echo - - # Basic system information - echo "### System Information" - echo - - nixos_version=$(echo "$node_info" | ${jq}/bin/jq -r ".[\"$node\"].nixosVersion") - hostname=$(echo "$node_info" | ${jq}/bin/jq -r ".[\"$node\"].hostName") - system=$(echo "$node_info" | ${jq}/bin/jq -r ".[\"$node\"].system") - - echo "| Property | Value |" - echo "|----------|-------|" - echo "| NixOS Version | \`$nixos_version\` |" - echo "| Hostname | \`$hostname\` |" - echo "| System | \`$system\` |" - echo - - # Homelab configuration - homelab_enabled=$(echo "$node_info" | ${jq}/bin/jq -r ".[\"$node\"].homelab.enabled") - - if [[ "$homelab_enabled" == "true" ]]; then - echo "### Homelab Configuration" - echo - - hl_hostname=$(echo "$node_info" | ${jq}/bin/jq -r ".[\"$node\"].homelab.hostname // \"N/A\"") - hl_domain=$(echo "$node_info" | ${jq}/bin/jq -r ".[\"$node\"].homelab.domain // \"N/A\"") - hl_external=$(echo "$node_info" | ${jq}/bin/jq -r ".[\"$node\"].homelab.externalDomain // \"N/A\"") - hl_env=$(echo "$node_info" | ${jq}/bin/jq -r ".[\"$node\"].homelab.environment // \"N/A\"") - hl_location=$(echo "$node_info" | ${jq}/bin/jq -r ".[\"$node\"].homelab.location // \"N/A\"") - hl_tags=$(echo "$node_info" | ${jq}/bin/jq -r ".[\"$node\"].homelab.tags | join(\", \")") - - echo "| Property | Value |" - echo "|----------|-------|" - echo "| Homelab Hostname | \`$hl_hostname\` |" - echo "| Domain | \`$hl_domain\` |" - echo "| External Domain | \`$hl_external\` |" - echo "| Environment | \`$hl_env\` |" - echo "| Location | \`$hl_location\` |" - echo "| Tags | $hl_tags |" - echo - - # Services - echo "### Services" - echo - - services_data=$(echo "$node_info" | ${jq}/bin/jq -r ".[\"$node\"].services") - service_count=$(echo "$services_data" | ${jq}/bin/jq 'length') - - if [[ "$service_count" -gt 0 ]]; then - echo "| Service | Enabled | Port | Description | Tags |" - echo "|---------|---------|------|-------------|------|" - - echo "$services_data" | ${jq}/bin/jq -r 'to_entries[] | - [.key, (.value.enabled | tostring), (.value.port // "N/A" | tostring), (.value.description // "N/A"), (.value.tags | join(", "))] | - @tsv' | while IFS=$'\t' read -r service enabled port description tags; do - enabled_icon=$(if [[ "$enabled" == "true" ]]; then echo "✅"; else echo "❌"; fi) - echo "| \`$service\` | $enabled_icon | $port | $description | $tags |" - done - else - echo "No services configured." - fi - else - echo "### Homelab Configuration" - echo - echo "❌ Homelab is not enabled on this node." - fi - - echo - echo "---" - echo - done - ''; - - # Service documentation generator - refocused on service capabilities - serviceDocsGenerator = writeShellScriptBin "homelab-docs-services" '' - #!/usr/bin/env bash - set -euo pipefail - - cat << EOF - # Service Catalog - - > Available services and their configuration options - > - > Generated on: $(date) - - This document catalogs all available homelab services, their configuration options, and integration capabilities. - - EOF - - # Get all services and their configurations - services_catalog=$(colmena eval -E '{ nodes, pkgs, lib, ... }: - let - # Collect all services from all nodes to build a complete catalog - allServiceConfigs = lib.flatten (lib.mapAttrsToList (nodeName: node: - if (node.config.homelab.enable or false) then - lib.mapAttrsToList (serviceName: service: { - inherit serviceName; - config = { - # Core service options - enable = service.enable or false; - port = service.port or null; - description = service.description or serviceName; - tags = service.tags or []; - - # Integration options - monitoring = { - enabled = service.monitoring.enable or false; - metricsPath = service.monitoring.metrics.path or "/metrics"; - healthPath = service.monitoring.healthCheck.path or "/health"; - extraLabels = service.monitoring.extraLabels or {}; - }; - - logging = { - enabled = service.logging.enable or false; - files = service.logging.files or []; - extraLabels = service.logging.extraLabels or {}; - }; - - proxy = { - enabled = service.proxy.enable or false; - subdomain = service.proxy.subdomain or serviceName; - enableAuth = service.proxy.enableAuth or false; - additionalSubdomains = service.proxy.additionalSubdomains or []; - }; - - # Service-specific options (everything else) - serviceSpecific = removeAttrs service [ - "enable" "port" "description" "tags" - "monitoring" "logging" "proxy" - ]; - }; - }) (node.config.homelab.services or {}) - else [] - ) nodes); - - # Group by service name and merge configurations - serviceGroups = lib.groupBy (svc: svc.serviceName) allServiceConfigs; - - # Get unique services with merged configuration examples - uniqueServices = lib.mapAttrs (serviceName: instances: - let - # Take the first enabled instance as the canonical example - enabledInstances = lib.filter (inst: inst.config.enable) instances; - canonicalConfig = if enabledInstances != [] then (lib.head enabledInstances).config else (lib.head instances).config; - in { - inherit serviceName; - config = canonicalConfig; - deploymentCount = lib.length (lib.filter (inst: inst.config.enable) instances); - availableOn = lib.unique (map (inst: inst.nodeName or "unknown") enabledInstances); - } - ) serviceGroups; - - in { - services = uniqueServices; - totalUniqueServices = lib.length (lib.attrNames uniqueServices); - }') - - total_services=$(echo "$services_catalog" | ${jq}/bin/jq -r '.totalUniqueServices') - - echo "## Overview" - echo - echo "**Total Available Services:** $total_services" - echo - echo "## Service Reference" - echo - - # Process each service - echo "$services_catalog" | ${jq}/bin/jq -r '.services | to_entries[] | .key' | sort | while read -r service; do - echo "### $service" - echo - - # Get service details - service_data=$(echo "$services_catalog" | ${jq}/bin/jq -r ".services[\"$service\"]") - - description=$(echo "$service_data" | ${jq}/bin/jq -r '.config.description // "No description available"') - port=$(echo "$service_data" | ${jq}/bin/jq -r '.config.port // "N/A"') - tags=$(echo "$service_data" | ${jq}/bin/jq -r '.config.tags | join(", ")') - deployment_count=$(echo "$service_data" | ${jq}/bin/jq -r '.deploymentCount') - - echo "**Description:** $description" - echo - echo "**Default Port:** \`$port\`" - echo - if [[ -n "$tags" && "$tags" != "" ]]; then - echo "**Tags:** $tags" - echo - fi - echo "**Current Deployments:** $deployment_count instance(s)" - echo - - # Core Configuration - echo "#### Core Configuration" - echo - echo "\`\`\`nix" - echo "homelab.services.$service = {" - echo " enable = true;" - if [[ "$port" != "N/A" ]]; then - echo " port = $port;" - fi - echo " description = \"$description\";" - if [[ -n "$tags" && "$tags" != "" ]]; then - echo " tags = [ $(echo "$tags" | sed 's/, /" "/g' | sed 's/^/"/; s/$/"/') ];" - fi - echo "};" - echo "\`\`\`" - echo - - # Service-specific options - service_specific=$(echo "$service_data" | ${jq}/bin/jq -r '.config.serviceSpecific') - if [[ "$service_specific" != "{}" && "$service_specific" != "null" ]]; then - echo "#### Service-Specific Options" - echo - echo "Available configuration options for $service:" - echo - echo "\`\`\`nix" - echo "homelab.services.$service = {" - echo " # ... core options above ..." - echo - echo " # Service-specific configuration" - echo "$service_specific" | ${jq}/bin/jq -r 'to_entries[] | " \(.key) = \(.value | tostring);"' - echo "};" - echo "\`\`\`" - echo - fi - - # Integration Options - monitoring_enabled=$(echo "$service_data" | ${jq}/bin/jq -r '.config.monitoring.enabled') - logging_enabled=$(echo "$service_data" | ${jq}/bin/jq -r '.config.logging.enabled') - proxy_enabled=$(echo "$service_data" | ${jq}/bin/jq -r '.config.proxy.enabled') - - if [[ "$monitoring_enabled" == "true" || "$logging_enabled" == "true" || "$proxy_enabled" == "true" ]]; then - echo "#### Available Integrations" - echo - fi - - # Monitoring Integration - if [[ "$monitoring_enabled" == "true" ]]; then - metrics_path=$(echo "$service_data" | ${jq}/bin/jq -r '.config.monitoring.metricsPath') - health_path=$(echo "$service_data" | ${jq}/bin/jq -r '.config.monitoring.healthPath') - extra_labels=$(echo "$service_data" | ${jq}/bin/jq -r '.config.monitoring.extraLabels') - - echo "##### 📊 Monitoring" - echo - echo "- **Metrics Endpoint:** \`$metrics_path\`" - echo "- **Health Check:** \`$health_path\`" - if [[ "$extra_labels" != "{}" ]]; then - echo "- **Default Labels:** $(echo "$extra_labels" | ${jq}/bin/jq -r 'to_entries[] | "\(.key)=\(.value)"' | paste -sd, -)" - fi - echo - echo "\`\`\`nix" - echo "homelab.services.$service.monitoring = {" - echo " enable = true;" - echo " metrics.path = \"$metrics_path\";" - echo " healthCheck.path = \"$health_path\";" - if [[ "$extra_labels" != "{}" ]]; then - echo " extraLabels = $extra_labels;" - fi - echo "};" - echo "\`\`\`" - echo - fi - - # Logging Integration - if [[ "$logging_enabled" == "true" ]]; then - log_files=$(echo "$service_data" | ${jq}/bin/jq -r '.config.logging.files[]?') - log_labels=$(echo "$service_data" | ${jq}/bin/jq -r '.config.logging.extraLabels') - - echo "##### 📝 Logging" - echo - if [[ -n "$log_files" ]]; then - echo "- **Log Files:**" - echo "$log_files" | while read -r file; do - echo " - \`$file\`" - done - fi - if [[ "$log_labels" != "{}" ]]; then - echo "- **Default Labels:** $(echo "$log_labels" | ${jq}/bin/jq -r 'to_entries[] | "\(.key)=\(.value)"' | paste -sd, -)" - fi - echo - echo "\`\`\`nix" - echo "homelab.services.$service.logging = {" - echo " enable = true;" - if [[ -n "$log_files" ]]; then - echo " files = [" - echo "$log_files" | while read -r file; do - echo " \"$file\"" - done - echo " ];" - fi - if [[ "$log_labels" != "{}" ]]; then - echo " extraLabels = $log_labels;" - fi - echo "};" - echo "\`\`\`" - echo - fi - - # Proxy Integration - if [[ "$proxy_enabled" == "true" ]]; then - subdomain=$(echo "$service_data" | ${jq}/bin/jq -r '.config.proxy.subdomain') - enable_auth=$(echo "$service_data" | ${jq}/bin/jq -r '.config.proxy.enableAuth') - additional_subdomains=$(echo "$service_data" | ${jq}/bin/jq -r '.config.proxy.additionalSubdomains') - - echo "##### 🔀 Reverse Proxy" - echo - echo "- **Primary Subdomain:** \`$subdomain\`" - echo "- **Authentication Required:** $(if [[ "$enable_auth" == "true" ]]; then echo "✅ Yes"; else echo "❌ No"; fi)" - if [[ "$additional_subdomains" != "[]" && "$additional_subdomains" != "null" ]]; then - echo "- **Additional Subdomains:** Available" - fi - echo - echo "\`\`\`nix" - echo "homelab.services.$service.proxy = {" - echo " enable = true;" - echo " subdomain = \"$subdomain\";" - echo " enableAuth = $enable_auth;" - if [[ "$additional_subdomains" != "[]" && "$additional_subdomains" != "null" ]]; then - echo " additionalSubdomains = [" - echo " # Configure additional proxy entries as needed" - echo " ];" - fi - echo "};" - echo "\`\`\`" - echo - fi - - # Usage Examples - echo "#### Complete Example" - echo - echo "\`\`\`nix" - echo "# Full configuration example for $service" - echo "homelab.services.$service = {" - echo " enable = true;" - if [[ "$port" != "N/A" ]]; then - echo " port = $port;" - fi - echo " description = \"$description\";" - - # Add integration examples - if [[ "$monitoring_enabled" == "true" ]]; then - echo " " - echo " # Monitoring integration" - echo " monitoring.enable = true;" - fi - - if [[ "$logging_enabled" == "true" ]]; then - echo " " - echo " # Logging integration" - echo " logging.enable = true;" - fi - - if [[ "$proxy_enabled" == "true" ]]; then - echo " " - echo " # Reverse proxy integration" - echo " proxy = {" - echo " enable = true;" - echo " subdomain = \"$subdomain\";" - echo " enableAuth = $enable_auth;" - echo " };" - fi - - echo "};" - echo "\`\`\`" - echo - echo "---" - echo - done - - echo "## Integration Summary" - echo - echo "### Available Integration Types" - echo - echo "| Integration | Purpose | Configuration |" - echo "|-------------|---------|---------------|" - echo "| **Monitoring** | Prometheus metrics + health checks | \`monitoring.enable = true\` |" - echo "| **Logging** | Centralized log collection | \`logging.enable = true\` |" - echo "| **Proxy** | Reverse proxy with SSL + auth | \`proxy.enable = true\` |" - echo - echo "### Integration Benefits" - echo - echo "- **🔄 Automatic Discovery:** Enabled integrations are automatically discovered by fleet-wide services" - echo "- **📊 Unified Monitoring:** All metrics and health checks appear in Prometheus/Grafana" - echo "- **📝 Centralized Logging:** All logs are collected and indexed in Loki" - echo "- **🌐 Consistent Access:** All services get consistent subdomain access with SSL" - echo - echo "---" - echo - echo "*This service catalog is generated from actual service configurations across your homelab fleet.*" - ''; - - # Current deployment generator - deploymentDocsGenerator = writeShellScriptBin "homelab-docs-deployment" '' - #!/usr/bin/env bash - set -euo pipefail - - cat << EOF - # Current Deployment State - - > Current homelab deployment configuration - > - > Generated on: $(date) - > Working directory: $(pwd) - - ## Deployment Summary - - EOF - - # Get deployment summary - deployment_summary=$(colmena eval -E '{ nodes, pkgs, lib, ... }: - let - homelabNodes = lib.filterAttrs (name: node: node.config.homelab.enable or false) nodes; - allServices = lib.flatten (lib.mapAttrsToList (nodeName: node: - lib.attrNames (lib.filterAttrs (n: v: v.enable or false) (node.config.homelab.services or {})) - ) homelabNodes); - in { - totalNodes = lib.length (lib.attrNames nodes); - homelabEnabledNodes = lib.length (lib.attrNames homelabNodes); - uniqueServices = lib.length (lib.unique allServices); - totalServiceInstances = lib.length allServices; - nodeNames = lib.attrNames nodes; - homelabNodeNames = lib.attrNames homelabNodes; - }') - - total_nodes=$(echo "$deployment_summary" | ${jq}/bin/jq -r '.totalNodes') - homelab_nodes=$(echo "$deployment_summary" | ${jq}/bin/jq -r '.homelabEnabledNodes') - unique_services=$(echo "$deployment_summary" | ${jq}/bin/jq -r '.uniqueServices') - service_instances=$(echo "$deployment_summary" | ${jq}/bin/jq -r '.totalServiceInstances') - - echo "| Metric | Count |" - echo "|--------|-------|" - echo "| Total Nodes | $total_nodes |" - echo "| Homelab-Enabled Nodes | $homelab_nodes |" - echo "| Unique Services | $unique_services |" - echo "| Service Instances | $service_instances |" - echo - - echo "## Node Status" - echo - - # Get detailed node status - node_status=$(colmena eval -E '{ nodes, pkgs, lib, ... }: - lib.mapAttrs (name: node: { - homelabEnabled = node.config.homelab.enable or false; - environment = node.config.homelab.environment or "unknown"; - serviceCount = if (node.config.homelab.enable or false) then - lib.length (lib.attrNames (lib.filterAttrs (n: v: v.enable or false) (node.config.homelab.services or {}))) - else 0; - monitoringEnabled = if (node.config.homelab.enable or false) then - node.config.homelab.monitoring.enable or false - else false; - backupsEnabled = if (node.config.homelab.enable or false) then - node.config.homelab.backups.enable or false - else false; - proxyEnabled = if (node.config.homelab.enable or false) then - node.config.homelab.reverseProxy.enable or false - else false; - }) nodes') - - echo "| Node | Homelab | Environment | Services | Monitoring | Backups | Proxy |" - echo "|------|---------|-------------|----------|------------|---------|-------|" - - echo "$node_status" | ${jq}/bin/jq -r 'to_entries[] | - [.key, (.value.homelabEnabled | if . then "✅" else "❌" end), .value.environment, (.value.serviceCount | tostring), - (.value.monitoringEnabled | if . then "✅" else "❌" end), - (.value.backupsEnabled | if . then "✅" else "❌" end), - (.value.proxyEnabled | if . then "✅" else "❌" end)] | - @tsv' | while IFS=$'\t' read -r node homelab env services monitoring backups proxy; do - echo "| \`$node\` | $homelab | $env | $services | $monitoring | $backups | $proxy |" - done - - echo - echo "---" - echo - echo "*Deployment state extracted from live colmena configuration*" - ''; - - # README generator - readmeGenerator = writeShellScriptBin "homelab-docs-readme" '' - #!/usr/bin/env bash - set -euo pipefail - - cat << EOF - # Homelab Documentation - - > Auto-generated documentation for the homelab deployment - > - > Generated on: $(date) - > Source: $(pwd) - - ## 📚 Documentation Files - - This documentation is automatically generated from your colmena flake configuration. - - ### 📊 Overview Documents - - **[Fleet Overview](fleet-overview.md)** - High-level fleet statistics and service distribution - - **[Current Deployment](current-deployment.md)** - Current deployment state and node status - - ### 📖 Detailed Configuration - - **[Node Configurations](nodes.md)** - Per-node detailed configuration and services - - **[Service Configurations](services.md)** - Service configurations across the fleet - - ## 🚀 Quick Actions - - ### View Current Status - \`\`\`bash - # Service status across fleet (if homelab CLI is available) - homelab services --global - - # Backup status - homelab backups --global - - # Overall status - homelab status - \`\`\` - - ### Update Documentation - \`\`\`bash - # Regenerate all documentation - homelab-generate-docs ./docs - - # Generate in different directory - homelab-generate-docs /path/to/output - \`\`\` - - ## 📋 Quick Stats - - EOF - - # Add live stats - quick_stats=$(colmena eval -E '{ nodes, pkgs, lib, ... }: - let - homelabNodes = lib.filterAttrs (name: node: node.config.homelab.enable or false) nodes; - in { - totalNodes = lib.length (lib.attrNames nodes); - homelabNodes = lib.length (lib.attrNames homelabNodes); - }') - - total_nodes=$(echo "$quick_stats" | ${jq}/bin/jq -r '.totalNodes') - homelab_nodes=$(echo "$quick_stats" | ${jq}/bin/jq -r '.homelabNodes') - - echo "- **Total Nodes**: $total_nodes" - echo "- **Homelab-Enabled Nodes**: $homelab_nodes" - echo "- **Generated**: $(date)" - echo - echo "## 🛠️ Management Tools" - echo - echo "### Documentation Commands" - echo "- \`homelab-generate-docs\` - Regenerate this documentation" - echo "- \`homelab-docs-fleet\` - Generate fleet overview only" - echo "- \`homelab-docs-nodes\` - Generate node configurations only" - echo "- \`homelab-docs-services\` - Generate service configurations only" - echo "- \`homelab-docs-deployment\` - Generate deployment state only" - echo - echo "### Colmena Commands" - echo "- \`colmena eval\` - Evaluate flake expressions" - echo "- \`colmena apply\` - Deploy configuration changes" - echo "- \`colmena build\` - Build configurations without deploying" - echo - echo "---" - echo - echo "*This documentation reflects the live state of your homelab deployment as evaluated by colmena.*" - ''; -in - stdenv.mkDerivation { - pname = "homelab-docs"; - version = "1.0.0"; - - dontUnpack = true; - dontBuild = true; - - installPhase = '' - mkdir -p $out/bin - - # Install all the generators - cp ${docsGenerator}/bin/homelab-generate-docs $out/bin/ - cp ${fleetDocsGenerator}/bin/homelab-docs-fleet $out/bin/ - cp ${nodeDocsGenerator}/bin/homelab-docs-nodes $out/bin/ - cp ${serviceDocsGenerator}/bin/homelab-docs-services $out/bin/ - cp ${deploymentDocsGenerator}/bin/homelab-docs-deployment $out/bin/ - cp ${readmeGenerator}/bin/homelab-docs-readme $out/bin/ - - # Make sure they're executable - chmod +x $out/bin/* - ''; - - meta = with lib; { - description = "Documentation generator for homelab colmena deployments"; - longDescription = '' - A collection of tools to generate comprehensive documentation - for homelab deployments managed with colmena. Extracts configuration - from flakes and generates markdown documentation. - ''; - license = licenses.mit; - maintainers = []; - platforms = platforms.all; - }; - } diff --git a/pkgs/homelab-docs/default.nix b/pkgs/homelab-docs/default.nix new file mode 100644 index 0000000..30dc69e --- /dev/null +++ b/pkgs/homelab-docs/default.nix @@ -0,0 +1,50 @@ +# homelab-docs.nix - Main documentation generator package +{ + lib, + stdenv, + writeShellScriptBin, + jq, + nixfmt, +}: let + # Import individual CLI generators + docsGenerator = import ./main.nix {inherit writeShellScriptBin;}; + fleetDocsGenerator = import ./fleet.nix {inherit writeShellScriptBin jq;}; + nodeDocsGenerator = import ./nodes.nix {inherit writeShellScriptBin jq;}; + serviceDocsGenerator = import ./services.nix {inherit writeShellScriptBin jq;}; + deploymentDocsGenerator = import ./deployment.nix {inherit writeShellScriptBin jq;}; + readmeGenerator = import ./readme.nix {inherit writeShellScriptBin jq;}; +in + stdenv.mkDerivation { + pname = "homelab-docs"; + version = "1.0.0"; + + dontUnpack = true; + dontBuild = true; + + installPhase = '' + mkdir -p $out/bin + + # Install all the generators + cp ${docsGenerator}/bin/homelab-generate-docs $out/bin/ + cp ${fleetDocsGenerator}/bin/homelab-docs-fleet $out/bin/ + cp ${nodeDocsGenerator}/bin/homelab-docs-nodes $out/bin/ + cp ${serviceDocsGenerator}/bin/homelab-docs-services $out/bin/ + cp ${deploymentDocsGenerator}/bin/homelab-docs-deployment $out/bin/ + cp ${readmeGenerator}/bin/homelab-docs-readme $out/bin/ + + # Make sure they're executable + chmod +x $out/bin/* + ''; + + meta = with lib; { + description = "Documentation generator for homelab colmena deployments"; + longDescription = '' + A collection of tools to generate comprehensive documentation + for homelab deployments managed with colmena. Extracts configuration + from flakes and generates markdown documentation. + ''; + license = licenses.mit; + maintainers = []; + platforms = platforms.all; + }; + } diff --git a/pkgs/homelab-docs/deployment.nix b/pkgs/homelab-docs/deployment.nix new file mode 100644 index 0000000..f9636a7 --- /dev/null +++ b/pkgs/homelab-docs/deployment.nix @@ -0,0 +1,89 @@ +# homelab-docs-deployment.nix - Deployment documentation generator CLI +{ + writeShellScriptBin, + jq, +}: +writeShellScriptBin "homelab-docs-deployment" '' + #!/usr/bin/env bash + set -euo pipefail + + cat << 'EOF' + # Current Deployment State + + > Current homelab deployment configuration + > + > Generated on: $(date) + > Working directory: $(pwd) + + ## Deployment Summary + + EOF + + # Get deployment summary + deployment_summary=$(colmena eval -E '{ nodes, pkgs, lib, ... }: + let + homelabNodes = lib.filterAttrs (name: node: node.config.homelab.enable or false) nodes; + allServices = lib.flatten (lib.mapAttrsToList (nodeName: node: + lib.attrNames (lib.filterAttrs (n: v: v.enable or false) (node.config.homelab.services or {})) + ) homelabNodes); + in { + totalNodes = lib.length (lib.attrNames nodes); + homelabEnabledNodes = lib.length (lib.attrNames homelabNodes); + uniqueServices = lib.length (lib.unique allServices); + totalServiceInstances = lib.length allServices; + nodeNames = lib.attrNames nodes; + homelabNodeNames = lib.attrNames homelabNodes; + }') + + total_nodes=$(echo "$deployment_summary" | ${jq}/bin/jq -r '.totalNodes') + homelab_nodes=$(echo "$deployment_summary" | ${jq}/bin/jq -r '.homelabEnabledNodes') + unique_services=$(echo "$deployment_summary" | ${jq}/bin/jq -r '.uniqueServices') + service_instances=$(echo "$deployment_summary" | ${jq}/bin/jq -r '.totalServiceInstances') + + echo "| Metric | Count |" + echo "|--------|-------|" + echo "| Total Nodes | $total_nodes |" + echo "| Homelab-Enabled Nodes | $homelab_nodes |" + echo "| Unique Services | $unique_services |" + echo "| Service Instances | $service_instances |" + echo + + echo "## Node Status" + echo + + # Get detailed node status + node_status=$(colmena eval -E '{ nodes, pkgs, lib, ... }: + lib.mapAttrs (name: node: { + homelabEnabled = node.config.homelab.enable or false; + environment = node.config.homelab.environment or "unknown"; + serviceCount = if (node.config.homelab.enable or false) then + lib.length (lib.attrNames (lib.filterAttrs (n: v: v.enable or false) (node.config.homelab.services or {}))) + else 0; + monitoringEnabled = if (node.config.homelab.enable or false) then + node.config.homelab.monitoring.enable or false + else false; + backupsEnabled = if (node.config.homelab.enable or false) then + node.config.homelab.backups.enable or false + else false; + proxyEnabled = if (node.config.homelab.enable or false) then + node.config.homelab.reverseProxy.enable or false + else false; + }) nodes') + + echo "| Node | Homelab | Environment | Services | Monitoring | Backups | Proxy |" + echo "|------|---------|-------------|----------|------------|---------|-------|" + + echo "$node_status" | ${jq}/bin/jq -r 'to_entries[] | + [.key, (.value.homelabEnabled | if . then "✅" else "❌" end), .value.environment, (.value.serviceCount | tostring), + (.value.monitoringEnabled | if . then "✅" else "❌" end), + (.value.backupsEnabled | if . then "✅" else "❌" end), + (.value.proxyEnabled | if . then "✅" else "❌" end)] | + @tsv' | while IFS=$'\t' read -r node homelab env services monitoring backups proxy; do + echo "| \`$node\` | $homelab | $env | $services | $monitoring | $backups | $proxy |" + done + + echo + echo "---" + echo + echo "*Deployment state extracted from live colmena configuration*" +'' diff --git a/pkgs/homelab-docs/fleet.nix b/pkgs/homelab-docs/fleet.nix new file mode 100644 index 0000000..8ca3066 --- /dev/null +++ b/pkgs/homelab-docs/fleet.nix @@ -0,0 +1,91 @@ +{ + writeShellScriptBin, + jq, +}: +writeShellScriptBin "homelab-docs-fleet" '' + #!/usr/bin/env bash + set -euo pipefail + + cat << 'EOF' + # Homelab Fleet Overview + + > Auto-generated fleet overview + > + > Generated on: $(date) + > Source: $(pwd) + + ## Fleet Statistics + + EOF + + # Get basic fleet stats + echo "### Basic Information" + echo + + fleet_stats=$(colmena eval -E '{ nodes, pkgs, lib, ... }: { + totalNodes = lib.length (lib.attrNames nodes); + nodeNames = lib.attrNames nodes; + }') + + total_nodes=$(echo "$fleet_stats" | ${jq}/bin/jq -r '.totalNodes') + node_names=$(echo "$fleet_stats" | ${jq}/bin/jq -r '.nodeNames[]' | tr '\n' ' ') + + echo "| Metric | Value |" + echo "|--------|-------|" + echo "| Total Nodes | $total_nodes |" + echo "| Node Names | $node_names |" + echo + + # Get homelab-enabled nodes + echo "### Homelab Configuration" + echo + + homelab_info=$(colmena eval -E '{ nodes, pkgs, lib, ... }: { + homelabNodes = lib.mapAttrs (name: node: { + enabled = node.config.homelab.enable or false; + hostname = node.config.homelab.hostname or null; + environment = node.config.homelab.environment or null; + }) nodes; + }') + + echo "| Node | Homelab Enabled | Hostname | Environment |" + echo "|------|----------------|----------|-------------|" + + echo "$homelab_info" | ${jq}/bin/jq -r '.homelabNodes | to_entries[] | + [.key, (.value.enabled | tostring), (.value.hostname // "N/A"), (.value.environment // "N/A")] | + @tsv' | while IFS=$'\t' read -r node enabled hostname environment; do + enabled_icon=$(if [[ "$enabled" == "true" ]]; then echo "✅"; else echo "❌"; fi) + echo "| \`$node\` | $enabled_icon | $hostname | $environment |" + done + + echo + + # Get service distribution + echo "### Service Distribution" + echo + + service_info=$(colmena eval -E '{ nodes, pkgs, lib, ... }: + lib.mapAttrs (name: node: + if (node.config.homelab.enable or false) then { + serviceCount = lib.length (lib.attrNames (lib.filterAttrs (n: v: v.enable or false) (node.config.homelab.services or {}))); + serviceNames = lib.attrNames (lib.filterAttrs (n: v: v.enable or false) (node.config.homelab.services or {})); + } else { + serviceCount = 0; + serviceNames = []; + } + ) nodes') + + echo "| Node | Service Count | Services |" + echo "|------|---------------|----------|" + + echo "$service_info" | ${jq}/bin/jq -r 'to_entries[] | + [.key, (.value.serviceCount | tostring), (.value.serviceNames | join(", "))] | + @tsv' | while IFS=$'\t' read -r node count services; do + echo "| \`$node\` | $count | $services |" + done + + echo + echo "---" + echo + echo "*Fleet overview generated from colmena evaluation*" +'' diff --git a/pkgs/homelab-docs/main.nix b/pkgs/homelab-docs/main.nix new file mode 100644 index 0000000..e3389d2 --- /dev/null +++ b/pkgs/homelab-docs/main.nix @@ -0,0 +1,73 @@ +{writeShellScriptBin}: +writeShellScriptBin "homelab-generate-docs" '' + #!/usr/bin/env bash + set -euo pipefail + + # Colors + BLUE='\033[0;34m' + GREEN='\033[0;32m' + YELLOW='\033[1;33m' + RED='\033[0;31m' + NC='\033[0m' + + info() { echo -e "''${BLUE}$1''${NC}"; } + success() { echo -e "''${GREEN}$1''${NC}"; } + warn() { echo -e "''${YELLOW}$1''${NC}"; } + error() { echo -e "''${RED}$1''${NC}"; } + + # Configuration + DOCS_DIR="''${1:-./docs}" + + info "📚 Generating homelab documentation..." + echo " Output directory: $DOCS_DIR" + echo + + # Check if we're in a directory with a flake + if [[ ! -f flake.nix ]]; then + error "No flake.nix found in current directory" + echo "Please run this command from your homelab flake directory" + exit 1 + fi + + # Check if colmena is available + if ! command -v colmena >/dev/null 2>&1; then + error "colmena command not found." + echo "Please ensure colmena is available in your environment" + exit 1 + fi + + # Create docs directory + mkdir -p "$DOCS_DIR" + + # Generate fleet overview + info " 🌐 Generating fleet overview..." + homelab-docs-fleet > "$DOCS_DIR/fleet-overview.md" + + # Generate node documentation + info " 🖥️ Generating node configurations..." + homelab-docs-nodes > "$DOCS_DIR/nodes.md" + + # Generate service documentation + info " ⚙️ Generating service configurations..." + homelab-docs-services > "$DOCS_DIR/services.md" + + # Generate current deployment + info " 🏠 Generating current deployment..." + homelab-docs-deployment > "$DOCS_DIR/current-deployment.md" + + # Generate README + info " 📋 Generating README..." + homelab-docs-readme > "$DOCS_DIR/README.md" + + success "✅ Documentation generated successfully!" + echo + echo "Generated files:" + echo " 🌐 fleet-overview.md - Fleet statistics and overview" + echo " 🖥️ nodes.md - Per-node configurations" + echo " ⚙️ services.md - Service configurations" + echo " 🏠 current-deployment.md - Current deployment state" + echo " 📋 README.md - Documentation index" + echo + echo "💡 Tip: Add these files to your repository and set up GitHub Actions" + echo " to automatically regenerate documentation on changes!" +'' diff --git a/pkgs/homelab-docs/nodes.nix b/pkgs/homelab-docs/nodes.nix new file mode 100644 index 0000000..b47fd05 --- /dev/null +++ b/pkgs/homelab-docs/nodes.nix @@ -0,0 +1,123 @@ +{ + writeShellScriptBin, + jq, +}: +writeShellScriptBin "homelab-docs-nodes" '' + #!/usr/bin/env bash + set -euo pipefail + + cat << 'EOF' + # Node Configurations + + > Detailed per-node configuration + > + > Generated on: $(date) + + EOF + + # Get all node information + node_info=$(colmena eval -E '{ nodes, pkgs, lib, ... }: + lib.mapAttrs (name: node: { + # Basic system info + nixosVersion = node.config.system.nixos.version; + hostName = node.config.networking.hostName; + system = node.config.nixpkgs.system; + + # Homelab config (safe extraction) + homelab = if (node.config.homelab.enable or false) then { + enabled = true; + hostname = node.config.homelab.hostname or null; + domain = node.config.homelab.domain or null; + externalDomain = node.config.homelab.externalDomain or null; + environment = node.config.homelab.environment or null; + location = node.config.homelab.location or null; + tags = node.config.homelab.tags or []; + } else { + enabled = false; + }; + + # Services (safe extraction) + services = if (node.config.homelab.enable or false) then + lib.mapAttrs (svcName: svc: { + enabled = svc.enable or false; + port = svc.port or null; + description = svc.description or svcName; + tags = svc.tags or []; + }) (node.config.homelab.services or {}) + else {}; + }) nodes') + + echo "$node_info" | ${jq}/bin/jq -r 'to_entries[] | .key' | while read -r node; do + echo "## Node: $node" + echo + + # Basic system information + echo "### System Information" + echo + + nixos_version=$(echo "$node_info" | ${jq}/bin/jq -r ".[\"$node\"].nixosVersion") + hostname=$(echo "$node_info" | ${jq}/bin/jq -r ".[\"$node\"].hostName") + system=$(echo "$node_info" | ${jq}/bin/jq -r ".[\"$node\"].system") + + echo "| Property | Value |" + echo "|----------|-------|" + echo "| NixOS Version | \`$nixos_version\` |" + echo "| Hostname | \`$hostname\` |" + echo "| System | \`$system\` |" + echo + + # Homelab configuration + homelab_enabled=$(echo "$node_info" | ${jq}/bin/jq -r ".[\"$node\"].homelab.enabled") + + if [[ "$homelab_enabled" == "true" ]]; then + echo "### Homelab Configuration" + echo + + hl_hostname=$(echo "$node_info" | ${jq}/bin/jq -r ".[\"$node\"].homelab.hostname // \"N/A\"") + hl_domain=$(echo "$node_info" | ${jq}/bin/jq -r ".[\"$node\"].homelab.domain // \"N/A\"") + hl_external=$(echo "$node_info" | ${jq}/bin/jq -r ".[\"$node\"].homelab.externalDomain // \"N/A\"") + hl_env=$(echo "$node_info" | ${jq}/bin/jq -r ".[\"$node\"].homelab.environment // \"N/A\"") + hl_location=$(echo "$node_info" | ${jq}/bin/jq -r ".[\"$node\"].homelab.location // \"N/A\"") + hl_tags=$(echo "$node_info" | ${jq}/bin/jq -r ".[\"$node\"].homelab.tags | join(\", \")") + + echo "| Property | Value |" + echo "|----------|-------|" + echo "| Homelab Hostname | \`$hl_hostname\` |" + echo "| Domain | \`$hl_domain\` |" + echo "| External Domain | \`$hl_external\` |" + echo "| Environment | \`$hl_env\` |" + echo "| Location | \`$hl_location\` |" + echo "| Tags | $hl_tags |" + echo + + # Services + echo "### Services" + echo + + services_data=$(echo "$node_info" | ${jq}/bin/jq -r ".[\"$node\"].services") + service_count=$(echo "$services_data" | ${jq}/bin/jq 'length') + + if [[ "$service_count" -gt 0 ]]; then + echo "| Service | Enabled | Port | Description | Tags |" + echo "|---------|---------|------|-------------|------|" + + echo "$services_data" | ${jq}/bin/jq -r 'to_entries[] | + [.key, (.value.enabled | tostring), (.value.port // "N/A" | tostring), (.value.description // "N/A"), (.value.tags | join(", "))] | + @tsv' | while IFS=$'\t' read -r service enabled port description tags; do + enabled_icon=$(if [[ "$enabled" == "true" ]]; then echo "✅"; else echo "❌"; fi) + echo "| \`$service\` | $enabled_icon | $port | $description | $tags |" + done + else + echo "No services configured." + fi + else + echo "### Homelab Configuration" + echo + echo "❌ Homelab is not enabled on this node." + fi + + echo + echo "---" + echo + done +'' diff --git a/pkgs/homelab-docs/readme.nix b/pkgs/homelab-docs/readme.nix new file mode 100644 index 0000000..7a0891f --- /dev/null +++ b/pkgs/homelab-docs/readme.nix @@ -0,0 +1,124 @@ +# homelab-docs-readme.nix - README generator CLI +{ + writeShellScriptBin, + jq, +}: +writeShellScriptBin "homelab-docs-readme" '' + #!/usr/bin/env bash + set -euo pipefail + + cat << 'EOF' + # Homelab Documentation + + > Auto-generated documentation for the homelab deployment + > + > Generated on: $(date) + > Source: $(pwd) + + ## 📚 Documentation Files + + This documentation is automatically generated from your colmena flake configuration. + + ### 📊 Overview Documents + - **[Fleet Overview](fleet-overview.md)** - High-level fleet statistics and service distribution + - **[Current Deployment](current-deployment.md)** - Current deployment state and node status + + ### 📖 Detailed Configuration + - **[Node Configurations](nodes.md)** - Per-node detailed configuration and services + - **[Service Configurations](services.md)** - Service configurations across the fleet + + ## 🚀 Quick Actions + + ### View Current Status + \`\`\`bash + # Service status across fleet (if homelab CLI is available) + homelab services --global + + # Backup status + homelab backups --global + + # Overall status + homelab status + \`\`\` + + ### Update Documentation + \`\`\`bash + # Regenerate all documentation + homelab-generate-docs ./docs + + # Generate in different directory + homelab-generate-docs /path/to/output + \`\`\` + + ## 📋 Quick Stats + + EOF + + # Add live stats + quick_stats=$(colmena eval -E '{ nodes, pkgs, lib, ... }: + let + homelabNodes = lib.filterAttrs (name: node: node.config.homelab.enable or false) nodes; + in { + totalNodes = lib.length (lib.attrNames nodes); + homelabNodes = lib.length (lib.attrNames homelabNodes); + }') + + total_nodes=$(echo "$quick_stats" | ${jq}/bin/jq -r '.totalNodes') + homelab_nodes=$(echo "$quick_stats" | ${jq}/bin/jq -r '.homelabNodes') + + echo "- **Total Nodes**: $total_nodes" + echo "- **Homelab-Enabled Nodes**: $homelab_nodes" + echo "- **Generated**: $(date)" + echo + echo "## 🛠️ Management Tools" + echo + echo "### Documentation Commands" + echo "- \`homelab-generate-docs\` - Regenerate this documentation" + echo "- \`homelab-docs-fleet\` - Generate fleet overview only" + echo "- \`homelab-docs-nodes\` - Generate node configurations only" + echo "- \`homelab-docs-services\` - Generate service configurations only" + echo "- \`homelab-docs-deployment\` - Generate deployment state only" + echo + echo "### Colmena Commands" + echo "- \`colmena eval\` - Evaluate flake expressions" + echo "- \`colmena apply\` - Deploy configuration changes" + echo "- \`colmena build\` - Build configurations without deploying" + echo + echo "## 🎯 Integration with CI/CD" + echo + echo "### GitHub Actions Example" + echo + echo "\`\`\`yaml" + echo "name: Generate Documentation" + echo "on:" + echo " push:" + echo " branches: [ main ]" + echo "" + echo "jobs:" + echo " docs:" + echo " runs-on: ubuntu-latest" + echo " steps:" + echo " - uses: actions/checkout@v4" + echo " - uses: cachix/install-nix-action@v24" + echo " - name: Generate docs" + echo " run: nix develop --command homelab-generate-docs ./docs" + echo " - name: Commit docs" + echo " run: |" + echo " git add docs/" + echo " git commit -m \"docs: update homelab documentation\" || exit 0" + echo " git push" + echo "\`\`\`" + echo + echo "### Manual Generation" + echo + echo "\`\`\`bash" + echo "# From your homelab directory" + echo "nix develop" + echo "homelab-generate-docs ./docs" + echo "git add docs/ && git commit -m \"Update docs\"" + echo "\`\`\`" + echo + echo "---" + echo + echo "*This documentation reflects the live state of your homelab deployment as evaluated by colmena.*" +'' diff --git a/pkgs/homelab-docs/services.nix b/pkgs/homelab-docs/services.nix new file mode 100644 index 0000000..7e6c8a3 --- /dev/null +++ b/pkgs/homelab-docs/services.nix @@ -0,0 +1,270 @@ +# homelab-docs-services.nix - Service documentation generator CLI +{ + writeShellScriptBin, + jq, +}: +writeShellScriptBin "homelab-docs-services" '' + #!/usr/bin/env bash + set -euo pipefail + + cat << 'EOF' + # Service Catalog + + > Available services and their configuration options + > + > Generated on: $(date) + + This document catalogs all available homelab services, their configuration options, and integration capabilities. + + EOF + + # Get all services and their configurations + services_catalog=$(colmena eval -E '{ nodes, pkgs, lib, ... }: + let + # Collect all services from all nodes to build a complete catalog + allServiceConfigs = lib.flatten (lib.mapAttrsToList (nodeName: node: + if (node.config.homelab.enable or false) then + lib.mapAttrsToList (serviceName: service: { + inherit serviceName; + config = { + # Core service options + enable = service.enable or false; + port = service.port or null; + description = service.description or serviceName; + tags = service.tags or []; + + # Integration options + monitoring = { + enabled = service.monitoring.enable or false; + metricsPath = service.monitoring.metrics.path or "/metrics"; + healthPath = service.monitoring.healthCheck.path or "/health"; + extraLabels = service.monitoring.extraLabels or {}; + }; + + logging = { + enabled = service.logging.enable or false; + files = service.logging.files or []; + extraLabels = service.logging.extraLabels or {}; + }; + + proxy = { + enabled = service.proxy.enable or false; + subdomain = service.proxy.subdomain or serviceName; + enableAuth = service.proxy.enableAuth or false; + }; + + # Service-specific options (everything else) + serviceSpecific = removeAttrs service [ + "enable" "port" "description" "tags" + "monitoring" "logging" "proxy" + ]; + }; + deployedOn = nodeName; + }) (node.config.homelab.services or {}) + else [] + ) nodes); + + # Group by service name and merge configurations + serviceGroups = lib.groupBy (svc: svc.serviceName) allServiceConfigs; + + # Get unique services with merged configuration examples + uniqueServices = lib.mapAttrs (serviceName: instances: + let + # Take the first enabled instance as the canonical example + enabledInstances = lib.filter (inst: inst.config.enable) instances; + canonicalConfig = if enabledInstances != [] then (lib.head enabledInstances).config else (lib.head instances).config; + in { + inherit serviceName; + config = canonicalConfig; + deploymentCount = lib.length (lib.filter (inst: inst.config.enable) instances); + deployedOn = lib.unique (map (inst: inst.deployedOn or "unknown") enabledInstances); + } + ) serviceGroups; + + in { + services = uniqueServices; + totalUniqueServices = lib.length (lib.attrNames uniqueServices); + }') + + total_services=$(echo "$services_catalog" | ${jq}/bin/jq -r '.totalUniqueServices') + + echo "## Overview" + echo + echo "**Total Available Services:** $total_services" + echo + + # Create a summary table of services and their default integrations + echo "## Service Integration Matrix" + echo + echo "| Service | Monitoring | Logging | Proxy | Auth Default |" + echo "|---------|------------|---------|-------|--------------|" + + echo "$services_catalog" | ${jq}/bin/jq -r '.services | to_entries[] | .key' | sort | while read -r service; do + service_data=$(echo "$services_catalog" | ${jq}/bin/jq -r ".services[\"$service\"]") + + monitoring_enabled=$(echo "$service_data" | ${jq}/bin/jq -r '.config.monitoring.enabled') + logging_enabled=$(echo "$service_data" | ${jq}/bin/jq -r '.config.logging.enabled') + proxy_enabled=$(echo "$service_data" | ${jq}/bin/jq -r '.config.proxy.enabled') + auth_default=$(echo "$service_data" | ${jq}/bin/jq -r '.config.proxy.enableAuth') + + monitoring_icon=$(if [[ "$monitoring_enabled" == "true" ]]; then echo "✅"; else echo "❌"; fi) + logging_icon=$(if [[ "$logging_enabled" == "true" ]]; then echo "✅"; else echo "❌"; fi) + proxy_icon=$(if [[ "$proxy_enabled" == "true" ]]; then echo "✅"; else echo "❌"; fi) + auth_icon=$(if [[ "$auth_default" == "true" ]]; then echo "🔒"; else echo "🌐"; fi) + + echo "| \`$service\` | $monitoring_icon | $logging_icon | $proxy_icon | $auth_icon |" + done + + echo + echo "**Legend:** ✅ = Enabled by default, ❌ = Available but disabled, 🔒 = Auth required, 🌐 = Public access" + echo + + echo "## Service Reference" + echo + + # Process each service + echo "$services_catalog" | ${jq}/bin/jq -r '.services | to_entries[] | .key' | sort | while read -r service; do + echo "### $service" + echo + + # Get service details + service_data=$(echo "$services_catalog" | ${jq}/bin/jq -r ".services[\"$service\"]") + + description=$(echo "$service_data" | ${jq}/bin/jq -r '.config.description // "No description available"') + port=$(echo "$service_data" | ${jq}/bin/jq -r '.config.port // "N/A"') + tags=$(echo "$service_data" | ${jq}/bin/jq -r '.config.tags | join(", ")') + deployment_count=$(echo "$service_data" | ${jq}/bin/jq -r '.deploymentCount') + deployed_on=$(echo "$service_data" | ${jq}/bin/jq -r '.deployedOn | join(", ")') + + echo "**Description:** $description" + echo + echo "**Default Port:** \`$port\`" + echo + if [[ -n "$tags" && "$tags" != "" ]]; then + echo "**Tags:** $tags" + echo + fi + echo "**Current Deployments:** $deployment_count instance(s) on: $deployed_on" + echo + + # Integration Status Overview + monitoring_enabled=$(echo "$service_data" | ${jq}/bin/jq -r '.config.monitoring.enabled') + logging_enabled=$(echo "$service_data" | ${jq}/bin/jq -r '.config.logging.enabled') + proxy_enabled=$(echo "$service_data" | ${jq}/bin/jq -r '.config.proxy.enabled') + + echo "#### Default Integration Status" + echo + echo "| Integration | Status | Default Configuration |" + echo "|-------------|--------|----------------------|" + + # Monitoring status + if [[ "$monitoring_enabled" == "true" ]]; then + metrics_path=$(echo "$service_data" | ${jq}/bin/jq -r '.config.monitoring.metricsPath') + health_path=$(echo "$service_data" | ${jq}/bin/jq -r '.config.monitoring.healthPath') + echo "| 📊 Monitoring | ✅ **Enabled** | Metrics: \`$metrics_path\`, Health: \`$health_path\` |" + else + echo "| 📊 Monitoring | ❌ Disabled | Available but requires \`monitoring.enable = true\` |" + fi + + # Logging status + if [[ "$logging_enabled" == "true" ]]; then + log_files=$(echo "$service_data" | ${jq}/bin/jq -r '.config.logging.files | length') + if [[ "$log_files" -gt 0 ]]; then + echo "| 📝 Logging | ✅ **Enabled** | Collecting $log_files log file(s) |" + else + echo "| 📝 Logging | ✅ **Enabled** | Auto-configured log collection |" + fi + else + echo "| 📝 Logging | ❌ Disabled | Available but requires \`logging.enable = true\` |" + fi + + # Proxy status + if [[ "$proxy_enabled" == "true" ]]; then + subdomain=$(echo "$service_data" | ${jq}/bin/jq -r '.config.proxy.subdomain') + enable_auth=$(echo "$service_data" | ${jq}/bin/jq -r '.config.proxy.enableAuth') + auth_status=$(if [[ "$enable_auth" == "true" ]]; then echo "🔒 Auth required"; else echo "🌐 Public access"; fi) + echo "| 🔀 Proxy | ✅ **Enabled** | Subdomain: \`$subdomain\`, $auth_status |" + else + echo "| 🔀 Proxy | ❌ Disabled | Available but requires \`proxy.enable = true\` |" + fi + + echo + + # Core Configuration + echo "#### Core Configuration" + echo + echo "\`\`\`nix" + echo "homelab.services.$service = {" + echo " enable = true;" + if [[ "$port" != "N/A" ]]; then + echo " port = $port;" + fi + echo " description = \"$description\";" + if [[ -n "$tags" && "$tags" != "" ]]; then + echo " tags = [ $(echo "$tags" | sed 's/, /" "/g' | sed 's/^/"/; s/$/"/') ];" + fi + echo + echo " # Default integrations (adjust as needed)" + if [[ "$monitoring_enabled" == "true" ]]; then + echo " monitoring.enable = true; # ✅ Enabled by default" + else + echo " # monitoring.enable = true; # ❌ Disabled by default" + fi + if [[ "$logging_enabled" == "true" ]]; then + echo " logging.enable = true; # ✅ Enabled by default" + else + echo " # logging.enable = true; # ❌ Disabled by default" + fi + if [[ "$proxy_enabled" == "true" ]]; then + echo " proxy.enable = true; # ✅ Enabled by default" + else + echo " # proxy.enable = true; # ❌ Disabled by default" + fi + echo "};" + echo "\`\`\`" + echo + + # Service-specific options + service_specific=$(echo "$service_data" | ${jq}/bin/jq -r '.config.serviceSpecific') + if [[ "$service_specific" != "{}" && "$service_specific" != "null" ]]; then + echo "#### Service-Specific Options" + echo + echo "Available configuration options for $service:" + echo + echo "\`\`\`nix" + echo "homelab.services.$service = {" + echo " # ... core options above ..." + echo + echo " # Service-specific configuration" + echo "$service_specific" | ${jq}/bin/jq -r 'to_entries[] | " \(.key) = \(.value | tostring);"' + echo "};" + echo "\`\`\`" + echo + fi + + echo "---" + echo + done + + echo "## Integration Summary" + echo + echo "### Available Integration Types" + echo + echo "| Integration | Purpose | Default Behavior | Configuration |" + echo "|-------------|---------|------------------|---------------|" + echo "| **📊 Monitoring** | Prometheus metrics + health checks | Service-dependent | \`monitoring.enable = true\` |" + echo "| **📝 Logging** | Centralized log collection | Service-dependent | \`logging.enable = true\` |" + echo "| **🔀 Proxy** | Reverse proxy with SSL + auth | Service-dependent | \`proxy.enable = true\` |" + echo + echo "### Integration Benefits" + echo + echo "- **🔄 Automatic Discovery:** Enabled integrations are automatically discovered by fleet-wide services" + echo "- **📊 Unified Monitoring:** All metrics and health checks appear in Prometheus/Grafana" + echo "- **📝 Centralized Logging:** All logs are collected and indexed in Loki" + echo "- **🌐 Consistent Access:** All services get consistent subdomain access with SSL" + echo "- **🎯 Smart Defaults:** Each service comes with sensible default configurations" + echo + echo "---" + echo + echo "*This service catalog is generated from actual service configurations across your homelab fleet.*" +''