Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
c614032
super resolution example
bconsolvo Feb 20, 2026
c87208e
updates to docs
bconsolvo Mar 2, 2026
b9c3bc4
docs: Mintlify docs overhaul (CI, restructure, model tables, ownership)
bconsolvo Jun 9, 2026
6b4dd7d
chore(docs): remove test-run junk artifacts from docs/winml
bconsolvo Jun 9, 2026
7064034
chore(repo): rewrite README, Apache-2.0 LICENSE, move CODEOWNERS to d…
bconsolvo Jun 9, 2026
b5687c2
docs: restructure folders to match nav categories (2-level paths)
bconsolvo Jun 9, 2026
fb4b722
ci: run Test Code Samples on bconsolvo branch (self-hosted hardware p…
bconsolvo Jun 9, 2026
711f0a2
docs: promote Vision/LLMs/Audio to top-level categories (all 2-level)
bconsolvo Jun 9, 2026
cfd64fc
docs(ci): add per-page code-block test report from Strix Halo run
bconsolvo Jun 9, 2026
da04e5d
docs(ci): overhaul CI tooling and finalize docs restructure
bconsolvo Jun 9, 2026
1444bc4
docs(ci): fix stale paths in notify workflow/script and folder READMEs
bconsolvo Jun 9, 2026
d1ed3f2
ci(notify): also email full failure report to shared Ryzen AI support DL
bconsolvo Jun 9, 2026
b406058
ci(engine): compile C/C++, lint json/yaml/toml/cmake, run bash via WSL
bconsolvo Jun 9, 2026
bb682e4
docs(ci): add explicit pipeline execution order (what runs first) to …
bconsolvo Jun 9, 2026
f54b949
docs(ci): order Workflows and Scripts tables by run order; drop gen_c…
bconsolvo Jun 9, 2026
3d77ec2
docs(ci): reorder pipeline to ownership + model tables first, then co…
bconsolvo Jun 9, 2026
eec9eb2
docs: add official AMD logo (light/dark) + AMD favicon in the navbar
bconsolvo Jun 9, 2026
ab1a848
docs: hide single nav tab, shrink AMD logo, favicon = AMD triangle only
bconsolvo Jun 9, 2026
f06ce2c
docs: black favicon, bigger logo text, internal install links, alphab…
bconsolvo Jun 9, 2026
ef72cd4
docs(overview): showcase Lemonade + GAIA; add Classical Vision Models…
bconsolvo Jun 9, 2026
3afe758
docs(vision): link SD3/SD3.5 to public stabilityai repos (amd/ mirror…
bconsolvo Jun 9, 2026
71dad27
docs(vision): reclassify super-resolution; rename category index page…
bconsolvo Jun 9, 2026
264c752
ci(report): full new-engine run report (280 blocks: 35 pass, 245 fail)
bconsolvo Jun 10, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
5 changes: 5 additions & 0 deletions .github/scripts/.vale.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
StylesPath = styles
MinAlertLevel = suggestion

[*.{md,mdx}]
BasedOnStyles = Vale
547 changes: 547 additions & 0 deletions .github/scripts/CODE_TEST_REPORT.md

Large diffs are not rendered by default.

262 changes: 262 additions & 0 deletions .github/scripts/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,262 @@
# Docs-as-Code CI

Everything that powers the documentation CI for the Mintlify site in `../../docs`.
This is the **single** doc for the CI - scripts, conventions, runners, reporting,
and deployment. (The only other file you should ever read here is the generated
`CODE_TEST_REPORT.md`.)

## Table of contents

1. [Pipeline: execution order (what runs first)](#pipeline-at-a-glance)
- [Workflows (in run order)](#workflows-in-run-order) · [Scripts (in run order)](#scripts-in-run-order)
2. [Code-block testing model (test-by-default)](#code-block-testing-model-test-by-default)
4. [Languages: what runs, what doesn't (incl. C++)](#languages-what-runs-what-doesnt-incl-c)
5. [Authoring conventions](#authoring-conventions)
6. [Runners (hardware execution)](#runners-hardware-execution)
7. [Per-page report & dashboard](#per-page-report--dashboard)
8. [Failure routing (CODEOWNERS + notify)](#failure-routing-codeowners--notify)
9. [End-to-end walkthrough](#end-to-end-walkthrough)
10. [Deploying the site](#deploying-the-site)
11. [Run it locally](#run-it-locally)

---

## Pipeline at a glance

### Execution order (what runs first)

The pipeline below runs on a push/PR touching `docs/**`. It is ordered
**generate first, then test, then check prose**: ownership and the model tables
(generated content) come first, then the code is extracted and tested, then
links/prose are checked.

```text
push / PR to docs/**
1. CODEOWNERS & Page Ownership ...................... cloud
│ a. check_owners.py ........ every page has an owner header
│ b. generate_codeowners.py . rebuild docs/CODEOWNERS; fail if stale
│ c. codeowners-validator ... syntax / no-unowned / no-shadow
2. Update Model List ............................... cloud (weekly / on demand)
│ a. fetch_models.py ........ refresh Vision/LLMs/Audio model tables
3. Test Code Samples ............................... cloud → self-hosted
│ a. syntax-check (cloud) .... extract_code_blocks.py --syntax-only
│ └ python syntax + json/yaml/toml/cmake lint
│ b. test-hardware (runner) .. extract_code_blocks.py --run [needs 3a]
│ └ run python/powershell/cmd · bash via WSL · compile C/C++ · lint
│ └ report.py → CODE_TEST_REPORT.md (uploaded artifact)
│ c. record (always) ......... record_run.py → ci-history.json
4. Mintlify Docs Checks ............................ cloud
│ a. validate (mint validate → broken-links) b. external links
│ c. Vale prose d. cspell e. record → ci-history.json
5. Notify Owner On Failure (only if 3 or 4 failed) . cloud
resolve owner → open issue @mention → email NOTIFY_EMAIL (if SMTP_* set)
```

> On GitHub, stages 1, 3, and 4 are separate workflows triggered by the same PR,
> so they actually run **concurrently**; the numbering is the intended logical
> order (and within stage 3 the cloud syntax gate truly runs before the hardware
> run via `needs`). Stage 2 runs on a weekly schedule / on demand and
> regenerates the tables the other stages build on. Also weekly: **Link Check**
> (external links → opens an issue). Want hard serialization (1 → 2 → 3 → 4)?
> That needs `workflow_run` chaining - say the word.

### Workflows (in run order)

| # | Workflow (`.github/workflows/`) | Trigger | Where |
|---|---|---|---|
| 1 | `codeowners.yml` | PR/push `docs/**`,`.github/**` | cloud |
| 2 | `update-model-list.yml` | weekly + manual | cloud |
| 3 | `test-code-samples.yml` | PR/push `docs/**` | cloud → self-hosted |
| 4 | `mintlify-checks.yml` | PR/push `docs/**` | cloud |
| 5 | `notify-owner.yml` | `workflow_run` completed (on failure) | cloud |
| — | `link-check.yml` | weekly + manual | cloud |

### Scripts (in run order)

| # | Script | Runs during | What it does |
|---|---|---|---|
| 1 | `check_owners.py` | CODEOWNERS — step 1 | every page has an owner header |
| 2 | `generate_codeowners.py` | CODEOWNERS — step 2 | rebuild `docs/CODEOWNERS` from headers; fail if stale |
| 3 | `fetch_models.py` | Update Model List | refresh the Vision/LLMs/Audio model tables |
| 4 | `extract_code_blocks.py` | Test Code Samples — **syntax-check** (cloud), then **test-hardware** (runner) | parse every block in `docs/**` (`.mdx` + `.md`); `--syntax-only` = python syntax + format lint; `--run` = execute / compile / WSL |
| 5 | `report.py` | Test Code Samples — after test-hardware | run JSON → `CODE_TEST_REPORT.md` + inject the dashboard table |
| 6 | `record_run.py` | end of Test Code Samples **and** Mintlify Docs Checks | append the run (status + per-page result + owner) to `ci-history.json` |
| 7 | `resolve_owner.py` | Notify Owner (helper, also used by 5 & 6) | read the owner GitHub ID from a page header |
| 8 | `notify_owner.py` | Notify Owner — on failure | compose the issue/email body @mentioning owners |

## Code-block testing model (test-by-default)

**Every fenced block in a runnable language is executed on every run.** There is
no opt-in. The single opt-out is `notest`.

````
```python -> EXECUTED (always; also python-syntax-checked)
```powershell -> EXECUTED (Windows-native)
```bash -> EXECUTED via WSL on Windows
```cpp / ```c -> COMPILED with g++/gcc (run if it has main())
```json / ```yaml -> LINTED for format validity
```python notest -> the only opt-out: skipped entirely
````

Optional **authoring** tags (they do not make testing optional):

- `npu` / `gpu` / `cpu` - device scope (`--device npu` runs npu-tagged + untagged).
- `timeout=600`, `workdir=examples`, `continue_on_error=true`, `setup=<id>`.
- Page directives in MDX comments: `{/* @os:windows */}…{/* @os:end */}`,
`{/* @device:npu */}…`, `{/* @setup:id=… command="…" */}`,
`{/* @var:id=… device=npu value="…" */}`, `{/* @require:<id> */}`.

A page's blocks run **in order in one sandbox dir**, so a `git clone` / `cd` in
an early block persists for later blocks, and nothing pollutes the docs tree.

## Languages: what runs, what doesn't (incl. C++)

| Fence | Tested how | Where it runs |
|---|---|---|
| `python` | syntax-compiled, then **run** | Ryzen AI conda env (NPU/GPU/CPU providers visible) |
| `powershell` / `pwsh` / `ps1` | **run** | Windows-native (`powershell -NoProfile -Command`) |
| `cmd` / `bat` / `batch` | **run** | Windows-native (`cmd /c`) |
| `bash` / `sh` / `shell` | **run** | **WSL** on Windows (`wsl bash -lc`); native `bash` on Linux |
| `cpp` / `c` | **compiled** with `g++`/`gcc` (and run if it defines `main()`; otherwise `-fsyntax-only`) | WSL on Windows; native on Linux |
| `json` / `yaml` / `toml` / `cmake` / `text` | **linted** for format validity (`json.loads`, `yaml.safe_load`, `tomllib`, paren-balance, plain-text) | in-process (no shell) |
| `mdx` / `ini` / other | skipped | n/a (not runnable or lintable) |

**Linux vs Windows routing:** `bash`, C/C++, and anything inside an `@os:linux`
scope are Linux work, so on the Windows runner they execute through **WSL**
(Ubuntu). Windows blocks (`powershell`, `cmd`) run natively. Pass `--no-wsl` to
skip Linux blocks instead of running them through WSL. A dedicated Ubuntu runner
can replace WSL later with no doc changes (the routing is automatic).

## Authoring conventions

1. **Owner header (required)** - first line after frontmatter:
`{/* owner: <github-id> */}`. Drives CODEOWNERS + failure routing. Default
owner: `@dwithchenna`.
2. **Language tabs** - when the same step exists in multiple languages, show
**Python first**, then **C++**, using Mintlify `<Tabs><Tab title="Python">…`.
3. **2-level page paths** - `folder/page.mdx` (e.g. `llms/hybrid_oga.mdx`). The
link checker and Mintlify only resolve 2-level page paths; do deeper grouping
in `docs.json` nav, not on disk. (Top-level standalone pages like
`index.mdx` and `installation.mdx` are fine.)
4. **Icons** - only on top-level categories (group `icon` in `docs.json`, or a
frontmatter `icon:` on a top-level page). Never on second-level pages.

## Runners (hardware execution)

The `test-hardware` job runs on a **self-hosted runner**. Devices are chosen by
the `DOCS_CI_DEVICES` repo variable (a JSON array) so you never edit the
workflow to add hardware:

| Variable | Default | Meaning |
|---|---|---|
| `DOCS_CI_DEVICES` | `["halo"]` | runner device labels to target (e.g. `["halo","stxp","krk"]`) |
| `DOCS_CI_OS` | `Windows` | OS label in the runner triple |
| `RYZEN_AI_ENV` | `ryzen-ai-1.7.1` | conda env on the runner with the NPU/GPU/CPU providers |

A job targets `runs-on: [self-hosted, <DOCS_CI_OS>, <device>]`. To add a machine:
label it `self-hosted`, the OS, and a device tag, then add that tag to
`DOCS_CI_DEVICES`.

**Today:** a single local **Strix Halo** box (`AMD Ryzen AI Max+`, NPU present)
is registered to this repo with the label `halo`, so `DOCS_CI_DEVICES=["halo"]`
runs the whole suite end-to-end on real hardware.

**Shared AMD pool (future):** the AMD Playbooks lab machines (`xsj-aimlab-halo-*`,
`xsj-aimlab-stxp-*`, `…-krk-*`) use the same `[self-hosted, Windows, <device>]`
label scheme. They are registered to an AMD **org runner group**, so to use them
the docs repo must live under the `amd` org and be added to that group (a fork
under a personal account gets `403` and the job queues forever). Then set
`DOCS_CI_DEVICES=["halo","stxp","krk"]`.

Register the local box (stopgap): repo -> Settings -> Actions -> Runners -> New
self-hosted runner (Windows); add the label `halo`; run as a service
(`./svc.cmd install && ./svc.cmd start`).

## Per-page report & dashboard

- **`report.py`** reads a run's JSON (`--output-json` from
`extract_code_blocks.py`) and writes **`CODE_TEST_REPORT.md`** - a summary
table (one row per page: blocks / pass / fail / skip / owner) plus a per-page
detail table (one row per block: `#`, lang, result, short detail). It covers
**every** `.mdx` and `.md`, including pages with no code (`no code`).
- It also injects the summary table into `docs/reference/ci-dashboard.mdx`
between `{/* RESULTS_START */}` / `{/* RESULTS_END */}`, so the published CI
dashboard reflects all pages.
- **`ci-history.json`** is the append-only run log (status + per-page result +
owner) that `record_run.py` writes; the in-repo dashboard
(`.github/scripts/dashboard/index.html`) and the Cursor canvas read it.

## Failure routing (CODEOWNERS + notify)

- `generate_codeowners.py` rebuilds `docs/CODEOWNERS` from page headers: a
catch-all default, infra rules, a per-folder default (the folder's dominant
owner), then a per-page rule for every page. `codeowners.yml` fails a PR if
the committed file is stale.
- On a failing run, `notify_owner.py` resolves the owner from the page header
and opens an issue that **@mentions** them - GitHub emails them through its own
system, so no individual email addresses are stored anywhere.
- **Plus a full report by email to the shared support DL.** `notify-owner.yml`
also emails the report to `NOTIFY_EMAIL` (repo variable, default
`dl.ryzenai.support@amd.com`). A distribution list is safe to keep public - it
is not a person's address. This email step runs only when the SMTP relay
secrets are configured: `SMTP_SERVER`, `SMTP_PORT`, `SMTP_USERNAME`,
`SMTP_PASSWORD`. Without them, the GitHub issue is still opened; the email is
simply skipped.

## End-to-end walkthrough

Take `docs/vision/super_resolution.mdx` (owner `@bconsolvo`), which has a
runnable `python` PSNR block.

1. A PR edits `docs/**` -> `Mintlify Docs Checks` and `Test Code Samples` run.
2. `mint validate` + `broken-links` confirm the build and links.
3. `extract_code_blocks.py --syntax-only` compiles every python block (cloud).
4. `extract_code_blocks.py --run` executes the PSNR block on the Strix Halo
runner; it prints `PSNR @ MSE=100 -> 28.13 dB` and passes.
5. If it regressed, the page is written to `failed-pages.txt`, `notify-owner`
resolves `@bconsolvo` from the header and opens an issue mentioning them.
6. `generate_codeowners.py` keeps `CODEOWNERS` in sync from the same header, so
review assignment and the notifier always agree.

## Deploying the site

The site is hosted by **Mintlify** from the `/docs` subfolder; the GitHub repo
can stay private while the site is public.

1. mintlify.com -> connect the repo via the Mintlify GitHub App (works on
private repos; scope it to this repo).
2. Dashboard -> Git Settings -> enable **monorepo**, set the docs path to
`/docs` (otherwise it looks for `docs.json` at the root and fails).
3. Push/merge to `main` -> auto rebuild + deploy. Every PR gets a preview build.
4. The public URL (e.g. `https://ryzen-ai-xxxx.mintlify.app`) shows on the
dashboard Overview.

Notes: GitHub/Discord sidebar links, icons, theme, and the AI "Ask/Copy" menu
live in `docs/docs.json`. The "View as Markdown" / Ask-AI routes are produced by
Mintlify's hosted build and 404 under local `mint dev` - expected.

## Run it locally

```bash
# 1. Ownership + generated tables first (the pipeline order)
python .github/scripts/check_owners.py
python .github/scripts/generate_codeowners.py
python .github/scripts/fetch_models.py

# 2. Cloud-equivalent syntax + format check (fast, no hardware)
python .github/scripts/extract_code_blocks.py --syntax-only --docs docs

# 3. Full hardware run (inside the Ryzen AI conda env), then build the report
conda run -n ryzen-ai-1.7.1 python .github/scripts/extract_code_blocks.py \
--run --docs docs --output-json report_run.json --failed-pages report_failed.txt
python .github/scripts/report.py --results report_run.json --docs docs \
--out .github/scripts/CODE_TEST_REPORT.md --dashboard docs/reference/ci-dashboard.mdx

# Preview the site
cd docs && npx mint dev # http://localhost:3000
```
39 changes: 39 additions & 0 deletions .github/scripts/check_owners.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#!/usr/bin/env python3
"""
Fail CI if any docs page is missing an owner header.

Every page under docs/ must declare an owner inline:
{/* owner: <github-id> */}

This is our guarantee that "every docs page has ownership" - it complements
CODEOWNERS (which routes GitHub review) by recording the owner's GitHub ID on
the page itself. Notifications are sent GitHub-natively by @mentioning the ID.
"""

import re
import sys
from pathlib import Path

# {/* owner: <id> */} (an optional legacy "| email" tail is tolerated)
OWNER_RE = re.compile(r"\{/\*\s*owner:\s*[^*|]+?\s*(?:\|[^*]*)?\*/\}")
DOCS = Path("docs")


def main() -> None:
missing = []
for mdx in sorted(DOCS.rglob("*.mdx")):
text = mdx.read_text(encoding="utf-8", errors="replace")
if not OWNER_RE.search(text):
missing.append(mdx.as_posix())
total = len(list(DOCS.rglob("*.mdx")))
if missing:
print(f"ERROR: {len(missing)}/{total} docs pages have no owner header:")
for m in missing:
print(f" - {m}")
print("\nAdd a header: {/* owner: <github-id> */}")
sys.exit(1)
print(f"OK: all {total} docs pages have an owner header.")


if __name__ == "__main__":
main()
Loading