#!/usr/bin/env bash set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)" INVENTORY_FILE="${REPO_ROOT}/homelab.yml" README_TEMPLATE="${REPO_ROOT}/README.md.tmpl" README_OUTPUT="${REPO_ROOT}/README.md" usage() { cat <<'USAGE' Usage: scripts/render-docs [--check] Render generated Markdown docs from repo-local templates. Options: --check fail if generated docs differ from committed output USAGE } read_inventory_value() { local path="$1" python3 - "${INVENTORY_FILE}" "${path}" <<'PY' import re import sys inventory_file, requested_path = sys.argv[1:3] stack = [] values = {} pattern = re.compile(r"^(\s*)([A-Za-z_][A-Za-z0-9_]*):(?:\s*(.*?))?\s*$") with open(inventory_file, encoding="utf-8") as handle: for raw_line in handle: if not raw_line.strip() or raw_line.lstrip().startswith("#") or raw_line.lstrip().startswith("- "): continue match = pattern.match(raw_line.rstrip("\n")) if not match: continue indent = len(match.group(1)) key = match.group(2) value = (match.group(3) or "").strip() while stack and stack[-1][0] >= indent: stack.pop() current_path = ".".join([item[1] for item in stack] + [key]) if value == "": stack.append((indent, key)) continue if " #" in value: value = value.split(" #", 1)[0].strip() values[current_path] = value.strip("\"'") value = values.get(requested_path) if not value: raise SystemExit(1) print(value) PY } render_readme() { local main_script="$1" if grep -Eo '{{[[:space:]]*[A-Za-z_][A-Za-z0-9_]*[[:space:]]*}}' "${README_TEMPLATE}" | grep -Evq '{{[[:space:]]*main_script[[:space:]]*}}'; then printf 'Unsupported template variable in %s\n' "${README_TEMPLATE}" >&2 return 1 fi sed "s/{{[[:space:]]*main_script[[:space:]]*}}/${main_script}/g" "${README_TEMPLATE}" } main() { local mode="render" local main_script local rendered case "${1:-}" in "") ;; --check) mode="check" ;; -h | --help) usage return 0 ;; *) usage >&2 return 2 ;; esac if [[ ! -s "${INVENTORY_FILE}" ]]; then printf 'Missing homelab inventory: %s\n' "${INVENTORY_FILE}" >&2 return 1 fi if [[ ! -s "${README_TEMPLATE}" ]]; then printf 'Missing README template: %s\n' "${README_TEMPLATE}" >&2 return 1 fi main_script="$(read_inventory_value metadata.main_script)" if [[ -z "${main_script}" ]]; then printf 'homelab.yml metadata.main_script must not be empty\n' >&2 return 1 fi rendered="$(mktemp)" render_readme "${main_script}" >"${rendered}" if [[ "${mode}" == "check" ]]; then "${REPO_ROOT}/scripts/render-service-catalog" --check if ! cmp -s "${rendered}" "${README_OUTPUT}"; then printf 'Generated README.md is stale. Run scripts/render-docs.\n' >&2 diff -u "${README_OUTPUT}" "${rendered}" || true rm -f "${rendered}" return 1 fi printf 'docs are current\n' rm -f "${rendered}" return 0 fi mv "${rendered}" "${README_OUTPUT}" "${REPO_ROOT}/scripts/render-service-catalog" printf 'Rendered %s from %s\n' "${README_OUTPUT}" "${README_TEMPLATE}" } main "$@"