Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
132 changes: 132 additions & 0 deletions .github/workflows/nerves_board.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
name: Nerves Board

# Publishes the PyAutoNerves Board — every config file and option across the
# library and workspace config folders — from one renderer (scripts/board.py,
# the Gut-board shape):
# * index.html — sources overview, the override map, the PYAUTO_* env-var
# panel and a search box over every key, file and comment,
# * repos/<Repo>.html — one page per source: each file's keys, its source
# (comments preserved, line-numbered) and a GitHub link; priors as tables,
# * badge.json (the shields endpoint), board.json (the full snapshot),
# * state.json (the organ-cockpit feed — contract v1, owned by
# PyAutoBrain board/_state.py and validated below),
# * dashboard.md (the markdown mirror).
#
# Read-only: the board edits nothing. Truth is a sparse checkout of each
# source's config folder (blobless clone, only `<config dir>/**` checked out)
# taken fresh on every run; slugs come from the Mind's body map.

on:
schedule:
- cron: "55 5 * * *"
workflow_dispatch:

permissions:
contents: read
pages: write
id-token: write

concurrency:
group: nerves-board-pages
cancel-in-progress: false

jobs:
board:
name: Render + publish the board
runs-on: ubuntu-latest
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- uses: actions/checkout@v4

# The family look (theme) and the cockpit contract live in the Brain;
# the body map (repos.yaml: each source's GitHub slug) in the Mind.
- uses: actions/checkout@v4
with:
repository: PyAutoLabs/PyAutoBrain
path: PyAutoBrain

- uses: actions/checkout@v4
with:
repository: PyAutoLabs/PyAutoMind
path: PyAutoMind

- uses: actions/setup-python@v5
with:
python-version: "3.12"

- name: Install PyYAML
run: pip install --quiet pyyaml

# The source list is the renderer's own SOURCES table, so the workflow
# and the board can never disagree about what is collected. A source
# that fails to clone is simply absent — the board reports it under
# "unavailable this render" rather than pretending it was read.
- name: Sparse-clone every config folder
run: |
python3 - <<'PY'
import importlib.util, subprocess, yaml
from pathlib import Path
spec = importlib.util.spec_from_file_location("board", "scripts/board.py")
board = importlib.util.module_from_spec(spec)
spec.loader.exec_module(board)
repos = yaml.safe_load(Path("PyAutoMind/repos.yaml").read_text())["repos"]
for src in board.SOURCES:
name, cfg = src["repo"], src["config"]
home = (repos.get(name) or {}).get("github")
if not home:
print(f"::warning::{name}: no github slug in repos.yaml")
continue
dest = Path("sources") / name
try:
subprocess.run(["git", "clone", "--quiet", "--depth", "1",
"--filter=blob:none", "--no-checkout",
f"https://github.com/{home}", str(dest)],
check=True, timeout=300)
subprocess.run(["git", "-C", str(dest), "sparse-checkout",
"set", "--no-cone", f"{cfg}/**"],
check=True, timeout=60)
subprocess.run(["git", "-C", str(dest), "checkout", "--quiet"],
check=True, timeout=300)
except (subprocess.CalledProcessError,
subprocess.TimeoutExpired) as e:
print(f"::warning::config checkout skipped {name}: {e}")
PY

- name: Collect the snapshot
run: |
python scripts/board.py --brain PyAutoBrain --mind PyAutoMind \
--sources sources --collect board_snapshot.json

- name: Render the site
run: |
python scripts/board.py --brain PyAutoBrain \
--snapshot board_snapshot.json --site _site

- name: Validate the cockpit feed against the Brain contract
run: |
# The contract is owned by the Brain (board/_state.py); the Nerves
# only emit it. A feed the cockpit cannot read fails loudly here.
python3 PyAutoBrain/board/_state.py _site/state.json

- name: Write the board to the job step summary
run: |
python scripts/board.py --brain PyAutoBrain \
--snapshot board_snapshot.json --md-brief >> "$GITHUB_STEP_SUMMARY"

# The Pages site is created once by a human token (the repo token cannot
# create it); enablement: true is kept so a re-created repo self-heals.
- uses: actions/configure-pages@v5
if: github.ref == 'refs/heads/main'
with:
enablement: true

- uses: actions/upload-pages-artifact@v3
if: github.ref == 'refs/heads/main'
with:
path: _site

- id: deployment
uses: actions/deploy-pages@v4
if: github.ref == 'refs/heads/main'
22 changes: 21 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ on autonerves, so any public-API change here ripples downstream.
```bash
pip install -e ".[dev]" # install with dev/test extras
python -m pytest test_autonerves/ # full test suite
python -m pytest test_autonerves/tools/test_decorators.py # one focused test
python -m pytest test_autonerves/test_decorator.py # one focused test
black autonerves/ # formatter (advisory — not gated)
```

Expand All @@ -78,6 +78,26 @@ writable caches:
NUMBA_CACHE_DIR=/tmp/numba_cache MPLCONFIGDIR=/tmp/matplotlib python -m pytest test_autonerves/
```

## The Nerves board

`scripts/board.py` renders the read-only Nerves board
(<https://pyautolabs.github.io/PyAutoNerves/>): every YAML file under each
library's `<package>/config/` and each workspace's `config/`, with keys,
comments, source, prior tables, the workspace → library override map, the
`PYAUTO_*` env vars, and a `state.json` cockpit feed (contract owned by
`PyAutoBrain/board/_state.py`). The config sources are the `SOURCES` table at
the top of the script (repo, config dir, kind, library lookup stack) — add a
new library or workspace there. It is stdlib + PyYAML, is **not** packaged
(`scripts/` is excluded in `pyproject.toml` / `MANIFEST.in`), and is tested by
`test_autonerves/test_board.py`. Published by
`.github/workflows/nerves_board.yml` (daily + dispatch). Local run:

```bash
python scripts/board.py --brain ../PyAutoBrain --root <workspace root> --collect snap.json
python scripts/board.py --brain ../PyAutoBrain --snapshot snap.json --site /tmp/nerves_site
python ../PyAutoBrain/board/_state.py /tmp/nerves_site/state.json # -> state: ok
```

## CI / definition of green

PRs must pass `pytest --cov` on the CI matrix (Python 3.12 **and** 3.13). There
Expand Down
2 changes: 2 additions & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ include README.md
prune .git
prune build
prune dist
# scripts/board.py renders the Nerves board (Pages); it is not package code.
prune scripts
recursive-exclude *.egg-info *
include requirements.txt

Expand Down
30 changes: 30 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,36 @@ output_to_fits(values=arr, file_path="demo.fits", overwrite=True)
loaded = ndarray_via_fits_from(file_path="demo.fits", hdu=0) # np.allclose(arr, loaded)
```

## Nerves board

**<https://pyautolabs.github.io/PyAutoNerves/>** — a read-only browser of every
config file and option across the organism: each library's `<package>/config/`
(PyAutoFit, PyAutoArray, PyAutoGalaxy, PyAutoLens, PyAutoCTI) and each
workspace's `config/` that overrides them.

- **Per repo** (`repos/<Repo>.html`): every YAML file with its top-level keys,
its source line-numbered with the comments kept (the comments *are* the
option docs) and a GitHub link; prior files render as a table
(Class · param · type · mean/σ or bounds · width modifier · limits).
- **Search**: the index page's box searches every key, file and comment
across all repos and jumps to the line.
- **Override map**: autonerves resolves a key workspace → last-imported
library → … → PyAutoFit (`Config.push(keep_first=True)`, keys lowercased),
so each workspace file is compared, by relative path, against the same file
across its library stack in that order (lens → galaxy → array → fit; cti →
array → fit). Keys whose value differs, keys no library defines (*orphans*)
and keys that fall through to the libraries are listed per file.
`build/*.yaml` is workspace tooling and is grouped apart.
- **Environment variables**: the `PYAUTO_*` switches autonerves reads.
- **Cockpit feed** (`state.json`): green when every file parses and no
workspace key is orphaned; yellow with one item per unparseable file or
orphan-carrying workspace file; grey when nothing was collected. Never red.

Rendered daily by [`.github/workflows/nerves_board.yml`](.github/workflows/nerves_board.yml)
from sparse checkouts of the config folders; the renderer is
[`scripts/board.py`](scripts/board.py) (not part of the `autonerves` package).
Nothing on the board edits config.

## Links

- Source & tests: [`autonerves/`](autonerves), [`test_autonerves/`](test_autonerves)
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ Homepage = "https://github.com/PyAutoLabs/PyAutoNerves"
include-package-data = true

[tool.setuptools.packages.find]
exclude = ["docs", "test_autonerves", "test_autonerves*"]
exclude = ["docs", "test_autonerves", "test_autonerves*", "scripts", "scripts*"]

[tool.setuptools_scm]
version_scheme = "post-release"
Expand Down
Loading
Loading