Adds the verb-group that kills the single biggest reasoning sink in agent sessions — watching a build/deploy to completion (proven the session that built it: hours hand-rolling Woodpecker polling + DB-schema spelunking for one CI incident). - ci status/watch: Woodpecker REST API (version-stable, not its DB schema), reached via the internal Traefik LB (dial 10.0.20.203, SNI=ci.viktorbarzin.me so the cert verifies — the Go form of the house `curl --resolve` pattern), token from WOODPECKER_TOKEN/Vault, repo id resolved from the cwd remote, with retries that ride Woodpecker's intermittent empty responses. watch matches the HEAD/given commit (avoids the post-push race) and exits non-zero on failure. - deploy wait: image-sha match THEN rollout status (rollout status alone returns success on the old ReplicaSet); kubectl-based. - work land now auto-watches CI to green on the landed commit (--no-ci-watch to skip), closing the v0.1 gap. - ci logs deferred to v0.4.1 (Woodpecker detail/log endpoints were the least reliable; status/watch use the working list endpoint). Live-verified ci status/watch against the live API. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
40 lines
1.2 KiB
Go
40 lines
1.2 KiB
Go
package main
|
|
|
|
import "testing"
|
|
|
|
func TestParseOwnerRepo(t *testing.T) {
|
|
cases := []struct{ in, owner, repo string }{
|
|
{"https://forgejo.viktorbarzin.me/viktor/infra.git", "viktor", "infra"},
|
|
{"https://forgejo.viktorbarzin.me/viktor/infra", "viktor", "infra"},
|
|
{"git@github.com:ViktorBarzin/infra.git", "ViktorBarzin", "infra"},
|
|
{"https://github.com/ViktorBarzin/tripit/", "ViktorBarzin", "tripit"},
|
|
}
|
|
for _, c := range cases {
|
|
o, r, err := parseOwnerRepo(c.in)
|
|
if err != nil || o != c.owner || r != c.repo {
|
|
t.Errorf("parseOwnerRepo(%q) = (%q, %q, %v), want (%q, %q)", c.in, o, r, err, c.owner, c.repo)
|
|
}
|
|
}
|
|
if _, _, err := parseOwnerRepo("nonsense"); err == nil {
|
|
t.Error("expected error for unparseable remote")
|
|
}
|
|
}
|
|
|
|
func TestStatusClassification(t *testing.T) {
|
|
for _, s := range []string{"success", "failure", "error", "killed"} {
|
|
if !isTerminalStatus(s) {
|
|
t.Errorf("%q should be terminal", s)
|
|
}
|
|
}
|
|
for _, s := range []string{"running", "pending"} {
|
|
if isTerminalStatus(s) {
|
|
t.Errorf("%q should not be terminal", s)
|
|
}
|
|
}
|
|
if !isFailureStatus("failure") || !isFailureStatus("error") {
|
|
t.Error("failure/error should classify as failure")
|
|
}
|
|
if isFailureStatus("success") {
|
|
t.Error("success must not classify as failure")
|
|
}
|
|
}
|