d78644d173
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
60 lines
1.6 KiB
Go
60 lines
1.6 KiB
Go
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
|
|
}
|