Files
agent-tools/internal/agent/token.go
T
unkin-agent d78644d173
ci/woodpecker/pr/build Pipeline failed
ci/woodpecker/pr/test Pipeline failed
ci/woodpecker/pr/pre-commit Pipeline failed
Add agentpr and watchpr CLI tools
agentpr manages PRs/comments/whoami as unkin-agent (Vault AppRole -> gitea creds
-> Gitea API), fixing tea's post-as-Ben default. watchpr polls PRs and alerts
only on merge/close, human comment, CI failure, or lost mergeability.

- cobra multi-binary layout mirroring node-lookup (cmd/ + internal/)
- Makefile (build, patch|minor|major, completions, rpm), nfpm RPM with both
  binaries + bash/zsh/fish completions, woodpecker CI publishing to rpm-internal
- unit tests for parsing, meaningful-change detection, and the Vault+Gitea client
2026-08-12 21:37:09 +10:00

87 lines
2.6 KiB
Go

// Package agent holds the plumbing shared by the agent-tools CLIs (agentpr and
// watchpr): obtaining a Gitea token via Vault AppRole, talking to the Gitea
// API, parsing PR references, and deciding when a watched PR changed
// meaningfully. Both tools acquire their Gitea token the same way, so that
// logic lives here once.
package agent
import (
"os"
"sync"
)
const (
// DefaultVaultAddr is the OpenBao/Vault address used when VAULT_ADDR is unset.
DefaultVaultAddr = "https://vault.service.consul:8200"
// DefaultRoleID is the agent AppRole role_id used when AGENT_APPROLE_ROLE_ID
// is unset. Login uses role_id only (no secret_id).
DefaultRoleID = "ababbcd3-9c77-5c6a-be2d-287fce9214a6"
// GiteaCredsPath is the Vault path that mints a scoped Gitea token.
GiteaCredsPath = "gitea/creds/unkin-agent"
// DefaultGiteaURL is the Gitea base URL used when GITEA_URL is unset.
DefaultGiteaURL = "https://git.unkin.net"
// DefaultAgentLogin is the Gitea login of the agent whose own comments are
// ignored by watchpr. Overridable via AGENT_LOGIN.
DefaultAgentLogin = "unkin-agent"
)
// VaultAddr returns the configured Vault address (env VAULT_ADDR or the default).
func VaultAddr() string {
if v := os.Getenv("VAULT_ADDR"); v != "" {
return v
}
return DefaultVaultAddr
}
// RoleID returns the configured AppRole role_id (env AGENT_APPROLE_ROLE_ID or
// the default).
func RoleID() string {
if v := os.Getenv("AGENT_APPROLE_ROLE_ID"); v != "" {
return v
}
return DefaultRoleID
}
// GiteaURL returns the configured Gitea base URL (env GITEA_URL or the default).
func GiteaURL() string {
if v := os.Getenv("GITEA_URL"); v != "" {
return v
}
return DefaultGiteaURL
}
// AgentLogin returns the login whose comments watchpr ignores (env AGENT_LOGIN
// or the default).
func AgentLogin() string {
if v := os.Getenv("AGENT_LOGIN"); v != "" {
return v
}
return DefaultAgentLogin
}
var (
tokenOnce sync.Once
tokenValue string
tokenErr error
)
// GiteaToken returns a Gitea token, minting it via Vault AppRole on first call
// and caching it in-process for the lifetime of the command.
func GiteaToken() (string, error) {
tokenOnce.Do(func() {
tokenValue, tokenErr = fetchGiteaToken(VaultAddr(), RoleID())
})
return tokenValue, tokenErr
}
// fetchGiteaToken performs the AppRole login and reads the Gitea creds. It is
// separated from GiteaToken so tests can exercise it directly against an
// httptest server without touching the process-wide cache.
func fetchGiteaToken(vaultAddr, roleID string) (string, error) {
clientToken, err := approleLogin(vaultAddr, roleID)
if err != nil {
return "", err
}
return readGiteaCreds(vaultAddr, clientToken)
}