mirror of
https://github.com/Klipper4FlashForge/firmware.git
synced 2026-10-19 03:49:50 -05:00
docs/ rendered on GitHub and nowhere else. It is a built, versioned site now.
The alternative was a wiki, and a wiki is a separate git repo: a page would stop moving in the same commit and the same review as the code it describes, with nothing able to catch the drift, and there would be exactly one live version of it. This project ships a package per tag and tells owners which checkboxes have touched hardware; the docs for the build someone flashed have to be readable, so Read the Docs builds this repo per tag and `latest` off master, with the version switcher that follows from that. Nothing moved. The audience split that the pages already have -- owner pages against build-and-test pages -- is the nav in mkdocs.yml, so a reader lands in their own section and never falls out of it, while the 30+ relative links between README.md, docs/*.md and docs/notes/ keep working in the repo view and the links people have already shared keep resolving. notes/70-error-codes.md sits in the owner section because README.md was already sending owners there. Two things had to hold for that to be true, and hooks/mkdocs_readme.py is both. Links out of docs/ -- `../payload/klipper/extras/ff_tool.py` and 18 others in toolchange.md -- are written relative because that is what works where a contributor reads them, in the repo; they become GitHub URLs at build time, and a link whose target does not exist is left alone so the build still fails on it. README.md is the front door and stays it: docs/index.md is generated from it on every build and gitignored, rather than being a second front page free to drift from the first. Anchors use GitHub's slug rules for the same reason. `#runout--clog` is what GitHub makes of `## Runout / clog`, python-markdown would have made `#runout-clog`, and toolchange.md:340 already pointed at the first. CI builds it with --strict, which is the cheap half of keeping docs true: a rename in payload/ now breaks the build instead of quietly leaving a page pointing at nothing. It cannot check that the prose is still right. Only a reader can. The site is not up yet -- the project has to be created on readthedocs.org and its versions activated -- so nothing links to it from README.md. The slug is free; mkdocs.yml claims it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S3XZh4SqPdXAowVndz6tMr
This commit is contained in:
1 parent
d3df810189
commit
7347f56418
7 files changed
+248
No files matched your search
@@ -289,3 +289,27 @@ jobs:
|
||||
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||
-e REQUIRE_PRINTER_SIM=1 \
|
||||
creator5-fw-build ./test/run-tests.py
|
||||
|
||||
# The docs make claims about a machine that can be left unbootable, and the
|
||||
# pages cross-link each other and the payload sources heavily -- 30+ links
|
||||
# between README.md, docs/*.md and docs/notes/. This is the cheap half of
|
||||
# keeping them true: --strict fails on a link whose target has moved or been
|
||||
# deleted, so a rename in payload/ cannot quietly leave a doc pointing at
|
||||
# nothing. It cannot check that the prose is still right; only a reader can.
|
||||
#
|
||||
# Read the Docs builds the same mkdocs.yml with fail_on_warning, so this job
|
||||
# going red is also the warning that the published site is about to fail.
|
||||
docs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.12" # what .readthedocs.yaml builds with
|
||||
|
||||
- name: Install the docs toolchain
|
||||
run: pip install -r docs/requirements.txt
|
||||
|
||||
- name: Build the site
|
||||
run: mkdocs build --strict
|
||||
@@ -8,3 +8,7 @@ dist/
|
||||
# Python bytecode from editing the klippy extras in payload/klipper/extras/
|
||||
__pycache__/
|
||||
*.pyc
|
||||
|
||||
# Generated from README.md by hooks/mkdocs_readme.py at docs build time.
|
||||
docs/index.md
|
||||
site/
|
||||
@@ -0,0 +1,20 @@
|
||||
# Read the Docs builds this repo's docs/ directory as-is: same commit, same
|
||||
# review, and one built version per git tag, so an owner running an older
|
||||
# package can read that package's docs rather than master's.
|
||||
version: 2
|
||||
|
||||
build:
|
||||
os: ubuntu-24.04
|
||||
tools:
|
||||
python: "3.12"
|
||||
|
||||
mkdocs:
|
||||
configuration: mkdocs.yml
|
||||
# A broken cross-link fails the build. The docs make claims about a machine
|
||||
# that can be left unbootable; a link that quietly stops resolving is the
|
||||
# cheapest of those claims to check, so it is checked.
|
||||
fail_on_warning: true
|
||||
|
||||
python:
|
||||
install:
|
||||
- requirements: docs/requirements.txt
|
||||
@@ -287,3 +287,42 @@ printer, with a traceback that names cffi and not the build. Both a stale
|
||||
prebuilt .so (v0.12 binary under a v0.13 tree) and a package that skipped
|
||||
the fork entirely (v20260824-nova-kakhovka) have shipped; the compile-fresh
|
||||
rule plus these gates are what make the third time structurally hard.
|
||||
|
||||
## The docs site
|
||||
|
||||
These pages are the site at
|
||||
[reforge.readthedocs.io](https://reforge.readthedocs.io/). Read the Docs
|
||||
builds `mkdocs.yml` out of this repo on every push to `master` and once per
|
||||
tag, so a page and the code it describes move in the same commit and the same
|
||||
review, and an owner running an older package can read that package's docs
|
||||
from the version switcher rather than master's.
|
||||
|
||||
To preview locally:
|
||||
|
||||
```sh
|
||||
python3 -m venv .venv && .venv/bin/pip install -r docs/requirements.txt
|
||||
.venv/bin/mkdocs serve # http://127.0.0.1:8000
|
||||
.venv/bin/mkdocs build --strict # what CI and RTD run
|
||||
```
|
||||
|
||||
Three things about it are worth knowing before you add a page.
|
||||
|
||||
**The nav is split by audience, and the split is the point.** `mkdocs.yml` has
|
||||
a *Using Reforge* section for someone who owns the printer and a *For
|
||||
contributors* section for someone building or testing the firmware, with the
|
||||
`docs/notes/` internals nested inside the second. A new page that is not in
|
||||
that nav is built but unreachable. Put it in the section its reader is
|
||||
standing in.
|
||||
|
||||
**Links stay repo-relative.** Write `../payload/klipper/extras/ff_tool.py` and
|
||||
`notes/40-offsets.md` exactly as you would for someone reading the page on
|
||||
GitHub; `hooks/mkdocs_readme.py` turns the ones that leave `docs/` into GitHub
|
||||
URLs at build time. Anchors use GitHub's slug rules, so `#runout--clog` for
|
||||
`## Runout / clog` works in both places. The same hook generates
|
||||
`docs/index.md` from `README.md` on every build — edit the README, never that
|
||||
file, which is gitignored.
|
||||
|
||||
**A dead link fails the build.** CI's `docs` job and RTD both build with
|
||||
`--strict`, so renaming a file in `payload/` breaks the build rather than
|
||||
leaving a doc pointing at nothing. It cannot check that the prose is still
|
||||
true; only a reader can.
|
||||
@@ -0,0 +1,4 @@
|
||||
# Docs toolchain. Pinned for the same reason the stock packages in CI are
|
||||
# pinned by sha: an unpinned build can change under us and start rendering
|
||||
# something other than what was reviewed.
|
||||
mkdocs-material==9.7.7
|
||||
@@ -0,0 +1,68 @@
|
||||
"""Two jobs, both about keeping one source of truth.
|
||||
|
||||
`on_pre_build` publishes README.md as the site's front page. The README is the
|
||||
project's front door on GitHub and has to stay that; a hand-copied
|
||||
docs/index.md would be a second front page, free to drift from the first, so
|
||||
the build generates index.md on every run and .gitignore keeps it out of the
|
||||
tree.
|
||||
|
||||
`on_page_markdown` rewrites the links that point out of docs/ -- at
|
||||
`../payload/klipper/extras/ff_tool.py` and friends -- into GitHub URLs. Those
|
||||
links are written relative because that is what works when the page is read in
|
||||
the repo, which is where a contributor reads it; on the site they would 404.
|
||||
Rewriting them here means nobody has to remember two link styles, and a link
|
||||
whose target does not exist at all is still left alone, so mkdocs --strict can
|
||||
fail the build over it.
|
||||
"""
|
||||
|
||||
import pathlib
|
||||
import re
|
||||
|
||||
REPO = "https://github.com/Klipper4FlashForge/firmware"
|
||||
BRANCH = "master"
|
||||
ROOT = pathlib.Path(__file__).resolve().parent.parent
|
||||
DOCS = ROOT / "docs"
|
||||
|
||||
# A markdown link target that is not absolute, an anchor, or a mail address.
|
||||
LINK = re.compile(r"\]\((?!https?:|#|mailto:)([^)\s]+)\)")
|
||||
|
||||
|
||||
def on_pre_build(config, **kwargs):
|
||||
text = (ROOT / "README.md").read_text(encoding="utf-8")
|
||||
# The README's paths are relative to the repo root, the site's to docs/:
|
||||
# resolve from the root first, so `gcode/` and `docs/notes/` become GitHub
|
||||
# URLs, then strip the prefix from what is left, which is the site itself.
|
||||
text = _rewrite_links(text, ROOT)
|
||||
text = re.sub(r"\]\(docs/", "](", text)
|
||||
text = re.sub(r'(src|href)="docs/', r"\1=\"", text)
|
||||
index = DOCS / "index.md"
|
||||
# Only write on a real change: `mkdocs serve` watches docs/, and touching
|
||||
# index.md every build would rebuild forever.
|
||||
if not index.exists() or index.read_text(encoding="utf-8") != text:
|
||||
index.write_text(text, encoding="utf-8")
|
||||
|
||||
|
||||
def on_page_markdown(markdown, page, **kwargs):
|
||||
return _rewrite_links(markdown, (DOCS / page.file.src_path).parent)
|
||||
|
||||
|
||||
def _rewrite_links(markdown, here):
|
||||
def rewrite(match):
|
||||
target = match.group(1)
|
||||
path, _, fragment = target.partition("#")
|
||||
if not path:
|
||||
return match.group(0)
|
||||
resolved = (here / path).resolve()
|
||||
try:
|
||||
relative = resolved.relative_to(ROOT)
|
||||
except ValueError:
|
||||
return match.group(0) # outside the repo; not ours to fix
|
||||
if not resolved.exists():
|
||||
return match.group(0) # broken: let --strict say so
|
||||
if resolved.is_file() and DOCS in resolved.parents:
|
||||
return match.group(0) # a page or an image; mkdocs has it
|
||||
kind = "tree" if resolved.is_dir() else "blob"
|
||||
suffix = f"#{fragment}" if fragment else ""
|
||||
return f"]({REPO}/{kind}/{BRANCH}/{relative}{suffix})"
|
||||
|
||||
return LINK.sub(rewrite, markdown)
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
# The docs are built from this repo, not a wiki, so a page and the code it
|
||||
# describes move in the same commit and the same review. CI builds this with
|
||||
# --strict (see .github/workflows/ci.yml): a cross-link that stops resolving
|
||||
# fails the build rather than rotting in place.
|
||||
site_name: Reforge
|
||||
site_description: >-
|
||||
Custom firmware for the FlashForge Creator 5 and Creator 5 Pro.
|
||||
site_url: https://reforge.readthedocs.io/
|
||||
repo_url: https://github.com/Klipper4FlashForge/firmware
|
||||
repo_name: Klipper4FlashForge/firmware
|
||||
edit_uri: edit/master/docs/
|
||||
|
||||
theme:
|
||||
name: material
|
||||
features:
|
||||
- navigation.sections # the public/contributor split, always visible
|
||||
- navigation.top
|
||||
- content.code.copy
|
||||
- search.suggest
|
||||
- toc.follow
|
||||
palette:
|
||||
- media: "(prefers-color-scheme: light)"
|
||||
scheme: default
|
||||
toggle:
|
||||
icon: material/weather-night
|
||||
name: Dark
|
||||
- media: "(prefers-color-scheme: dark)"
|
||||
scheme: slate
|
||||
toggle:
|
||||
icon: material/weather-sunny
|
||||
name: Light
|
||||
|
||||
markdown_extensions:
|
||||
- admonition
|
||||
- attr_list
|
||||
- md_in_html # the <img> pairs in README.md and toolchange.md
|
||||
- tables
|
||||
- toc:
|
||||
permalink: true
|
||||
# GitHub's heading slugs, so an anchor written for the repo view --
|
||||
# `#runout--clog` for `## Runout / clog` -- resolves on the site too.
|
||||
slugify: !!python/object/apply:pymdownx.slugs.slugify {kwds: {case: lower}}
|
||||
- pymdownx.details
|
||||
- pymdownx.superfences
|
||||
|
||||
# docs/index.md is generated from README.md at build time; see the hook.
|
||||
hooks:
|
||||
- hooks/mkdocs_readme.py
|
||||
|
||||
# Release notes are published on the Releases page, which is where the README
|
||||
# sends people. They are built but stay out of the nav rather than growing it
|
||||
# by one entry per tag.
|
||||
not_in_nav: |
|
||||
releases/*
|
||||
|
||||
# Two audiences, and a reader should never fall from one into the other. The
|
||||
# first section is for someone who owns the printer; the second is for someone
|
||||
# building or testing the firmware.
|
||||
nav:
|
||||
- Home: index.md
|
||||
|
||||
- Using Reforge:
|
||||
- After the flash: hardware-testing.md
|
||||
- Toolchanges: toolchange.md
|
||||
- Nozzle-offset calibration: calibration.md
|
||||
- How it works: how-it-works.md
|
||||
- Error codes: notes/70-error-codes.md
|
||||
|
||||
- For contributors:
|
||||
- Building packages: building.md
|
||||
- Testing: testing.md
|
||||
- The printer replica: printer-replica.md
|
||||
- Internals:
|
||||
- Overview: notes/00-overview.md
|
||||
- Hardware and MCU topology: notes/10-hardware.md
|
||||
- The FlashForge Klipper fork: notes/20-klipper-fork.md
|
||||
- firmwareExe vs Klipper: notes/25-app-vs-klipper-ownership.md
|
||||
- Toolchange sequences: notes/30-toolchange.md
|
||||
- Offsets and per-unit config: notes/40-offsets.md
|
||||
- Offset calibration, the port: notes/45-tool-offset-calibration.md
|
||||
- Offset calibration, recovered: notes/46-offset-calibration-recovered.md
|
||||
- Filament load, recovered: notes/47-filament-load-recovered.md
|
||||
- Filament load, the port: notes/48-filament-load-port.md
|
||||
- Runout and clog handling: notes/49-runout-recovered.md
|
||||
- Print lifecycle: notes/50-print-lifecycle.md
|
||||
- Nozzle clean, recovered: notes/50a-nozzle-clean-recovered.md
|
||||
- Nozzle clean, the port: notes/50b-nozzle-clean-port.md
|
||||
- Filament, calibration, persistent state: notes/60-background.md
|
||||
- Migrating to s6: notes/80-s6-migration.md
|
||||
Reference in new issue
Block a user