248 lines
6.9 KiB
Go
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
|
|
}
|