The worker committed as a hard-coded `gbr-bot` author, and a token without Write access only surfaced when a finished build's push failed.
- On start, the worker reads the token's account (`GET /user`) and uses its login and email as the commit author.
- It refuses to start, with a clear message, if that account can't push to the games repo.
- `worker_test.go` covers a read-only account, an account that can push, and a bad token.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
The worker committed as a hard-coded "gbr-bot", and a token without Write
access only showed up when a build finished and its push failed. On start it
now reads the token's account (GET /user) for the commit author, and
refuses to run if that account can't push to the games repo.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
None identified. The logic is correct and the change is well-scoped.
2. Non-blocking suggestions
Email fallback domain. The fallback in checkBot() sets c.botEmail = user.Login + "@users.noreply", which has no domain. Gitea's actual noreply pattern is <id>+<login>@users.noreply.<gitea-host>. Since this only appears as the commit author email and isn't validated by Gitea on push, it's cosmetic — but if anyone ever greps the log for a clickable email or the panel surfaces it, git_ai_bot@users.noreply won't resolve. Consider deriving the domain from c.gitea (e.g. net.ParseURL(c.gitea) → host) or at minimum using a more descriptive placeholder.
URL-encode the repo path.c.giteaGet("/repos/"+c.repo, &repo) concatenates c.repo directly into the URL. Gitea org/repo names are constrained to [a-zA-Z0-9_-] in practice, so this is safe today, but a one-liner with url.PathEscape on each segment (or just using url.JoinPath) would make the intent explicit and guard against a future config value that slips through:
New http.Client per call.giteaGet constructs a fresh http.Client{Timeout: 30 * time.Second} each invocation. Since it's called exactly twice at startup this is negligible, but if giteaGet ever becomes a general-purpose helper (the name suggests it might), a shared client with a transport would be more idiomatic. Not a problem today.
Token-in-URL on push (existing). The diff shows the push step uses -c auth (presumably https://oauth2:<token>@host/…). This is pre-existing code and not changed here, just noting for awareness.
3. Tests
The test covers the three main paths (no push, push OK, bad token) and verifies the captured identity. Two small gaps worth adding:
Empty-email fallback. Add a case where the /user response has "email":"" (or omits the field) and assert that c.botEmail == "git_ai_bot@users.noreply". This exercises the one-line branch that currently has zero coverage.
giteaGet 404 / unexpected shape. A quick test that a non-200 response (e.g. 404 on the repo endpoint) produces the expected wrapped error would protect against a regression in the error path.
Neither is a blocker; the existing test already validates the critical behaviour.
4. Overall assessment
Clean, minimal fix. The startup checkBot() call turns a 30-minute failure into a startup error, the commit author now matches the token's actual account, and the doc update keeps operators informed. The test is focused and exercises the right paths. No correctness, security, or concurrency concerns. Ship it.
Automated review by AI Git Bot
## 🤖 AI Code Review
## 1. Blocking issues
None identified. The logic is correct and the change is well-scoped.
## 2. Non-blocking suggestions
- **Email fallback domain.** The fallback in `checkBot()` sets `c.botEmail = user.Login + "@users.noreply"`, which has no domain. Gitea's actual noreply pattern is `<id>+<login>@users.noreply.<gitea-host>`. Since this only appears as the commit author email and isn't validated by Gitea on push, it's cosmetic — but if anyone ever greps the log for a clickable email or the panel surfaces it, `git_ai_bot@users.noreply` won't resolve. Consider deriving the domain from `c.gitea` (e.g. `net.ParseURL(c.gitea)` → host) or at minimum using a more descriptive placeholder.
- **URL-encode the repo path.** `c.giteaGet("/repos/"+c.repo, &repo)` concatenates `c.repo` directly into the URL. Gitea org/repo names are constrained to `[a-zA-Z0-9_-]` in practice, so this is safe today, but a one-liner with `url.PathEscape` on each segment (or just using `url.JoinPath`) would make the intent explicit and guard against a future config value that slips through:
```go
path := "/repos/" + url.PathEscape(strings.ReplaceAll(c.repo, "/", url.PathSeparator))
```
Low priority given the current character set.
- **New `http.Client` per call.** `giteaGet` constructs a fresh `http.Client{Timeout: 30 * time.Second}` each invocation. Since it's called exactly twice at startup this is negligible, but if `giteaGet` ever becomes a general-purpose helper (the name suggests it might), a shared client with a transport would be more idiomatic. Not a problem today.
- **Token-in-URL on push (existing).** The diff shows the push step uses `-c auth` (presumably `https://oauth2:<token>@host/…`). This is pre-existing code and not changed here, just noting for awareness.
## 3. Tests
The test covers the three main paths (no push, push OK, bad token) and verifies the captured identity. Two small gaps worth adding:
- **Empty-email fallback.** Add a case where the `/user` response has `"email":""` (or omits the field) and assert that `c.botEmail == "git_ai_bot@users.noreply"`. This exercises the one-line branch that currently has zero coverage.
- **`giteaGet` 404 / unexpected shape.** A quick test that a non-200 response (e.g. 404 on the repo endpoint) produces the expected wrapped error would protect against a regression in the error path.
Neither is a blocker; the existing test already validates the critical behaviour.
## 4. Overall assessment
Clean, minimal fix. The startup `checkBot()` call turns a 30-minute failure into a startup error, the commit author now matches the token's actual account, and the doc update keeps operators informed. The test is focused and exercises the right paths. No correctness, security, or concurrency concerns. Ship it.
---
*Automated review by AI Git Bot*
greg
merged commit 425c242ae7 into main2026-10-25 21:07:49 +00:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
The worker committed as a hard-coded
gbr-botauthor, and a token without Write access only surfaced when a finished build's push failed.GET /user) and uses its login and email as the commit author.worker_test.gocovers a read-only account, an account that can push, and a bad token.🤖 Generated with Claude Code
🤖 AI Code Review
1. Blocking issues
None identified. The logic is correct and the change is well-scoped.
2. Non-blocking suggestions
Email fallback domain. The fallback in
checkBot()setsc.botEmail = user.Login + "@users.noreply", which has no domain. Gitea's actual noreply pattern is<id>+<login>@users.noreply.<gitea-host>. Since this only appears as the commit author email and isn't validated by Gitea on push, it's cosmetic — but if anyone ever greps the log for a clickable email or the panel surfaces it,git_ai_bot@users.noreplywon't resolve. Consider deriving the domain fromc.gitea(e.g.net.ParseURL(c.gitea)→ host) or at minimum using a more descriptive placeholder.URL-encode the repo path.
c.giteaGet("/repos/"+c.repo, &repo)concatenatesc.repodirectly into the URL. Gitea org/repo names are constrained to[a-zA-Z0-9_-]in practice, so this is safe today, but a one-liner withurl.PathEscapeon each segment (or just usingurl.JoinPath) would make the intent explicit and guard against a future config value that slips through:Low priority given the current character set.
New
http.Clientper call.giteaGetconstructs a freshhttp.Client{Timeout: 30 * time.Second}each invocation. Since it's called exactly twice at startup this is negligible, but ifgiteaGetever becomes a general-purpose helper (the name suggests it might), a shared client with a transport would be more idiomatic. Not a problem today.Token-in-URL on push (existing). The diff shows the push step uses
-c auth(presumablyhttps://oauth2:<token>@host/…). This is pre-existing code and not changed here, just noting for awareness.3. Tests
The test covers the three main paths (no push, push OK, bad token) and verifies the captured identity. Two small gaps worth adding:
/userresponse has"email":""(or omits the field) and assert thatc.botEmail == "git_ai_bot@users.noreply". This exercises the one-line branch that currently has zero coverage.giteaGet404 / unexpected shape. A quick test that a non-200 response (e.g. 404 on the repo endpoint) produces the expected wrapped error would protect against a regression in the error path.Neither is a blocker; the existing test already validates the critical behaviour.
4. Overall assessment
Clean, minimal fix. The startup
checkBot()call turns a 30-minute failure into a startup error, the commit author now matches the token's actual account, and the doc update keeps operators informed. The test is focused and exercises the right paths. No correctness, security, or concurrency concerns. Ship it.Automated review by AI Git Bot