// Command agentpr manages Gitea pull requests and comments as the unkin-agent // user. It obtains a scoped Gitea token from Vault (AppRole login, then reads // gitea/creds/unkin-agent) so actions are attributed to the agent rather than // to whoever runs the tool. // // agentpr pr create --repo owner/repo --base main --head feature --title T --body B // agentpr pr comment --repo owner/repo --pr 12 --body "..." // agentpr whoami package main import ( "fmt" "os" "git.unkin.net/unkin/agent-tools/internal/agent" "github.com/spf13/cobra" ) var version = "dev" func main() { root := &cobra.Command{ Use: "agentpr", Short: "Manage Gitea PRs and comments as the unkin-agent user.", Long: "agentpr manages Gitea pull requests and comments as unkin-agent, using a\nGitea token minted from Vault (AppRole login + gitea/creds/unkin-agent).", Version: version, SilenceUsage: true, } root.SetVersionTemplate("{{.Version}}\n") root.AddCommand(newPRCmd(), newWhoamiCmd(), newVersionCmd()) if err := root.Execute(); err != nil { os.Exit(1) } } // client mints a Gitea token via Vault and returns a ready client. func client() (*agent.GiteaClient, error) { token, err := agent.GiteaToken() if err != nil { return nil, err } return agent.NewGiteaClient(token), nil } func newPRCmd() *cobra.Command { cmd := &cobra.Command{ Use: "pr", Short: "Create PRs and post PR comments", } cmd.AddCommand(newPRCreateCmd(), newPRCommentCmd()) return cmd } func newPRCreateCmd() *cobra.Command { var repo, base, head, title, body string cmd := &cobra.Command{ Use: "create", Short: "Open a pull request", SilenceUsage: true, RunE: func(cmd *cobra.Command, args []string) error { owner, name, err := agent.ParseRepo(repo) if err != nil { return err } if base == "" || head == "" || title == "" { return fmt.Errorf("--base, --head and --title are required") } c, err := client() if err != nil { return err } pr, err := c.CreatePR(owner+"/"+name, agent.CreatePROptions{ Base: base, Head: head, Title: title, Body: body, }) if err != nil { return err } fmt.Printf("#%d %s\n", pr.Number, pr.HTMLURL) return nil }, } f := cmd.Flags() f.StringVar(&repo, "repo", "", "Repository as owner/repo (required)") f.StringVar(&base, "base", "", "Base branch (required)") f.StringVar(&head, "head", "", "Head branch (required)") f.StringVar(&title, "title", "", "PR title (required)") f.StringVar(&body, "body", "", "PR body") _ = cmd.MarkFlagRequired("repo") return cmd } func newPRCommentCmd() *cobra.Command { var repo, body string var pr int cmd := &cobra.Command{ Use: "comment", Short: "Post a comment on a pull request", SilenceUsage: true, RunE: func(cmd *cobra.Command, args []string) error { owner, name, err := agent.ParseRepo(repo) if err != nil { return err } if pr <= 0 { return fmt.Errorf("--pr must be a positive PR number") } if body == "" { return fmt.Errorf("--body is required") } c, err := client() if err != nil { return err } cm, err := c.CreateComment(owner+"/"+name, pr, body) if err != nil { return err } fmt.Printf("comment %d posted on %s/%s#%d\n", cm.ID, owner, name, pr) return nil }, } f := cmd.Flags() f.StringVar(&repo, "repo", "", "Repository as owner/repo (required)") f.IntVar(&pr, "pr", 0, "PR number (required)") f.StringVar(&body, "body", "", "Comment body (required)") _ = cmd.MarkFlagRequired("repo") _ = cmd.MarkFlagRequired("pr") _ = cmd.MarkFlagRequired("body") return cmd } func newWhoamiCmd() *cobra.Command { return &cobra.Command{ Use: "whoami", Short: "Print the authenticated Gitea login (should be unkin-agent)", SilenceUsage: true, RunE: func(cmd *cobra.Command, args []string) error { c, err := client() if err != nil { return err } u, err := c.Whoami() if err != nil { return err } fmt.Println(u.Login) return nil }, } } func newVersionCmd() *cobra.Command { return &cobra.Command{ Use: "version", Short: "Print the version", Run: func(cmd *cobra.Command, args []string) { fmt.Println(version) }, SilenceUsage: true, } }