Files
GitOcean-Old/internal/app/cli_pr.go
T
2026-06-08 13:34:53 -05:00

248 lines
6.9 KiB
Go

package app
import (
"context"
"errors"
"flag"
"fmt"
"net/http"
"strings"
"time"
)
func cliPR(args []string) error {
if len(args) < 1 {
return errors.New("usage: gitocean pr <create|list|view|diff|checkout|comment|comments|close|merge>")
}
switch args[0] {
case "create":
return cliPRCreate(args[1:])
case "list":
return cliPRList(args[1:])
case "view":
return cliPRView(args[1:])
case "diff":
return cliPRDiff(args[1:])
case "checkout":
return cliPRCheckout(args[1:])
case "comment":
return cliPRComment(args[1:])
case "comments":
return cliPRComments(args[1:])
case "close":
return cliPRAction(args[1:], "close")
case "merge":
return cliPRAction(args[1:], "merge")
default:
return fmt.Errorf("unknown pr command %q", args[0])
}
}
func cliPRCreate(args []string) error {
fs := flag.NewFlagSet("pr create", flag.ExitOnError)
repoFlag := fs.String("repo", "", "target repo for same-repository PR")
from := fs.String("from", "", "source branch or OWNER/REPO:BRANCH")
to := fs.String("to", "", "target branch or OWNER/REPO:BRANCH")
title := fs.String("title", "", "title")
desc := fs.String("description", "", "description")
if err := fs.Parse(args); err != nil {
return err
}
if *from == "" || *to == "" || *title == "" {
return errors.New("usage: gitocean pr create --from SRC --to DST --title TITLE [--description DESC]")
}
var targetOwner, targetRepo, targetBranch, sourceOwner, sourceRepo, sourceBranch string
if *repoFlag != "" {
var err error
targetOwner, targetRepo, err = splitOwnerRepo(*repoFlag)
if err != nil {
return err
}
sourceOwner, sourceRepo = targetOwner, targetRepo
sourceBranch, targetBranch = *from, *to
} else {
var err error
sourceOwner, sourceRepo, sourceBranch, err = splitRepoBranch(*from)
if err != nil {
return err
}
targetOwner, targetRepo, targetBranch, err = splitRepoBranch(*to)
if err != nil {
return err
}
}
cfg, err := loadConfig()
if err != nil {
return err
}
body := map[string]string{"source_owner": sourceOwner, "source_repo": sourceRepo, "source_branch": sourceBranch, "target_branch": targetBranch, "title": *title, "description": *desc}
var out PullRequest
if err := apiRequest(context.Background(), cfg.Server, http.MethodPost, fmt.Sprintf("/api/repos/%s/%s/pulls", targetOwner, targetRepo), cfg.Token, body, &out); err != nil {
return err
}
printPR(out)
return nil
}
func cliPRList(args []string) error {
jsonOut := removeBoolFlag(&args, "--json")
if len(args) != 1 {
return errors.New("usage: gitocean pr list OWNER/REPO")
}
owner, repo, err := splitOwnerRepo(args[0])
if err != nil {
return err
}
server, token := envServer(), ""
if cfg, err := loadConfig(); err == nil {
server, token = cfg.Server, cfg.Token
}
var out []PullRequest
if err := apiRequest(context.Background(), server, http.MethodGet, fmt.Sprintf("/api/repos/%s/%s/pulls", owner, repo), token, nil, &out); err != nil {
return err
}
if jsonOut {
printJSON(out)
return nil
}
for _, pr := range out {
fmt.Printf("#%-4d %-8s %-30s %s/%s:%s -> %s\n", pr.Number, pr.Status, pr.Title, pr.SourceOwner, pr.SourceRepo, pr.SourceBranch, pr.TargetBranch)
}
return nil
}
func cliPRView(args []string) error {
jsonOut := removeBoolFlag(&args, "--json")
if len(args) != 2 {
return errors.New("usage: gitocean pr view OWNER/REPO NUMBER")
}
owner, repo, err := splitOwnerRepo(args[0])
if err != nil {
return err
}
server, token := envServer(), ""
if cfg, err := loadConfig(); err == nil {
server, token = cfg.Server, cfg.Token
}
var out PullRequest
if err := apiRequest(context.Background(), server, http.MethodGet, fmt.Sprintf("/api/repos/%s/%s/pulls/%s", owner, repo, args[1]), token, nil, &out); err != nil {
return err
}
if jsonOut {
printJSON(out)
return nil
}
printPR(out)
return nil
}
func cliPRDiff(args []string) error {
if len(args) != 2 {
return errors.New("usage: gitocean pr diff OWNER/REPO NUMBER")
}
owner, repo, err := splitOwnerRepo(args[0])
if err != nil {
return err
}
server, token := envServer(), ""
if cfg, err := loadConfig(); err == nil {
server, token = cfg.Server, cfg.Token
}
text, err := apiTextRequest(context.Background(), server, fmt.Sprintf("/api/repos/%s/%s/pulls/%s/diff", owner, repo, args[1]), token)
if err != nil {
return err
}
fmt.Print(text)
return nil
}
func cliPRCheckout(args []string) error {
if len(args) != 2 {
return errors.New("usage: gitocean pr checkout OWNER/REPO NUMBER")
}
owner, repo, err := splitOwnerRepo(args[0])
if err != nil {
return err
}
server, token := envServer(), ""
if cfg, err := loadConfig(); err == nil {
server, token = cfg.Server, cfg.Token
}
var pr PullRequest
if err := apiRequest(context.Background(), server, http.MethodGet, fmt.Sprintf("/api/repos/%s/%s/pulls/%s", owner, repo, args[1]), token, nil, &pr); err != nil {
return err
}
branch := "pr-" + args[1]
remote := repoCloneURL(server, pr.SourceOwner, pr.SourceRepo)
if err := runGit("", "fetch", remote, pr.SourceBranch+":"+branch); err != nil {
_ = runGit("", "branch", "-D", branch)
if err := runGit("", "fetch", remote, pr.SourceBranch+":"+branch); err != nil {
return err
}
}
return runGit("", "checkout", branch)
}
func cliPRComment(args []string) error {
if len(args) < 3 {
return errors.New("usage: gitocean pr comment OWNER/REPO NUMBER \"text\"")
}
owner, repo, err := splitOwnerRepo(args[0])
if err != nil {
return err
}
cfg, err := loadConfig()
if err != nil {
return err
}
body := strings.Join(args[2:], " ")
var out PRComment
if err := apiRequest(context.Background(), cfg.Server, http.MethodPost, fmt.Sprintf("/api/repos/%s/%s/pulls/%s/comments", owner, repo, args[1]), cfg.Token, map[string]string{"body": body}, &out); err != nil {
return err
}
fmt.Printf("Commented as %s\n", out.Author)
return nil
}
func cliPRComments(args []string) error {
if len(args) != 2 {
return errors.New("usage: gitocean pr comments OWNER/REPO NUMBER")
}
owner, repo, err := splitOwnerRepo(args[0])
if err != nil {
return err
}
server, token := envServer(), ""
if cfg, err := loadConfig(); err == nil {
server, token = cfg.Server, cfg.Token
}
var out []PRComment
if err := apiRequest(context.Background(), server, http.MethodGet, fmt.Sprintf("/api/repos/%s/%s/pulls/%s/comments", owner, repo, args[1]), token, nil, &out); err != nil {
return err
}
for _, c := range out {
fmt.Printf("%s by %s:\n%s\n\n", c.CreatedAt.Format(time.RFC3339), c.Author, c.Body)
}
return nil
}
func cliPRAction(args []string, action string) error {
if len(args) != 2 {
return fmt.Errorf("usage: gitocean pr %s OWNER/REPO NUMBER", action)
}
owner, repo, err := splitOwnerRepo(args[0])
if err != nil {
return err
}
cfg, err := loadConfig()
if err != nil {
return err
}
var out PullRequest
if err := apiRequest(context.Background(), cfg.Server, http.MethodPost, fmt.Sprintf("/api/repos/%s/%s/pulls/%s/%s", owner, repo, args[1], action), cfg.Token, nil, &out); err != nil {
return err
}
printPR(out)
return nil
}