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
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// PRRef identifies a single pull request by repository and number.
|
||||
type PRRef struct {
|
||||
Owner string
|
||||
Repo string
|
||||
Number int
|
||||
}
|
||||
|
||||
// String renders the ref in canonical owner/repo#N form.
|
||||
func (r PRRef) String() string {
|
||||
return fmt.Sprintf("%s/%s#%d", r.Owner, r.Repo, r.Number)
|
||||
}
|
||||
|
||||
// RepoPath returns the "owner/repo" portion used in Gitea API URLs.
|
||||
func (r PRRef) RepoPath() string {
|
||||
return r.Owner + "/" + r.Repo
|
||||
}
|
||||
|
||||
// ParsePRRef parses "owner/repo#N" or "owner/repo:N" into a PRRef.
|
||||
func ParsePRRef(s string) (PRRef, error) {
|
||||
s = strings.TrimSpace(s)
|
||||
sep := strings.IndexAny(s, "#:")
|
||||
if sep < 0 {
|
||||
return PRRef{}, fmt.Errorf("invalid PR reference %q: expected owner/repo#N or owner/repo:N", s)
|
||||
}
|
||||
repoPart := s[:sep]
|
||||
numPart := s[sep+1:]
|
||||
|
||||
owner, repo, ok := strings.Cut(repoPart, "/")
|
||||
if !ok || owner == "" || repo == "" {
|
||||
return PRRef{}, fmt.Errorf("invalid PR reference %q: repo must be owner/repo", s)
|
||||
}
|
||||
if strings.Contains(repo, "/") {
|
||||
return PRRef{}, fmt.Errorf("invalid PR reference %q: repo must be owner/repo", s)
|
||||
}
|
||||
|
||||
n, err := strconv.Atoi(numPart)
|
||||
if err != nil || n <= 0 {
|
||||
return PRRef{}, fmt.Errorf("invalid PR reference %q: PR number must be a positive integer", s)
|
||||
}
|
||||
return PRRef{Owner: owner, Repo: repo, Number: n}, nil
|
||||
}
|
||||
|
||||
// ParseRepo validates and splits an "owner/repo" string.
|
||||
func ParseRepo(s string) (owner, repo string, err error) {
|
||||
s = strings.TrimSpace(s)
|
||||
owner, repo, ok := strings.Cut(s, "/")
|
||||
if !ok || owner == "" || repo == "" || strings.Contains(repo, "/") {
|
||||
return "", "", fmt.Errorf("invalid repo %q: expected owner/repo", s)
|
||||
}
|
||||
return owner, repo, nil
|
||||
}
|
||||
Reference in New Issue
Block a user