Gitea's pull_request webhook (HMAC-checked) queues the review at once; a periodic sweep over the configured repositories catches lost deliveries. The service keeps the listed Mistral agent in sync with the embedded prompt, asks it for a review, refuses answers that called non-read-only tools, cleans the report, derives the review event and posts as the review bot. State is the pr-review:sha marker in the bot's own review. /healthz, /readyz and /metrics. Standard library only, tests against fake Gitea and Mistral servers, built as a scratch image by build-ci-images.yml. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
61 lines
1.3 KiB
Go
61 lines
1.3 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"sort"
|
|
"strings"
|
|
"sync"
|
|
)
|
|
|
|
// Metrics is a small set of counters and gauges rendered in the Prometheus text format.
|
|
type Metrics struct {
|
|
mu sync.Mutex
|
|
vals map[string]float64
|
|
}
|
|
|
|
func newMetrics() *Metrics { return &Metrics{vals: map[string]float64{}} }
|
|
|
|
func series(name string, labels []string) string {
|
|
if len(labels) == 0 {
|
|
return "review_bot_" + name
|
|
}
|
|
var parts []string
|
|
for i := 0; i+1 < len(labels); i += 2 {
|
|
parts = append(parts, fmt.Sprintf("%s=%q", labels[i], labels[i+1]))
|
|
}
|
|
return "review_bot_" + name + "{" + strings.Join(parts, ",") + "}"
|
|
}
|
|
|
|
// Inc adds one to a counter; labels are key, value pairs.
|
|
func (m *Metrics) Inc(name string, labels ...string) {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
m.vals[series(name, labels)]++
|
|
}
|
|
|
|
func (m *Metrics) Add(name string, delta float64) {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
m.vals[series(name, nil)] += delta
|
|
}
|
|
|
|
func (m *Metrics) Set(name string, v float64) {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
m.vals[series(name, nil)] = v
|
|
}
|
|
|
|
func (m *Metrics) Render(w io.Writer) {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
keys := make([]string, 0, len(m.vals))
|
|
for k := range m.vals {
|
|
keys = append(keys, k)
|
|
}
|
|
sort.Strings(keys)
|
|
for _, k := range keys {
|
|
fmt.Fprintf(w, "%s %g\n", k, m.vals[k])
|
|
}
|
|
}
|