From afb69ced9a8427e3177d645a89e5018afce40b4c Mon Sep 17 00:00:00 2001 From: AI Dev Date: Thu, 27 Aug 2026 17:35:00 +0000 Subject: [PATCH 1/2] docs: stop telling contributors to register a device in master.yml Step 6 of "Adding a new device" still says to add `- ` to the matrix in .github/workflows/master.yml, and calls that matrix "the *only* build registry". Neither has been true since #120 moved the matrix into .github/scripts/ci-matrix.py, where it is derived from the tree rather than listed. master.yml now has no device list at all. This is not a hypothetical staleness. Two of the pull requests open right now do exactly what step 6 says: #111 (Tapo TC70 v3) and #108 (KC110) each append a line to a block that no longer exists, so both are merge conflicts rather than the one-line changes they were written as. Their authors did the documented thing. So step 6 is now the opposite instruction -- there is nothing to add, and adding it is the mistake. It also names NOT_BUILT, since that is the one list a contributor could reasonably go looking for, and says to leave it alone. Two adjacent claims went stale with the same change and are fixed here: the master.yml bullet described a cron-and-dispatch workflow with no pull_request trigger, and nothing in the file mentioned ci-matrix.py at all -- including that --self-test runs in the Select devices job and will fail a PR that puts the tree and the selector out of step. Step 8 no longer says "locally" as though that were the only way a device gets built. --- CLAUDE.md | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index a0f728bf8..2117146fd 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -116,14 +116,19 @@ size first (the README device table lists all of these for existing boards). `libsns_.so` not provided by the SoC osdrv, a patched `load_hisilicon`, a sensor `.ini`), or a custom kernel config at `br-ext-chip-/board//.generic.config`. Keep the file count minimal — anything reusable belongs upstream in `OpenIPC/firmware`. -6. **Register for nightly CI.** Add `- ` to the matrix in - `.github/workflows/master.yml` under the matching group (SoC/APFPV/FPV/Ruby/etc.). This - matrix is the *only* build registry — a device not listed here is never built or released. +6. **CI registration is automatic — there is nothing to add.** The build matrix is derived + from the tree by `.github/scripts/ci-matrix.py`: a device is registered by having + `devices//.../_defconfig`, full stop. Do **not** add the device to + `.github/workflows/master.yml` — it holds no device list, and a PR that adds one is just a + merge conflict. The only written-down list is `NOT_BUILT` in `ci-matrix.py`, the opt-out + for devices that exist but are deliberately not built; leave it alone unless you mean to + opt out. Opening the PR is therefore what gets the device built, narrowed to just it. 7. **Document it.** Add a row to the device table in `README.md` (and the clones table if it's a rebrand of an existing board). -8. **Build & verify locally.** `./builder.sh `, then check +8. **Build & verify.** `./builder.sh `, then check `archive///` for the image and confirm it fits the flash size. Use - `package.sh ` to iterate on a single package without a full re-clone. + `package.sh ` to iterate on a single package without a full re-clone. Pushing the PR + builds it on CI too, but a local build is the faster loop while the defconfig is moving. ## `package/` — builder-local Buildroot packages @@ -135,9 +140,10 @@ via `SITE_METHOD = local`), `demo-openipc`. Read the package `Config.in` help te ## CI (`.github/workflows/`) -- **master.yml** ("Build") — nightly cron (03:00 UTC) + manual dispatch. Builds the full - device matrix; it always rebuilds (the input that actually changes is firmware HEAD / - toolchain / kernel, all *outside* this repo, so there is no skip-gate). Caches ccache and +- **master.yml** ("Build") — nightly cron (03:00 UTC), manual dispatch, and pull requests. + Every event except `pull_request` builds the full device matrix and always rebuilds (the + input that actually changes is firmware HEAD / toolchain / kernel, all *outside* this repo, + so there is no skip-gate); a PR builds only the devices its diff can reach. Caches ccache and Buildroot's `BR2_DL_DIR` at `/tmp/builder-dl` (outside `openipc/` because `builder.sh` `rm -rf`s that tree each run). Uploads each image to three release tags: dated `nightly-YYYYMMDD-`, rolling `nightly`, and legacy `latest`; pushes the NOR build to @@ -149,6 +155,15 @@ via `SITE_METHOD = local`), `demo-openipc`. Read the package `Config.in` help te `manifest.json` + `manifest.flat` on the `gh-pages` branch via `.github/scripts/enrich_manifest.py`. - **cleanup.yml** — weekly prune of dated `nightly-*` releases beyond the newest 90. +- **ci-matrix.py** (`.github/scripts/`) — not a workflow, but where the device registry now + lives. It maps the paths a PR touches to the devices that build them, reading the mapping + off the tree the same way `builder.sh` does: a file affects exactly the devices whose + defconfig shares its directory (which is why `devices/common/` is 18 targets, not one). + Recognising a path can only *narrow*; anything it has never heard of widens back to the + full matrix, so a misclassification costs runner time, never coverage. The nightly is never + narrowed. `ci-matrix.py --self-test` checks the file still agrees with the tree and runs in + the `Select devices` job, so it fails the PR rather than silently skipping a device — run it + after touching anything under `devices/`. ### Moving-ref cache caveat The monthly `builder-dl` cache can freeze packages pinned to a moving ref (VERSION = HEAD / From c905f6e90d414321d08f06799635065f54d20d96 Mon Sep 17 00:00:00 2001 From: AI Dev Date: Thu, 27 Aug 2026 17:48:35 +0000 Subject: [PATCH 2/2] docs: say that a draft PR builds nothing Step 6 said opening the PR is what gets the device built. That is true only for a non-draft one: classify() returns needs_build=False for a draft, and self_test() asserts it ('a draft pull request must not build'). master.yml carries the other half -- ready_for_review is in its trigger types precisely because a PR opened as a draft would otherwise never build once marked ready. Left as written, a contributor could open a draft, see no build, and read that as the device being fine. Also mentions the ci:full label, which is the other contributor-facing lever over the matrix and was equally undocumented. Caught by Qodo on #134. --- CLAUDE.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 2117146fd..583e654ef 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -122,12 +122,14 @@ size first (the README device table lists all of these for existing boards). `.github/workflows/master.yml` — it holds no device list, and a PR that adds one is just a merge conflict. The only written-down list is `NOT_BUILT` in `ci-matrix.py`, the opt-out for devices that exist but are deliberately not built; leave it alone unless you mean to - opt out. Opening the PR is therefore what gets the device built, narrowed to just it. + opt out. Opening the PR is therefore what gets the device built, narrowed to just it — + but a **draft** PR deliberately builds nothing, so mark it ready for review when you want + that build. `ci:full` as a PR label overrides the narrowing and builds every device. 7. **Document it.** Add a row to the device table in `README.md` (and the clones table if it's a rebrand of an existing board). 8. **Build & verify.** `./builder.sh `, then check `archive///` for the image and confirm it fits the flash size. Use - `package.sh ` to iterate on a single package without a full re-clone. Pushing the PR + `package.sh ` to iterate on a single package without a full re-clone. A non-draft PR builds it on CI too, but a local build is the faster loop while the defconfig is moving. ## `package/` — builder-local Buildroot packages