Add AGENTS.md and fill the gaps in the README
AGENTS.md describes the project for anyone -- human or agent -- changing a script: what the repository is, where it sits inside the OpenWrt tree, which scripts run from where, the shell conventions the existing code follows, and the bash traps that have already caused bugs here. README: document the options that were missing (--refresh, --skip-defconfig, --extras-file/--no-extras-file, --extras, --wrap), distinguish --extras from --extras-file, and replace the Debian-only Go install line with a distribution-neutral description of how update-go-path.sh finds a toolchain. Drop the hedged MIT claim, since no LICENSE file exists. Both documents stay environment-neutral: no absolute home paths and no assumption of a distribution, with platform-specific advice kept under an explicit conditional heading. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
149
AGENTS.md
Normal file
149
AGENTS.md
Normal file
@@ -0,0 +1,149 @@
|
||||
# AGENTS.md
|
||||
|
||||
Guidance for coding agents working in this repository.
|
||||
|
||||
## What this is
|
||||
|
||||
A set of Bash scripts that automate building OpenWrt from source: install the
|
||||
host dependencies, fetch the source tree, import a device's official
|
||||
configuration, and apply a per-device package list.
|
||||
|
||||
There is no build system, no test suite and no dependency manifest. The
|
||||
deliverable *is* the scripts. Everything is plain Bash plus `curl`/`wget`,
|
||||
`git`, `awk`, `sed` and `python3` (only for reading JSON — `jq` is deliberately
|
||||
not required).
|
||||
|
||||
The governing design rule: **a new OpenWrt release must not require editing any
|
||||
file here.** Anything version specific is fetched from `downloads.openwrt.org`
|
||||
at run time; only the user's own package choices are stored in git. Reject
|
||||
changes that would reintroduce a version-pinned file, a hardcoded device table
|
||||
or a copied-in package list.
|
||||
|
||||
## Repository layout
|
||||
|
||||
This repository is `helper/` *inside* an OpenWrt source tree. `prepare-openwrt.sh`
|
||||
puts it there on first run:
|
||||
|
||||
```
|
||||
<parent>/openwrt/ OpenWrt source root (not this repository)
|
||||
<parent>/openwrt/helper/ this repository
|
||||
<parent>/openwrt-build-helper symlink to openwrt/helper, for stale shells
|
||||
```
|
||||
|
||||
```
|
||||
helper/
|
||||
*.sh the scripts
|
||||
lib-openwrt-upstream.sh shared library, sourced not executed
|
||||
packages/<device-id>.txt tracked: the user's packages for one device
|
||||
.generated/ ignored: combined lists, saved buildinfo
|
||||
README.md user-facing documentation
|
||||
```
|
||||
|
||||
`<device-id>` is the official OpenWrt profile id (`linksys_mx8500`), so the
|
||||
filename, the `--device` argument and upstream's `profiles.json` key are always
|
||||
the same string.
|
||||
|
||||
## Where the scripts run from
|
||||
|
||||
Two groups, and mixing them up is the most common mistake:
|
||||
|
||||
- `prepare-openwrt-env.sh` and `prepare-openwrt.sh` run from **this directory**;
|
||||
they exist to create the OpenWrt tree, so they cannot assume one.
|
||||
- Everything else runs from the **OpenWrt root** (`./helper/<script>.sh`) because
|
||||
it reads or writes `.config`, `feeds/`, `tmp/` or `package/`. Each of those
|
||||
guards its entry point — `require_openwrt_root` from the library, or an
|
||||
equivalent inline test for `feeds.conf.default` + `Makefile`. Keep that guard
|
||||
when adding a script to this group. The exception is `gen-package-list.sh`,
|
||||
which only produces text: it needs the tree for version detection (or
|
||||
`--version`) and delegates the root check to `add-openwrt-packages.sh` on
|
||||
`--apply`.
|
||||
|
||||
Never derive the OpenWrt root from `$0`'s parent alone: the compat symlink means
|
||||
the script may be invoked through a path outside the tree. Resolve with
|
||||
`pwd -P`, and use `find_openwrt_root` when a version query must be anchored to
|
||||
the OpenWrt tree rather than to this repository's own git checkout.
|
||||
|
||||
## The scripts
|
||||
|
||||
| Script | Role |
|
||||
|---|---|
|
||||
| `prepare-openwrt-env.sh` | install host build dependencies; dispatches on the package manager (apt/dnf/yum/pacman/zypper/apk) |
|
||||
| `prepare-openwrt.sh` | clone or update the OpenWrt tree, relocate this repository into it |
|
||||
| `lib-openwrt-upstream.sh` | shared upstream access: URLs, caching, version detection, device lookup |
|
||||
| `download-config.sh` | install a device's official `config.buildinfo` as `.config` |
|
||||
| `gen-package-list.sh` | built-in packages from `profiles.json` + `packages/<id>.txt` |
|
||||
| `add-openwrt-packages.sh` | apply a package list to `.config` |
|
||||
| `update-go-path.sh` | point the build at an installed Go toolchain |
|
||||
| `add-external-repos.sh` | clone extra package repositories into `package/` |
|
||||
|
||||
Only `download-config.sh` and `gen-package-list.sh` source the library; it is
|
||||
guarded against double-sourcing by `_OWRT_LIB_LOADED`.
|
||||
|
||||
## Conventions
|
||||
|
||||
Match the existing style rather than introducing your own:
|
||||
|
||||
- `set -euo pipefail` and `umask 022` at the top of every executable script
|
||||
(`add-external-repos.sh` predates the convention and still uses bare
|
||||
`set -e`). The library sets nothing — the caller owns shell options.
|
||||
- The usage block is the file's own header comment, printed by
|
||||
`usage() { sed -n '<start>,<end>p' "$0" | sed 's/^# \{0,1\}//;s/^#$//'; }`.
|
||||
**Editing the header changes `--help`** — keep the line range correct, and
|
||||
update the header whenever you add an option.
|
||||
- All logging goes to **stderr** (`info`/`warn`/`err`). Functions return their
|
||||
value on stdout, so a stray `echo` to stdout corrupts `VAR="$(fn)"` at the
|
||||
call site. This has broken the code before.
|
||||
- Every destructive command goes through `run`, so `--dry-run` prints instead of
|
||||
acting; every prompt goes through `confirm`, so `-y` skips it. Both honour the
|
||||
`DRY_RUN` / `ASSUME_YES` globals.
|
||||
- Read-only discovery first, mutation after the confirmation prompt. Aborting at
|
||||
the prompt must leave the filesystem untouched.
|
||||
- Write to a temp file and validate before replacing something that matters;
|
||||
keep a `.bak`.
|
||||
- Tunables are `${VAR:-default}` so they can be overridden from the environment
|
||||
without editing the file.
|
||||
|
||||
Bash traps that have actually bitten this code:
|
||||
|
||||
- `local a="$1" b="$a"` — bash expands every word before assigning any, so `$a`
|
||||
is unset under `set -u`. Use one `local` per dependent variable.
|
||||
- `nullglob` only drops words that *contain* a wildcard; a literal path such as
|
||||
`/usr/local/go/` survives even when it does not exist. Check with `-d`.
|
||||
- `cmd | grep -q` under `pipefail` can fail on SIGPIPE. Prefer a pipe-free form.
|
||||
- In `awk -F'\t'`, `$2` is not whitespace-split. Extract with string operations
|
||||
when the field separator is not space.
|
||||
- `git checkout <ref>` is a no-op when HEAD is already at `<ref>`, so it does not
|
||||
restore deleted tracked files.
|
||||
|
||||
## Data files
|
||||
|
||||
`packages/<device-id>.txt` is hand written and holds **only** the user's extra
|
||||
packages — never the built-in set, which is always generated. Format:
|
||||
|
||||
```
|
||||
curl luci-app-ttyd whitespace separated, any number per line, applied as =y
|
||||
-luci-app-wol leading '-' removes a package (=n)
|
||||
##module following packages are set to =m
|
||||
##remove ... =n
|
||||
##built-in ... back to =y (the default)
|
||||
pkg # note text after a single '#' is a comment
|
||||
```
|
||||
|
||||
`.generated/` is git-ignored build output. Do not commit it, and do not make
|
||||
anything depend on a file in it existing.
|
||||
|
||||
## Checking your work
|
||||
|
||||
There is no test runner. Before committing:
|
||||
|
||||
- `bash -n <script>` on everything you touched.
|
||||
- `shellcheck` if available.
|
||||
- Run the script with `-n/--dry-run` and with `-h`; confirm `-h` still prints the
|
||||
full option list (the `sed` line range drifts silently).
|
||||
- Behaviour-changing edits to config handling deserve a real check against a
|
||||
concrete device: generate before and after, and diff the resulting sets of
|
||||
`=y` packages. Do not claim equivalence you have not diffed.
|
||||
|
||||
Assume the user's machine is not the only target: no absolute home paths, no
|
||||
assumption of a distro, an init system, or WSL. Platform-specific advice belongs
|
||||
in the README under an explicit conditional heading.
|
||||
32
README.md
32
README.md
@@ -89,6 +89,9 @@ make -j$(nproc) download world
|
||||
|
||||
## Scripts
|
||||
|
||||
Every script takes `-h/--help` and prints its full option list; the summaries
|
||||
below cover the options worth knowing about.
|
||||
|
||||
- **prepare-openwrt-env.sh** — installs the build dependencies from the official
|
||||
build system guide. Dispatches on the package manager rather than guessing from
|
||||
the distro version, and probes every package against the running release so a
|
||||
@@ -111,14 +114,21 @@ make -j$(nproc) download world
|
||||
the selected device and strips the buildbot flags (see below).
|
||||
`--device <id>`, `--target <t>`, `--version <ver>`, `--snapshot`,
|
||||
`--with-packages`, `--all-profiles`, `--keep-buildbot-flags`,
|
||||
`--save-buildinfo`, `--offline`, `--output <file>`, `-y`, `-n`.
|
||||
`--save-buildinfo`, `--offline`, `--output <file>`, `--refresh`,
|
||||
`--skip-defconfig`, `-y`, `-n`.
|
||||
|
||||
- **gen-package-list.sh** — builds the complete package list: the built-in part
|
||||
from the target's `profiles.json` (`default_packages` + `device_packages` plus
|
||||
the two packages the Firmware Selector adds), combined with your
|
||||
`packages/<device-id>.txt`.
|
||||
`--device <id>`, `--apply`, `--init-extras`, `--stdout`, `--full-stdout`,
|
||||
`--out <path>`, `--no-extras`, `-y`, `-n`.
|
||||
`--device <id>`, `--target <t>`, `--version <ver>`, `--snapshot`,
|
||||
`--apply`, `--init-extras`, `--extras-file <f>`, `--no-extras-file`,
|
||||
`--stdout`, `--full-stdout`, `--out <path>`, `--extras "<pkgs>"`,
|
||||
`--no-extras`, `--wrap <cols>`, `--refresh`, `-y`, `-n`.
|
||||
|
||||
`--extras`/`--no-extras` control the two packages the Firmware Selector adds
|
||||
on top of the target's own set; `--extras-file`/`--no-extras-file` control
|
||||
your `packages/<device-id>.txt`. They are different things.
|
||||
|
||||
- **add-openwrt-packages.sh** — applies a package list to `.config`. Rewrites
|
||||
existing `CONFIG_PACKAGE_` lines in place, so repeated runs do not grow the
|
||||
@@ -130,7 +140,7 @@ make -j$(nproc) download world
|
||||
`--external` clears `CONFIG_GOLANG_BUILD_BOOTSTRAP` so the installed toolchain
|
||||
builds host Go directly instead of OpenWrt building the whole bootstrap chain
|
||||
from source.
|
||||
`--external`, `--go-root <dir>`, `-n`.
|
||||
`--external`, `--go-root <dir>`, `--skip-defconfig`, `-n`.
|
||||
|
||||
- **add-external-repos.sh** — clones extra package repositories into `package/`.
|
||||
Edit the `REPOS` array to change the list.
|
||||
@@ -167,13 +177,17 @@ install an arbitrary kmod from your own build output.
|
||||
### Go packages
|
||||
Packages such as tailscale, adguardhome, easytier and cloudflared need a host Go.
|
||||
By default OpenWrt builds the entire bootstrap chain from source, which works but
|
||||
is slow. To skip it, install a Go matching the version the tree expects and run:
|
||||
is slow. To skip it, install a Go from your distribution (or from go.dev) at
|
||||
least as new as the version the tree expects, then run:
|
||||
|
||||
```bash
|
||||
sudo apt install golang-1.24-go
|
||||
./helper/update-go-path.sh --external
|
||||
```
|
||||
|
||||
The script searches `/usr/lib/go-*`, `/usr/local/go` and `/usr/lib/golang`,
|
||||
picks the newest, and warns if it is older than the bootstrap version the tree
|
||||
asks for. Point it elsewhere with `--go-root <dir>`.
|
||||
|
||||
### WSL: Windows PATH breaks Go package builds
|
||||
On WSL, `PATH` includes Windows directories containing spaces and parentheses
|
||||
(`/mnt/c/Program Files (x86)/...`). OpenWrt's Go build recipe interpolates `PATH`
|
||||
@@ -239,7 +253,9 @@ git clean -xdf -e /helper
|
||||
files unless you pass `--force`.
|
||||
- Scripts write under `package/` and `feeds/` inside your OpenWrt tree.
|
||||
|
||||
## License and authorship
|
||||
## Contributing and authorship
|
||||
|
||||
- Scripts authored by Zhe Yuan.
|
||||
- License: MIT (unless you choose a different license; update this line accordingly).
|
||||
- No `LICENSE` file is present yet; add one before redistributing.
|
||||
- [AGENTS.md](AGENTS.md) documents the layout, the shell conventions and the
|
||||
invariants to preserve — read it before changing a script.
|
||||
|
||||
Reference in New Issue
Block a user