From e98940d3082def9241ef9d8f04e7fa3cec90d34f Mon Sep 17 00:00:00 2001 From: Nebiyu Tadesse Date: Mon, 2 Nov 2026 00:29:45 -0700 Subject: [PATCH 1/5] Portal: unversioned MkDocs site on the shared HardFOC theme, published by ru-mkdocs-publish --- .github/workflows/build-and-deploy.yml | 18 ------------- .github/workflows/docs.yml | 36 ++++++++++++++++++++++++++ .gitignore | 3 +++ docs/.nav.yml | 13 ++++++++++ docs/about.md | 14 ---------- docs/api/README.md | 3 --- docs/getting-started/README.md | 3 --- docs/hardware/README.md | 3 --- docs/index.md | 3 --- docs/porting/README.md | 3 --- mkdocs.yml | 35 ++++++++++++++++--------- 11 files changed, 75 insertions(+), 59 deletions(-) delete mode 100644 .github/workflows/build-and-deploy.yml create mode 100644 .github/workflows/docs.yml create mode 100644 .gitignore create mode 100644 docs/.nav.yml delete mode 100644 docs/about.md delete mode 100644 docs/api/README.md delete mode 100644 docs/getting-started/README.md delete mode 100644 docs/hardware/README.md delete mode 100644 docs/index.md delete mode 100644 docs/porting/README.md diff --git a/.github/workflows/build-and-deploy.yml b/.github/workflows/build-and-deploy.yml deleted file mode 100644 index 37a915d..0000000 --- a/.github/workflows/build-and-deploy.yml +++ /dev/null @@ -1,18 +0,0 @@ -name: Build & Deploy -on: [push] - -jobs: - site: - runs-on: ubuntu-latest - permissions: { contents: write } - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-python@v5 - with: { python-version: '3.12' } - - run: pip install mkdocs-material - - run: mkdocs build --strict # makes ./site/ - - uses: peaceiris/actions-gh-pages@v4 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - publish_dir: ./site - cname: docs.hardfoc.dev # writes CNAME file diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 0000000..ccfba00 --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,36 @@ +--- +# The portal, https://hardfoc.github.io/: an unversioned site on the shared HardFOC theme. Pull requests build, +# check (strict build, offline link check, Lighthouse, screenshots) and never publish; a push to main publishes +# the built site as the whole gh-pages branch. The repository catalogue is generated from the GitHub API at build +# time (tools/portal/catalogue.py), so every publish shows the current repositories and releases. +name: ๐Ÿ“š Portal + +on: + push: + branches: [main] + pull_request: + branches: [main] + schedule: + - cron: '17 5 * * *' # daily: new repositories, releases and Pages sites appear without a commit + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: portal-${{ github.ref }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + +jobs: + site: + # The branch of hardfoc/hf-general-ci-tools#42 (the versioning input) until it is released; then @v1. + uses: hardfoc/hf-general-ci-tools/.github/workflows/ru-mkdocs-publish.yml@docs/unversioned-sites + permissions: + contents: write # the publish job pushes gh-pages; pull requests never publish + with: + versioning: false + check_commands: | + python3 -m unittest discover -s tests -v + python3 tools/portal/catalogue.py + hardfoc-docs check + deploy: true diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2f34327 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +build/ +site/ +__pycache__/ diff --git a/docs/.nav.yml b/docs/.nav.yml new file mode 100644 index 0000000..010eac0 --- /dev/null +++ b/docs/.nav.yml @@ -0,0 +1,13 @@ +# Site navigation (mkdocs-awesome-nav): the tabs and their pages, in reading order. +nav: + - Home: index.md + - Get started: + - get-started/index.md + - Vortex V1 motor controller: get-started/vortex.md + - Flux V1 valve controller: get-started/flux.md + - A chip driver on your MCU: get-started/drivers.md + - Repositories: repositories.md + - Architecture: + - Layers: architecture.md + - Figures: diagrams/index.md + - Contribute: contribute.md diff --git a/docs/about.md b/docs/about.md deleted file mode 100644 index 18faee8..0000000 --- a/docs/about.md +++ /dev/null @@ -1,14 +0,0 @@ -# About This Documentation - -The `docs/` directory holds all Markdown files that become the published site. -Each sub-folder provides a page on the site, for example: - -``` -docs/ - getting-started/ - api/ - porting/ - hardware/ -``` - -`mkdocs.yml` at the repository root describes the navigation and theme. diff --git a/docs/api/README.md b/docs/api/README.md deleted file mode 100644 index 0d65839..0000000 --- a/docs/api/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# API Documentation - -Reference material for the project's API. diff --git a/docs/getting-started/README.md b/docs/getting-started/README.md deleted file mode 100644 index 7a5bf3e..0000000 --- a/docs/getting-started/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Getting Started - -Basic introduction and setup instructions will go here. diff --git a/docs/hardware/README.md b/docs/hardware/README.md deleted file mode 100644 index 09ba7f1..0000000 --- a/docs/hardware/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Hardware - -Notes and schematics for hardware-related topics. diff --git a/docs/index.md b/docs/index.md deleted file mode 100644 index 1dcb9ba..0000000 --- a/docs/index.md +++ /dev/null @@ -1,3 +0,0 @@ -# Welcome - -This is the starting page for the hardfoc documentation. diff --git a/docs/porting/README.md b/docs/porting/README.md deleted file mode 100644 index 79bfa5c..0000000 --- a/docs/porting/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Porting Guide - -Instructions for porting the project to new hardware or platforms. diff --git a/mkdocs.yml b/mkdocs.yml index 8b1761c..7297a5a 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -1,13 +1,24 @@ -site_name: hardfoc Documentation -nav: - - Home: index.md - - About: about.md - - Getting Started: getting-started/README.md - - API: api/README.md - - Porting: porting/README.md - - Hardware: hardware/README.md +# The HardFOC portal: the organisation's home page at https://hardfoc.github.io/, built with the shared theme from +# hardfoc/hf-general-ci-tools. Unversioned: one current site, published from main at the root of gh-pages. +# +# pip install "git+https://github.com/hardfoc/hf-general-ci-tools@v1#subdirectory=packages/hardfoc-mkdocs-theme" +# python3 tools/portal/catalogue.py the repository catalogue, from the GitHub API (build/portal/catalogue.md) +# mkdocs serve http://127.0.0.1:8000 +# mkdocs build --strict what CI runs +# hardfoc-docs figures rebuild the layer figure after editing docs/diagrams/figures/ +site_name: HardFOC +site_description: >- + HardFOC: open firmware for ESP32 motor and valve controller boards, with hardware-agnostic C++ chip drivers, a + shared core, board HALs and application firmware. Every repository, its documentation and how to start. +repo_url: https://github.com/hardfoc/hardfoc.github.io + theme: - name: material - features: - - navigation.tabs - - navigation.sections + name: hardfoc + +plugins: + - hardfoc + - search + - awesome-nav + +extra_javascript: + - assets/catalogue.js From a43ef3af9bbd3ab02f9b40b228af58bdfdf5b84b Mon Sep 17 00:00:00 2001 From: Nebiyu Tadesse Date: Mon, 2 Nov 2026 00:29:45 -0700 Subject: [PATCH 2/5] Portal: repository catalogue generated from the GitHub API and org-metadata.json at build time --- tests/test_catalogue.py | 44 ++++++++++ tools/portal/catalogue.py | 166 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 210 insertions(+) create mode 100644 tests/test_catalogue.py create mode 100644 tools/portal/catalogue.py diff --git a/tests/test_catalogue.py b/tests/test_catalogue.py new file mode 100644 index 0000000..de39cde --- /dev/null +++ b/tests/test_catalogue.py @@ -0,0 +1,44 @@ +"""The catalogue's grouping and cards, without the network.""" + +from __future__ import annotations + +import importlib.util +import pathlib +import unittest + +spec = importlib.util.spec_from_file_location( + "catalogue", pathlib.Path(__file__).resolve().parents[1] / "tools/portal/catalogue.py") +catalogue = importlib.util.module_from_spec(spec) +spec.loader.exec_module(catalogue) + + +def repo(name, topics, docs="", release=None, description="A repository."): + return {"name": name, "html_url": f"https://github.com/hardfoc/{name}", "description": description, + "topics": ["hardfoc", *topics], "docs": docs, "release": release} + + +class CatalogueTest(unittest.TestCase): + def test_groups(self): + self.assertEqual(catalogue.group_of(["hardfoc", "library", "driver"]), "library") + self.assertEqual(catalogue.group_of(["legacy", "driver"]), "legacy") + self.assertEqual(catalogue.group_of(["hardfoc", "community-health"]), "other") + + def test_render(self): + out = catalogue.render([ + repo("hf-x-driver", ["driver", "spi"], docs="https://hardfoc.github.io/hf-x-driver/", + release=("v1.0.0", "https://github.com/hardfoc/hf-x-driver/releases/tag/v1.0.0")), + repo("hf-old", ["legacy"], description='Old <"quoted">'), + repo("hardfoc-vortex-v1", ["firmware"]), + ]) + self.assertLess(out.index("## Products"), out.index("## Chip drivers")) + self.assertLess(out.index("## Chip drivers"), out.index("## Legacy")) + self.assertNotIn("## Board HALs", out) # empty groups are left out + self.assertIn('data-topic="spi"', out) + self.assertNotIn('data-topic="hardfoc"', out) # on every repository, so not a filter + self.assertIn(">Release v1.0.0", out) + self.assertIn("Old <"quoted">", out) + self.assertIn('data-total="3"', out) + + +if __name__ == "__main__": + unittest.main() diff --git a/tools/portal/catalogue.py b/tools/portal/catalogue.py new file mode 100644 index 0000000..eceaefa --- /dev/null +++ b/tools/portal/catalogue.py @@ -0,0 +1,166 @@ +#!/usr/bin/env python3 +"""Write the repository catalogue of the portal from the GitHub API, at build time. + + python3 tools/portal/catalogue.py [--out build/portal/catalogue.md] [--metadata FILE] + +Sources, in this order of authority: + +* ``org/org-metadata.json`` in ``hardfoc/.github``: the organisation's own record of each repository's description + and topics (fetched from the API, or ``--metadata`` for a local copy); +* the GitHub API: which repositories are public, whether they publish GitHub Pages, their latest release, and the + description and topics of a public repository the metadata does not list yet. + +Only public repositories are listed. The output is Markdown with HTML cards, included by ``docs/repositories.md`` +(``--8<-- "build/portal/catalogue.md"``); it is a build product and is not committed. ``GH_TOKEN`` (or +``GITHUB_TOKEN``) raises the API rate limit; ru-mkdocs-publish sets it for its check commands. +""" + +from __future__ import annotations + +import argparse +import base64 +import html +import json +import os +import pathlib +import sys +import urllib.error +import urllib.request + +ORG = "hardfoc" +API = "https://api.github.com" +ROOT = pathlib.Path(__file__).resolve().parents[2] + +# Groups in reading order: (title, one-line purpose, role topic). A repository goes to the group of the first topic +# of ORDER it carries ("legacy" wins over everything), or to the last group. +GROUPS = [ + ("Products", "Application firmware, one repository per board.", "firmware"), + ("Core libraries", "The shared platform every board builds on.", "library"), + ("Board HALs", "One object per board, one manager per board function.", "hal"), + ("Chip drivers", "Hardware-agnostic C++ drivers: no platform code, a small bus adapter per MCU.", "driver"), + ("CI and tools", "Reusable workflows, project tooling and the documentation platform.", "ci-tools"), + ("Documentation and organisation", "The portal, the handbook, diagrams and the organisation profile.", None), + ("Legacy", "Superseded repositories, kept for history.", "legacy"), +] +ORDER = ["legacy", "firmware", "library", "hal", "driver", "ci-tools"] +HIDDEN_TOPICS = {"hardfoc"} # on every repository + + +def api(path: str): + req = urllib.request.Request(API + path, headers={"Accept": "application/vnd.github+json", + "X-GitHub-Api-Version": "2022-11-28", + "User-Agent": "hardfoc-portal"}) + token = os.environ.get("GH_TOKEN") or os.environ.get("GITHUB_TOKEN") + if token: + req.add_header("Authorization", f"Bearer {token}") + try: + with urllib.request.urlopen(req, timeout=30) as r: + return json.load(r) + except urllib.error.HTTPError as e: + if e.code == 404: + return None + raise SystemExit(f"catalogue: GET {path}: HTTP {e.code} {e.reason}") from e + + +def metadata(path: str | None) -> dict[str, dict]: + if path: + doc = json.loads(pathlib.Path(path).read_text()) + else: + blob = api(f"/repos/{ORG}/.github/contents/org/org-metadata.json") + if not blob: + raise SystemExit("catalogue: org/org-metadata.json not found in hardfoc/.github") + doc = json.loads(base64.b64decode(blob["content"])) + return {r["repo"]: r for r in doc["repos"]} + + +def public_repos() -> list[dict]: + out, page = [], 1 + while True: + batch = api(f"/orgs/{ORG}/repos?type=public&per_page=100&page={page}") or [] + out += batch + if len(batch) < 100: + return out + page += 1 + + +def group_of(topics: list[str]) -> str: + for role in ORDER: + if role in topics: + return role + return "other" + + +def card(r: dict) -> str: + name = r["name"] + topics = [t for t in r["topics"] if t not in HIDDEN_TOPICS] + search = " ".join([name, r["description"], *topics]).lower() + links = [f'Repository'] + if r["docs"]: + links.append(f'Documentation') + if r["release"]: + tag, url = r["release"] + links.append(f'Release {html.escape(tag)}') + chips = "".join(f'' + for t in topics) + return ( + f'
\n' + f'

{html.escape(name)}

\n' + f'

{html.escape(r["description"])}

\n' + f'

{chips}

\n' + f'\n' + "
" + ) + + +def render(repos: list[dict]) -> str: + by_group: dict[str, list[dict]] = {} + for r in repos: + by_group.setdefault(group_of(r["topics"]), []).append(r) + out = [f'

{len(repos)} public repositories.

', ""] + for title, purpose, role in GROUPS: + items = sorted(by_group.get(role or "other", []), key=lambda r: r["name"].lower()) + if not items: + continue + out += [f'
', "", f"## {title}", "", purpose, "", + '
'] + out += [card(r) for r in items] + out += ["
", "", "
", ""] + return "\n".join(out) + + +def collect(meta: dict[str, dict]) -> list[dict]: + repos = [] + for gh in public_repos(): + if gh.get("archived") and "legacy" not in gh.get("topics", []): + continue + name = gh["name"] + m = meta.get(name, {}) + rel = api(f"/repos/{ORG}/{name}/releases/latest") + repos.append({ + "name": name, + "html_url": gh["html_url"], + "description": (m.get("description") or gh.get("description") or "").strip(), + "topics": m.get("topics") or gh.get("topics") or [], + "docs": f"https://{ORG}.github.io/{'' if name == f'{ORG}.github.io' else name + '/'}" if gh.get("has_pages") else "", + "release": (rel["tag_name"], rel["html_url"]) if rel else None, + }) + return repos + + +def main(argv=None) -> int: + ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) + ap.add_argument("--out", type=pathlib.Path, default=ROOT / "build/portal/catalogue.md") + ap.add_argument("--metadata", help="a local org-metadata.json instead of the one in hardfoc/.github") + args = ap.parse_args(argv) + repos = collect(metadata(args.metadata)) + if not repos: + raise SystemExit("catalogue: the API returned no public repositories") + args.out.parent.mkdir(parents=True, exist_ok=True) + args.out.write_text(render(repos) + "\n") + print(f"catalogue: {len(repos)} repositories -> {args.out.relative_to(ROOT) if args.out.is_relative_to(ROOT) else args.out}") + return 0 + + +if __name__ == "__main__": + sys.exit(main()) From 8c0f8fb3ca9cf4c7cbe17429a3c32419d0c7e0c9 Mon Sep 17 00:00:00 2001 From: Nebiyu Tadesse Date: Mon, 2 Nov 2026 00:29:45 -0700 Subject: [PATCH 3/5] Portal: home, layer figure, getting-started paths per board, catalogue page, architecture, contribute --- docs/architecture.md | 54 ++++++++++++++++++ docs/assets/catalogue.js | 49 ++++++++++++++++ docs/contribute.md | 35 ++++++++++++ docs/diagrams/figures.json | 13 +++++ docs/diagrams/figures/hardfoc-layers.py | 52 +++++++++++++++++ docs/diagrams/hardfoc-layers.dark.svg | 56 ++++++++++++++++++ docs/diagrams/hardfoc-layers.svg | 56 ++++++++++++++++++ docs/diagrams/index.md | 25 +++++++++ docs/get-started/drivers.md | 29 ++++++++++ docs/get-started/flux.md | 48 ++++++++++++++++ docs/get-started/index.md | 21 +++++++ docs/get-started/vortex.md | 53 +++++++++++++++++ docs/index.md | 75 +++++++++++++++++++++++++ docs/repositories.md | 18 ++++++ 14 files changed, 584 insertions(+) create mode 100644 docs/architecture.md create mode 100644 docs/assets/catalogue.js create mode 100644 docs/contribute.md create mode 100644 docs/diagrams/figures.json create mode 100644 docs/diagrams/figures/hardfoc-layers.py create mode 100644 docs/diagrams/hardfoc-layers.dark.svg create mode 100644 docs/diagrams/hardfoc-layers.svg create mode 100644 docs/diagrams/index.md create mode 100644 docs/get-started/drivers.md create mode 100644 docs/get-started/flux.md create mode 100644 docs/get-started/index.md create mode 100644 docs/get-started/vortex.md create mode 100644 docs/index.md create mode 100644 docs/repositories.md diff --git a/docs/architecture.md b/docs/architecture.md new file mode 100644 index 0000000..f73d10e --- /dev/null +++ b/docs/architecture.md @@ -0,0 +1,54 @@ +# Architecture + +How HardFOC firmware is split into repositories, and how the layers are pinned together. + + + + + Layer diagram, top to bottom: products (hardfoc-vortex-v1, hardfoc-flux-v1) build on board HALs (hf-
+    hal-vortex-v1, hf-hal-flux-v1), which build on hf-core (hf-core-drivers and hf-core-utils), which
+    gathers the hardware-agnostic chip drivers (hf-*-driver) and the peripheral interface wrap. + + + +## Layers + +| Layer | Repositories | Depends on | Platform code | +| --- | --- | --- | --- | +| Products | `hardfoc-vortex-v1`, `hardfoc-flux-v1` | One board HAL | ESP-IDF application, tasks, CANopen (Vortex) | +| Board HALs | `hf-hal-vortex-v1`, `hf-hal-flux-v1` | `hf-core` | One object per board, one manager per board function | +| Core | `hf-core`, gathering `hf-core-drivers` and `hf-core-utils` | Drivers, interface wrap, utilities | Device handlers, RTOS wrappers, CANopen helpers | +| Interface wrap | `hf-internal-interface-wrap`, `hf-pincfg` | ESP-IDF | Peripheral base classes (GPIO, ADC, I2C, SPI, UART, CAN, PWM) and their ESP-IDF implementations; per-board pin maps | +| Utilities | `hf-utils-general`, `hf-utils-rtos-wrap`, `hf-utils-canopen` | None, or FreeRTOS | Header-only helpers, FreeRTOS wrappers, CANopen frame builders | +| Chip drivers | `hf-*-driver` | Nothing | None: each driver is a template over a small communication interface | + +The [repository catalogue](repositories.md) lists every repository in each layer, with its documentation. + +## Submodules, not packages + +Each arrow in the figure is a Git submodule pinned to a commit or tag: + +```mermaid +flowchart LR + drv["Chip drivers
hf-*-driver"] --> cd["hf-core-drivers"] + iw["hf-internal-interface-wrap
hf-pincfg"] --> cd + ut["hf-utils-general
hf-utils-rtos-wrap
hf-utils-canopen"] --> cu["hf-core-utils"] + cd --> core["hf-core"] + cu --> core + core --> hal["Board HALs"] + hal --> prod["Products"] +``` + +A product therefore builds exactly the commits it pins, all the way down. Moving a layer forward is a pull request +that updates one submodule pointer and passes that repository's CI. + +## Shared tooling + +| Repository | Provides | +| --- | --- | +| [`hf-general-ci-tools`](https://hardfoc.github.io/hf-general-ci-tools/latest/) | Reusable workflows (lint, analysis, host builds, the CI gate, releases) and the documentation platform this site is built with | +| [`hf-espidf-ci-tools`](https://github.com/hardfoc/hf-espidf-ci-tools) | ESP-IDF builds of examples and firmware, releases with artifacts | +| [`hf-espidf-project-tools`](https://github.com/hardfoc/hf-espidf-project-tools) | Build and flash scripts for ESP-IDF example projects | + +Next: [Get started](get-started/index.md). diff --git a/docs/assets/catalogue.js b/docs/assets/catalogue.js new file mode 100644 index 0000000..00eab94 --- /dev/null +++ b/docs/assets/catalogue.js @@ -0,0 +1,49 @@ +// Repository catalogue filter: the search field and the topic chips hide the cards (and empty groups) that do not +// match. Works with Material's instant navigation (document$), and leaves every card visible without JavaScript. +(function () { + function setup() { + var input = document.getElementById("hf-repo-filter"); + if (!input || input.dataset.ready) return; + input.dataset.ready = "1"; + var cards = Array.prototype.slice.call(document.querySelectorAll(".hf-repo")); + var groups = Array.prototype.slice.call(document.querySelectorAll(".hf-catalogue__group")); + var count = document.querySelector(".hf-catalogue__count"); + var total = count ? count.getAttribute("data-total") : String(cards.length); + var topic = ""; + + function apply() { + var words = input.value.toLowerCase().split(/\s+/).filter(Boolean); + var shown = 0; + cards.forEach(function (c) { + var hay = c.getAttribute("data-search"); + var topics = " " + c.getAttribute("data-topics") + " "; + var ok = words.every(function (w) { return hay.indexOf(w) !== -1; }) && + (!topic || topics.indexOf(" " + topic + " ") !== -1); + c.hidden = !ok; + if (ok) shown++; + }); + groups.forEach(function (g) { g.hidden = !g.querySelector(".hf-repo:not([hidden])"); }); + document.querySelectorAll(".hf-chipbtn").forEach(function (b) { + b.setAttribute("aria-pressed", b.getAttribute("data-topic") === topic ? "true" : "false"); + }); + if (count) { + count.textContent = shown === Number(total) ? total + " public repositories." : + shown + " of " + total + " repositories" + (topic ? " with topic " + topic : "") + "."; + } + } + + input.addEventListener("input", apply); + document.addEventListener("click", function (e) { + var b = e.target.closest && e.target.closest(".hf-chipbtn"); + if (!b) return; + var t = b.getAttribute("data-topic"); + topic = topic === t ? "" : t; + apply(); + }); + var q = new URLSearchParams(location.search); + if (q.get("q")) input.value = q.get("q"); + if (q.get("topic")) topic = q.get("topic"); + apply(); + } + if (window.document$) window.document$.subscribe(setup); else document.addEventListener("DOMContentLoaded", setup); +})(); diff --git a/docs/contribute.md b/docs/contribute.md new file mode 100644 index 0000000..5dc9aa0 --- /dev/null +++ b/docs/contribute.md @@ -0,0 +1,35 @@ +# Contribute + +How to report a problem, propose a change and report a vulnerability, for every HardFOC repository. + +## Report a problem or ask a question + +Open an issue in the repository closest to the problem: the driver for a chip, the HAL for a board, the product +for the application. If you cannot tell, open it in the product and say what you saw. + +## Propose a change + +| Step | Where | +| --- | --- | +| Read the rules for branches, commits, pull requests and the CI gate | [CONTRIBUTING.md](https://github.com/hardfoc/.github/blob/main/CONTRIBUTING.md) | +| Follow the code of conduct | [CODE_OF_CONDUCT.md](https://github.com/hardfoc/.github/blob/main/CODE_OF_CONDUCT.md) | +| Write documentation to the shared standard | [Documentation style guide](https://hardfoc.github.io/hf-general-ci-tools/latest/DOCS-STYLE-GUIDE/) | +| Add CI to a repository | [CI quickstart](https://hardfoc.github.io/hf-general-ci-tools/latest/quickstart/) | + +A repository with its own `CONTRIBUTING.md` follows that file instead. + +## Report a vulnerability + +!!! danger "Never in public" + Do not open a public issue, pull request or discussion for a suspected vulnerability. + +Report it privately with a GitHub security advisory: in the affected repository, **Security** > **Advisories** > +**Report a vulnerability**. The [security policy](https://github.com/hardfoc/.github/blob/main/SECURITY.md) says +what to include and what is in scope. + +## This site + +The portal is [`hardfoc/hardfoc.github.io`](https://github.com/hardfoc/hardfoc.github.io). The repository list is +built from the GitHub API and the organisation's repository record each time the site is published; to change a +description or topics, change +[`org/org-metadata.json`](https://github.com/hardfoc/.github/blob/main/org/org-metadata.json) in `hardfoc/.github`. diff --git a/docs/diagrams/figures.json b/docs/diagrams/figures.json new file mode 100644 index 0000000..63e66c4 --- /dev/null +++ b/docs/diagrams/figures.json @@ -0,0 +1,13 @@ +{ + "hardfoc-layers": { + "title": "Four layers, each a set of repositories", + "alt": "Layer diagram, top to bottom: products (hardfoc-vortex-v1, hardfoc-flux-v1) build on board HALs (hf-hal-vortex-v1, hf-hal-flux-v1), which build on hf-core (hf-core-drivers and hf-core-utils), which gathers the hardware-agnostic chip drivers (hf-*-driver) and the peripheral interface wrap.", + "description": "From bottom to top, each layer builds on the one below it as a Git submodule pinned to a commit or tag. Chip drivers are hardware-agnostic C++ libraries with no platform code; each one needs only a small bus adapter. hf-core gathers them with the peripheral interface wrap, the pin configuration and the utility libraries (RTOS wrappers, general helpers, CANopen). A board HAL builds one object per board from hf-core, with one manager per board function. A product is the application firmware for one board, on top of its HAL.", + "profile": "overview", + "width": 1200, + "source": "docs/diagrams/figures/hardfoc-layers.py", + "sources": [ + "mkdocs.yml" + ] + } +} diff --git a/docs/diagrams/figures/hardfoc-layers.py b/docs/diagrams/figures/hardfoc-layers.py new file mode 100644 index 0000000..8262512 --- /dev/null +++ b/docs/diagrams/figures/hardfoc-layers.py @@ -0,0 +1,52 @@ +"""The four layers of HardFOC firmware: chip drivers, core, board HALs, products. + +Reads: From bottom to top, each layer builds on the one below it as a Git submodule pinned to a commit or tag. +Chip drivers are hardware-agnostic C++ libraries with no platform code; each one needs only a small bus adapter. +hf-core gathers them with the peripheral interface wrap, the pin configuration and the utility libraries +(RTOS wrappers, general helpers, CANopen). A board HAL builds one object per board from hf-core, with one manager +per board function. A product is the application firmware for one board, on top of its HAL. +""" + +from hardfoc_mkdocs_theme.figkit import Fig + +SOURCES = ["mkdocs.yml"] + + +def build(_data): + f = Fig( + "hardfoc-layers", + "Four layers, each a set of repositories", + alt="Layer diagram, top to bottom: products (hardfoc-vortex-v1, hardfoc-flux-v1) build on board HALs " + "(hf-hal-vortex-v1, hf-hal-flux-v1), which build on hf-core (hf-core-drivers and hf-core-utils), which " + "gathers the hardware-agnostic chip drivers (hf-*-driver) and the peripheral interface wrap.", + width=1200, + kicker="Architecture", + subtitle="Each layer is a Git submodule of the one above it, pinned to a commit or tag.", + profile="overview", + legend=[("teal", "application firmware"), ("brand", "board and platform"), ("slate", "portable libraries")], + ) + W = f.body_w + gap = 24 + half = (W - gap) / 2 + third = (W - 2 * gap) / 3 + vortex = f.card(0, 0, half, "hardfoc-vortex-v1", ["Vortex V1 motor controller firmware"], tone="teal", accent=True) + flux = f.card(half + gap, 0, half, "hardfoc-flux-v1", ["Flux V1 valve controller firmware"], tone="teal", + accent=True, h=vortex.h) + y = vortex.b + 48 + hv = f.card(0, y, half, "hf-hal-vortex-v1", ["ESP32-C6, TMC9660, AS5047U"], tone="brand") + hfx = f.card(half + gap, y, half, "hf-hal-flux-v1", ["ESP32-S3, MAX22200, TLE92466ED"], tone="brand", h=hv.h) + y = hv.b + 48 + core = f.card(0, y, W, "hf-core", ["hf-core-drivers + hf-core-utils: device handlers, RTOS, CANopen"], + tone="brand", accent=True) + y = core.b + 48 + drv = f.card(0, y, third, "Chip drivers", ["hf-*-driver, no platform code"], tone="slate") + wrap = f.card(third + gap, y, third, "Interface wrap", ["hf-internal-interface-wrap, hf-pincfg"], tone="slate") + utils = f.card(2 * (third + gap), y, third, "Utilities", ["hf-utils-general, hf-utils-rtos-wrap, hf-utils-canopen"], tone="slate") + for a, b in ((vortex, hv), (flux, hfx)): + f.connect(a, b, "bottom", "top", tone="rail") + # Straight arrows: each one leaves and enters at the same x. + for hal in (hv, hfx): + f.connect(hal, core, "bottom", "top", bt=(hal.cx - core.x) / core.w, tone="rail") + for b in (drv, wrap, utils): + f.connect(core, b, "bottom", "top", at=(b.cx - core.x) / core.w, tone="rail") + return f diff --git a/docs/diagrams/hardfoc-layers.dark.svg b/docs/diagrams/hardfoc-layers.dark.svg new file mode 100644 index 0000000..b537995 --- /dev/null +++ b/docs/diagrams/hardfoc-layers.dark.svg @@ -0,0 +1,56 @@ + +Four layers, each a set of repositories +Layer diagram, top to bottom: products (hardfoc-vortex-v1, hardfoc-flux-v1) build on board HALs (hf-hal-vortex-v1, hf-hal-flux-v1), which build on hf-core (hf-core-drivers and hf-core-utils), which gathers the hardware-agnostic chip drivers (hf-*-driver) and the peripheral interface wrap. + + +ARCHITECTURE +Four layers, each a set of repositories +Each layer is a Git submodule of the one above it, pinned to a commit or tag. + + + + + + + + + + + + + + + + + + + + + + + + +hardfoc-vortex-v1 +Vortex V1 motor controller firmware +hardfoc-flux-v1 +Flux V1 valve controller firmware +hf-hal-vortex-v1 +ESP32-C6, TMC9660, AS5047U +hf-hal-flux-v1 +ESP32-S3, MAX22200, TLE92466ED +hf-core +hf-core-drivers + hf-core-utils: device handlers, RTOS, CANopen +Chip drivers +hf-*-driver, no platform code +Interface wrap +hf-internal-interface-wrap, hf-pincfg +Utilities +hf-utils-general, hf-utils-rtos-wrap, +hf-utils-canopen + +application firmware + +board and platform + +portable libraries + diff --git a/docs/diagrams/hardfoc-layers.svg b/docs/diagrams/hardfoc-layers.svg new file mode 100644 index 0000000..a196c0e --- /dev/null +++ b/docs/diagrams/hardfoc-layers.svg @@ -0,0 +1,56 @@ + +Four layers, each a set of repositories +Layer diagram, top to bottom: products (hardfoc-vortex-v1, hardfoc-flux-v1) build on board HALs (hf-hal-vortex-v1, hf-hal-flux-v1), which build on hf-core (hf-core-drivers and hf-core-utils), which gathers the hardware-agnostic chip drivers (hf-*-driver) and the peripheral interface wrap. + + +ARCHITECTURE +Four layers, each a set of repositories +Each layer is a Git submodule of the one above it, pinned to a commit or tag. + + + + + + + + + + + + + + + + + + + + + + + + +hardfoc-vortex-v1 +Vortex V1 motor controller firmware +hardfoc-flux-v1 +Flux V1 valve controller firmware +hf-hal-vortex-v1 +ESP32-C6, TMC9660, AS5047U +hf-hal-flux-v1 +ESP32-S3, MAX22200, TLE92466ED +hf-core +hf-core-drivers + hf-core-utils: device handlers, RTOS, CANopen +Chip drivers +hf-*-driver, no platform code +Interface wrap +hf-internal-interface-wrap, hf-pincfg +Utilities +hf-utils-general, hf-utils-rtos-wrap, +hf-utils-canopen + +application firmware + +board and platform + +portable libraries + diff --git a/docs/diagrams/index.md b/docs/diagrams/index.md new file mode 100644 index 0000000..846798d --- /dev/null +++ b/docs/diagrams/index.md @@ -0,0 +1,25 @@ +# Figures + + + +Every figure on this site is drawn by a Python module from facts extracted from the code, so it is rebuilt, +not redrawn, when the code changes. CI runs `hardfoc-docs figures --check` and fails when a +committed figure no longer matches the code. Each figure below lists the files it is drawn from. + +## Four layers, each a set of repositories + + + + + Layer diagram, top to bottom: products (hardfoc-vortex-v1, hardfoc-flux-v1) build on board HALs (hf-
+    hal-vortex-v1, hf-hal-flux-v1), which build on hf-core (hf-core-drivers and hf-core-utils), which
+    gathers the hardware-agnostic chip drivers (hf-*-driver) and the peripheral interface wrap. + + + +Figure module: [`docs/diagrams/figures/hardfoc-layers.py`](../../docs/diagrams/figures/hardfoc-layers.py) + +Drawn from: + +- [`mkdocs.yml`](../../mkdocs.yml) diff --git a/docs/get-started/drivers.md b/docs/get-started/drivers.md new file mode 100644 index 0000000..08d5c70 --- /dev/null +++ b/docs/get-started/drivers.md @@ -0,0 +1,29 @@ +# A chip driver on your MCU + +Use one HardFOC chip driver in your own firmware, on any MCU, without the rest of HardFOC. + +Every `hf-*-driver` repository is a C++ library with no platform code. The driver is a class template over a small +communication interface: you write one class that moves bytes over the bus (SPI, I2C or UART) and toggles the +chip's control pins, and the driver does the rest. Each one ships ESP32 examples that build in CI. + +## Path + +
+ +1. **Find the chip** in the [catalogue](../repositories.md?topic=driver) (filter by bus: `spi`, `i2c`, `uart`). +2. **Read its quickstart**: wiring, the port class and a short example, compiled in CI. For example, the + [TMC9660 quickstart](https://hardfoc.github.io/hf-tmc9660-driver/latest/quickstart/). +3. **Add it as a submodule** and link its CMake target, or add it as an ESP-IDF component. +4. **Write the port** for your MCU, following the driver's porting page, and run the example. + +
+ +## What every driver has + +| Page | What it covers | +| --- | --- | +| Quickstart | Wiring, the port, a short example, "if it does not work" | +| Hardware | Pins, bus settings and timing, power, errata | +| Porting | The interface methods and their contracts | +| API guide and reference | Task-oriented guide, and Doxygen under `api/` | +| Examples | The ESP32 examples and what each one shows | diff --git a/docs/get-started/flux.md b/docs/get-started/flux.md new file mode 100644 index 0000000..befda20 --- /dev/null +++ b/docs/get-started/flux.md @@ -0,0 +1,48 @@ +# Flux V1 valve controller + +From a new Flux V1 board to its examples running, and where to read next. + +
+
+Valve controller board +Flux V1 +HardFOC +
+ +MCU +: ESP32-S3 + +Solenoid drivers +: TLE92466ED (six low-side channels), MAX22200 (eight channels) + +Valves +: On/off and proportional solenoid channels + +Firmware +: ESP-IDF 5.5, FreeRTOS, C++ +
+ +## Path + +
+ +1. **Bring the board up with the HAL.** The [HAL quickstart](https://hardfoc.github.io/hf-hal-flux-v1/latest/quickstart/) + installs ESP-IDF, builds an example and reads its log. +2. **Check the hardware.** The [pin map](https://hardfoc.github.io/hf-hal-flux-v1/latest/hardware/pin-map/) and the + [bring-up guide](https://hardfoc.github.io/hf-hal-flux-v1/latest/bring-up/). +3. **Drive the valves.** The [ValveManager](https://hardfoc.github.io/hf-hal-flux-v1/latest/managers/valves/) page + and the examples. + +
+ +The product firmware, `hardfoc-flux-v1`, is a private repository. Its documentation is built in CI as an artifact +for people with access, and is not published here. + +## Documentation + +| Site | What it covers | +| --- | --- | +| [hf-hal-flux-v1](https://hardfoc.github.io/hf-hal-flux-v1/latest/) | The board: hardware, pin map, managers (valves, ADC, GPIO, LEDs, temperature, NVS, comms), examples | +| [hf-tle92466ed-driver](https://hardfoc.github.io/hf-tle92466ed-driver/latest/) | The six-channel solenoid driver | +| [hf-max22200-driver](https://hardfoc.github.io/hf-max22200-driver/latest/) | The eight-channel solenoid driver | +| [hf-core](https://hardfoc.github.io/hf-core/latest/) | The shared core the HAL is built on | diff --git a/docs/get-started/index.md b/docs/get-started/index.md new file mode 100644 index 0000000..105e97d --- /dev/null +++ b/docs/get-started/index.md @@ -0,0 +1,21 @@ +# Get started + +Pick the path that matches what you have on the bench: a HardFOC board, or a chip and your own MCU. + +| You have | Start with | Then | +| --- | --- | --- | +| A Vortex V1 motor controller | [Vortex V1](vortex.md): flash a HAL example, then the product firmware | CANopen, configuration, the HAL managers | +| A Flux V1 valve controller | [Flux V1](flux.md): flash a HAL example | The valve and ADC managers | +| A chip from the driver list and any MCU | [A chip driver on your MCU](drivers.md) | The driver's own quickstart and porting guide | + +Every path needs the same tools: + +| Tool | Version | Notes | +| --- | --- | --- | +| ESP-IDF | 5.5 | The boards' firmware and the ESP32 examples; each repository says which release it pins | +| Git | with submodules | Every layer is a submodule of the one above it | +| A C++ compiler | C++17 or C++20, as the repository says | For a driver on another MCU, your own toolchain | +| Python 3 | 3.10 or newer | Only to build documentation locally | + +!!! warning "Power" + Bring a new board up on a current-limited bench supply, with the motor or valves unloaded. diff --git a/docs/get-started/vortex.md b/docs/get-started/vortex.md new file mode 100644 index 0000000..3b7aacd --- /dev/null +++ b/docs/get-started/vortex.md @@ -0,0 +1,53 @@ +# Vortex V1 motor controller + +From a new Vortex V1 board to the product firmware running, and where to read next. + +
+
+Motor controller board +Vortex V1 +HardFOC +
+ +MCU +: ESP32-C6 + +Motor control +: TMC9660 smart gate driver, FOC on chip + +Feedback +: AS5047U magnetic encoder + +Field bus +: CAN, CANopen node in the product firmware + +Firmware +: ESP-IDF 5.5, FreeRTOS, C++ +
+ +## Path + +
+ +1. **Bring the board up with the HAL.** The HAL's quickstart installs ESP-IDF, builds an example and reads its + log, one step at a time: [HAL quickstart](https://hardfoc.github.io/hf-hal-vortex-v1/latest/quickstart/). +2. **Check the hardware.** The [pin map](https://hardfoc.github.io/hf-hal-vortex-v1/latest/hardware/pin-map/) and + the [bring-up guide](https://hardfoc.github.io/hf-hal-vortex-v1/latest/bring-up/) list every signal and what to + check first. +3. **Build and flash the product firmware.** The + [getting-started page](https://hardfoc.github.io/hardfoc-vortex-v1/latest/getting-started/) of + `hardfoc-vortex-v1` builds with the pinned ESP-IDF and explains the boot log. +4. **Talk to it over CAN.** [CANopen](https://hardfoc.github.io/hardfoc-vortex-v1/latest/canopen/) and the + [object dictionary](https://hardfoc.github.io/hardfoc-vortex-v1/latest/od-reference/). + +
+ +## Documentation + +| Site | What it covers | +| --- | --- | +| [hardfoc-vortex-v1](https://hardfoc.github.io/hardfoc-vortex-v1/latest/) | The product: architecture, threads, safety, boot and updates, CANopen, configuration, design notes | +| [hf-hal-vortex-v1](https://hardfoc.github.io/hf-hal-vortex-v1/latest/) | The board: hardware, pin map, managers (motor, encoder, IMU, ADC, LEDs, temperature, NVS, comms), examples | +| [hf-tmc9660-driver](https://hardfoc.github.io/hf-tmc9660-driver/latest/) | The motor controller chip: bootloader, FOC, telemetry | +| [hf-as5047u-driver](https://hardfoc.github.io/hf-as5047u-driver/latest/) | The encoder | +| [hf-core](https://hardfoc.github.io/hf-core/latest/) | The shared core the HAL is built on | diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..4bb435f --- /dev/null +++ b/docs/index.md @@ -0,0 +1,75 @@ +--- +hide: + - navigation + - toc +--- + +
+Hardware-based field-oriented control + +# HardFOC + +

Open firmware for ESP32 motor and valve controller boards. Motor commutation runs on dedicated FOC driver chips, +such as the TMC9660, instead of in MCU software; the firmware around them is C++ on ESP-IDF and FreeRTOS, split +into portable chip drivers, a shared core, board HALs and one application per board.

+ +[Get started](get-started/index.md){ .md-button .md-button--primary } +[All repositories](repositories.md){ .md-button } +[HardFOC on GitHub](https://github.com/hardfoc){ .md-button } +
+ +## Start with a board, or a chip + +
+ +- **[Vortex V1 motor controller](get-started/vortex.md)** + + ESP32-C6 with a TMC9660 smart gate driver running FOC on chip, an AS5047U magnetic encoder and CAN + (CANopen). + +- **[Flux V1 valve controller](get-started/flux.md)** + + ESP32-S3 with on/off and proportional solenoid channels on a TLE92466ED and a MAX22200. + +- **[A chip driver on your own MCU](get-started/drivers.md)** + + Every `hf-*-driver` is hardware-agnostic C++: no platform code, one small bus adapter, ESP32 examples. + +
+ +## How the code is layered + + + + + Layer diagram, top to bottom: products (hardfoc-vortex-v1, hardfoc-flux-v1) build on board HALs (hf-
+    hal-vortex-v1, hf-hal-flux-v1), which build on hf-core (hf-core-drivers and hf-core-utils), which
+    gathers the hardware-agnostic chip drivers (hf-*-driver) and the peripheral interface wrap. + + + +Each box is one or more repositories; each arrow is a Git submodule pinned to a commit or tag. The +[architecture page](architecture.md) shows the repositories in each layer. + +## Documentation + +Every repository with a documentation site publishes it at `https://hardfoc.github.io//`, versioned: +`latest/` is the newest release (or the default branch until the first release), and older versions stay +available. The [repository catalogue](repositories.md) links each one, with its topics and latest release. + +
+ +- **[Repositories](repositories.md)** + + Every public repository, searchable by name, description and topic. + +- **[Architecture](architecture.md)** + + Drivers, core, HALs and products, and how they are pinned together. + +- **[Contribute](contribute.md)** + + How to report a problem, propose a change, and report a vulnerability privately. + +
diff --git a/docs/repositories.md b/docs/repositories.md new file mode 100644 index 0000000..1dd11c3 --- /dev/null +++ b/docs/repositories.md @@ -0,0 +1,18 @@ +--- +hide: + - toc +--- + +# Repositories + +Every public HardFOC repository, with its topics, documentation and latest release; type to filter, or pick a +topic. + + + +--8<-- "build/portal/catalogue.md" + +This list is built from the GitHub API and the organisation's repository record +([`org/org-metadata.json`](https://github.com/hardfoc/.github/blob/main/org/org-metadata.json)) each time the +portal is published. A link to a topic: `repositories/?topic=spi`. From f952ff8d7d961557af085de29a37ca16fb409a08 Mon Sep 17 00:00:00 2001 From: Nebiyu Tadesse Date: Mon, 2 Nov 2026 00:29:45 -0700 Subject: [PATCH 4/5] README: README standard, link the site --- README.md | 79 +++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 51 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index edd6060..cade5da 100644 --- a/README.md +++ b/README.md @@ -1,37 +1,60 @@ # hardfoc.github.io -This repository hosts the documentation site for the hardfoc project. It uses -[MkDocs](https://www.mkdocs.org/) to build the static site and GitHub Pages to -publish it. +The HardFOC portal: what HardFOC is, how its repositories fit together, every public repository with its +documentation, and where to start with each board. -The site uses the [Material for MkDocs](https://squidfunk.github.io/mkdocs-material/) -theme, which provides a sidebar and top navigation tabs configured in -`mkdocs.yml`. +[![Portal](https://github.com/hardfoc/hardfoc.github.io/actions/workflows/docs.yml/badge.svg?branch=main)](https://hardfoc.github.io/) +[![License](https://img.shields.io/github/license/hardfoc/hardfoc.github.io)](LICENSE) -## Folder structure +**Site: ** -``` -. -โ”œโ”€โ”€ docs/ # Markdown sources -โ”‚ โ”œโ”€โ”€ getting-started/ -โ”‚ โ”œโ”€โ”€ api/ -โ”‚ โ”œโ”€โ”€ porting/ -โ”‚ โ”œโ”€โ”€ hardware/ -โ”‚ โ”œโ”€โ”€ index.md # Home page -โ”‚ โ””โ”€โ”€ about.md # About page -โ”œโ”€โ”€ mkdocs.yml # MkDocs configuration -โ””โ”€โ”€ .github/workflows/ # CI workflow + + + + Layer diagram, top to bottom: products (hardfoc-vortex-v1, hardfoc-flux-v1) build on board HALs (hf-
+    hal-vortex-v1, hf-hal-flux-v1), which build on hf-core (hf-core-drivers and hf-core-utils), which
+    gathers the hardware-agnostic chip drivers (hf-*-driver) and the peripheral interface wrap. + + + +## What it does + +- Introduces HardFOC and its two boards, Vortex V1 and Flux V1. +- Shows the four layers (chip drivers, core, board HALs, products) and how they are pinned together. +- Lists every public repository as a card with its description, topics, documentation and latest release, + generated from the GitHub API and [`org/org-metadata.json`](https://github.com/hardfoc/.github/blob/main/org/org-metadata.json) + at every publish, and filterable by text or topic. +- Gives a getting-started path per board, and for a chip driver on any MCU. +- Links the contribution rules and the private vulnerability report. + +## Build locally + +```bash +pip install "git+https://github.com/hardfoc/hf-general-ci-tools@v1#subdirectory=packages/hardfoc-mkdocs-theme" +GH_TOKEN=$(gh auth token) python3 tools/portal/catalogue.py # writes build/portal/catalogue.md +mkdocs serve # http://127.0.0.1:8000 ``` -Pushes to `main` trigger the workflow in `.github/workflows/build-and-deploy.yml` -which builds the site and updates the `gh-pages` branch. +The site is unversioned: `.github/workflows/docs.yml` calls `ru-mkdocs-publish.yml` from +[hf-general-ci-tools](https://hardfoc.github.io/hf-general-ci-tools/latest/) with `versioning: false`, and a push +to `main` (or the daily run) publishes the built site as the whole `gh-pages` branch. + +## Documentation + +| Page | What it covers | +| --- | --- | +| [Get started](https://hardfoc.github.io/get-started/) | A path per board, and for a chip driver on your MCU | +| [Repositories](https://hardfoc.github.io/repositories/) | Every public repository, searchable | +| [Architecture](https://hardfoc.github.io/architecture/) | The layers and how they are pinned | +| [Contribute](https://hardfoc.github.io/contribute/) | Issues, pull requests, vulnerability reports | + +## Tests + +```bash +python3 -m unittest discover -s tests && mkdocs build --strict +``` -### How the workflow works +## License -1. **Checkout** โ€“ pulls the repo so the runner has the docs and config. -2. **Setup Python** โ€“ installs a recent Python release for MkDocs. -3. **Install MkDocs** โ€“ installs `mkdocs-material` to render the pages. -4. **Build** โ€“ `mkdocs build --strict` generates the static site in `./site`. -5. **Publish** โ€“ `peaceiris/actions-gh-pages` pushes that folder to the - `gh-pages` branch and writes the `CNAME` file so GitHub Pages serves - `https://docs.hardfoc.dev`. +See [LICENSE](LICENSE). From b84121756d5d0b958f6c31dbe41753b67a1160b7 Mon Sep 17 00:00:00 2001 From: Nebiyu Tadesse Date: Mon, 2 Nov 2026 04:14:46 -0700 Subject: [PATCH 5/5] Use the released v1 docs workflows --- .github/workflows/docs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index ccfba00..0c718de 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -24,7 +24,7 @@ concurrency: jobs: site: # The branch of hardfoc/hf-general-ci-tools#42 (the versioning input) until it is released; then @v1. - uses: hardfoc/hf-general-ci-tools/.github/workflows/ru-mkdocs-publish.yml@docs/unversioned-sites + uses: hardfoc/hf-general-ci-tools/.github/workflows/ru-mkdocs-publish.yml@v1 permissions: contents: write # the publish job pushes gh-pages; pull requests never publish with: