M2 GATE: green build via push (Drone + exec runner); OAuth bootstrap script + docs
All checks were successful
continuous-integration/drone/push Build is passing

Build #1 success (clone+hello on exec runner). Drone<->Gitea OAuth scripted as
one-time bootstrap-drone-oauth.sh. M2 claimed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-26 23:08:38 +01:00
parent 0d89e2882a
commit e251a1177c
5 changed files with 105 additions and 8 deletions

View File

@ -26,9 +26,12 @@ Two single-writer sections (§6.1): Builder edits only `## Build backlog`; Adver
CLAIMED 2026-05-26, awaiting Adversary.
### M2 — Drone online
- [ ] Drone server + exec runner via Nix; Gitea OAuth app
- [ ] hello-world .drone.yml runs green; logs in Drone UI
- [ ] Gate: M2 — push to cc-ci triggers visible green build
- [x] Drone server (coop-cloud recipe, reconcile oneshot) + exec runner via Nix; Gitea OAuth app.
Server healthz 200 via gateway; runner polling (capacity=2, type=exec).
- [x] hello-world .drone.yml runs green; logs visible (Drone UI + API). Build #1 success: clone +
hello (echo/whoami=root/abra 0.13.0-beta/swarm=active), both exit 0.
- [x] Gate: M2 — push to cc-ci triggers visible green build → CLAIMED 2026-05-26, awaiting Adversary.
OAuth link via one-time `scripts/bootstrap-drone-oauth.sh` (documented in install.md §2).
### M3 — Comment bridge
- [ ] comment-bridge service: HMAC verify, !testme exact match, collaborator check, Drone API call

View File

@ -245,3 +245,24 @@ cc-ci repo activated in Drone, which requires the bot's Gitea OAuth login (brows
Drone a Gitea token (to sync repos + set the push webhook). Next tick: script the OAuth login to mint
a Drone token, activate cc-ci, push .drone.yml, confirm green. (DRONE_USER_CREATE made autonomic-bot
the admin.)
## 2026-05-26 — M2 GATE MET: green build via push (Drone + exec runner)
**Drone↔Gitea OAuth (scripted, the one manual bootstrap):** logged the bot into Gitea (CSRF cookie
→ form), drove Drone `/login` → Gitea authorize consent (POST `/login/oauth/grant` with _csrf+state+
granted=true) → code callback → Drone `_session_`. Captured the whole flow in
`scripts/bootstrap-drone-oauth.sh` (reads bot creds from env; documented in install.md §2; one-time,
token persists in Drone's data volume).
**Repo activation:** `GET /api/user` → autonomic-bot admin=true; `GET /api/user/repos?latest=true`
synced 12 repos; `POST /api/repos/recipe-maintainers/cc-ci` → active=true, config_path .drone.yml
(sets the Gitea push webhook).
**Green build:** added `.drone.yml` (exec pipeline), pushed (0d89e28). Polled
`/api/repos/recipe-maintainers/cc-ci/builds` → build #1 pending→running→**success**. Steps:
clone success exit 0; hello success exit 0 — log shows `whoami=root`, `abra 0.13.0-beta-06a57de`,
`swarm=active` (ran on the host via the exec runner). **M2 gate met; CLAIMED.**
**Next:** M3 — comment-bridge service: Gitea issue_comment webhook → verify HMAC + `!testme` exact +
collaborator → resolve PR head repo/SHA → trigger a parameterized Drone build; post a PR comment with
the run link. Need a Drone API token for the bridge (mint from the bot's Drone account).

View File

@ -1,8 +1,8 @@
# STATUS — cc-ci Builder
**Phase:** M1 complete & CLAIMED → starting M2 (Drone). M0 PASS (Adversary @21:35Z). M1 awaiting verdict.
**In-flight:** M2Drone server + exec runner via Nix + Gitea OAuth app (first M2 task).
**Last updated:** 2026-05-26 (M1 claimed)
**Phase:** M2 complete & CLAIMED → starting M3 (comment bridge). M0+M1 PASS (Adversary). M2 awaiting verdict.
**In-flight:** M3comment-bridge service (!testme webhook → Drone build trigger).
**Last updated:** 2026-05-26 (M2 claimed, green build #1)
## Gates
- **Gate: M0 — CLAIMED, awaiting Adversary** (2026-05-26). Evidence: flake rebuilds cc-ci from repo
@ -16,7 +16,12 @@
deployed by hand → HTTP 200 over HTTPS via gateway at cchtml1.ci.commoninternet.net with the
wildcard cert; torn down clean (services/volumes/secrets/containers all 0). Repro:
`scripts/deploy-proxy.sh` + `abra app new/deploy/undeploy`. Starting M2 as independent work; will
not flip M2's gate until M1 shows PASS.
not flip M2's gate until M1 shows PASS.**M1 PASS** @2026-05-26T22:20Z.
- **Gate: M2 — CLAIMED, awaiting Adversary** (2026-05-26). Evidence: Drone server (coop-cloud recipe,
reconcile oneshot, Gitea SSO) healthz 200 via gateway; exec runner polling (capacity=2). cc-ci repo
activated (push webhook). Pushing `.drone.yml` triggered build #1**success** (clone + hello exec
steps, exit 0; ran abra/docker on the host). Repro: `nixos-rebuild switch` + one-time
`scripts/bootstrap-drone-oauth.sh`. Starting M3 as independent work; won't flip M3 gate until M2 PASS.
## Blocked
- (none)

View File

@ -49,7 +49,22 @@ curl -ks --resolve drone.ci.commoninternet.net:443:<gateway-ip> \
> it survives a momentary drop, and **use the absolute flake path** (systemd units run with cwd `/`):
> `systemd-run --unit=ccci-sw --property=Type=oneshot nixos-rebuild switch --flake /root/cc-ci#cc-ci`
## 2. (later milestones) comment-bridge, dashboard, recipe enrollment
## 2. One-time: link Drone ↔ Gitea (OAuth grant)
The only manual post-rebuild step. Drone needs the bot's Gitea OAuth token (granted by an
interactive login) before it can sync/clone repos; this can't be Nix-declared without putting the
bot password on the box. The token then persists in Drone's `data` volume.
```sh
GITEA_USERNAME=autonomic-bot GITEA_PASSWORD=… bash scripts/bootstrap-drone-oauth.sh
# -> "drone login ok (admin=true)" / "repo recipe-maintainers/cc-ci active=true"
```
Verify a build runs green: push any commit to the cc-ci repo and watch
`https://drone.ci.commoninternet.net` (or the API) — the push webhook (set on activation) triggers
the `.drone.yml` self-test on the exec runner.
## 3. (later milestones) comment-bridge, dashboard, recipe enrollment
See `docs/enroll-recipe.md` (D5), `docs/secrets.md` (D6), `docs/runbook.md`. Each new piece of infra
is added as another idempotent reconcile oneshot, so this install stays a single `nixos-rebuild`.

View File

@ -0,0 +1,53 @@
#!/usr/bin/env bash
# One-time Drone<->Gitea OAuth bootstrap (the ONLY manual post-`nixos-rebuild` step).
#
# Drone can only sync/clone repos once it holds the bot's Gitea OAuth token, which is granted by
# an interactive OAuth login. This cannot be Nix-declared without putting the bot's Gitea password
# on cc-ci, so it's a one-time operator step. The token then persists in Drone's `data` volume, so
# reconcile redeploys keep working. Re-run only if that volume is wiped.
#
# Usage (run ON cc-ci, as root):
# GITEA_USERNAME=autonomic-bot GITEA_PASSWORD=… bash scripts/bootstrap-drone-oauth.sh
# Optionally ACTIVATE a repo: REPO=recipe-maintainers/cc-ci (default).
set -euo pipefail
: "${GITEA_USERNAME:?set GITEA_USERNAME}"; : "${GITEA_PASSWORD:?set GITEA_PASSWORD}"
GITEA="${GITEA:-https://git.autonomic.zone}"
DRONE="${DRONE:-https://drone.ci.commoninternet.net}"
CLIENT_ID="${CLIENT_ID:-ab4cdb9d-ee96-4867-875f-87384505fc52}"
REPO="${REPO:-recipe-maintainers/cc-ci}"
RES=(--resolve "drone.ci.commoninternet.net:443:127.0.0.1")
export PATH=/run/current-system/sw/bin:"$PATH"
cj=$(mktemp); dj=$(mktemp); az=$(mktemp)
trap 'rm -f "$cj" "$dj" "$az"' EXIT
# 1) Gitea web login (CSRF cookie -> form field).
curl -s -c "$cj" "$GITEA/user/login" -o /dev/null
gcsrf=$(awk '/_csrf/{v=$NF} END{print v}' "$cj")
curl -s -b "$cj" -c "$cj" -o /dev/null \
--data-urlencode "_csrf=$gcsrf" \
--data-urlencode "user_name=$GITEA_USERNAME" \
--data-urlencode "password=$GITEA_PASSWORD" \
"$GITEA/user/login"
# 2) Drone /login -> Gitea authorize URL.
loc=$(curl -sk -c "$dj" -o /dev/null -D - "${RES[@]}" "$DRONE/login" \
| awk 'tolower($1)=="location:"{print $2}' | tr -d '\r')
curl -sk -b "$cj" -c "$cj" -o "$az" "$loc"
# 3) Grant consent -> code callback -> complete Drone login (sets Drone session).
acsrf=$(grep -oE 'name="_csrf" value="[^"]*"' "$az" | head -1 | sed -E 's/.*value="([^"]*)".*/\1/')
state=$(grep -oE 'name="state" value="[^"]*"' "$az" | head -1 | sed -E 's/.*value="([^"]*)".*/\1/')
cb=$(curl -sk -b "$cj" -c "$cj" -o /dev/null -D - \
--data-urlencode "_csrf=$acsrf" --data-urlencode "client_id=$CLIENT_ID" \
--data-urlencode "state=$state" --data-urlencode "scope=" --data-urlencode "nonce=" \
--data-urlencode "redirect_uri=$DRONE/login" --data-urlencode "granted=true" \
"$GITEA/login/oauth/grant" | awk 'tolower($1)=="location:"{print $2}' | tr -d '\r')
curl -sk -b "$dj" -c "$dj" -o /dev/null -L "${RES[@]}" "$cb"
# 4) Verify + sync + activate the repo.
admin=$(curl -sk -b "$dj" "${RES[@]}" "$DRONE/api/user" | jq -r '.admin')
echo "drone login ok (admin=$admin)"
curl -sk -b "$dj" "${RES[@]}" "$DRONE/api/user/repos?latest=true" >/dev/null
active=$(curl -sk -b "$dj" "${RES[@]}" -X POST "$DRONE/api/repos/$REPO" | jq -r '.active')
echo "repo $REPO active=$active"