Skip to content

Commit e9d5c00

Browse files
BunsDevclaude
andauthored
feat(release): config doctor, Docker assets, and self-hosting smoke for M5 (#28)
Closes the executable gaps in the M5 self-hosting/release-readiness pass: - config: add `Config::load` + `Config::check` validation surfacing placeholder secrets, missing PEM/coven-code binary, empty/duplicate familiars; covered by unit tests. - server: add `doctor` subcommand (exit-coded preflight) and make `serve` fail fast on config errors instead of crashing mid-request. - docker: real multi-stage Dockerfile (non-root, secret-free), compose.yaml, and .dockerignore replacing the inline doc snippet. - scripts: smoke-webhook.sh exercising unsigned/bad/valid HMAC paths; verified locally against a booted server (401/401/200). - docs: self-hosting guide wired to the committed Docker assets, doctor step, and signed-webhook smoke; README literal-\n fix + doctor/docker steps. - RELEASE.md: gate checklist (fmt/check/clippy/test/docs-smoke green; disposable-repo E2E remains human-gated before tagging). Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
1 parent b2c52e6 commit e9d5c00

11 files changed

Lines changed: 779 additions & 16 deletions

File tree

.dockerignore

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# Keep the Docker build context small and secret-free.
2+
target/
3+
.git/
4+
.worktrees/
5+
6+
# Never ship secrets or local config into an image.
7+
keys/
8+
config/local.toml
9+
*.pem
10+
11+
# Editor / OS noise
12+
.DS_Store

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Dockerfile

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# syntax=docker/dockerfile:1
2+
#
3+
# Multi-stage build for the coven-github webhook server.
4+
#
5+
# docker build -t coven-github .
6+
# docker run --rm -p 3000:3000 \
7+
# -v "$PWD/config:/config:ro" -v "$PWD/keys:/keys:ro" \
8+
# coven-github serve --config /config/local.toml
9+
#
10+
# See docs/self-hosting.md for the full walkthrough and compose.yaml for a
11+
# ready-to-edit Compose service.
12+
13+
# ── Builder ─────────────────────────────────────────────────────────────────
14+
FROM rust:1.83-bookworm AS builder
15+
WORKDIR /app
16+
17+
# Copy the whole workspace and build the release binary. (A cold build pulls the
18+
# full dependency tree; subsequent builds reuse Docker's layer cache.)
19+
COPY . .
20+
RUN cargo build --release --locked -p coven-github
21+
22+
# ── Runtime ─────────────────────────────────────────────────────────────────
23+
FROM debian:bookworm-slim AS runtime
24+
25+
# ca-certificates: TLS to api.github.com. git: clone target repos.
26+
RUN apt-get update \
27+
&& apt-get install -y --no-install-recommends ca-certificates git \
28+
&& rm -rf /var/lib/apt/lists/*
29+
30+
# Run as an unprivileged user. Secrets are mounted read-only at runtime, never
31+
# baked into the image (see the .dockerignore — keys/ and config/local.toml are
32+
# excluded from the build context).
33+
RUN useradd --system --create-home --uid 10001 coven
34+
USER coven
35+
WORKDIR /home/coven
36+
37+
COPY --from=builder /app/target/release/coven-github /usr/local/bin/coven-github
38+
39+
EXPOSE 3000
40+
41+
# `doctor` exits non-zero on a broken config, so it works as a preflight check.
42+
ENTRYPOINT ["coven-github"]
43+
CMD ["serve", "--config", "/config/local.toml"]

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,10 +136,16 @@ cp config/example.toml config/local.toml
136136
# Then set in config/local.toml: github.app_id, github.private_key_path,
137137
# github.webhook_secret, worker.coven_code_bin, and a [[familiars]] block.
138138

139+
# Validate the config (catches placeholder secrets, missing PEM/binary, etc.)
140+
./target/release/coven-github doctor --config config/local.toml
141+
139142
# Run
140143
./target/release/coven-github serve --config config/local.toml
141144
```
142145

146+
Prefer containers? A multi-stage [`Dockerfile`](Dockerfile) and
147+
[`compose.yaml`](compose.yaml) ship in the repo root.
148+
143149
See [docs/self-hosting.md](docs/self-hosting.md) for full setup including GitHub App registration. For a minimal familiar route, start from [`examples/familiar-github-starter`](examples/familiar-github-starter/).
144150

145151
---

RELEASE.md

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
# Release checklist — coven-github
2+
3+
The first usable release (`v0.1.0`) ships only after every gate below passes.
4+
Gates 1–5 are runnable from a clean checkout with no GitHub credentials. Gate 6
5+
needs a disposable repo with the App installed and a live `coven-code` binary —
6+
it is the human-in-the-loop step that authorizes the tag.
7+
8+
## Gates
9+
10+
```bash
11+
# 1. Format
12+
cargo fmt --all -- --check
13+
14+
# 2. Type/borrow check
15+
cargo check --workspace
16+
17+
# 3. Lint (warnings are errors)
18+
cargo clippy --workspace --all-targets -- -D warnings
19+
20+
# 4. Tests
21+
cargo test --workspace
22+
23+
# 5. Docs smoke — config validation + webhook signature path
24+
# (boot the server against a throwaway config, then:)
25+
cargo build --release -p coven-github
26+
./target/release/coven-github doctor --config config/local.toml
27+
scripts/smoke-webhook.sh http://localhost:3000/webhook "$WEBHOOK_SECRET"
28+
```
29+
30+
Gate 5 detail: `doctor` must exit `0` on a filled-in config, and
31+
`scripts/smoke-webhook.sh` must report unsigned → 401, bad signature → 401,
32+
valid signature → 200. See [docs/self-hosting.md](docs/self-hosting.md).
33+
34+
## 6. Disposable-repo end-to-end (human-gated)
35+
36+
On a throwaway repo with the GitHub App installed and `worker.coven_code_bin`
37+
pointing at a real `coven-code`:
38+
39+
1. Open an issue and assign it to the configured bot user (or apply a
40+
`trigger_labels` label such as `coven:fix`).
41+
2. Confirm a Check Run appears and the familiar session starts.
42+
3. Confirm a draft PR opens in the familiar's voice and links back to the issue.
43+
4. Confirm the Check Run resolves to success/failure (not stuck).
44+
45+
Capture the issue/PR links in the release notes as evidence.
46+
47+
## Cut the tag
48+
49+
Only after gates 1–6 pass:
50+
51+
```bash
52+
# Ensure the version in Cargo.toml ([workspace.package].version) is correct,
53+
# the tree is clean, and you are on main.
54+
git tag -s v0.1.0 -m "coven-github v0.1.0"
55+
git push origin v0.1.0
56+
```
57+
58+
> Tags are signed (`-s`). Do not push an unsigned release tag.
59+
60+
## Status of automatable gates (this branch)
61+
62+
| Gate | Result |
63+
|---|---|
64+
| 1. `cargo fmt --all -- --check` | ✅ clean |
65+
| 2. `cargo check --workspace` | ✅ clean |
66+
| 3. `cargo clippy … -D warnings` | ✅ clean |
67+
| 4. `cargo test --workspace` | ✅ 18 passing |
68+
| 5. docs smoke (`doctor` + `smoke-webhook.sh`) | ✅ verified locally |
69+
| 6. disposable-repo E2E | ⏳ requires live App creds + `coven-code` |

compose.yaml

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# Docker Compose service for coven-github.
2+
#
3+
# 1. Copy config/example.toml → config/local.toml and fill it in.
4+
# 2. Drop your GitHub App PEM into ./keys/ and point
5+
# github.private_key_path at /keys/<your-key>.pem in local.toml.
6+
# 3. Validate, then run:
7+
# docker compose run --rm coven-github doctor --config /config/local.toml
8+
# docker compose up
9+
#
10+
# coven-code must be reachable inside the container. Either bake it into a
11+
# derived image and set worker.coven_code_bin to its path, or mount it via an
12+
# extra read-only volume below.
13+
services:
14+
coven-github:
15+
build: .
16+
image: coven-github:local
17+
command: ["serve", "--config", "/config/local.toml"]
18+
ports:
19+
- "3000:3000"
20+
volumes:
21+
# Config and secrets are mounted read-only — never baked into the image.
22+
- ./config:/config:ro
23+
- ./keys:/keys:ro
24+
# Ephemeral per-task workspaces. Keep worker.workspace_root pointed here.
25+
- coven-workspaces:/tmp/coven-github-tasks
26+
environment:
27+
# Adjust log verbosity, e.g. RUST_LOG=coven_github=debug
28+
RUST_LOG: "coven_github=info"
29+
restart: unless-stopped
30+
31+
volumes:
32+
coven-workspaces:

crates/config/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,4 @@ license.workspace = true
88
anyhow.workspace = true
99
serde.workspace = true
1010
serde_json.workspace = true
11+
toml.workspace = true

0 commit comments

Comments
 (0)