diff --git a/README.md b/README.md index ff328b4..aa1c983 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,226 @@ # spectral -A monolithic kernel work wrapper to make it easy. \ No newline at end of file +A monolithic kernel work wrapper, to make it easy. + +> **Status: scaffold.** The command surface parses, every verb is wired +> through `main` to its module, and the paths resolve — but the bodies are +> `todo!()`. Twenty of them. Nothing builds a kernel, boots qemu, scrapes +> bugzilla, or sends mail yet. What exists is the shape of the thing and the +> seams to fill in, which is exactly what the *Where to pitch in* section +> below is for. + +## Why + +The kernel patch lifecycle is a dozen commands you retype every time. Find +something to fix, write it, run checkpatch, run it again after `--fix`, +commit with the right trailer, build, boot it under qemu, diff against +master, work out who the maintainers are for those files, send. Then a week +later do it all again as v2, remembering to thread it off the original. + +None of those steps are hard. They are just easy to get subtly wrong, and +the subtle mistakes — a missing `Signed-off-by`, a v2 that doesn't thread, +a missing CC — are the ones that get a patch ignored rather than reviewed. + +spectral is a thin wrapper over that loop. It is deliberately *thin*: every +verb shells out to a tool you already have and already trust. + +## What it wraps + +Nothing here reimplements kernel tooling, and nothing vendors it: + +| spectral | actually runs | +|---|---| +| `patch check` / `patch format` | `scripts/checkpatch.pl` **from your tree** | +| `patch commit` | `git commit` | +| `patch create` | `git diff ` | +| `patch submit` | `scripts/get_maintainer.pl` **from your tree**, then `git send-email` | +| `kernel test` | `make`, then `qemu-system-x86_64` | +| `kernel quest` | HTTP against `bugzilla.kernel.org` | + +The two scripts are called out because it matters: they come from the tree +you are working in, so spectral cannot have a stale copy of the kernel's own +style rules or maintainer map. Upgrade your tree, get the new rules. + +And because it is thin, you can always drop the wrapper. `patch submit +--dry-run` prints the recipients and the exact `git send-email` command line +instead of sending, so you can check the plumbing or just run it yourself. + +## The loop + +What the finished tool should feel like — none of this runs yet: + +```console +$ spectral kernel quest +12345 usb: xhci: device does not enumerate after resume +https://bugzilla.kernel.org/show_bug.cgi?id=12345 + +# ... go and fix it, in the tree ... + +$ spectral patch check +$ spectral patch format # checkpatch --fix, then check again +$ spectral patch commit "usb: xhci: re-arm the port after resume" --signoff +$ spectral kernel test # build, then boot it under qemu +$ spectral patch create 000-xhci-port-rearm +~/.spectral/patches/000-xhci-port-rearm.patch + +$ spectral patch submit 000-xhci-port-rearm.patch --dry-run +# happy? drop --dry-run and it goes out + +# ... wait for review ... + +$ spectral patch update 000-xhci-port-rearm.patch -v 2 +~/.spectral/patches/v2-000-xhci-port-rearm.patch +$ spectral patch submit v2-000-xhci-port-rearm.patch --in-reply-to '' +``` + +One deviation from the obvious design: `patch update` is a single subcommand +taking `-v N` rather than a generated `update-v2` / `update-v3`. clap +subcommands are static, and a flag beats a subcommand you cannot tab-complete. +Leave `-v` off and it infers the next revision from the file's current `vN-` +prefix. + +## Install + +Needs a recent stable Rust — edition 2024, so **1.85 or newer**; developed +against 1.98.1. + +```console +$ git clone https://git.spectoria.dev/huntedbytheirs/spectral.git +$ cd spectral +$ cargo build --release +$ install -Dm755 target/release/spectral ~/.local/bin/spectral +``` + +Not on crates.io, so `cargo install spectral` will get you something else. +`cargo install --path .` from a clone works too. + +Beyond Rust you want: + +- **a kernel tree** — for `scripts/checkpatch.pl` and + `scripts/get_maintainer.pl`. Both ship with the kernel; spectral does not + carry its own. +- **git with `send-email` configured** — `git send-email` must work from a + plain shell first. If it does not, `patch submit` cannot make it work, and + is not meant to. +- **qemu** — `qemu-system-x86_64` on `$PATH`, for `kernel test`. + +## Configuration + +There is no config file yet. Two paths matter, and both come from the +environment with `$HOME`-relative defaults: + +| what | resolution | +|---|---| +| kernel tree | `$SPECTRAL_KERNEL`, else `~/.spectral/linux` | +| patch dir | `~/.spectral/patches` | + +```console +$ export SPECTRAL_KERNEL=$HOME/src/linux +``` + +A tree is accepted only if it has `scripts/checkpatch.pl`; otherwise you get +`not a kernel source tree` rather than a confusing failure three steps later. +A missing tree reports the path and the variable that would have set it. The +tree is not required for `kernel quest`, which only needs the network. + +`~/.config/spectral/config.toml` and a `spectral init` to clone the tree are +planned, not built — `src/config.rs` is the only file that will have to change +for either. + +## Commands + +```console +$ spectral --help +A monolithic kernel work wrapper to make it easy. + +Usage: spectral + +Commands: + kernel Find work, build it, boot it + patch Carry a change from working tree to mailing list + help Print this message or the help of the given subcommand(s) +``` + +`spectral kernel quest` · `kernel test` + +`spectral patch check` · `format` · `commit` · `create` · `submit` · `update` + +Every one of them has `--help` that says more than this README does — and, +for now, a body that panics with a description of what it is supposed to do. + +## Where to pitch in + +Every stub is a small, self-contained function with its signature and doc +comment already written, its caller already wired, and a `todo!()` naming the +command it should run. Pick one and the blast radius is that file. No stub +needs you to have read the rest of the crate. + +Roughly in order of how much they unblock: + +**`patch submit` — the most self-contained win.** Needs a kernel tree but no +network and no scraper. + +- `patch/maintainers.rs` · `lookup` — run `get_maintainer.pl --git` over the + patch, split the output into `To:` and `Cc:`, and collect the files the + patch touches +- `patch/maintainers.rs` · `add_cc` — fold `--cc` flags in without duplicates +- `patch/mod.rs` · `submit` — the above plus `git send-email`, with + `--dry-run` stopping one step short + +**`patch check` / `format` / `commit` / `create` — the everyday verbs.** +`patch/checkpatch.rs` · `run` already defines the `Target` enum (working +tree, a revision, or a patch file) and `Report` with an `is_clean`, so what +is missing is the process call and parsing the error/warning counts out of +its output. + +**`patch update` — small, and worth doing with a test.** `patch/mod.rs` · +`reroll_path` is the naming rule in one pure function: strip one leading +`vN-`, prepend `v-`. Re-running it at the same revision should be a +no-op, which is easier to assert than to describe. + +**`kernel quest` — the fun one, if you like HTML.** `kernel/quest.rs` · +`Bugzilla::fetch_open` is the only place `reqwest` and `scraper` earn their +place in `Cargo.toml`; `run` then applies `--filter` and picks one. The +`QuestSource` trait is the seam for swapping bugzilla for syzbot or a lore +thread later. + +**`kernel test` — needs a machine you are willing to boot kernels on.** +`kernel/qemu.rs` · `build` then `boot`. Streaming serial output as it arrives +beats buffering it until qemu exits, which is what makes a boot hang +diagnosable. + +**`git.rs` — the four plumbing calls.** `current_branch`, `diff_against`, +`commit`, `rev_parse`, each a few lines over the `run` that is already +written. Take these if you want to warm up on something tiny. + +There are no tests yet. The naming rule in `reroll_path` and the recipient +split in `lookup` are the two that most want one. + +## Development + +```console +$ cargo fmt --all +$ cargo clippy --all-targets -- -D warnings +$ cargo test +``` + +All three are clean on `main` and are the bar for a change. + +A few conventions, in the spirit of keeping the crate reviewable: + +- `src/cli.rs` holds the whole command surface. It is one file on purpose — + the CLI is the specification, and reading it top to bottom should tell you + what the tool does. +- Command modules return what they produced; `main` prints it. Output policy, + formatting and all, lives in one place. +- Errors go through the one `Error` enum and `?`. The command paths have no + `unwrap`. +- A new stub gets a `todo!()` that names the command it will end up running. + If a whole module is unreachable until its caller exists, it carries a + single `#[allow(dead_code)]` with the reason attached — there are no + crate-wide allows, so the warnings come back as the stubs get filled in. +- No `unsafe`, no FFI. + +## License + +MIT — see `LICENSE`, © 2026 huntedbytheirs.