diff --git a/.forgejo/workflows/docs.yml b/.forgejo/workflows/docs.yml index f9d9995..a453130 100644 --- a/.forgejo/workflows/docs.yml +++ b/.forgejo/workflows/docs.yml @@ -26,14 +26,16 @@ jobs: - name: Install Python dependencies run: pip install --quiet -r requirements.txt - - name: Regenerate hardware index - run: python3 scripts/gen_overview.py --category hardware - - - name: Fail on drift in docs/hardware/index.md + - name: Regenerate hardware and services indices run: | - if ! git diff --exit-code docs/hardware/index.md; then + python3 scripts/gen_overview.py --category hardware + python3 scripts/gen_overview.py --category services + + - name: Fail on drift in generated indices + run: | + if ! git diff --exit-code docs/hardware/index.md docs/services/index.md; then echo - echo "::error::docs/hardware/index.md is stale." + echo "::error::A generated index is stale." echo "Regenerate locally via 'make docs-index' and commit the result." exit 1 fi diff --git a/CLAUDE.md b/CLAUDE.md index 884fcf7..8603279 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -24,6 +24,7 @@ From `notes/todo/2026-04-14-todo.md`: ``` docs/ # everything here is built and shipped to docs.makerfloss.eu hardware/ # auto-indexed per-host frontmatter (mf00..mf03, makerfloss.eu) + services/ # auto-indexed per-service frontmatter (docs, forgejo, …) infrastructure/ # labdesign, VPS/DNS, etc. presentations/ # Marp decks (build-slides.sh) notes/ # repo-only working material, not built diff --git a/Makefile b/Makefile index fbd716b..b6de832 100644 --- a/Makefile +++ b/Makefile @@ -2,14 +2,15 @@ help: @echo "Targets:" - @echo " docs-index Regenerate docs/hardware/index.md from per-host frontmatter" + @echo " docs-index Regenerate docs/{hardware,services}/index.md from per-item frontmatter" @echo " docs-build Build the static MkDocs site into ./site (strict)" @echo " docs-serve Run a live-reload local preview server" - @echo " docs-check Drift-check: regenerate index, fail if it differs from the committed copy" + @echo " docs-check Drift-check: regenerate indices, fail if they differ from the committed copies" @echo " slides Run build-slides.sh (Marp slides)" docs-index: python3 scripts/gen_overview.py --category hardware + python3 scripts/gen_overview.py --category services docs-build: mkdocs build --strict @@ -19,7 +20,8 @@ docs-serve: docs-check: python3 scripts/gen_overview.py --category hardware - git diff --exit-code docs/hardware/index.md + python3 scripts/gen_overview.py --category services + git diff --exit-code docs/hardware/index.md docs/services/index.md slides: ./build-slides.sh diff --git a/README.md b/README.md index 124171d..7a4c2fa 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,7 @@ Documentation and working notes for **MakerFLOSS**, an Orange Makerspace initiat ``` docs/ # built into the public site (docs.makerfloss.eu) hardware/ # auto-indexed per-host frontmatter (mf00..mf03, makerfloss.eu) + services/ # auto-indexed per-service frontmatter (docs, forgejo, …) infrastructure/ # labdesign, VPS/DNS, etc. presentations/ # Marp decks (also published to slides.makerfloss.eu) notes/ # repo-only working material — not part of the site diff --git a/docs/services/docs.md b/docs/services/docs.md new file mode 100644 index 0000000..7d259b4 --- /dev/null +++ b/docs/services/docs.md @@ -0,0 +1,14 @@ +--- +name: docs +kind: static-site +status: in-use +host: makerfloss.eu +url: https://docs.makerfloss.eu +upstream: https://www.mkdocs.org +tech: MkDocs Material +tls: letsencrypt +--- + +## Notes + +Public-facing documentation site. Built from this repo's `docs/` tree on every push to `main` by the Forgejo Actions runner (`.forgejo/workflows/docs.yml`), then rsynced to the VPS and served by nginx behind Traefik. diff --git a/docs/services/forgejo.md b/docs/services/forgejo.md new file mode 100644 index 0000000..3d9db03 --- /dev/null +++ b/docs/services/forgejo.md @@ -0,0 +1,14 @@ +--- +name: forgejo +kind: web-app +status: in-use +host: makerfloss.eu +url: https://forgejo.makerfloss.eu +upstream: https://codeberg.org/forgejo/forgejo +tech: Go +tls: letsencrypt +--- + +## Notes + +Self-hosted git forge. Hosts this repository and the AnsibleBaobabV4 project. SSH access on port 7577 (`ssh://git@forgejo.makerfloss.eu:7577//.git`). Also runs the Forgejo Actions runner that builds [docs](docs.md) and [slides](slides.md) on push. diff --git a/docs/services/gandi-dns.md b/docs/services/gandi-dns.md new file mode 100644 index 0000000..14248c1 --- /dev/null +++ b/docs/services/gandi-dns.md @@ -0,0 +1,12 @@ +--- +name: gandi-dns +kind: dns +status: in-use +host: Gandi.net (external) +url: https://www.gandi.net +tech: Gandi LiveDNS API +--- + +## Notes + +External DNS provider for the `makerfloss.eu` zone. Records are managed declaratively from the [AnsibleBaobabV4](https://forgejo.nyumbani.baobab.band/sjat/AnsibleBaobabV4) project (`play_dns.yml --limit makerfloss`); **never edit DNS records directly in the Gandi web UI** — Ansible will overwrite them on the next run. diff --git a/docs/services/index.md b/docs/services/index.md new file mode 100644 index 0000000..93a7117 --- /dev/null +++ b/docs/services/index.md @@ -0,0 +1,34 @@ +# Services Overview + +_Auto-generated from `docs/services/*.md` — do not edit by hand. Run `make docs-index` after changing a file._ + +## DNS + +| Name | URL | Host | Tech | Status | +|---|---|---|---|---| +| [gandi-dns](gandi-dns.md) | [www.gandi.net](https://www.gandi.net) | Gandi.net (external) | Gandi LiveDNS API | in-use | + +## Libraries + +| Name | URL | Host | Tech | Status | +|---|---|---|---|---| +| [mermaid](mermaid.md) | [cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs](https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs) | CDN (jsdelivr) | JavaScript (ESM) | in-use | + +## Slide builders + +| Name | URL | Host | Tech | Status | +|---|---|---|---|---| +| [marp](marp.md) | | CI runner (Forgejo Actions) | Node.js (marp-cli) / Docker | in-use | + +## Static sites + +| Name | URL | Host | Tech | Status | +|---|---|---|---|---| +| [docs](docs.md) | [docs.makerfloss.eu](https://docs.makerfloss.eu) | makerfloss.eu | MkDocs Material | in-use | +| [slides](slides.md) | [slides.makerfloss.eu](https://slides.makerfloss.eu) | makerfloss.eu | Marp + Mermaid.js | in-use | + +## Web applications + +| Name | URL | Host | Tech | Status | +|---|---|---|---|---| +| [forgejo](forgejo.md) | [forgejo.makerfloss.eu](https://forgejo.makerfloss.eu) | makerfloss.eu | Go | in-use | diff --git a/docs/services/marp.md b/docs/services/marp.md new file mode 100644 index 0000000..344e3e7 --- /dev/null +++ b/docs/services/marp.md @@ -0,0 +1,12 @@ +--- +name: marp +kind: slide-builder +status: in-use +host: CI runner (Forgejo Actions) +upstream: https://marp.app +tech: Node.js (marp-cli) / Docker +--- + +## Notes + +Markdown-to-HTML slide compiler. Invoked by `build-slides.sh` against every `docs/presentations/*.md` (and any other `.md` with `marp: true` in frontmatter), either via a local `marp` binary or the `marpteam/marp-cli` Docker image. Output feeds the [slides](slides.md) site. See [`notes/dev/marp-mermaid-setup.md`](https://forgejo.makerfloss.eu/sjat/MakerFLOSS/src/branch/main/notes/dev/marp-mermaid-setup.md) for the [mermaid](mermaid.md) integration design. diff --git a/docs/services/mermaid.md b/docs/services/mermaid.md new file mode 100644 index 0000000..1e34fe6 --- /dev/null +++ b/docs/services/mermaid.md @@ -0,0 +1,13 @@ +--- +name: mermaid +kind: library +status: in-use +host: CDN (jsdelivr) +url: https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs +upstream: https://mermaid.js.org +tech: JavaScript (ESM) +--- + +## Notes + +Diagram-rendering JS library, injected into built slide HTML by the `inject_mermaid` step of `build-slides.sh`. Each `
` block emitted by [marp](marp.md) is rewritten into `
`, then `mermaid.run()` renders it client-side on page load. diff --git a/docs/services/slides.md b/docs/services/slides.md new file mode 100644 index 0000000..ea00c5b --- /dev/null +++ b/docs/services/slides.md @@ -0,0 +1,14 @@ +--- +name: slides +kind: static-site +status: in-use +host: makerfloss.eu +url: https://slides.makerfloss.eu +upstream: https://marp.app +tech: Marp + Mermaid.js +tls: letsencrypt +--- + +## Notes + +Slide-deck site. Decks are authored as Marp markdown in `docs/presentations/` and compiled to HTML by `build-slides.sh` (CI invokes it via the [marp](marp.md) toolchain). Built output is rsynced to the VPS and served alongside [docs](docs.md). diff --git a/mkdocs.yml b/mkdocs.yml index b89c0a6..74e2227 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -55,4 +55,6 @@ nav: - Home: index.md - Hardware: - hardware/index.md + - Services: + - services/index.md - House rules: house-rules.md diff --git a/scripts/gen_overview.py b/scripts/gen_overview.py index b235d88..84a16f4 100755 --- a/scripts/gen_overview.py +++ b/scripts/gen_overview.py @@ -50,11 +50,16 @@ def validate(path: Path, fm: dict, cfg: dict) -> None: raise SchemaError( f"{path}: {field}={fm[field]!r} not in {allowed}" ) - stem = path.stem - hostname = fm["hostname"] - if stem != hostname: + key_field = cfg.get("key_field", "hostname") + if key_field not in fm: raise SchemaError( - f"{path}: filename stem {stem!r} != hostname {hostname!r}" + f"{path}: missing key field {key_field!r}" + ) + stem = path.stem + value = fm[key_field] + if stem != value: + raise SchemaError( + f"{path}: filename stem {stem!r} != {key_field} {value!r}" ) @@ -122,9 +127,15 @@ def fmt_nic(fm: dict) -> str: def cell(fm: dict, col: dict) -> str: kind = col.get("kind") - if kind == "hostname-link": - h = fm["hostname"] - return f"[{h}]({h}.md)" + if kind == "key-link": + v = fm[col["field"]] + return f"[{v}]({v}.md)" + if kind == "url-link": + u = fm.get(col["field"], "") + if not u: + return "" + label = u.removeprefix("https://").removeprefix("http://") + return f"[{label}]({u})" if kind == "cpu": return fmt_cpu(fm) if kind == "ram": diff --git a/scripts/mkdocs_hooks.py b/scripts/mkdocs_hooks.py index b74f4ef..36acfdb 100644 --- a/scripts/mkdocs_hooks.py +++ b/scripts/mkdocs_hooks.py @@ -1,4 +1,4 @@ -"""MkDocs build hook: render a Specs section on each hardware host page from its YAML frontmatter.""" +"""MkDocs build hook: render a Specs section on each hardware or service page from its YAML frontmatter.""" from __future__ import annotations import sys @@ -8,7 +8,16 @@ sys.path.insert(0, str(Path(__file__).resolve().parent)) from gen_overview import fmt_cpu, fmt_nic, fmt_ram, fmt_storage # noqa: E402 -def _render_specs(meta: dict) -> str: +def _table(rows: list[tuple[str, str]]) -> str: + lines = ["| Field | Value |", "|---|---|"] + for label, value in rows: + if not value: + continue + lines.append(f"| {label} | {value} |") + return "\n".join(lines) + + +def _hardware_page(meta: dict, body: str) -> str: rows = [ ("Model", meta.get("model")), ("Location", meta.get("location")), @@ -18,17 +27,29 @@ def _render_specs(meta: dict) -> str: ("NIC", fmt_nic(meta)), ("Status", meta.get("status")), ] - lines = ["| Field | Value |", "|---|---|"] - for label, value in rows: - if not value: - continue - lines.append(f"| {label} | {value} |") - return "\n".join(lines) + return f"# {meta['hostname']}\n\n## Specs\n\n{_table(rows)}\n\n{body}" + + +def _service_page(meta: dict, body: str) -> str: + url = meta.get("url") + upstream = meta.get("upstream") + rows = [ + ("Kind", meta.get("kind")), + ("Host", meta.get("host")), + ("URL", f"[{url}]({url})" if url else ""), + ("Tech", meta.get("tech")), + ("Upstream", f"[{upstream}]({upstream})" if upstream else ""), + ("TLS", meta.get("tls")), + ("Status", meta.get("status")), + ] + return f"# {meta['name']}\n\n## Service\n\n{_table(rows)}\n\n{body}" def on_page_markdown(markdown, page, config, files): # noqa: ARG001 meta = page.meta or {} - hostname = meta.get("hostname") - if not hostname: - return markdown - return f"# {hostname}\n\n## Specs\n\n{_render_specs(meta)}\n\n{markdown}" + src = (page.file.src_uri or page.file.src_path or "").replace("\\", "/") + if src.startswith("hardware/") and meta.get("hostname"): + return _hardware_page(meta, markdown) + if src.startswith("services/") and meta.get("name"): + return _service_page(meta, markdown) + return markdown diff --git a/scripts/overview_config.yml b/scripts/overview_config.yml index f5e92c7..9b806c9 100644 --- a/scripts/overview_config.yml +++ b/scripts/overview_config.yml @@ -8,6 +8,7 @@ hardware: title: "Hardware Overview" source_dir: docs/hardware output_file: docs/hardware/index.md + key_field: hostname required_fields: - hostname - kind @@ -28,7 +29,7 @@ hardware: desktop: Desktops sort_by: hostname columns: - - { header: Hostname, kind: hostname-link } + - { header: Hostname, kind: key-link, field: hostname } - { header: Model, field: model } - { header: Location, field: location } - { header: CPU, kind: cpu } @@ -36,3 +37,32 @@ hardware: - { header: Storage, kind: storage } - { header: NIC, kind: nic } - { header: Status, field: status } + +services: + title: "Services Overview" + source_dir: docs/services + output_file: docs/services/index.md + key_field: name + required_fields: + - name + - kind + - status + enums: + kind: [web-app, static-site, dns, slide-builder, library, reverse-proxy, mail] + status: [in-use, staging, planned, broken, decommissioned] + group_by: kind + group_titles: + web-app: Web applications + static-site: Static sites + dns: DNS + slide-builder: Slide builders + library: Libraries + reverse-proxy: Reverse proxies + mail: Mail + sort_by: name + columns: + - { header: Name, kind: key-link, field: name } + - { header: URL, kind: url-link, field: url } + - { header: Host, field: host } + - { header: Tech, field: tech } + - { header: Status, field: status }