mirror of https://gitlab.com/gitlab-org/cli.git
refactor: enhance error handling
This commit is contained in:
parent
4306c23cea
commit
09ba1378f4
4
Makefile
4
Makefile
|
@ -20,9 +20,9 @@ GO_LDFLAGS := -X main.version=$(GLAB_VERSION) $(GO_LDFLAGS)
|
|||
GO_LDFLAGS := -X main.build=$(BUILD_DATE) $(GO_LDFLAGS)
|
||||
|
||||
build:
|
||||
go build -trimpath -ldflags "$(GO_LDFLAGS)" -o ./bin/glab ./cmd/glab
|
||||
go build -trimpath -ldflags "$(GO_LDFLAGS) -X main.usageMode=prod" -o ./bin/glab ./cmd/glab
|
||||
run:
|
||||
go run -trimpath -ldflags "$(GO_LDFLAGS)" ./cmd/glab $(var)
|
||||
go run -trimpath -ldflags "$(GO_LDFLAGS) -X main.usageMode=dev" ./cmd/glab $(var)
|
||||
test:
|
||||
go test ./...
|
||||
rt: #Test release
|
||||
|
|
|
@ -1,22 +1,86 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"glab/commands"
|
||||
"glab/internal/utils"
|
||||
"io"
|
||||
"net"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"glab/commands"
|
||||
"glab/internal/config"
|
||||
)
|
||||
|
||||
// Version is set at build
|
||||
var version string
|
||||
|
||||
// build is set at build
|
||||
var build string
|
||||
// usage mode is set at build to either "dev" or "prod" depending how binary is created
|
||||
var usageMode string
|
||||
var debug bool
|
||||
|
||||
func main() {
|
||||
commands.Version = version
|
||||
commands.Build = build
|
||||
if err := commands.Execute(); err != nil {
|
||||
fmt.Println(err)
|
||||
|
||||
initConfig()
|
||||
if usageMode == "dev" {
|
||||
debug = true
|
||||
}
|
||||
if cmd, err := commands.Execute(); err != nil {
|
||||
printError(os.Stderr, err, cmd, debug)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func initConfig() {
|
||||
config.SetGlobalPathDir()
|
||||
config.UseGlobalConfig = true
|
||||
|
||||
if config.GetEnv("GITLAB_URI") == "NOTFOUND" || config.GetEnv("GITLAB_URI") == "OK" {
|
||||
config.SetEnv("GITLAB_URI", "https://gitlab.com")
|
||||
}
|
||||
if config.GetEnv("GIT_REMOTE_URL_VAR") == "NOTFOUND" || config.GetEnv("GIT_REMOTE_URL_VAR") == "OK" {
|
||||
config.SetEnv("GIT_REMOTE_URL_VAR", "origin")
|
||||
}
|
||||
|
||||
config.UseGlobalConfig = false
|
||||
}
|
||||
|
||||
func printError(out io.Writer, err error, cmd *cobra.Command, debug bool) {
|
||||
if err == utils.SilentError {
|
||||
return
|
||||
}
|
||||
|
||||
var dnsError *net.DNSError
|
||||
if errors.As(err, &dnsError) {
|
||||
_, _ = fmt.Fprintf(out, "error connecting to %s\n", dnsError.Name)
|
||||
if debug {
|
||||
_, _ = fmt.Fprintln(out, dnsError)
|
||||
}
|
||||
_, _ = fmt.Fprintln(out, "check your internet connection or status.gitlab.com or 'Run sudo gitlab-ctl status' on your server if self-hosted")
|
||||
return
|
||||
}
|
||||
|
||||
re := regexp.MustCompile(`(?s){(.*)}`)
|
||||
m := re.FindAllStringSubmatch(err.Error(), -1)
|
||||
if len(m) != 0 {
|
||||
if len(m[0]) >= 1 {
|
||||
_, _ = fmt.Fprintln(out, m[0][1])
|
||||
}
|
||||
} else {
|
||||
_, _ = fmt.Fprintln(out, err)
|
||||
}
|
||||
|
||||
var flagError *utils.FlagError
|
||||
if errors.As(err, &flagError) || strings.HasPrefix(err.Error(), "unknown command ") {
|
||||
if !strings.HasSuffix(err.Error(), "\n") {
|
||||
_, _ = fmt.Fprintln(out)
|
||||
}
|
||||
_, _ = fmt.Fprintln(out, cmd.UsageString())
|
||||
}
|
||||
}
|
|
@ -0,0 +1,90 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"flag"
|
||||
"fmt"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
"glab/internal/config"
|
||||
"glab/internal/utils"
|
||||
"math/rand"
|
||||
"net"
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Test started when the test binary is started
|
||||
// and calls the main function
|
||||
func TestGlab(t *testing.T) {
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
flag.Set("test", "../coverage-" + strconv.Itoa(int(rand.Uint64())) + ".out")
|
||||
main()
|
||||
}
|
||||
|
||||
func Test_printError(t *testing.T) {
|
||||
cmd := &cobra.Command{}
|
||||
|
||||
type args struct {
|
||||
err error
|
||||
cmd *cobra.Command
|
||||
debug bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
wantOut string
|
||||
}{
|
||||
{
|
||||
name: "generic error",
|
||||
args: args{
|
||||
err: errors.New("the app exploded"),
|
||||
cmd: nil,
|
||||
debug: false,
|
||||
},
|
||||
wantOut: "the app exploded\n",
|
||||
},
|
||||
{
|
||||
name: "DNS error",
|
||||
args: args{
|
||||
err: fmt.Errorf("DNS oopsie: %w", &net.DNSError{
|
||||
Name: config.GetEnv("GITLAB_URI")+"/api/v4",
|
||||
}),
|
||||
cmd: nil,
|
||||
debug: false,
|
||||
},
|
||||
wantOut: `error connecting to `+config.GetEnv("GITLAB_URI")+`/api/v4
|
||||
check your internet connection or status.gitlab.com or 'Run sudo gitlab-ctl status' on your server if self-hosted
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "Cobra flag error",
|
||||
args: args{
|
||||
err: &utils.FlagError{Err: errors.New("unknown flag --foo")},
|
||||
cmd: cmd,
|
||||
debug: false,
|
||||
},
|
||||
wantOut: "unknown flag --foo\n\nUsage:\n\n",
|
||||
},
|
||||
{
|
||||
name: "unknown Cobra command error",
|
||||
args: args{
|
||||
err: errors.New("unknown command foo"),
|
||||
cmd: cmd,
|
||||
debug: false,
|
||||
},
|
||||
wantOut: "unknown command foo\n\nUsage:\n\n",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
out := &bytes.Buffer{}
|
||||
printError(out, tt.args.err, tt.args.cmd, tt.args.debug)
|
||||
if gotOut := out.String(); gotOut != tt.wantOut {
|
||||
t.Errorf("printError() = %q, want %q", gotOut, tt.wantOut)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -6,7 +6,6 @@ import (
|
|||
"github.com/xanzy/go-gitlab"
|
||||
"glab/internal/git"
|
||||
"glab/internal/manip"
|
||||
"log"
|
||||
"strings"
|
||||
)
|
||||
|
||||
|
@ -16,10 +15,10 @@ var issueCloseCmd = &cobra.Command{
|
|||
Long: ``,
|
||||
Aliases: []string{"unsub"},
|
||||
Args: cobra.ExactArgs(1),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if len(args) > 1 {
|
||||
cmdErr(cmd, args)
|
||||
return
|
||||
return nil
|
||||
}
|
||||
if len(args) > 0 {
|
||||
issueID := strings.TrimSpace(args[0])
|
||||
|
@ -34,7 +33,7 @@ var issueCloseCmd = &cobra.Command{
|
|||
fmt.Println("Closing Issue...")
|
||||
issue, resp, err := gitlabClient.Issues.UpdateIssue(repo, manip.StringToInt(i2), l)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
return err
|
||||
}
|
||||
if isSuccessful(resp.StatusCode) {
|
||||
fmt.Println("Issue #" + i2 + " closed")
|
||||
|
@ -47,7 +46,9 @@ var issueCloseCmd = &cobra.Command{
|
|||
}
|
||||
} else {
|
||||
cmdErr(cmd, args)
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package commands
|
||||
|
||||
import (
|
||||
"log"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
@ -16,10 +15,10 @@ var issueCreateCmd = &cobra.Command{
|
|||
Long: ``,
|
||||
Aliases: []string{"new"},
|
||||
Args: cobra.ExactArgs(0),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if len(args) > 0 {
|
||||
cmdErr(cmd, args)
|
||||
return
|
||||
return nil
|
||||
}
|
||||
|
||||
l := &gitlab.CreateIssueOptions{}
|
||||
|
@ -31,11 +30,6 @@ var issueCreateCmd = &cobra.Command{
|
|||
} else {
|
||||
issueTitle = manip.AskQuestionWithInput("Title", "", true)
|
||||
}
|
||||
if label, _ := cmd.Flags().GetString("label"); label != "" {
|
||||
issueLabel = strings.Trim(label, "[] ")
|
||||
} else {
|
||||
issueLabel = manip.AskQuestionWithInput("Label(s) [Comma Separated]", "", false)
|
||||
}
|
||||
if description, _ := cmd.Flags().GetString("description"); description != "" {
|
||||
issueDescription = strings.Trim(description, " ")
|
||||
} else {
|
||||
|
@ -49,6 +43,11 @@ var issueCreateCmd = &cobra.Command{
|
|||
})
|
||||
}
|
||||
}
|
||||
if label, _ := cmd.Flags().GetString("label"); label != "" {
|
||||
issueLabel = strings.Trim(label, "[] ")
|
||||
} else {
|
||||
issueLabel = manip.AskQuestionWithInput("Label(s) [Comma Separated]", "", false)
|
||||
}
|
||||
l.Title = gitlab.String(issueTitle)
|
||||
l.Labels = &gitlab.Labels{issueLabel}
|
||||
l.Description = &issueDescription
|
||||
|
@ -81,9 +80,10 @@ var issueCreateCmd = &cobra.Command{
|
|||
}
|
||||
issue, _, err := gitlabClient.Issues.CreateIssue(repo, l)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
return err
|
||||
}
|
||||
displayIssue(issue)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
@ -14,10 +14,10 @@ var issueDeleteCmd = &cobra.Command{
|
|||
Long: ``,
|
||||
Aliases: []string{"del"},
|
||||
Args: cobra.ExactArgs(1),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if len(args) > 1 {
|
||||
cmdErr(cmd, args)
|
||||
return
|
||||
return nil
|
||||
}
|
||||
if len(args) > 0 {
|
||||
issueID := strings.TrimSpace(args[0])
|
||||
|
@ -40,6 +40,7 @@ var issueDeleteCmd = &cobra.Command{
|
|||
} else {
|
||||
cmdErr(cmd, args)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
@ -4,7 +4,6 @@ import (
|
|||
"github.com/spf13/cobra"
|
||||
"github.com/xanzy/go-gitlab"
|
||||
"glab/internal/git"
|
||||
"log"
|
||||
)
|
||||
|
||||
var issueListCmd = &cobra.Command{
|
||||
|
@ -13,7 +12,7 @@ var issueListCmd = &cobra.Command{
|
|||
Long: ``,
|
||||
Aliases: []string{"ls"},
|
||||
Args: cobra.ExactArgs(0),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
var state string
|
||||
if lb, _ := cmd.Flags().GetBool("all"); lb {
|
||||
state = "all"
|
||||
|
@ -45,9 +44,10 @@ var issueListCmd = &cobra.Command{
|
|||
}
|
||||
issues, _, err := gitlabClient.Issues.ListProjectIssues(repo, l)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
return err
|
||||
}
|
||||
displayAllIssues(issues)
|
||||
return nil
|
||||
|
||||
},
|
||||
}
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
package commands
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"glab/internal/git"
|
||||
"glab/internal/manip"
|
||||
"log"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
gitlab "github.com/xanzy/go-gitlab"
|
||||
"glab/internal/git"
|
||||
"glab/internal/manip"
|
||||
)
|
||||
|
||||
var issueNoteCreateCmd = &cobra.Command{
|
||||
|
@ -16,7 +15,7 @@ var issueNoteCreateCmd = &cobra.Command{
|
|||
Short: "Add a comment or note to an issue on Gitlab",
|
||||
Long: ``,
|
||||
Args: cobra.ExactArgs(1),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
|
||||
gitlabClient, repo := git.InitGitlabClient()
|
||||
mID := args[0]
|
||||
|
@ -25,13 +24,11 @@ var issueNoteCreateCmd = &cobra.Command{
|
|||
repo = r
|
||||
}
|
||||
if err != nil {
|
||||
er(err)
|
||||
return
|
||||
return err
|
||||
}
|
||||
mr, _, err := gitlabClient.Issues.GetIssue(repo, manip.StringToInt(mID))
|
||||
if err != nil {
|
||||
er(err)
|
||||
return
|
||||
return err
|
||||
}
|
||||
if body == "" {
|
||||
body = manip.Editor(manip.EditorOptions{
|
||||
|
@ -41,16 +38,17 @@ var issueNoteCreateCmd = &cobra.Command{
|
|||
})
|
||||
}
|
||||
if body == "" {
|
||||
log.Fatal("Aborted... Note is empty")
|
||||
return errors.New("aborted... Note is empty")
|
||||
}
|
||||
|
||||
noteInfo,_, err := gitlabClient.Notes.CreateIssueNote(repo, manip.StringToInt(mID), &gitlab.CreateIssueNoteOptions{
|
||||
Body: &body,
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
return err
|
||||
}
|
||||
fmt.Printf("%s#note_%d\n",mr.WebURL, noteInfo.ID)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
@ -6,7 +6,6 @@ import (
|
|||
"github.com/xanzy/go-gitlab"
|
||||
"glab/internal/git"
|
||||
"glab/internal/manip"
|
||||
"log"
|
||||
"strings"
|
||||
)
|
||||
|
||||
|
@ -16,10 +15,10 @@ var issueReopenCmd = &cobra.Command{
|
|||
Long: ``,
|
||||
Aliases: []string{"open"},
|
||||
Args: cobra.ExactArgs(1),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if len(args) > 1 {
|
||||
cmdErr(cmd, args)
|
||||
return
|
||||
return nil
|
||||
}
|
||||
if len(args) > 0 {
|
||||
issueID := strings.TrimSpace(args[0])
|
||||
|
@ -34,7 +33,7 @@ var issueReopenCmd = &cobra.Command{
|
|||
fmt.Println("Reopening Issue...")
|
||||
issue, resp, err := gitlabClient.Issues.UpdateIssue(repo, manip.StringToInt(i2), l)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
return err
|
||||
}
|
||||
if isSuccessful(resp.StatusCode) {
|
||||
fmt.Println("Issue #" + i2 + " eopened")
|
||||
|
@ -48,6 +47,7 @@ var issueReopenCmd = &cobra.Command{
|
|||
} else {
|
||||
cmdErr(cmd, args)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
@ -14,10 +14,10 @@ var issueSubscribeCmd = &cobra.Command{
|
|||
Long: ``,
|
||||
Aliases: []string{"sub"},
|
||||
Args: cobra.ExactArgs(1),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if len(args) > 1 {
|
||||
cmdErr(cmd, args)
|
||||
return
|
||||
return nil
|
||||
}
|
||||
if len(args) > 0 {
|
||||
mergeID := strings.TrimSpace(args[0])
|
||||
|
@ -42,6 +42,7 @@ var issueSubscribeCmd = &cobra.Command{
|
|||
} else {
|
||||
cmdErr(cmd, args)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
package commands
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
test "github.com/smartystreets/goconvey/convey"
|
||||
)
|
||||
|
||||
func TestIssueCmd(t *testing.T) {
|
||||
test.Convey("test issue", t, func() {
|
||||
args := []string{"issue"}
|
||||
RootCmd.SetArgs(args)
|
||||
test.ShouldBeNil(RootCmd.Execute())
|
||||
})
|
||||
}
|
|
@ -14,10 +14,10 @@ var issueUnsubscribeCmd = &cobra.Command{
|
|||
Long: ``,
|
||||
Aliases: []string{"unsub"},
|
||||
Args: cobra.ExactArgs(1),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if len(args) > 1 {
|
||||
cmdErr(cmd, args)
|
||||
return
|
||||
return nil
|
||||
}
|
||||
if len(args) > 0 {
|
||||
mergeID := strings.TrimSpace(args[0])
|
||||
|
@ -28,8 +28,10 @@ var issueUnsubscribeCmd = &cobra.Command{
|
|||
arrIds := strings.Split(strings.Trim(mergeID, "[] "), ",")
|
||||
for _, i2 := range arrIds {
|
||||
fmt.Println("Unsubscribing to Issue #" + i2)
|
||||
issue, resp, _ := gitlabClient.Issues.UnsubscribeFromIssue(repo, manip.StringToInt(i2))
|
||||
|
||||
issue, resp, err := gitlabClient.Issues.UnsubscribeFromIssue(repo, manip.StringToInt(i2))
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
if isSuccessful(resp.StatusCode) {
|
||||
fmt.Println("Unsubscribed to issue #" + i2)
|
||||
displayIssue(issue)
|
||||
|
@ -42,6 +44,7 @@ var issueUnsubscribeCmd = &cobra.Command{
|
|||
} else {
|
||||
cmdErr(cmd, args)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
@ -11,7 +11,6 @@ import (
|
|||
"glab/internal/git"
|
||||
"glab/internal/manip"
|
||||
"glab/internal/utils"
|
||||
"log"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
@ -22,10 +21,10 @@ var issueViewCmd = &cobra.Command{
|
|||
Long: ``,
|
||||
Aliases: []string{"show"},
|
||||
Args: cobra.ExactArgs(1),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if len(args) == 0 || len(args) > 1 {
|
||||
cmdErr(cmd, args)
|
||||
return
|
||||
return nil
|
||||
}
|
||||
pid := manip.StringToInt(args[0])
|
||||
|
||||
|
@ -37,17 +36,17 @@ var issueViewCmd = &cobra.Command{
|
|||
|
||||
issue, _, err := gitlabClient.Issues.GetIssue(repo, pid)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
return err
|
||||
}
|
||||
if lb, _ := cmd.Flags().GetBool("web"); lb { //open in browser if --web flag is specified
|
||||
a, err := browser.Command(issue.WebURL)
|
||||
if err != nil {
|
||||
er(err)
|
||||
return err
|
||||
}
|
||||
if err := a.Run(); err != nil {
|
||||
er(err)
|
||||
return err
|
||||
}
|
||||
return
|
||||
return nil
|
||||
}
|
||||
var issueState string
|
||||
if issue.State == "opened" {
|
||||
|
@ -108,7 +107,7 @@ var issueViewCmd = &cobra.Command{
|
|||
l := &gitlab.ListIssueNotesOptions{}
|
||||
notes, _, err := gitlabClient.Notes.ListIssueNotes(repo, pid, l)
|
||||
if err != nil {
|
||||
er(err)
|
||||
return err
|
||||
}
|
||||
|
||||
table := uitable.New()
|
||||
|
@ -138,6 +137,7 @@ var issueViewCmd = &cobra.Command{
|
|||
fmt.Println("There are no comments on this issue")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
@ -7,11 +7,15 @@ var labelCmd = &cobra.Command{
|
|||
Use: "label <command> [flags]",
|
||||
Short: `Manage labels on remote`,
|
||||
Long: ``,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if len(args) == 0 || len(args) > 2 {
|
||||
cmd.Help()
|
||||
return
|
||||
err := cmd.Help()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
package commands
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
test "github.com/smartystreets/goconvey/convey"
|
||||
)
|
||||
|
||||
func TestLabelCmd(t *testing.T) {
|
||||
test.Convey("test label cmd", t, func() {
|
||||
args := []string{"label"}
|
||||
RootCmd.SetArgs(args)
|
||||
test.ShouldBeNil(RootCmd.Execute())
|
||||
})
|
||||
}
|
|
@ -16,7 +16,7 @@ var mrCreateNoteCmd = &cobra.Command{
|
|||
Short: "Add a comment or note to merge request",
|
||||
Long: ``,
|
||||
Args: cobra.ExactArgs(1),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
|
||||
gitlabClient, repo := git.InitGitlabClient()
|
||||
mID := args[0]
|
||||
|
@ -25,13 +25,11 @@ var mrCreateNoteCmd = &cobra.Command{
|
|||
repo = r
|
||||
}
|
||||
if err != nil {
|
||||
er(err)
|
||||
return
|
||||
return err
|
||||
}
|
||||
mr, _, err := gitlabClient.MergeRequests.GetMergeRequest(repo, manip.StringToInt(mID), &gitlab.GetMergeRequestsOptions{})
|
||||
if err != nil {
|
||||
er(err)
|
||||
return
|
||||
return err
|
||||
}
|
||||
if body == "" {
|
||||
body = manip.Editor(manip.EditorOptions{
|
||||
|
@ -48,9 +46,10 @@ var mrCreateNoteCmd = &cobra.Command{
|
|||
Body: &body,
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
return err
|
||||
}
|
||||
fmt.Printf("%s#note_%d\n",mr.WebURL, noteInfo.ID)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
package commands
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
test "github.com/smartystreets/goconvey/convey"
|
||||
)
|
||||
|
||||
func TestMrCmd(t *testing.T) {
|
||||
test.Convey("test mr", t, func() {
|
||||
args := []string{"mr"}
|
||||
RootCmd.SetArgs(args)
|
||||
RootCmd.Execute()
|
||||
})
|
||||
}
|
|
@ -293,7 +293,7 @@ func pipelineJobTraceWithSha(pid interface{}, sha, name string) (io.Reader, *git
|
|||
return r, job, err
|
||||
}
|
||||
|
||||
// CIJobs returns a list of jobs in a pipeline for a given sha. The jobs are
|
||||
// pipelineJobsWithSha returns a list of jobs in a pipeline for a given sha. The jobs are
|
||||
// returned sorted by their CreatedAt time
|
||||
func pipelineJobsWithSha(pid interface{}, sha string) ([]*gitlab.Job, error) {
|
||||
gitlabClient, _ := git.InitGitlabClient()
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
package commands
|
||||
|
||||
import (
|
||||
test "github.com/smartystreets/goconvey/convey"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestPipelineCmd(t *testing.T) {
|
||||
test.Convey("test pipeline cmd", t, func() {
|
||||
args := []string{"pipeline"}
|
||||
RootCmd.SetArgs(args)
|
||||
test.ShouldBeNil(RootCmd.Execute())
|
||||
})
|
||||
test.Convey("test pipeline alias cmd", t, func() {
|
||||
args := []string{"pipe"}
|
||||
RootCmd.SetArgs(args)
|
||||
test.ShouldBeNil(RootCmd.Execute())
|
||||
})
|
||||
}
|
|
@ -55,9 +55,9 @@ var RootCmd = &cobra.Command{
|
|||
}
|
||||
|
||||
// Execute executes the root command.
|
||||
func Execute() error {
|
||||
func Execute() (*cobra.Command, error) {
|
||||
RootCmd.Flags().BoolP("version", "v", false, "show glab version information")
|
||||
return RootCmd.Execute()
|
||||
return RootCmd.ExecuteC()
|
||||
}
|
||||
|
||||
// versionCmd represents the version command
|
||||
|
@ -79,7 +79,6 @@ var configCmd = &cobra.Command{
|
|||
}
|
||||
|
||||
func init() {
|
||||
cobra.OnInitialize(initConfig)
|
||||
RootCmd.AddCommand(updateCmd)
|
||||
initConfigCmd()
|
||||
RootCmd.AddCommand(configCmd)
|
||||
|
@ -94,18 +93,6 @@ func cmdErr(cmd *cobra.Command, args []string) {
|
|||
_ = cmd.Usage()
|
||||
}
|
||||
|
||||
func initConfig() {
|
||||
config.SetGlobalPathDir()
|
||||
config.UseGlobalConfig = true
|
||||
if config.GetEnv("GITLAB_URI") == "NOTFOUND" || config.GetEnv("GITLAB_URI") == "OK" {
|
||||
config.SetEnv("GITLAB_URI", "https://gitlab.com")
|
||||
}
|
||||
if config.GetEnv("GIT_REMOTE_URL_VAR") == "NOTFOUND" || config.GetEnv("GIT_REMOTE_URL_VAR") == "OK" {
|
||||
config.SetEnv("GIT_REMOTE_URL_VAR", "origin")
|
||||
}
|
||||
config.UseGlobalConfig = false
|
||||
}
|
||||
|
||||
func initConfigCmd() {
|
||||
configCmd.Flags().BoolP("global", "g", false, "Set configuration globally")
|
||||
configCmd.Flags().StringP("url", "u", "", "specify the url of the gitlab server if self hosted (eg: https://gitlab.example.com).")
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
package commands
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/xanzy/go-gitlab"
|
||||
"glab/internal/git"
|
||||
)
|
||||
|
||||
func currentUser(token string) (string, error) {
|
||||
gLab, _ := git.InitGitlabClient()
|
||||
u, _, err := gLab.Users.CurrentUser()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return u.Username, nil
|
||||
}
|
||||
|
||||
func getUser(uid int) (*gitlab.User, error) {
|
||||
gLab, _ := git.InitGitlabClient()
|
||||
u, _, err := gLab.Users.GetUser(uid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return u, nil
|
||||
}
|
||||
|
||||
func getUsername(uid int) string {
|
||||
u, err := getUser(uid)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
return u.Username
|
||||
}
|
||||
|
||||
func getUserActivities() ([]*gitlab.UserActivity, error) {
|
||||
gLab, _ := git.InitGitlabClient()
|
||||
l := &gitlab.GetUserActivitiesOptions{}
|
||||
ua, _, err := gLab.Users.GetUserActivities(l)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ua, nil
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
package commands
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
test "github.com/smartystreets/goconvey/convey"
|
||||
)
|
||||
|
||||
func TestVersionCmd(t *testing.T) {
|
||||
test.Convey("test version cmd", t, func() {
|
||||
args := []string{"version"}
|
||||
RootCmd.SetArgs(args)
|
||||
test.ShouldBeNil(RootCmd.Execute())
|
||||
})
|
||||
}
|
1
go.mod
1
go.mod
|
@ -21,6 +21,7 @@ require (
|
|||
github.com/onsi/gomega v1.10.1 // indirect
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/rivo/tview v0.0.0-20200712113419-c65badfc3d92
|
||||
github.com/smartystreets/goconvey v1.6.4
|
||||
github.com/spf13/cobra v1.0.0
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/tcnksm/go-gitconfig v0.1.2
|
||||
|
|
11
go.sum
11
go.sum
|
@ -26,6 +26,7 @@ github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+Ce
|
|||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||
github.com/charmbracelet/glamour v0.2.0 h1:mTgaiNiumpqTZp3qVM6DH9UB0NlbY17wejoMf1kM8Pg=
|
||||
github.com/charmbracelet/glamour v0.2.0/go.mod h1:UA27Kwj3QHialP74iU6C+Gpc8Y7IOAKupeKMLLBURWM=
|
||||
github.com/cli/cli v0.11.1 h1:NqGtJSScC6vqfgMdplu5dBRME9pcActcYSFfcQbBa3k=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
|
||||
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||
|
@ -89,6 +90,8 @@ github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaU
|
|||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
|
||||
github.com/gookit/color v1.2.7 h1:4qePMNWZhrmbfYJDix+J4V2l0iVW+6jQGjicELlN14E=
|
||||
github.com/gookit/color v1.2.7/go.mod h1:AhIE+pS6D4Ql0SQWbBeXPHw7gY0/sjHoA4s/n1KB7xg=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||
github.com/gosuri/uilive v0.0.4 h1:hUEBpQDj8D8jXgtCdBu7sWsy5sbW/5GhuO8KBwJ2jyY=
|
||||
github.com/gosuri/uilive v0.0.4/go.mod h1:V/epo5LjjlDE5RJUcqx8dbw+zc93y5Ya3yg8tfZ74VI=
|
||||
|
@ -111,6 +114,8 @@ github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpO
|
|||
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
|
||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
|
||||
|
@ -206,6 +211,10 @@ github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAm
|
|||
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
|
||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI=
|
||||
|
@ -317,6 +326,8 @@ golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGm
|
|||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b h1:mSUCVIwDx4hfXJfWsOPfdzEHxzb2Xjl6BQ8YgPnazQA=
|
||||
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
|
|
|
@ -4,10 +4,6 @@ import (
|
|||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/tcnksm/go-gitconfig"
|
||||
"github.com/xanzy/go-gitlab"
|
||||
"glab/internal/config"
|
||||
"glab/internal/run"
|
||||
"log"
|
||||
"net/url"
|
||||
"os"
|
||||
|
@ -15,6 +11,11 @@ import (
|
|||
"path"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/tcnksm/go-gitconfig"
|
||||
"github.com/xanzy/go-gitlab"
|
||||
"glab/internal/config"
|
||||
"glab/internal/run"
|
||||
)
|
||||
|
||||
// GetRepo returns the repo name of the git directory with the namespace like profclems/glab
|
||||
|
@ -378,6 +379,107 @@ func firstLine(output []byte) string {
|
|||
return string(output)
|
||||
}
|
||||
|
||||
var remoteRE = regexp.MustCompile(`(.+)\s+(.+)\s+\((push|fetch)\)`)
|
||||
|
||||
// RemoteSet is a slice of git remotes
|
||||
type RemoteSet []*Remote
|
||||
|
||||
func NewRemote(name string, u string) *Remote {
|
||||
pu, _ := url.Parse(u)
|
||||
return &Remote{
|
||||
Name: name,
|
||||
FetchURL: pu,
|
||||
PushURL: pu,
|
||||
}
|
||||
}
|
||||
|
||||
// Remote is a parsed git remote
|
||||
type Remote struct {
|
||||
Name string
|
||||
FetchURL *url.URL
|
||||
PushURL *url.URL
|
||||
}
|
||||
|
||||
func (r *Remote) String() string {
|
||||
return r.Name
|
||||
}
|
||||
|
||||
// Remotes gets the git remotes set for the current repo
|
||||
func Remotes() (RemoteSet, error) {
|
||||
list, err := listRemotes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return parseRemotes(list), nil
|
||||
}
|
||||
|
||||
func parseRemotes(gitRemotes []string) (remotes RemoteSet) {
|
||||
for _, r := range gitRemotes {
|
||||
match := remoteRE.FindStringSubmatch(r)
|
||||
if match == nil {
|
||||
continue
|
||||
}
|
||||
name := strings.TrimSpace(match[1])
|
||||
urlStr := strings.TrimSpace(match[2])
|
||||
urlType := strings.TrimSpace(match[3])
|
||||
|
||||
var rem *Remote
|
||||
if len(remotes) > 0 {
|
||||
rem = remotes[len(remotes)-1]
|
||||
if name != rem.Name {
|
||||
rem = nil
|
||||
}
|
||||
}
|
||||
if rem == nil {
|
||||
rem = &Remote{Name: name}
|
||||
remotes = append(remotes, rem)
|
||||
}
|
||||
|
||||
u, err := ParseURL(urlStr)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
switch urlType {
|
||||
case "fetch":
|
||||
rem.FetchURL = u
|
||||
case "push":
|
||||
rem.PushURL = u
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// AddRemote adds a new git remote and auto-fetches objects from it
|
||||
func AddRemote(name, u string) (*Remote, error) {
|
||||
addCmd := exec.Command("git", "remote", "add", "-f", name, u)
|
||||
err := run.PrepareCmd(addCmd).Run()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var urlParsed *url.URL
|
||||
if strings.HasPrefix(u, "https") {
|
||||
urlParsed, err = url.Parse(u)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
} else {
|
||||
urlParsed, err = ParseURL(u)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return &Remote{
|
||||
Name: name,
|
||||
FetchURL: urlParsed,
|
||||
PushURL: urlParsed,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func RunCmd(args []string) (err error) {
|
||||
gitCmd := GitCommand(args...)
|
||||
gitCmd.Stdin = os.Stdin
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
package git
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCommits(t *testing.T) {
|
||||
type args struct {
|
||||
baseRef string
|
||||
headRef string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want []*Commit
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "Commit",
|
||||
args: args{"trunk","HEAD"},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := Commits(tt.args.baseRef, tt.args.headRef)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("Commits() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("Commits() got = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -44,17 +44,6 @@ func ParseURL(rawURL string) (u *url.URL, err error) {
|
|||
}
|
||||
|
||||
// IsValidUrl tests a string to determine if it is a well-structured url or not.
|
||||
func IsValidURL(toTest string) bool {
|
||||
_, err := url.ParseRequestURI(toTest)
|
||||
if err != nil {
|
||||
if strings.HasPrefix(toTest, "git@") {
|
||||
return true
|
||||
}
|
||||
if strings.HasPrefix(toTest, "ssh:") {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
func IsValidURL(uri string) bool {
|
||||
return strings.HasPrefix(uri, "git@") || protocolRe.MatchString(uri)
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ func TestIsValidURL(t *testing.T) {
|
|||
{"Group namespace", args{"group/namespace/repo"}, false},
|
||||
{"HTTPS Protocol", args{"https://gitlab.com/profclems/glab.git"}, true},
|
||||
{"With SSH", args{"git@gitlab.com:profclems/glab.git"}, true},
|
||||
{"SSH Protocol", args{"ssh:user@example.com:my-project"}, true},
|
||||
{"SSH Protocol", args{"ssh://user@example.com:my-project"}, true},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
package utils
|
||||
|
||||
import "errors"
|
||||
|
||||
// FlagError is the kind of error raised in flag processing
|
||||
type FlagError struct {
|
||||
Err error
|
||||
}
|
||||
|
||||
func (fe FlagError) Error() string {
|
||||
return fe.Err.Error()
|
||||
}
|
||||
|
||||
func (fe FlagError) Unwrap() error {
|
||||
return fe.Err
|
||||
}
|
||||
|
||||
// SilentError is an error that triggers exit code 1 without any error messaging
|
||||
var SilentError = errors.New("SilentError")
|
Loading…
Reference in New Issue