From d6a1c32761c02139ec3262160ced425bc55bf766 Mon Sep 17 00:00:00 2001 From: Dmitry Ilyin <6576495+widgetii@users.noreply.github.com> Date: Sat, 27 Jun 2026 07:32:12 +0300 Subject: [PATCH] docs: add CLAUDE.md for Claude Code (#107) Document the builder overlay model (per-device deltas layered onto a fresh OpenIPC/firmware clone), the build flow in builder.sh, device directory anatomy, the package/ and devices/common/ conventions, the CI workflows (including the moving-ref cache caveat), and a step-by-step "Adding a new device" walkthrough. Co-authored-by: Claude Opus 4.8 (1M context) --- CLAUDE.md | 166 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 166 insertions(+) create mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..a0f728b --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,166 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## What this repo is + +OpenIPC **builder** is a thin Buildroot *overlay* layer for building OpenIPC IP-camera +firmware for specific, named consumer devices. It contains **only the per-device deltas** +(a defconfig, a first-boot customizer, a rootfs exclude list, occasional sensor/board +files). Everything common — the actual Buildroot tree, packages, kernels, toolchains — lives +in [`OpenIPC/firmware`](https://github.com/openipc/firmware), which `builder.sh` clones fresh +on every run. This repo is never built in isolation; it is layered on top of a firmware +checkout. + +## Build commands + +```sh +./builder.sh # interactive whiptail menu of all devices +./builder.sh # build one device non-interactively +``` + +`` is the directory name under `devices/` (minus the `_defconfig` suffix on its +config), e.g. `hi3518ev200_lite_switcam-hs303`. It is passed straight through as +`make BOARD=`. There is no separate test/lint suite — "passing" means the firmware +image builds and (ideally) boots on hardware. + +What `builder.sh ` does, in order: +1. `git pull` (self-update the builder repo). +2. `rm -rf openipc` then clone `OpenIPC/firmware` — HEAD by default, or the ref in + `$OPENIPC_FW_REV` if set (used for cross-repo bisects; see build-one.yml). +3. `copy_extra_packages` — copy `package/*` into `openipc/general/package/` and append a + `source "...Config.in"` line for each into the external tree's `Config.in`. +4. Copy `devices//*` over the firmware tree (defconfig, overlay, excludes, board). +5. `make BOARD=` then best-effort `make BOARD= size-report`. +6. `copy_to_archive` → `archive///`. For `hi3518ev200_lite` it also runs + `autoup_rootfs` to wrap the images as `autoupdate-*.img` via `mkimage`. + +`openipc/`, `archive/`, `cache/`, `output/` are all gitignored build artifacts. + +### Other scripts +- `repack.sh [uboot] [firmware] [ssid] [pass]` — does **not** build. Downloads a prebuilt + release `.tgz` + u-boot from GitHub releases, optionally bakes in WiFi creds via + `fw_setenv`, and `dd`s a flashable NOR image. Needs `squashfs-tools`. +- `package.sh [pkg]` — force a full rebuild of one Buildroot package inside an existing + `openipc/` tree (`dirclean` + `rebuild`; defaults to `busybox`). + +## Device anatomy + +Device directory names encode `__-[-]`: +- **soc** — OpenIPC SoC name: `hi3518ev200`, `ssc337de`, `t31`, `gk7205v200`, … +- **flavor** — firmware track: `lite` (default, the vast majority), `ultimate`, `fpv`, + `rubyfpv`, `apfpv`. Prefer `lite` for new devices unless flash size forces otherwise. + +The minimal required files for a registered device (per README "Requirements"): +``` +devices//br-ext-chip-/configs/_defconfig +devices//general/overlay/usr/share/openipc/customizer.sh +devices//general/scripts/excludes/_.list +``` +- **`br-ext-chip-/`** — the chip-vendor folder mirrors firmware's `BR2_EXTERNAL` + layout. One of: `br-ext-chip-hisilicon`, `br-ext-chip-sigmastar`, `br-ext-chip-goke`, + `br-ext-chip-ingenic` (Ingenic = the T-series SoCs). The defconfig inside selects + toolchain/kernel/SoC drivers and majestic/webui packages; it references `$(OPENIPC_*)` + and `$(EXTERNAL_VENDOR)` macros resolved by the firmware Makefile, not by this repo. +- **`customizer.sh`** — runs on first boot. Applies device-specific runtime config: + `fw_setenv` (upgrade URL, `wlandev`, `ptz` profile, WiFi) and `cli -s . ` + to seed majestic's config (sensor ini, IR-cut/backlight GPIO pins, codec, fps). +- **`_.list`** — paths to **delete** from the rootfs (unused sensor `.so`/`.ini`, + unused WiFi `.ko`, etc.) so the image fits NOR flash. This is the main lever for the + "doesn't fit in 8M NOR" problem. + +Optional per-device files: extra `general/overlay/...` payload (a sensor `libsns_*.so`, a +sensor `.ini`, a patched `load_hisilicon`), or a custom kernel config at +`br-ext-chip-/board//.generic.config`. + +### `devices/common/` +Generic, non-device-specific defconfigs (`*_fpv`, `*_venc`, `*_lte`, `*_mini`) plus their +shared kernel configs and exclude lists. These are the matrix entries with a single +underscore (`hi3516ev200_fpv`, `hi3518ev200_mini`). The CI artifact-naming logic keys off +this: `COMMON = (underscore count) - 1`; when `COMMON == 1` the firmware's canonically-named +image is uploaded as-is, otherwise the compound `__-` image is +renamed to `-nor.tgz` / `-nand.tgz` to avoid release-asset collisions. + +## Adding a new device + +The fastest correct path is to clone the closest existing device — **same SoC and same +flavor** — and edit the deltas. Identify the target's SoC, image sensor, WiFi chip, and flash +size first (the README device table lists all of these for existing boards). + +1. **Copy a sibling.** `cp -r devices/__ devices/__-`. + The new directory name *is* the `BOARD`/`` token; keep it + `__-[-]`, lowercase, hyphen-separated vendor/model. +2. **Rename + edit the defconfig.** It lives at + `br-ext-chip-/configs/_defconfig` and its filename must match the new + directory name exactly (this is what `make BOARD=` looks up). Inside, the lines you + typically change: + - WiFi driver — the `BR2_PACKAGE__OPENIPC=y` line (e.g. `RTL8188FU`, `RTL8189FS`, + `ATBM…`). Pick the one matching the device's WiFi module. + - `BR2_OPENIPC_FLASH_SIZE="8"` / `"16"` to match the NOR/NAND size. + - Leave `BR2_OPENIPC_SOC_*`, `BR2_OPENIPC_VARIANT`, the toolchain/kernel block, and the + `*_OSDRV_*` sensor-driver package alone unless the SoC/flavor actually differs. +3. **Edit `general/overlay/usr/share/openipc/customizer.sh`** (runs on first boot): + - `fw_setenv upgrade '…/releases/download/latest/-nor.tgz'` — the filename **must** + equal `-nor.tgz`, because that is exactly what CI renames the artifact to (see + the `COMMON` logic above). Getting this wrong breaks self-update. + - `fw_setenv wlandev `; optional `fw_setenv ptz `. + - `cli -s . ` to seed majestic — IR-cut / backlight / light-sensor GPIO pins, + codec, fps. Only set `.isp.sensorConfig /etc/sensors/.ini` when the sensor needs + a non-default config (most boards don't — it's the exception, e.g. MIPI sensors). +4. **Trim the exclude list.** Keep the filename `_.list` (it is named after + soc+flavor, **not** the full device name — do not rename it to the device). Remove from the + list the sensor `.so`/`.ini` and WiFi `.ko` that *this* board actually uses, so they survive + into the rootfs; everything else listed gets stripped to fit flash. Sibling devices on the + same `_` often share an identical list — diff against them. +5. **Optional payload.** Add files under `general/overlay/…` only when required (a + `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. +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 + `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/` — builder-local Buildroot packages + +Standard Buildroot packages (`Config.in` + `.mk` + `src/` and/or `files/`) that aren't +(yet) upstream in firmware. `copy_extra_packages` in `builder.sh` injects them at build time. +Examples: `kc110-board-support` (a device-specific C PTZ/IR binary + init scripts, installed +via `SITE_METHOD = local`), `demo-openipc`. Read the package `Config.in` help text — e.g. +`kc110-board-support` documents the device's exact GPIO/pinmux map. + +## 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 + 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 + Telegram. +- **build-one.yml** — dispatch one platform at a chosen builder `commit` and/or firmware + `firmware_ref`. Built for `git bisect run` across either repo; publishes to + `nightly-bisect-*`. +- **manifest.yml** — after Build finishes (success *or* partial failure), regenerates + `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. + +### Moving-ref cache caveat +The monthly `builder-dl` cache can freeze packages pinned to a moving ref (VERSION = HEAD / +branch, or the `majestic-webui` `dist` asset) because they download under a constant +`-.tar.gz` filename. Both build workflows have a "Refresh moving-ref package +downloads" step that deletes `*-HEAD/master/main/dist.tar.gz` from the cache so Buildroot +re-fetches them each run. If you add a package pinned to a branch, make sure its tarball name +matches that glob or it will go stale silently. + +## Project memory (kaeru) + +Device-specific build/runtime gotchas, recovery procedures, and hardware quirks are recorded +in the **kaeru** `builder` initiative — query it before debugging a board (e.g. the +"overlay wipe removes ssh authorized_keys", "busybox `ip` has no `br` flag", and +generic→builder migration notes already there). Put new technical findings in kaeru, not here.