Skip to content

Commit 07ea916

Browse files
committed
docs: add MkDocs Material site deployed to GitHub Pages
Set up a full documentation site for HyperCache, built with MkDocs Material and published automatically to GitHub Pages on every push to `main`. - Add `mkdocs.yml` with Material theme, pymdownx extensions, mermaid support, and the include-markdown + glightbox plugins. - Add eight navigated doc pages: landing, quickstart, 5-node cluster tutorial, Helm chart guide, server-binary reference, RFC index, changelog, and distributed-backend architecture stub. The changelog and server-binary pages are included from their canonical sources (CHANGELOG.md / cmd/hypercache-server/README.md) via include-markdown to avoid content drift. - Add `_mkdocs/hooks.py`: a build-time hook that rewrites repo-relative source-code links (e.g. `../pkg/foo.go`) to canonical GitHub URLs, keeping the same markdown valid on both github.com and the Pages site under strict mode. - Add `.github/workflows/docs.yml`: builds with `--strict` on every PR and deploys via `actions/deploy-pages@v4` on pushes to `main`. - Add `docs-build`, `docs-serve`, and `docs-publish` Makefile targets (pyenv-aware, using the `mkdocs` virtualenv). - Relax mdl rules that conflict with MkDocs idioms: MD041 (YAML frontmatter), MD010 (Go tab-in-code-blocks), MD033/MD032 (Material grid-card HTML wrappers). - Update README with a docs badge and a link to the rendered site. - Add MkDocs-related terms to cspell dictionary and ignore the generated `/site/` directory and `_mkdocs/__pycache__/`.
1 parent 4e07775 commit 07ea916

16 files changed

Lines changed: 824 additions & 1 deletion

File tree

.github/workflows/docs.yml

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
---
2+
name: docs
3+
4+
# Build and deploy the MkDocs site to GitHub Pages.
5+
# * pull_request — build only (validates the docs render
6+
# without publishing).
7+
# * push to main — build + deploy to gh-pages.
8+
# * workflow_dispatch — same as push (lets operators
9+
# re-publish without a docs change).
10+
11+
on:
12+
pull_request:
13+
paths:
14+
- "docs/**"
15+
- "mkdocs.yml"
16+
- "CHANGELOG.md"
17+
- "cmd/hypercache-server/README.md"
18+
- ".github/workflows/docs.yml"
19+
push:
20+
branches: [main]
21+
paths:
22+
- "docs/**"
23+
- "mkdocs.yml"
24+
- "CHANGELOG.md"
25+
- "cmd/hypercache-server/README.md"
26+
- ".github/workflows/docs.yml"
27+
workflow_dispatch:
28+
29+
# Pages deployments require these permissions; the build-only
30+
# branch (PR) doesn't actually use the deploy steps so the
31+
# extra permissions are harmless.
32+
permissions:
33+
contents: read
34+
pages: write
35+
id-token: write
36+
37+
# A single in-flight deploy at a time. Newer pushes cancel
38+
# older ones to avoid an out-of-order publish.
39+
concurrency:
40+
group: pages
41+
cancel-in-progress: true
42+
43+
jobs:
44+
build:
45+
name: build
46+
runs-on: ubuntu-latest
47+
timeout-minutes: 10
48+
steps:
49+
- uses: actions/checkout@v6
50+
with:
51+
fetch-depth: 0 # docs/ may reference files via relative paths
52+
53+
- name: Setup Python
54+
uses: actions/setup-python@v6
55+
with:
56+
python-version: "3.13"
57+
cache: pip
58+
59+
- name: Install MkDocs + plugins
60+
run: |
61+
python -m pip install --upgrade pip
62+
pip install mkdocs-material \
63+
mkdocs-include-markdown-plugin \
64+
mkdocs-glightbox
65+
66+
- name: Build site (strict)
67+
# Strict in CI catches broken links / missing pages on PR;
68+
# `mkdocs serve` locally relaxes this for fast iteration.
69+
run: mkdocs build --strict
70+
71+
- name: Upload Pages artifact
72+
if: github.event_name == 'push' || github.event_name == 'workflow_dispatch'
73+
uses: actions/upload-pages-artifact@v3
74+
with:
75+
path: ./site
76+
77+
deploy:
78+
name: deploy
79+
needs: build
80+
if: github.event_name == 'push' || github.event_name == 'workflow_dispatch'
81+
runs-on: ubuntu-latest
82+
timeout-minutes: 5
83+
environment:
84+
name: github-pages
85+
url: ${{ steps.deployment.outputs.page_url }}
86+
steps:
87+
- name: Deploy to GitHub Pages
88+
id: deployment
89+
uses: actions/deploy-pages@v4

.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,3 +96,9 @@ tags
9696

9797
### Project ###
9898
.dccache
99+
100+
### MkDocs site build output (CI publishes; local builds shouldn't be committed) ###
101+
/site/
102+
103+
### Python bytecode caches from MkDocs hooks ###
104+
_mkdocs/__pycache__/

.mdl_style.rb

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,22 @@
1313
# under distinct parent headings — which is exactly the Keep-a-Changelog
1414
# shape, and still catches genuine duplicates within the same section.
1515
rule "MD024", :allow_different_nesting => true
16+
17+
# MkDocs pages start with YAML frontmatter (---\ntitle: ...\n---), so
18+
# the first line cannot be a top-level heading. MD041 fights that
19+
# convention; the alternative would be losing per-page metadata.
20+
exclude_rule 'MD041'
21+
22+
# Hard tabs in code blocks are valid — Go source uses tabs by
23+
# convention (gofmt enforces it), and MkDocs preserves them. The
24+
# default rule flags every Go example as broken, which would push
25+
# us to manually convert tabs in every code block.
26+
exclude_rule 'MD010'
27+
28+
# MkDocs Material's "grid cards" feature requires `<div class="grid cards">`
29+
# HTML wrappers around a markdown list. MD033 (no inline HTML) flags
30+
# every grid block. Ditto for the surrounding-blank-line rule (MD032)
31+
# which doesn't see the list inside the div as a list. Skipping both
32+
# is the standard Material-theme posture.
33+
exclude_rule 'MD033'
34+
exclude_rule 'MD032'

CHANGELOG.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,26 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
88

99
### Added
1010

11+
- **Documentation site on GitHub Pages**, built with MkDocs Material
12+
and published automatically on every push to `main`. Eight
13+
navigated pages — landing, quickstart, 5-node cluster tutorial,
14+
Helm chart guide, server-binary reference, distributed-backend
15+
architecture, operations runbook, RFC index — plus the
16+
CHANGELOG and the `cmd/hypercache-server/README.md` pulled in
17+
via the include-markdown plugin so they don't drift. A
18+
build-time hook at [`_mkdocs/hooks.py`](_mkdocs/hooks.py)
19+
rewrites repo-relative source-code references (`../pkg/foo.go`)
20+
into canonical GitHub URLs so the same markdown renders
21+
correctly both on github.com and on the rendered Pages site.
22+
Workflow at
23+
[`.github/workflows/docs.yml`](.github/workflows/docs.yml)
24+
builds with `--strict` on every PR (catches broken docs-internal
25+
links on submission) and deploys via `actions/deploy-pages@v4`
26+
on main pushes. The README now links to the rendered site.
27+
Polishing pass on the existing markdown surface: relaxed
28+
`mdl` rules that fight MkDocs/frontmatter idioms (MD041
29+
for YAML frontmatter pages, MD010 for Go's tab-in-code-blocks
30+
convention, MD033/MD032 for Material's grid-cards HTML).
1131
- **Richer client API — metadata inspection, JSON envelopes, batch
1232
operations.** Three additions to the
1333
`cmd/hypercache-server` HTTP surface:

Makefile

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,15 @@ sec:
185185
@echo "\nRunning gosec..."
186186
gosec -exclude-generated -exclude-dir=__examples/size ./...
187187

188+
docs-build:
189+
PYENV_VERSION=mkdocs mkdocs build --strict
190+
191+
docs-publish: docs-build
192+
PYENV_VERSION=mkdocs mkdocs gh-deploy
193+
194+
docs-serve: docs-build
195+
PYENV_VERSION=mkdocs mkdocs serve
196+
188197
# check_command_exists is a helper function that checks if a command exists.
189198
define check_command_exists
190199
@which $(1) > /dev/null 2>&1 || (echo "$(1) command not found" && exit 1)
@@ -219,6 +228,11 @@ help:
219228
@echo " update-deps\t\t\tUpdate all dependencies and tidy go.mod"
220229
@echo
221230
@echo
231+
@echo "Documentation commands:"
232+
@echo " docs-build"
233+
@echo " docs-publish"
234+
@echo " docs-serve"
235+
@echo
222236
@echo "For more information, see the project README."
223237

224238
.PHONY: init prepare-toolchain prepare-base-tools update-toolchain test test-race typecheck build ci bench bench-baseline vet update-deps lint sec help

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
# HyperCache
22

3-
[![Go](https://github.com/hyp3rd/hypercache/actions/workflows/go.yml/badge.svg)][build-link] [![CodeQL](https://github.com/hyp3rd/hypercache/actions/workflows/codeql.yml/badge.svg)][codeql-link]
3+
[![Go](https://github.com/hyp3rd/hypercache/actions/workflows/go.yml/badge.svg)][build-link] [![CodeQL](https://github.com/hyp3rd/hypercache/actions/workflows/codeql.yml/badge.svg)][codeql-link] [![Docs](https://img.shields.io/badge/docs-github--pages-blue)](https://hyp3rd.github.io/hypercache/)
4+
5+
> **📖 Full documentation**: <https://hyp3rd.github.io/hypercache/>
46
57
## Synopsis
68

_mkdocs/hooks.py

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
"""MkDocs hooks for the HyperCache site.
2+
3+
Rewrites repo-relative links to source files (`../pkg/foo.go`,
4+
`../../hypercache.go`, etc.) into canonical GitHub URLs, so the same
5+
markdown source renders correctly both on github.com and on the
6+
GitHub Pages MkDocs build.
7+
8+
Without this, the operations runbook and the RFCs reference dozens
9+
of source files via paths like `../pkg/backend/dist_memory.go`.
10+
GitHub renders those as in-repo links; MkDocs's strict mode flags
11+
them as broken because `pkg/` is not part of the documentation
12+
tree. Rewriting them at build time keeps the source markdown
13+
GitHub-friendly while letting strict mode actually enforce
14+
docs-internal correctness.
15+
"""
16+
17+
import os
18+
import re
19+
from typing import Any
20+
21+
GITHUB_REPO_BASE = "https://github.com/hyp3rd/hypercache/blob/main"
22+
23+
# File extensions that we treat as "source code, not docs" — links
24+
# to these get rewritten to GitHub URLs. .md is intentionally NOT
25+
# in this list because doc-to-doc links should stay intra-site so
26+
# MkDocs can validate them.
27+
SOURCE_EXTENSIONS = (
28+
".go",
29+
".yaml",
30+
".yml",
31+
".sh",
32+
".rb",
33+
".txt",
34+
".dockerignore",
35+
".gitignore",
36+
".env",
37+
"Dockerfile",
38+
"Makefile",
39+
)
40+
41+
# Paths that are entire directories the docs reference for context
42+
# (e.g. "see internal/cluster/"). These get rewritten to GitHub
43+
# tree URLs — clicking takes the reader to a directory listing.
44+
SOURCE_DIR_PREFIXES = (
45+
"pkg/",
46+
"internal/",
47+
"cmd/",
48+
"chart/",
49+
"scripts/",
50+
"tests/",
51+
"__examples/",
52+
".github/",
53+
"docker/",
54+
)
55+
56+
LINK_RE = re.compile(r"\[([^\]]+)\]\(([^)]+)\)")
57+
58+
59+
def _is_source_link(target: str) -> bool:
60+
"""Return True when the link target looks like a repo source ref
61+
rather than an in-tree docs link."""
62+
# Strip anchor before extension/prefix checks.
63+
clean = target.split("#", 1)[0]
64+
65+
# Source files by extension or basename.
66+
if clean.endswith(SOURCE_EXTENSIONS):
67+
return True
68+
69+
# Directory references (no extension) that match known source
70+
# roots. We resolve `..` segments first so the prefix match
71+
# works against repo-rooted paths.
72+
parts = [p for p in clean.split("/") if p and p != "."]
73+
74+
# Drop leading `..` segments — they all collapse to repo root
75+
# for our purposes (rewrite-target side).
76+
while parts and parts[0] == "..":
77+
parts.pop(0)
78+
79+
if not parts:
80+
return False
81+
82+
repo_path = "/".join(parts)
83+
if any(repo_path.startswith(p) for p in SOURCE_DIR_PREFIXES):
84+
return True
85+
86+
return False
87+
88+
89+
def _resolve_to_repo_root(page_src_path: str, target: str) -> str:
90+
"""Translate a relative target into a repo-rooted path.
91+
92+
Page src_path is relative to docs/ (e.g. `rfcs/0001-foo.md`).
93+
Target is relative to the page (e.g. `../../pkg/foo.go`). The
94+
returned path is relative to the repo root.
95+
"""
96+
# `os.path.normpath` collapses `..` correctly; we anchor at
97+
# `docs/<page_dir>` and resolve from there.
98+
page_dir = os.path.dirname(page_src_path)
99+
docs_anchored = os.path.normpath(os.path.join("docs", page_dir, target))
100+
101+
# The result may still start with `../` if the relative target
102+
# walked above the repo root (it shouldn't in practice). Trim
103+
# any leading `../` defensively.
104+
while docs_anchored.startswith("../"):
105+
docs_anchored = docs_anchored[3:]
106+
107+
return docs_anchored
108+
109+
110+
def on_page_markdown(markdown: str, page: Any, **kwargs: Any) -> str:
111+
"""Rewrite source-code links on every page before MkDocs renders it."""
112+
page_src = page.file.src_path
113+
114+
def replace(match: re.Match[str]) -> str:
115+
link_text = match.group(1)
116+
link_target = match.group(2)
117+
118+
# Absolute URLs, mailtos, and pure anchors stay as-is.
119+
if link_target.startswith(("http://", "https://", "mailto:", "#")):
120+
return match.group(0)
121+
122+
if not _is_source_link(link_target):
123+
return match.group(0)
124+
125+
repo_path = _resolve_to_repo_root(page_src, link_target)
126+
127+
# Preserve any anchor on the target (e.g. line ranges like
128+
# `pkg/foo.go#L34-L58`).
129+
if "#" in link_target and "#" not in repo_path:
130+
anchor = "#" + link_target.split("#", 1)[1]
131+
repo_path += anchor
132+
133+
return f"[{link_text}]({GITHUB_REPO_BASE}/{repo_path})"
134+
135+
return LINK_RE.sub(replace, markdown)

cspell.config.yaml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,8 @@ words:
8181
- exhaustruct
8282
- Fanout
8383
- fasthttp
84+
- fontawesome
85+
- frontmatter
8486
- fatals
8587
- fctx
8688
- ferr
@@ -92,6 +94,7 @@ words:
9294
- freqs
9395
- funlen
9496
- geomean
97+
- glightbox
9598
- gerr
9699
- gitversion
97100
- GITVERSION
@@ -123,17 +126,20 @@ words:
123126
- idxs
124127
- Iface
125128
- ineff
129+
- inlinehilite
126130
- inmemory
127131
- intrange
128132
- ints
129133
- ireturn
130134
- Itemm
131135
- keyf
132136
- lamport
137+
- linenums
133138
- LFUDA
134139
- localmodule
135140
- logrus
136141
- longbridgeapp
142+
- mailtos
137143
- maxmemory
138144
- memprofile
139145
- Merkle
@@ -164,8 +170,11 @@ words:
164170
- podname
165171
- popd
166172
- Prealloc
173+
- productionization
167174
- protoc
168175
- pushd
176+
- pygments
177+
- pymdownx
169178
- recvcheck
170179
- rediscluster
171180
- repls
@@ -191,12 +200,15 @@ words:
191200
- strs
192201
- subtest
193202
- subtests
203+
- superfences
194204
- sval
195205
- thelper
196206
- toplevel
197207
- tparallel
208+
- tasklist
198209
- tracetest
199210
- traefik
211+
- twemoji
200212
- trunc
201213
- tunables
202214
- TTLMs

docs/changelog.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
title: Changelog
3+
---
4+
5+
{%
6+
include-markdown "../CHANGELOG.md"
7+
start="# Changelog"
8+
%}

0 commit comments

Comments
 (0)