chadtmiller.com/knotless
git that lives in your Atmosphere account
1
10
The install script brings the runner, labeller and reviewer
.changeset/install-runner-triage-reviewer.mdnew+7−0
| @@ -0,0 +1,7 @@ | ||
--- | ||
default: minor | ||
--- | ||
| ||
# The install script brings the runner, labeller and reviewer | ||
| ||
`curl -fsSL https://knotless.dev/install.sh | sh` now installs `knotless-run`, `knotless-triage`, `knotless-agent` and its `knotless-claude-review` script beside `knotless`, `git-remote-knotless` and `knotless-mcp`. A runner, labeller or reviewer no longer needs a clone of knotless and Rust to build it. Pinning `KNOTLESS_VERSION` to a release from before these shipped still installs the first three. | ||
crates/knotless-serve/src/install.sh+9−1
| @@ -1,5 +1,7 @@ | ||
#!/bin/sh | ||
# Installs knotless: `knotless`, `git-remote-knotless` and `knotless-mcp`, | ||
# and `knotless-run`, `knotless-triage`, `knotless-agent` and | ||
# `knotless-claude-review` for running a runner, labeller or reviewer, | ||
# from the releases of the knotless-serve that handed out this script. | ||
# | ||
# curl -fsSL __KNOTLESS_URL__/install.sh | sh | ||
| @@ -61,7 +63,13 @@ fetch "$releases/$version/$name.tar.gz.sha256" "$tmp/sum" || fail "no checksum f | ||
tar -xzf "$tmp/$name.tar.gz" -C "$tmp" | ||
| ||
mkdir -p "$dir" | ||
for bin in knotless git-remote-knotless knotless-mcp; do | ||
for bin in knotless git-remote-knotless knotless-mcp \ | ||
knotless-run knotless-triage knotless-agent knotless-claude-review; do | ||
# A release from before these shipped has only the first three. | ||
case "$bin" in | ||
knotless | git-remote-knotless | knotless-mcp) ;; | ||
*) [ -f "$tmp/$name/$bin" ] || continue ;; | ||
esac | ||
# Moved into place, so a running copy is replaced rather than written over. | ||
cp "$tmp/$name/$bin" "$dir/.$bin.new" | ||
chmod 755 "$dir/.$bin.new" | ||
crates/knotless-serve/tests/releases.rs+26−2
| @@ -12,7 +12,29 @@ use std::path::Path; | ||
use std::process::{Command, Output}; | ||
use std::sync::{Arc, Mutex}; | ||
| ||
const BINS: [&str; 3] = ["knotless", "git-remote-knotless", "knotless-mcp"]; | ||
const BINS: [&str; 7] = [ | ||
"knotless", | ||
"git-remote-knotless", | ||
"knotless-mcp", | ||
"knotless-run", | ||
"knotless-triage", | ||
"knotless-agent", | ||
"knotless-claude-review", | ||
]; | ||
| ||
/// What a release from before the runner, labeller and reviewer shipped | ||
/// holds. | ||
const OLD_BINS: usize = 3; | ||
| ||
/// The binaries a release of `version` holds: 0.1.0 is one from before | ||
/// the rest shipped. | ||
fn bins(version: &str) -> &'static [&'static str] { | ||
if version == "0.1.0" { | ||
&BINS[..OLD_BINS] | ||
} else { | ||
&BINS | ||
} | ||
} | ||
| ||
/// The target this machine's script asks for. | ||
fn target() -> String { | ||
| @@ -29,7 +51,7 @@ fn archive(dir: &Path, out: &Path, version: &str) -> std::path::PathBuf { | ||
let name = format!("knotless-{version}-{}", target()); | ||
let staged = dir.join("staging").join(&name); | ||
std::fs::create_dir_all(&staged).unwrap(); | ||
for bin in BINS { | ||
for bin in bins(version) { | ||
std::fs::write(staged.join(bin), format!("#!/bin/sh\necho {bin}\n")).unwrap(); | ||
} | ||
std::fs::create_dir_all(out).unwrap(); | ||
| @@ -151,6 +173,8 @@ fn installing_a_release() { | ||
assert!(!stdout.contains("is not on your PATH")); | ||
} | ||
assert!(bin.join("knotless-mcp").exists()); | ||
// A release from before the runner shipped installs without it. | ||
assert!(!bin.join("knotless-run").exists()); | ||
| ||
// No such version. | ||
let out = install(&server, &home, &[("KNOTLESS_VERSION", "9.9.9")]); | ||
deploy/build-client.sh+6−3
| @@ -1,6 +1,8 @@ | ||
#!/usr/bin/env bash | ||
# Builds the client, `knotless`, `git-remote-knotless` and `knotless-mcp`, | ||
# for every target the install script fetches, as one archive per target: | ||
# and the accounts that work for a repository, `knotless-run` (its runner), | ||
# `knotless-triage` (its labeller) and `knotless-agent` (its reviewer, with | ||
# the `knotless-claude-review` script it runs), for every target the install script fetches, as one archive per target: | ||
# | ||
# deploy/build-client.sh <out dir> | ||
# | ||
| @@ -35,12 +37,13 @@ for target in "${TARGETS[@]}"; do | ||
"Darwin "*-apple-darwin) build=build ;; | ||
*) build=zigbuild ;; | ||
esac | ||
$CARGO $build --release --locked --target "$target" -p knotless -p knotless-mcp | ||
$CARGO $build --release --locked --target "$target" -p knotless -p knotless-mcp -p knotless-run -p knotless-triage -p knotless-agent | ||
name="knotless-$VERSION-$target" | ||
mkdir "$OUT/$name" | ||
for bin in knotless git-remote-knotless knotless-mcp; do | ||
for bin in knotless git-remote-knotless knotless-mcp knotless-run knotless-triage knotless-agent; do | ||
cp "target/$target/release/$bin" "$OUT/$name/" | ||
done | ||
install -m 755 crates/knotless-agent/claude-review "$OUT/$name/knotless-claude-review" | ||
cp README.md LICENSE-MIT LICENSE-APACHE "$OUT/$name/" | ||
tar -czf "$OUT/$name.tar.gz" -C "$OUT" "$name" | ||
rm -r "$OUT/$name" | ||
deploy/README.md+2−1
| @@ -312,7 +312,8 @@ What it does not count: | ||
| ||
The server hands out the client, so installing it needs no clone and no | ||
Rust: `curl -fsSL https://knotless.dev/install.sh | sh` installs | ||
`knotless`, `git-remote-knotless` and `knotless-mcp` into `~/.local/bin` | ||
`knotless`, `git-remote-knotless`, `knotless-mcp`, `knotless-run`, | ||
`knotless-triage`, `knotless-agent` and `knotless-claude-review` into `~/.local/bin` | ||
(`KNOTLESS_INSTALL_DIR`), the latest release or `KNOTLESS_VERSION`'s, | ||
checked against its sha256. | ||
| ||
docs/guide/getting-started.md+7−3
| @@ -6,9 +6,13 @@ | ||
curl -fsSL https://knotless.dev/install.sh | sh | ||
``` | ||
| ||
The script installs three binaries into `$HOME/.local/bin`: `knotless` (the | ||
CLI), `git-remote-knotless` (which lets `git` use `knotless://` URLs) and | ||
`knotless-mcp` (for agents). `KNOTLESS_INSTALL_DIR` picks another directory, | ||
The script installs into `$HOME/.local/bin`: `knotless` (the CLI), | ||
`git-remote-knotless` (which lets `git` use `knotless://` URLs) and | ||
`knotless-mcp` (for agents). It also installs what you need to run an | ||
account that works for a repository: `knotless-run`, a | ||
[runner](workflows.md#start-a-runner), `knotless-triage`, a labeller, and | ||
`knotless-agent` with `knotless-claude-review`, a | ||
[reviewer](reviews.md). `KNOTLESS_INSTALL_DIR` picks another directory, | ||
and `KNOTLESS_VERSION` another release. | ||
| ||
To build from a clone instead: | ||
docs/guide/reviews.md+3−6
| @@ -7,8 +7,7 @@ | ||
- An atproto account for the reviewer, such as a new Bluesky account, and an app password for it. An app password is a password for one app, made in the account's settings (Bluesky: Settings > Privacy and security > App passwords). | ||
- Claude Code, with a Claude Pro or Max subscription or an Anthropic API key. | ||
- Node 18 or later. | ||
- `knotless-agent` and `knotless-mcp`, both built from the knotless repository. `knotless-mcp` is knotless's MCP server: Claude reads the pull through its tools. | ||
- `knotless-claude-review`, the script that runs Claude Code for each review. It is `crates/knotless-agent/claude-review` in the repository. | ||
- `knotless-agent`, `knotless-mcp` and `knotless-claude-review`, which the [install](getting-started.md#install) puts in place. `knotless-mcp` is knotless's MCP server: Claude reads the pull through its tools. `knotless-claude-review` is the script that runs Claude Code for each review. | ||
| ||
Use a new account, not your own. The owner's approvals count toward required reviews, and the reviewer posts as whichever account it logs in as. | ||
| ||
| @@ -16,12 +15,10 @@ Use a new account, not your own. The owner's approvals count toward required rev | ||
| ||
1. Make the reviewer's account, say `reviewer.example.com`, and make an app password for it. | ||
| ||
2. Install the two binaries and the script from a checkout of knotless: | ||
2. Install the two binaries and the script: | ||
| ||
```sh | ||
cargo install --path crates/knotless-agent | ||
cargo install --path crates/knotless-mcp | ||
install crates/knotless-agent/claude-review /usr/local/bin/knotless-claude-review | ||
curl -fsSL https://knotless.dev/install.sh | sh | ||
``` | ||
| ||
The knotless server image (`docker pull knotless.dev/chadtmiller.com/knotless/knotless-server`) has all three, with Claude Code, the script at `/usr/local/bin/knotless-claude-review`. | ||
docs/guide/workflows.md+3−0
| @@ -13,6 +13,9 @@ beside knotless-serve. | ||
| ||
## Start a runner | ||
| ||
`knotless-run` comes with the [install](getting-started.md#install), and | ||
runs steps in Docker, so the machine needs Docker too. | ||
| ||
A runner acts as an account of its own. Give it that account's handle and | ||
an app password, and name the repositories to watch: | ||
| ||
docs/reference/cli.md+4−2
| @@ -421,8 +421,10 @@ file that does not exist. It fails with `<file>: its tables are version | ||
<v>, this build's <version>: starting would rebuild it` otherwise. | ||
| ||
`/install.sh`, served with `--releases`, installs `knotless`, | ||
`git-remote-knotless` and `knotless-mcp` for `aarch64` or `x86_64`, macOS | ||
or Linux (musl), with curl or wget. It checks each download's SHA-256, and | ||
`git-remote-knotless`, `knotless-mcp`, `knotless-run`, `knotless-triage`, | ||
`knotless-agent` and `knotless-claude-review` for `aarch64` or `x86_64`, | ||
macOS or Linux (musl), with curl or wget. A release from before the last | ||
four shipped installs the first three. It checks each download's SHA-256, and | ||
moves each binary into place, so a running copy is replaced. The routes | ||
are in the [API reference](api.md#releases). | ||
README.md+1−1
| @@ -15,7 +15,7 @@ of pulls and issues, a gateway for collaborators, and web pages. It is cheap | ||
to run. | ||
| ||
```sh | ||
curl -fsSL https://knotless.dev/install.sh | sh # knotless, git-remote-knotless, knotless-mcp | ||
curl -fsSL https://knotless.dev/install.sh | sh # knotless, git-remote-knotless, knotless-mcp, and the runner, labeller and reviewer | ||
knotless login alice.bsky.social # OAuth, in your browser | ||
| ||
git push knotless://alice.bsky.social/my-project main | ||