From 6bad01c328310cc5598da5eb58ac775d2bd56bca Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Mon, 28 Sep 2020 00:00:07 +0200 Subject: [PATCH 01/22] Refactor: * Move Config & Login routines into intern package * rename global var in cmd * Move help func to utils --- cmd/config.go | 256 --------------------------------------- cmd/flags.go | 79 ++---------- cmd/issues.go | 14 ++- cmd/labels.go | 15 +-- cmd/login.go | 37 +++--- cmd/logout.go | 14 ++- cmd/milestone_issues.go | 12 +- cmd/milestones.go | 14 ++- cmd/notifications.go | 8 +- cmd/open.go | 3 +- cmd/pulls.go | 17 +-- cmd/releases.go | 15 +-- cmd/repos.go | 11 +- cmd/times.go | 13 +- modules/intern/config.go | 153 +++++++++++++++++++++++ modules/intern/login.go | 176 +++++++++++++++++++++++++++ modules/utils/path.go | 16 +++ 17 files changed, 448 insertions(+), 405 deletions(-) create mode 100644 modules/intern/config.go create mode 100644 modules/intern/login.go diff --git a/cmd/config.go b/cmd/config.go index 875f245..2cba75f 100644 --- a/cmd/config.go +++ b/cmd/config.go @@ -5,103 +5,11 @@ package cmd import ( - "crypto/tls" - "errors" - "fmt" - "io/ioutil" - "log" - "net/http" - "net/http/cookiejar" - "net/url" - "os" - "path/filepath" - "strings" - "code.gitea.io/sdk/gitea" - "code.gitea.io/tea/modules/git" - "code.gitea.io/tea/modules/utils" - "github.com/muesli/termenv" "github.com/urfave/cli/v2" - "gopkg.in/yaml.v2" ) -// Login represents a login to a gitea server, you even could add multiple logins for one gitea server -type Login struct { - Name string `yaml:"name"` - URL string `yaml:"url"` - Token string `yaml:"token"` - Default bool `yaml:"default"` - SSHHost string `yaml:"ssh_host"` - // optional path to the private key - SSHKey string `yaml:"ssh_key"` - Insecure bool `yaml:"insecure"` - // optional gitea username - User string `yaml:"user"` -} - -// Client returns a client to operate Gitea API -func (l *Login) Client() *gitea.Client { - httpClient := &http.Client{} - if l.Insecure { - cookieJar, _ := cookiejar.New(nil) - - httpClient = &http.Client{ - Jar: cookieJar, - Transport: &http.Transport{ - TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, - }} - } - - client, err := gitea.NewClient(l.URL, - gitea.SetToken(l.Token), - gitea.SetHTTPClient(httpClient), - ) - if err != nil { - log.Fatal(err) - } - return client -} - -// GetSSHHost returns SSH host name -func (l *Login) GetSSHHost() string { - if l.SSHHost != "" { - return l.SSHHost - } - - u, err := url.Parse(l.URL) - if err != nil { - return "" - } - - return u.Hostname() -} - -// Config reprensents local configurations -type Config struct { - Logins []Login `yaml:"logins"` -} - -var ( - config Config - yamlConfigPath string -) - -func init() { - homeDir, err := utils.Home() - if err != nil { - log.Fatal("Retrieve home dir failed") - } - - dir := filepath.Join(homeDir, ".tea") - err = os.MkdirAll(dir, os.ModePerm) - if err != nil { - log.Fatal("Init tea config dir " + dir + " failed") - } - - yamlConfigPath = filepath.Join(dir, "tea.yml") -} - func getGlamourTheme() string { if termenv.HasDarkBackground() { return "dark" @@ -109,170 +17,6 @@ func getGlamourTheme() string { return "light" } -func getOwnerAndRepo(repoPath, user string) (string, string) { - if len(repoPath) == 0 { - return "", "" - } - p := strings.Split(repoPath, "/") - if len(p) >= 2 { - return p[0], p[1] - } - return user, repoPath -} - -func getDefaultLogin() (*Login, error) { - if len(config.Logins) == 0 { - return nil, errors.New("No available login") - } - for _, l := range config.Logins { - if l.Default { - return &l, nil - } - } - - return &config.Logins[0], nil -} - -func getLoginByName(name string) *Login { - for _, l := range config.Logins { - if l.Name == name { - return &l - } - } - return nil -} - -func addLogin(login Login) error { - for _, l := range config.Logins { - if l.Name == login.Name { - if l.URL == login.URL && l.Token == login.Token { - return nil - } - return errors.New("Login name has already been used") - } - if l.URL == login.URL && l.Token == login.Token { - return errors.New("URL has been added") - } - } - - u, err := url.Parse(login.URL) - if err != nil { - return err - } - - if login.SSHHost == "" { - login.SSHHost = u.Hostname() - } - config.Logins = append(config.Logins, login) - - return nil -} - -func isFileExist(fileName string) (bool, error) { - f, err := os.Stat(fileName) - if err != nil { - if os.IsNotExist(err) { - return false, nil - } - return false, err - } - if f.IsDir() { - return false, errors.New("A directory with the same name exists") - } - return true, nil -} - -func loadConfig(ymlPath string) error { - exist, _ := isFileExist(ymlPath) - if exist { - Println("Found config file", ymlPath) - bs, err := ioutil.ReadFile(ymlPath) - if err != nil { - return err - } - - err = yaml.Unmarshal(bs, &config) - if err != nil { - return err - } - } - - return nil -} - -func saveConfig(ymlPath string) error { - bs, err := yaml.Marshal(&config) - if err != nil { - return err - } - return ioutil.WriteFile(ymlPath, bs, 0660) -} - -func curGitRepoPath(path string) (*Login, string, error) { - var err error - var repo *git.TeaRepo - if len(path) == 0 { - repo, err = git.RepoForWorkdir() - } else { - repo, err = git.RepoFromPath(path) - } - if err != nil { - return nil, "", err - } - gitConfig, err := repo.Config() - if err != nil { - return nil, "", err - } - - // if no remote - if len(gitConfig.Remotes) == 0 { - return nil, "", errors.New("No remote(s) found in this Git repository") - } - - // if only one remote exists - if len(gitConfig.Remotes) >= 1 && len(remoteValue) == 0 { - for remote := range gitConfig.Remotes { - remoteValue = remote - } - if len(gitConfig.Remotes) > 1 { - // if master branch is present, use it as the default remote - masterBranch, ok := gitConfig.Branches["master"] - if ok { - if len(masterBranch.Remote) > 0 { - remoteValue = masterBranch.Remote - } - } - } - } - - remoteConfig, ok := gitConfig.Remotes[remoteValue] - if !ok || remoteConfig == nil { - return nil, "", errors.New("Remote " + remoteValue + " not found in this Git repository") - } - - for _, l := range config.Logins { - for _, u := range remoteConfig.URLs { - p, err := git.ParseURL(strings.TrimSpace(u)) - if err != nil { - return nil, "", fmt.Errorf("Git remote URL parse failed: %s", err.Error()) - } - if strings.EqualFold(p.Scheme, "http") || strings.EqualFold(p.Scheme, "https") { - if strings.HasPrefix(u, l.URL) { - ps := strings.Split(p.Path, "/") - path := strings.Join(ps[len(ps)-2:], "/") - return &l, strings.TrimSuffix(path, ".git"), nil - } - } else if strings.EqualFold(p.Scheme, "ssh") { - if l.GetSSHHost() == strings.Split(p.Host, ":")[0] { - return &l, strings.TrimLeft(strings.TrimSuffix(p.Path, ".git"), "/"), nil - } - } - } - } - - return nil, "", errors.New("No Gitea login found. You might want to specify --repo (and --login) to work outside of a repository") -} - func getListOptions(ctx *cli.Context) gitea.ListOptions { page := ctx.Int("page") limit := ctx.Int("limit") diff --git a/cmd/flags.go b/cmd/flags.go index 133e12f..9e1dfc6 100644 --- a/cmd/flags.go +++ b/cmd/flags.go @@ -5,20 +5,16 @@ package cmd import ( - "log" - - "code.gitea.io/tea/modules/utils" - "github.com/urfave/cli/v2" ) // create global variables for global Flags to simplify // access to the options without requiring cli.Context var ( - loginValue string - repoValue string - outputValue string - remoteValue string + globalLoginValue string + globalRepoValue string + globalOutputValue string + globalRemoteValue string ) // LoginFlag provides flag to specify tea login profile @@ -26,7 +22,7 @@ var LoginFlag = cli.StringFlag{ Name: "login", Aliases: []string{"l"}, Usage: "Use a different Gitea login. Optional", - Destination: &loginValue, + Destination: &globalLoginValue, } // RepoFlag provides flag to specify repository @@ -34,7 +30,7 @@ var RepoFlag = cli.StringFlag{ Name: "repo", Aliases: []string{"r"}, Usage: "Repository to interact with. Optional", - Destination: &repoValue, + Destination: &globalRepoValue, } // RemoteFlag provides flag to specify remote repository @@ -42,7 +38,7 @@ var RemoteFlag = cli.StringFlag{ Name: "remote", Aliases: []string{"R"}, Usage: "Discover Gitea login from remote. Optional", - Destination: &remoteValue, + Destination: &globalRemoteValue, } // OutputFlag provides flag to specify output type @@ -50,7 +46,7 @@ var OutputFlag = cli.StringFlag{ Name: "output", Aliases: []string{"o"}, Usage: "Output format. (csv, simple, table, tsv, yaml)", - Destination: &outputValue, + Destination: &globalOutputValue, } // StateFlag provides flag to specify issue/pr state, defaulting to "open" @@ -108,62 +104,3 @@ var IssuePRFlags = append([]cli.Flag{ &PaginationPageFlag, &PaginationLimitFlag, }, AllDefaultFlags...) - -// initCommand returns repository and *Login based on flags -func initCommand() (*Login, string, string) { - var login *Login - - err := loadConfig(yamlConfigPath) - if err != nil { - log.Fatal("load config file failed ", yamlConfigPath) - } - - if login, err = getDefaultLogin(); err != nil { - log.Fatal(err.Error()) - } - - exist, err := utils.PathExists(repoValue) - if err != nil { - log.Fatal(err.Error()) - } - - if exist || len(repoValue) == 0 { - login, repoValue, err = curGitRepoPath(repoValue) - if err != nil { - log.Fatal(err.Error()) - } - } - - if loginValue != "" { - login = getLoginByName(loginValue) - if login == nil { - log.Fatal("Login name " + loginValue + " does not exist") - } - } - - owner, repo := getOwnerAndRepo(repoValue, login.User) - return login, owner, repo -} - -// initCommandLoginOnly return *Login based on flags -func initCommandLoginOnly() *Login { - err := loadConfig(yamlConfigPath) - if err != nil { - log.Fatal("load config file failed ", yamlConfigPath) - } - - var login *Login - if loginValue == "" { - login, err = getDefaultLogin() - if err != nil { - log.Fatal(err) - } - } else { - login = getLoginByName(loginValue) - if login == nil { - log.Fatal("Login name " + loginValue + " does not exist") - } - } - - return login -} diff --git a/cmd/issues.go b/cmd/issues.go index a957e60..743b9f8 100644 --- a/cmd/issues.go +++ b/cmd/issues.go @@ -9,6 +9,8 @@ import ( "log" "strconv" + "code.gitea.io/tea/modules/intern" + "code.gitea.io/sdk/gitea" "github.com/charmbracelet/glamour" "github.com/urfave/cli/v2" @@ -47,7 +49,7 @@ func runIssues(ctx *cli.Context) error { } func runIssueDetail(ctx *cli.Context, index string) error { - login, owner, repo := initCommand() + login, owner, repo := intern.InitCommand(globalRepoValue, globalLoginValue, globalRemoteValue) idx, err := argToIndex(index) if err != nil { @@ -71,7 +73,7 @@ func runIssueDetail(ctx *cli.Context, index string) error { } func runIssuesList(ctx *cli.Context) error { - login, owner, repo := initCommand() + login, owner, repo := intern.InitCommand(globalRepoValue, globalLoginValue, globalRemoteValue) state := gitea.StateOpen switch ctx.String("state") { @@ -105,7 +107,7 @@ func runIssuesList(ctx *cli.Context) error { var values [][]string if len(issues) == 0 { - Output(outputValue, headers, values) + Output(globalOutputValue, headers, values) return nil } @@ -130,7 +132,7 @@ func runIssuesList(ctx *cli.Context) error { }, ) } - Output(outputValue, headers, values) + Output(globalOutputValue, headers, values) return nil } @@ -156,7 +158,7 @@ var CmdIssuesCreate = cli.Command{ } func runIssuesCreate(ctx *cli.Context) error { - login, owner, repo := initCommand() + login, owner, repo := intern.InitCommand(globalRepoValue, globalLoginValue, globalRemoteValue) _, _, err := login.Client().CreateIssue(owner, repo, gitea.CreateIssueOption{ Title: ctx.String("title"), @@ -206,7 +208,7 @@ var CmdIssuesClose = cli.Command{ // editIssueState abstracts the arg parsing to edit the given issue func editIssueState(ctx *cli.Context, opts gitea.EditIssueOption) error { - login, owner, repo := initCommand() + login, owner, repo := intern.InitCommand(globalRepoValue, globalLoginValue, globalRemoteValue) if ctx.Args().Len() == 0 { log.Fatal(ctx.Command.ArgsUsage) } diff --git a/cmd/labels.go b/cmd/labels.go index 6daf842..248c964 100644 --- a/cmd/labels.go +++ b/cmd/labels.go @@ -12,8 +12,9 @@ import ( "strconv" "strings" - "code.gitea.io/sdk/gitea" + "code.gitea.io/tea/modules/intern" + "code.gitea.io/sdk/gitea" "github.com/muesli/termenv" "github.com/urfave/cli/v2" ) @@ -41,7 +42,7 @@ var CmdLabels = cli.Command{ } func runLabels(ctx *cli.Context) error { - login, owner, repo := initCommand() + login, owner, repo := intern.InitCommand(globalRepoValue, globalLoginValue, globalRemoteValue) headers := []string{ "Index", @@ -58,7 +59,7 @@ func runLabels(ctx *cli.Context) error { } if len(labels) == 0 { - Output(outputValue, headers, values) + Output(globalOutputValue, headers, values) return nil } @@ -89,7 +90,7 @@ func runLabels(ctx *cli.Context) error { }, ) } - Output(outputValue, headers, values) + Output(globalOutputValue, headers, values) } return nil @@ -143,7 +144,7 @@ func splitLabelLine(line string) (string, string, string) { } func runLabelCreate(ctx *cli.Context) error { - login, owner, repo := initCommand() + login, owner, repo := intern.InitCommand(globalRepoValue, globalLoginValue, globalRemoteValue) labelFile := ctx.String("file") var err error @@ -214,7 +215,7 @@ var CmdLabelUpdate = cli.Command{ } func runLabelUpdate(ctx *cli.Context) error { - login, owner, repo := initCommand() + login, owner, repo := intern.InitCommand(globalRepoValue, globalLoginValue, globalRemoteValue) id := ctx.Int64("id") var pName, pColor, pDescription *string @@ -262,7 +263,7 @@ var CmdLabelDelete = cli.Command{ } func runLabelDelete(ctx *cli.Context) error { - login, owner, repo := initCommand() + login, owner, repo := intern.InitCommand(globalRepoValue, globalLoginValue, globalRemoteValue) _, err := login.Client().DeleteLabel(owner, repo, ctx.Int64("id")) if err != nil { diff --git a/cmd/login.go b/cmd/login.go index 97d6053..8aa8c66 100644 --- a/cmd/login.go +++ b/cmd/login.go @@ -15,8 +15,9 @@ import ( "strings" "time" - "code.gitea.io/sdk/gitea" + "code.gitea.io/tea/modules/intern" + "code.gitea.io/sdk/gitea" "github.com/skratchdot/open-golang/open" "github.com/urfave/cli/v2" ) @@ -45,7 +46,7 @@ var cmdLoginEdit = cli.Command{ } func runLoginEdit(ctx *cli.Context) error { - return open.Start(yamlConfigPath) + return open.Start(intern.GetConfigPath()) } // cmdLoginSetDefault represents to login a gitea server. @@ -59,11 +60,11 @@ var cmdLoginSetDefault = cli.Command{ } func runLoginSetDefault(ctx *cli.Context) error { - if err := loadConfig(yamlConfigPath); err != nil { + if err := intern.LoadConfig(); err != nil { return err } if ctx.Args().Len() == 0 { - l, err := getDefaultLogin() + l, err := intern.GetDefaultLogin() if err != nil { return err } @@ -71,10 +72,10 @@ func runLoginSetDefault(ctx *cli.Context) error { return nil } loginExist := false - for i := range config.Logins { - config.Logins[i].Default = false - if config.Logins[i].Name == ctx.Args().First() { - config.Logins[i].Default = true + for i := range intern.Config.Logins { + intern.Config.Logins[i].Default = false + if intern.Config.Logins[i].Name == ctx.Args().First() { + intern.Config.Logins[i].Default = true loginExist = true } } @@ -83,7 +84,7 @@ func runLoginSetDefault(ctx *cli.Context) error { return fmt.Errorf("login '%s' not found", ctx.Args().First()) } - return saveConfig(yamlConfigPath) + return intern.SaveConfig() } // CmdLogin represents to login a gitea server. @@ -236,9 +237,9 @@ func runLoginAddMain(name, token, user, passwd, sshKey, giteaURL string, insecur log.Fatal("No user set") } - err := loadConfig(yamlConfigPath) + err := intern.LoadConfig() if err != nil { - log.Fatal("Unable to load config file " + yamlConfigPath) + log.Fatal(err) } httpClient := &http.Client{} @@ -293,7 +294,7 @@ func runLoginAddMain(name, token, user, passwd, sshKey, giteaURL string, insecur return err } name = strings.ReplaceAll(strings.Title(parsedURL.Host), ".", "") - for _, l := range config.Logins { + for _, l := range intern.Config.Logins { if l.Name == name { name += "_" + u.UserName break @@ -301,7 +302,7 @@ func runLoginAddMain(name, token, user, passwd, sshKey, giteaURL string, insecur } } - err = addLogin(Login{ + err = intern.AddLogin(intern.Login{ Name: name, URL: giteaURL, Token: token, @@ -313,7 +314,7 @@ func runLoginAddMain(name, token, user, passwd, sshKey, giteaURL string, insecur log.Fatal(err) } - err = saveConfig(yamlConfigPath) + err = intern.SaveConfig() if err != nil { log.Fatal(err) } @@ -331,9 +332,9 @@ var cmdLoginList = cli.Command{ } func runLoginList(ctx *cli.Context) error { - err := loadConfig(yamlConfigPath) + err := intern.LoadConfig() if err != nil { - log.Fatal("Unable to load config file " + yamlConfigPath) + log.Fatal(err) } headers := []string{ @@ -346,7 +347,7 @@ func runLoginList(ctx *cli.Context) error { var values [][]string - for _, l := range config.Logins { + for _, l := range intern.Config.Logins { values = append(values, []string{ l.Name, l.URL, @@ -356,7 +357,7 @@ func runLoginList(ctx *cli.Context) error { }) } - Output(outputValue, headers, values) + Output(globalOutputValue, headers, values) return nil } diff --git a/cmd/logout.go b/cmd/logout.go index 192d519..89b731a 100644 --- a/cmd/logout.go +++ b/cmd/logout.go @@ -9,6 +9,8 @@ import ( "log" "os" + "code.gitea.io/tea/modules/intern" + "github.com/urfave/cli/v2" ) @@ -37,23 +39,23 @@ func runLogout(ctx *cli.Context) error { return errors.New("Please specify a login name") } - err := loadConfig(yamlConfigPath) + err := intern.LoadConfig() if err != nil { - log.Fatal("Unable to load config file " + yamlConfigPath) + log.Fatal(err) } var idx = -1 - for i, l := range config.Logins { + for i, l := range intern.Config.Logins { if l.Name == name { idx = i break } } if idx > -1 { - config.Logins = append(config.Logins[:idx], config.Logins[idx+1:]...) - err = saveConfig(yamlConfigPath) + intern.Config.Logins = append(intern.Config.Logins[:idx], intern.Config.Logins[idx+1:]...) + err = intern.SaveConfig() if err != nil { - log.Fatal("Unable to save config file " + yamlConfigPath) + log.Fatal(err) } } diff --git a/cmd/milestone_issues.go b/cmd/milestone_issues.go index 0043356..20dfe1b 100644 --- a/cmd/milestone_issues.go +++ b/cmd/milestone_issues.go @@ -8,6 +8,8 @@ import ( "fmt" "strconv" + "code.gitea.io/tea/modules/intern" + "code.gitea.io/sdk/gitea" "github.com/urfave/cli/v2" ) @@ -62,7 +64,7 @@ var CmdMilestoneRemoveIssue = cli.Command{ } func runMilestoneIssueList(ctx *cli.Context) error { - login, owner, repo := initCommand() + login, owner, repo := intern.InitCommand(globalRepoValue, globalLoginValue, globalRemoteValue) client := login.Client() state := gitea.StateOpen @@ -112,7 +114,7 @@ func runMilestoneIssueList(ctx *cli.Context) error { var values [][]string if len(issues) == 0 { - Output(outputValue, headers, values) + Output(globalOutputValue, headers, values) return nil } @@ -137,12 +139,12 @@ func runMilestoneIssueList(ctx *cli.Context) error { }, ) } - Output(outputValue, headers, values) + Output(globalOutputValue, headers, values) return nil } func runMilestoneIssueAdd(ctx *cli.Context) error { - login, owner, repo := initCommand() + login, owner, repo := intern.InitCommand(globalRepoValue, globalLoginValue, globalRemoteValue) client := login.Client() if ctx.Args().Len() == 0 { return fmt.Errorf("need two arguments") @@ -168,7 +170,7 @@ func runMilestoneIssueAdd(ctx *cli.Context) error { } func runMilestoneIssueRemove(ctx *cli.Context) error { - login, owner, repo := initCommand() + login, owner, repo := intern.InitCommand(globalRepoValue, globalLoginValue, globalRemoteValue) client := login.Client() if ctx.Args().Len() == 0 { return fmt.Errorf("need two arguments") diff --git a/cmd/milestones.go b/cmd/milestones.go index 311cf11..9c095c6 100644 --- a/cmd/milestones.go +++ b/cmd/milestones.go @@ -8,6 +8,8 @@ import ( "fmt" "log" + "code.gitea.io/tea/modules/intern" + "code.gitea.io/sdk/gitea" "github.com/urfave/cli/v2" ) @@ -56,7 +58,7 @@ func runMilestones(ctx *cli.Context) error { } func runMilestoneDetail(ctx *cli.Context, name string) error { - login, owner, repo := initCommand() + login, owner, repo := intern.InitCommand(globalRepoValue, globalLoginValue, globalRemoteValue) client := login.Client() milestone, _, err := client.GetMilestoneByName(owner, repo, name) @@ -77,7 +79,7 @@ func runMilestoneDetail(ctx *cli.Context, name string) error { } func runMilestonesList(ctx *cli.Context) error { - login, owner, repo := initCommand() + login, owner, repo := intern.InitCommand(globalRepoValue, globalLoginValue, globalRemoteValue) state := gitea.StateOpen switch ctx.String("state") { @@ -129,7 +131,7 @@ func runMilestonesList(ctx *cli.Context) error { values = append(values, item) } - Output(outputValue, headers, values) + Output(globalOutputValue, headers, values) return nil } @@ -160,7 +162,7 @@ var CmdMilestonesCreate = cli.Command{ } func runMilestonesCreate(ctx *cli.Context) error { - login, owner, repo := initCommand() + login, owner, repo := intern.InitCommand(globalRepoValue, globalLoginValue, globalRemoteValue) title := ctx.String("title") if len(title) == 0 { @@ -207,7 +209,7 @@ var CmdMilestonesClose = cli.Command{ } func editMilestoneStatus(ctx *cli.Context, close bool) error { - login, owner, repo := initCommand() + login, owner, repo := intern.InitCommand(globalRepoValue, globalLoginValue, globalRemoteValue) client := login.Client() state := gitea.StateOpen @@ -234,7 +236,7 @@ var CmdMilestonesDelete = cli.Command{ } func deleteMilestone(ctx *cli.Context) error { - login, owner, repo := initCommand() + login, owner, repo := intern.InitCommand(globalRepoValue, globalLoginValue, globalRemoteValue) client := login.Client() _, err := client.DeleteMilestoneByName(owner, repo, ctx.Args().First()) diff --git a/cmd/notifications.go b/cmd/notifications.go index a393ae3..837a1f9 100644 --- a/cmd/notifications.go +++ b/cmd/notifications.go @@ -8,6 +8,8 @@ import ( "log" "strings" + "code.gitea.io/tea/modules/intern" + "code.gitea.io/sdk/gitea" "github.com/urfave/cli/v2" ) @@ -57,13 +59,13 @@ func runNotifications(ctx *cli.Context) error { } if ctx.Bool("all") { - login := initCommandLoginOnly() + login := intern.InitCommandLoginOnly(globalLoginValue) news, _, err = login.Client().ListNotifications(gitea.ListNotificationOptions{ ListOptions: listOpts, Status: status, }) } else { - login, owner, repo := initCommand() + login, owner, repo := intern.InitCommand(globalRepoValue, globalLoginValue, globalRemoteValue) news, _, err = login.Client().ListRepoNotifications(owner, repo, gitea.ListNotificationOptions{ ListOptions: listOpts, Status: status, @@ -107,7 +109,7 @@ func runNotifications(ctx *cli.Context) error { } if len(values) != 0 { - Output(outputValue, headers, values) + Output(globalOutputValue, headers, values) } return nil } diff --git a/cmd/open.go b/cmd/open.go index fbe67b4..8e6288c 100644 --- a/cmd/open.go +++ b/cmd/open.go @@ -10,6 +10,7 @@ import ( "strings" local_git "code.gitea.io/tea/modules/git" + "code.gitea.io/tea/modules/intern" "github.com/skratchdot/open-golang/open" "github.com/urfave/cli/v2" @@ -25,7 +26,7 @@ var CmdOpen = cli.Command{ } func runOpen(ctx *cli.Context) error { - login, owner, repo := initCommand() + login, owner, repo := intern.InitCommand(globalRepoValue, globalLoginValue, globalRemoteValue) var suffix string number := ctx.Args().Get(0) diff --git a/cmd/pulls.go b/cmd/pulls.go index cb053d8..895496d 100644 --- a/cmd/pulls.go +++ b/cmd/pulls.go @@ -11,6 +11,7 @@ import ( "strings" local_git "code.gitea.io/tea/modules/git" + "code.gitea.io/tea/modules/intern" "code.gitea.io/sdk/gitea" "github.com/charmbracelet/glamour" @@ -53,7 +54,7 @@ var CmdPullsList = cli.Command{ } func runPullDetail(ctx *cli.Context, index string) error { - login, owner, repo := initCommand() + login, owner, repo := intern.InitCommand(globalRepoValue, globalLoginValue, globalRemoteValue) idx, err := argToIndex(index) if err != nil { @@ -75,7 +76,7 @@ func runPullDetail(ctx *cli.Context, index string) error { } func runPullsList(ctx *cli.Context) error { - login, owner, repo := initCommand() + login, owner, repo := intern.InitCommand(globalRepoValue, globalLoginValue, globalRemoteValue) state := gitea.StateOpen switch ctx.String("state") { @@ -107,7 +108,7 @@ func runPullsList(ctx *cli.Context) error { var values [][]string if len(prs) == 0 { - Output(outputValue, headers, values) + Output(globalOutputValue, headers, values) return nil } @@ -135,7 +136,7 @@ func runPullsList(ctx *cli.Context) error { }, ) } - Output(outputValue, headers, values) + Output(globalOutputValue, headers, values) return nil } @@ -151,7 +152,7 @@ var CmdPullsCheckout = cli.Command{ } func runPullsCheckout(ctx *cli.Context) error { - login, owner, repo := initCommand() + login, owner, repo := intern.InitCommand(globalRepoValue, globalLoginValue, globalRemoteValue) if ctx.Args().Len() != 1 { log.Fatal("Must specify a PR index") } @@ -235,7 +236,7 @@ var CmdPullsClean = cli.Command{ } func runPullsClean(ctx *cli.Context) error { - login, owner, repo := initCommand() + login, owner, repo := intern.InitCommand(globalRepoValue, globalLoginValue, globalRemoteValue) if ctx.Args().Len() != 1 { return fmt.Errorf("Must specify a PR index") } @@ -336,7 +337,7 @@ var CmdPullsCreate = cli.Command{ } func runPullsCreate(ctx *cli.Context) error { - login, ownerArg, repoArg := initCommand() + login, ownerArg, repoArg := intern.InitCommand(globalRepoValue, globalLoginValue, globalRemoteValue) client := login.Client() repo, _, err := client.GetRepo(ownerArg, repoArg) @@ -393,7 +394,7 @@ func runPullsCreate(ctx *cli.Context) error { if err != nil { log.Fatal(err) } - owner, _ := getOwnerAndRepo(strings.TrimLeft(url.Path, "/"), "") + owner, _ := intern.GetOwnerAndRepo(strings.TrimLeft(url.Path, "/"), "") head = fmt.Sprintf("%s:%s", owner, branchName) } diff --git a/cmd/releases.go b/cmd/releases.go index 506664c..af06496 100644 --- a/cmd/releases.go +++ b/cmd/releases.go @@ -12,8 +12,9 @@ import ( "path/filepath" "strings" - "code.gitea.io/sdk/gitea" + "code.gitea.io/tea/modules/intern" + "code.gitea.io/sdk/gitea" "github.com/urfave/cli/v2" ) @@ -46,7 +47,7 @@ var CmdReleaseList = cli.Command{ } func runReleases(ctx *cli.Context) error { - login, owner, repo := initCommand() + login, owner, repo := intern.InitCommand(globalRepoValue, globalLoginValue, globalRemoteValue) releases, _, err := login.Client().ListReleases(owner, repo, gitea.ListReleasesOptions{ListOptions: getListOptions(ctx)}) if err != nil { @@ -64,7 +65,7 @@ func runReleases(ctx *cli.Context) error { var values [][]string if len(releases) == 0 { - Output(outputValue, headers, values) + Output(globalOutputValue, headers, values) return nil } @@ -86,7 +87,7 @@ func runReleases(ctx *cli.Context) error { }, ) } - Output(outputValue, headers, values) + Output(globalOutputValue, headers, values) return nil } @@ -135,7 +136,7 @@ var CmdReleaseCreate = cli.Command{ } func runReleaseCreate(ctx *cli.Context) error { - login, owner, repo := initCommand() + login, owner, repo := intern.InitCommand(globalRepoValue, globalLoginValue, globalRemoteValue) release, resp, err := login.Client().CreateRelease(owner, repo, gitea.CreateReleaseOption{ TagName: ctx.String("tag"), @@ -185,7 +186,7 @@ var CmdReleaseDelete = cli.Command{ } func runReleaseDelete(ctx *cli.Context) error { - login, owner, repo := initCommand() + login, owner, repo := intern.InitCommand(globalRepoValue, globalLoginValue, globalRemoteValue) client := login.Client() tag := ctx.Args().First() @@ -266,7 +267,7 @@ var CmdReleaseEdit = cli.Command{ } func runReleaseEdit(ctx *cli.Context) error { - login, owner, repo := initCommand() + login, owner, repo := intern.InitCommand(globalRepoValue, globalLoginValue, globalRemoteValue) client := login.Client() tag := ctx.Args().First() diff --git a/cmd/repos.go b/cmd/repos.go index 33ca875..7936267 100644 --- a/cmd/repos.go +++ b/cmd/repos.go @@ -10,6 +10,7 @@ import ( "net/http" "strings" + "code.gitea.io/tea/modules/intern" "code.gitea.io/tea/modules/utils" "code.gitea.io/sdk/gitea" @@ -140,7 +141,7 @@ func runRepos(ctx *cli.Context) error { // runReposList list repositories func runReposList(ctx *cli.Context) error { - login := initCommandLoginOnly() + login := intern.InitCommandLoginOnly(globalLoginValue) client := login.Client() var ownerID int64 @@ -233,15 +234,15 @@ func runReposList(ctx *cli.Context) error { }, ) } - Output(outputValue, headers, values) + Output(globalOutputValue, headers, values) return nil } func runRepoDetail(_ *cli.Context, path string) error { - login := initCommandLoginOnly() + login := intern.InitCommandLoginOnly(globalLoginValue) client := login.Client() - repoOwner, repoName := getOwnerAndRepo(path, login.User) + repoOwner, repoName := intern.GetOwnerAndRepo(path, login.User) repo, _, err := client.GetRepo(repoOwner, repoName) if err != nil { return err @@ -283,7 +284,7 @@ func runRepoDetail(_ *cli.Context, path string) error { } func runRepoCreate(ctx *cli.Context) error { - login := initCommandLoginOnly() + login := intern.InitCommandLoginOnly(globalLoginValue) client := login.Client() var ( repo *gitea.Repository diff --git a/cmd/times.go b/cmd/times.go index d3d34b7..19daf79 100644 --- a/cmd/times.go +++ b/cmd/times.go @@ -11,8 +11,9 @@ import ( "strings" "time" - "code.gitea.io/sdk/gitea" + "code.gitea.io/tea/modules/intern" + "code.gitea.io/sdk/gitea" "github.com/araddon/dateparse" "github.com/urfave/cli/v2" ) @@ -52,7 +53,7 @@ var CmdTrackedTimes = cli.Command{ } func runTrackedTimes(ctx *cli.Context) error { - login, owner, repo := initCommand() + login, owner, repo := intern.InitCommand(globalRepoValue, globalLoginValue, globalRemoteValue) client := login.Client() if err := client.CheckServerVersionConstraint(">= 1.11"); err != nil { @@ -97,7 +98,7 @@ func runTrackedTimes(ctx *cli.Context) error { } } - printTrackedTimes(times, outputValue, from, until, ctx.Bool("total")) + printTrackedTimes(times, globalOutputValue, from, until, ctx.Bool("total")) return nil } @@ -169,7 +170,7 @@ var CmdTrackedTimesAdd = cli.Command{ } func runTrackedTimesAdd(ctx *cli.Context) error { - login, owner, repo := initCommand() + login, owner, repo := intern.InitCommand(globalRepoValue, globalLoginValue, globalRemoteValue) if ctx.Args().Len() < 2 { return fmt.Errorf("No issue or duration specified.\nUsage:\t%s", ctx.Command.UsageText) @@ -206,7 +207,7 @@ var CmdTrackedTimesDelete = cli.Command{ } func runTrackedTimesDelete(ctx *cli.Context) error { - login, owner, repo := initCommand() + login, owner, repo := intern.InitCommand(globalRepoValue, globalLoginValue, globalRemoteValue) client := login.Client() if err := client.CheckServerVersionConstraint(">= 1.11"); err != nil { @@ -246,7 +247,7 @@ var CmdTrackedTimesReset = cli.Command{ } func runTrackedTimesReset(ctx *cli.Context) error { - login, owner, repo := initCommand() + login, owner, repo := intern.InitCommand(globalRepoValue, globalLoginValue, globalRemoteValue) client := login.Client() if err := client.CheckServerVersionConstraint(">= 1.11"); err != nil { diff --git a/modules/intern/config.go b/modules/intern/config.go new file mode 100644 index 0000000..a49abec --- /dev/null +++ b/modules/intern/config.go @@ -0,0 +1,153 @@ +// Copyright 2018 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package intern + +import ( + "errors" + "fmt" + "io/ioutil" + "log" + "os" + "path/filepath" + "strings" + + "code.gitea.io/tea/modules/git" + "code.gitea.io/tea/modules/utils" + + "gopkg.in/yaml.v2" +) + +// Config reprensents local configurations +type LocalConfig struct { + Logins []Login `yaml:"logins"` +} + +var ( + Config LocalConfig + yamlConfigPath string +) + +func init() { + homeDir, err := utils.Home() + if err != nil { + log.Fatal("Retrieve home dir failed") + } + + dir := filepath.Join(homeDir, ".tea") + err = os.MkdirAll(dir, os.ModePerm) + if err != nil { + log.Fatal("Init tea config dir " + dir + " failed") + } + + yamlConfigPath = filepath.Join(dir, "tea.yml") +} + +func LoadConfig() error { + ymlPath := yamlConfigPath + exist, _ := utils.FileExist(ymlPath) + if exist { + fmt.Println("Found config file", ymlPath) + bs, err := ioutil.ReadFile(ymlPath) + if err != nil { + return err + } + + err = yaml.Unmarshal(bs, &Config) + if err != nil { + return err + } + } + + return nil +} + +func SaveConfig() error { + ymlPath := yamlConfigPath + bs, err := yaml.Marshal(Config) + if err != nil { + return err + } + return ioutil.WriteFile(ymlPath, bs, 0660) +} + +func curGitRepoPath(repoValue, remoteValue string) (*Login, string, error) { + var err error + var repo *git.TeaRepo + if len(repoValue) == 0 { + repo, err = git.RepoForWorkdir() + } else { + repo, err = git.RepoFromPath(repoValue) + } + if err != nil { + return nil, "", err + } + gitConfig, err := repo.Config() + if err != nil { + return nil, "", err + } + + // if no remote + if len(gitConfig.Remotes) == 0 { + return nil, "", errors.New("No remote(s) found in this Git repository") + } + + // if only one remote exists + if len(gitConfig.Remotes) >= 1 && len(remoteValue) == 0 { + for remote := range gitConfig.Remotes { + remoteValue = remote + } + if len(gitConfig.Remotes) > 1 { + // if master branch is present, use it as the default remote + masterBranch, ok := gitConfig.Branches["master"] + if ok { + if len(masterBranch.Remote) > 0 { + remoteValue = masterBranch.Remote + } + } + } + } + + remoteConfig, ok := gitConfig.Remotes[remoteValue] + if !ok || remoteConfig == nil { + return nil, "", errors.New("Remote " + remoteValue + " not found in this Git repository") + } + + for _, l := range Config.Logins { + for _, u := range remoteConfig.URLs { + p, err := git.ParseURL(strings.TrimSpace(u)) + if err != nil { + return nil, "", fmt.Errorf("Git remote URL parse failed: %s", err.Error()) + } + if strings.EqualFold(p.Scheme, "http") || strings.EqualFold(p.Scheme, "https") { + if strings.HasPrefix(u, l.URL) { + ps := strings.Split(p.Path, "/") + path := strings.Join(ps[len(ps)-2:], "/") + return &l, strings.TrimSuffix(path, ".git"), nil + } + } else if strings.EqualFold(p.Scheme, "ssh") { + if l.GetSSHHost() == strings.Split(p.Host, ":")[0] { + return &l, strings.TrimLeft(strings.TrimSuffix(p.Path, ".git"), "/"), nil + } + } + } + } + + return nil, "", errors.New("No Gitea login found. You might want to specify --repo (and --login) to work outside of a repository") +} + +func GetConfigPath() string { + return yamlConfigPath +} + +func GetOwnerAndRepo(repoPath, user string) (string, string) { + if len(repoPath) == 0 { + return "", "" + } + p := strings.Split(repoPath, "/") + if len(p) >= 2 { + return p[0], p[1] + } + return user, repoPath +} diff --git a/modules/intern/login.go b/modules/intern/login.go new file mode 100644 index 0000000..28b78c1 --- /dev/null +++ b/modules/intern/login.go @@ -0,0 +1,176 @@ +// Copyright 2020 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package intern + +import ( + "crypto/tls" + "errors" + "log" + "net/http" + "net/http/cookiejar" + "net/url" + + "code.gitea.io/tea/modules/utils" + + "code.gitea.io/sdk/gitea" +) + +// Login represents a login to a gitea server, you even could add multiple logins for one gitea server +type Login struct { + Name string `yaml:"name"` + URL string `yaml:"url"` + Token string `yaml:"token"` + Default bool `yaml:"default"` + SSHHost string `yaml:"ssh_host"` + // optional path to the private key + SSHKey string `yaml:"ssh_key"` + Insecure bool `yaml:"insecure"` + // optional gitea username + User string `yaml:"user"` +} + +// Client returns a client to operate Gitea API +func (l *Login) Client() *gitea.Client { + httpClient := &http.Client{} + if l.Insecure { + cookieJar, _ := cookiejar.New(nil) + + httpClient = &http.Client{ + Jar: cookieJar, + Transport: &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + }} + } + + client, err := gitea.NewClient(l.URL, + gitea.SetToken(l.Token), + gitea.SetHTTPClient(httpClient), + ) + if err != nil { + log.Fatal(err) + } + return client +} + +// GetSSHHost returns SSH host name +func (l *Login) GetSSHHost() string { + if l.SSHHost != "" { + return l.SSHHost + } + + u, err := url.Parse(l.URL) + if err != nil { + return "" + } + + return u.Hostname() +} + +func GetDefaultLogin() (*Login, error) { + if len(Config.Logins) == 0 { + return nil, errors.New("No available login") + } + for _, l := range Config.Logins { + if l.Default { + return &l, nil + } + } + + return &Config.Logins[0], nil +} + +func GetLoginByName(name string) *Login { + for _, l := range Config.Logins { + if l.Name == name { + return &l + } + } + return nil +} + +func AddLogin(login Login) error { + for _, l := range Config.Logins { + if l.Name == login.Name { + if l.URL == login.URL && l.Token == login.Token { + return nil + } + return errors.New("Login name has already been used") + } + if l.URL == login.URL && l.Token == login.Token { + return errors.New("URL has been added") + } + } + + u, err := url.Parse(login.URL) + if err != nil { + return err + } + + if login.SSHHost == "" { + login.SSHHost = u.Hostname() + } + Config.Logins = append(Config.Logins, login) + + return nil +} + +// InitCommand returns repository and *Login based on flags +func InitCommand(repoValue, loginValue, remoteValue string) (*Login, string, string) { + var login *Login + + err := LoadConfig() + if err != nil { + log.Fatal("load config file failed ", yamlConfigPath) + } + + if login, err = GetDefaultLogin(); err != nil { + log.Fatal(err.Error()) + } + + exist, err := utils.PathExists(repoValue) + if err != nil { + log.Fatal(err.Error()) + } + + if exist || len(repoValue) == 0 { + login, repoValue, err = curGitRepoPath(repoValue, remoteValue) + if err != nil { + log.Fatal(err.Error()) + } + } + + if loginValue != "" { + login = GetLoginByName(loginValue) + if login == nil { + log.Fatal("Login name " + loginValue + " does not exist") + } + } + + owner, repo := GetOwnerAndRepo(repoValue, login.User) + return login, owner, repo +} + +// InitCommandLoginOnly return *Login based on flags +func InitCommandLoginOnly(loginValue string) *Login { + err := LoadConfig() + if err != nil { + log.Fatal("load config file failed ", yamlConfigPath) + } + + var login *Login + if loginValue == "" { + login, err = GetDefaultLogin() + if err != nil { + log.Fatal(err) + } + } else { + login = GetLoginByName(loginValue) + if login == nil { + log.Fatal("Login name " + loginValue + " does not exist") + } + } + + return login +} diff --git a/modules/utils/path.go b/modules/utils/path.go index 2253256..36927e1 100644 --- a/modules/utils/path.go +++ b/modules/utils/path.go @@ -5,6 +5,7 @@ package utils import ( + "errors" "os" ) @@ -19,3 +20,18 @@ func PathExists(path string) (bool, error) { } return true, err } + +// FileExist returns whether the given file exists or not +func FileExist(fileName string) (bool, error) { + f, err := os.Stat(fileName) + if err != nil { + if os.IsNotExist(err) { + return false, nil + } + return false, err + } + if f.IsDir() { + return false, errors.New("A directory with the same name exists") + } + return true, nil +} -- 2.40.1 From b084f085fa236c2152b751ec31d3d9b9a3e68903 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Mon, 28 Sep 2020 00:30:56 +0200 Subject: [PATCH 02/22] Move DetailViews stdout print func to print package --- cmd/config.go | 8 ---- cmd/issues.go | 14 +------ cmd/milestones.go | 11 +----- cmd/pulls.go | 12 +----- cmd/releases.go | 2 + cmd/repos.go | 31 +-------------- modules/print/issue.go | 30 ++++++++++++++ modules/print/milestone.go | 23 +++++++++++ modules/{utils/format.go => print/print.go} | 15 ++++++- modules/print/pull.go | 30 ++++++++++++++ modules/print/repo.go | 43 +++++++++++++++++++++ 11 files changed, 149 insertions(+), 70 deletions(-) create mode 100644 modules/print/issue.go create mode 100644 modules/print/milestone.go rename modules/{utils/format.go => print/print.go} (74%) create mode 100644 modules/print/pull.go create mode 100644 modules/print/repo.go diff --git a/cmd/config.go b/cmd/config.go index 2cba75f..f107069 100644 --- a/cmd/config.go +++ b/cmd/config.go @@ -6,17 +6,9 @@ package cmd import ( "code.gitea.io/sdk/gitea" - "github.com/muesli/termenv" "github.com/urfave/cli/v2" ) -func getGlamourTheme() string { - if termenv.HasDarkBackground() { - return "dark" - } - return "light" -} - func getListOptions(ctx *cli.Context) gitea.ListOptions { page := ctx.Int("page") limit := ctx.Int("limit") diff --git a/cmd/issues.go b/cmd/issues.go index 743b9f8..298135f 100644 --- a/cmd/issues.go +++ b/cmd/issues.go @@ -5,14 +5,13 @@ package cmd import ( - "fmt" "log" "strconv" "code.gitea.io/tea/modules/intern" + "code.gitea.io/tea/modules/print" "code.gitea.io/sdk/gitea" - "github.com/charmbracelet/glamour" "github.com/urfave/cli/v2" ) @@ -59,16 +58,7 @@ func runIssueDetail(ctx *cli.Context, index string) error { if err != nil { return err } - - in := fmt.Sprintf("# #%d %s (%s)\n%s created %s\n\n%s\n", issue.Index, - issue.Title, - issue.State, - issue.Poster.UserName, - issue.Created.Format("2006-01-02 15:04:05"), - issue.Body, - ) - out, err := glamour.Render(in, getGlamourTheme()) - fmt.Print(out) + print.IssueDetails(issue) return nil } diff --git a/cmd/milestones.go b/cmd/milestones.go index 9c095c6..cfe6c8e 100644 --- a/cmd/milestones.go +++ b/cmd/milestones.go @@ -9,6 +9,7 @@ import ( "log" "code.gitea.io/tea/modules/intern" + "code.gitea.io/tea/modules/print" "code.gitea.io/sdk/gitea" "github.com/urfave/cli/v2" @@ -66,15 +67,7 @@ func runMilestoneDetail(ctx *cli.Context, name string) error { return err } - fmt.Printf("%s\n", - milestone.Title, - ) - if len(milestone.Description) != 0 { - fmt.Printf("\n%s\n", milestone.Description) - } - if milestone.Deadline != nil && !milestone.Deadline.IsZero() { - fmt.Printf("\nDeadline: %s\n", milestone.Deadline.Format("2006-01-02 15:04:05")) - } + print.MilestoneDetails(milestone) return nil } diff --git a/cmd/pulls.go b/cmd/pulls.go index 895496d..4e655ab 100644 --- a/cmd/pulls.go +++ b/cmd/pulls.go @@ -12,9 +12,9 @@ import ( local_git "code.gitea.io/tea/modules/git" "code.gitea.io/tea/modules/intern" + "code.gitea.io/tea/modules/print" "code.gitea.io/sdk/gitea" - "github.com/charmbracelet/glamour" "github.com/go-git/go-git/v5" git_config "github.com/go-git/go-git/v5/config" "github.com/urfave/cli/v2" @@ -426,15 +426,7 @@ func runPullsCreate(ctx *cli.Context) error { log.Fatalf("could not create PR from %s to %s:%s: %s", head, ownerArg, base, err) } - in := fmt.Sprintf("# #%d %s (%s)\n%s created %s\n\n%s\n", pr.Index, - pr.Title, - pr.State, - pr.Poster.UserName, - pr.Created.Format("2006-01-02 15:04:05"), - pr.Body, - ) - out, err := glamour.Render(in, getGlamourTheme()) - fmt.Print(out) + print.PullDetails(pr) fmt.Println(pr.HTMLURL) return err diff --git a/cmd/releases.go b/cmd/releases.go index af06496..a95a890 100644 --- a/cmd/releases.go +++ b/cmd/releases.go @@ -49,6 +49,8 @@ var CmdReleaseList = cli.Command{ func runReleases(ctx *cli.Context) error { login, owner, repo := intern.InitCommand(globalRepoValue, globalLoginValue, globalRemoteValue) + // ToDo: ReleaseDetails + releases, _, err := login.Client().ListReleases(owner, repo, gitea.ListReleasesOptions{ListOptions: getListOptions(ctx)}) if err != nil { log.Fatal(err) diff --git a/cmd/repos.go b/cmd/repos.go index 7936267..0b7c404 100644 --- a/cmd/repos.go +++ b/cmd/repos.go @@ -11,7 +11,7 @@ import ( "strings" "code.gitea.io/tea/modules/intern" - "code.gitea.io/tea/modules/utils" + "code.gitea.io/tea/modules/print" "code.gitea.io/sdk/gitea" "github.com/urfave/cli/v2" @@ -252,34 +252,7 @@ func runRepoDetail(_ *cli.Context, path string) error { return err } - output := repo.FullName - if repo.Mirror { - output += " (mirror)" - } - if repo.Fork { - output += " (fork)" - } - if repo.Archived { - output += " (archived)" - } - if repo.Empty { - output += " (empty)" - } - output += "\n" - if len(topics) != 0 { - output += "Topics: " + strings.Join(topics, ", ") + "\n" - } - output += "\n" - output += repo.Description + "\n\n" - output += fmt.Sprintf( - "Open Issues: %d, Stars: %d, Forks: %d, Size: %s\n\n", - repo.OpenIssues, - repo.Stars, - repo.Forks, - utils.FormatSize(int64(repo.Size)), - ) - - fmt.Print(output) + print.RepoDetails(repo, topics) return nil } diff --git a/modules/print/issue.go b/modules/print/issue.go new file mode 100644 index 0000000..fccc276 --- /dev/null +++ b/modules/print/issue.go @@ -0,0 +1,30 @@ +// Copyright 2020 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package print + +import ( + "fmt" + + "code.gitea.io/sdk/gitea" + "github.com/charmbracelet/glamour" +) + +func IssueDetails(issue *gitea.Issue) { + + in := fmt.Sprintf("# #%d %s (%s)\n%s created %s\n\n%s\n", issue.Index, + issue.Title, + issue.State, + issue.Poster.UserName, + issue.Created.Format("2006-01-02 15:04:05"), + issue.Body, + ) + out, err := glamour.Render(in, getGlamourTheme()) + if err != nil { + // TODO: better Error handling + fmt.Printf("Error:\n%v\n\n", err) + return + } + fmt.Print(out) +} diff --git a/modules/print/milestone.go b/modules/print/milestone.go new file mode 100644 index 0000000..131101c --- /dev/null +++ b/modules/print/milestone.go @@ -0,0 +1,23 @@ +// Copyright 2020 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package print + +import ( + "fmt" + + "code.gitea.io/sdk/gitea" +) + +func MilestoneDetails(milestone *gitea.Milestone) { + fmt.Printf("%s\n", + milestone.Title, + ) + if len(milestone.Description) != 0 { + fmt.Printf("\n%s\n", milestone.Description) + } + if milestone.Deadline != nil && !milestone.Deadline.IsZero() { + fmt.Printf("\nDeadline: %s\n", milestone.Deadline.Format("2006-01-02 15:04:05")) + } +} diff --git a/modules/utils/format.go b/modules/print/print.go similarity index 74% rename from modules/utils/format.go rename to modules/print/print.go index 9435119..8bc5025 100644 --- a/modules/utils/format.go +++ b/modules/print/print.go @@ -2,9 +2,20 @@ // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. -package utils +package print -import "fmt" +import ( + "fmt" + + "github.com/muesli/termenv" +) + +func getGlamourTheme() string { + if termenv.HasDarkBackground() { + return "dark" + } + return "light" +} // FormatSize get kb in int and return string func FormatSize(kb int64) string { diff --git a/modules/print/pull.go b/modules/print/pull.go new file mode 100644 index 0000000..58c8d1d --- /dev/null +++ b/modules/print/pull.go @@ -0,0 +1,30 @@ +// Copyright 2020 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package print + +import ( + "fmt" + + "code.gitea.io/sdk/gitea" + "github.com/charmbracelet/glamour" +) + +func PullDetails(pr *gitea.PullRequest) { + + in := fmt.Sprintf("# #%d %s (%s)\n%s created %s\n\n%s\n", pr.Index, + pr.Title, + pr.State, + pr.Poster.UserName, + pr.Created.Format("2006-01-02 15:04:05"), + pr.Body, + ) + out, err := glamour.Render(in, getGlamourTheme()) + if err != nil { + // TODO: better Error handling + fmt.Printf("Error:\n%v\n\n", err) + return + } + fmt.Print(out) +} diff --git a/modules/print/repo.go b/modules/print/repo.go new file mode 100644 index 0000000..3b246ef --- /dev/null +++ b/modules/print/repo.go @@ -0,0 +1,43 @@ +// Copyright 2020 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package print + +import ( + "fmt" + "strings" + + "code.gitea.io/sdk/gitea" +) + +func RepoDetails(repo *gitea.Repository, topics []string) { + output := repo.FullName + if repo.Mirror { + output += " (mirror)" + } + if repo.Fork { + output += " (fork)" + } + if repo.Archived { + output += " (archived)" + } + if repo.Empty { + output += " (empty)" + } + output += "\n" + if len(topics) != 0 { + output += "Topics: " + strings.Join(topics, ", ") + "\n" + } + output += "\n" + output += repo.Description + "\n\n" + output += fmt.Sprintf( + "Open Issues: %d, Stars: %d, Forks: %d, Size: %s\n\n", + repo.OpenIssues, + repo.Stars, + repo.Forks, + FormatSize(int64(repo.Size)), + ) + + fmt.Print(output) +} -- 2.40.1 From 27360dd555da126272c406a2c01aa10c5cd65e4b Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Mon, 28 Sep 2020 00:31:15 +0200 Subject: [PATCH 03/22] smal nits --- cmd/issues.go | 4 ++-- cmd/labels.go | 1 - cmd/milestones.go | 6 +++--- cmd/repos.go | 6 +++--- modules/intern/config.go | 12 ++++++------ 5 files changed, 14 insertions(+), 15 deletions(-) diff --git a/cmd/issues.go b/cmd/issues.go index 298135f..d50541c 100644 --- a/cmd/issues.go +++ b/cmd/issues.go @@ -42,12 +42,12 @@ var CmdIssuesList = cli.Command{ func runIssues(ctx *cli.Context) error { if ctx.Args().Len() == 1 { - return runIssueDetail(ctx, ctx.Args().First()) + return runIssueDetail(ctx.Args().First()) } return runIssuesList(ctx) } -func runIssueDetail(ctx *cli.Context, index string) error { +func runIssueDetail(index string) error { login, owner, repo := intern.InitCommand(globalRepoValue, globalLoginValue, globalRemoteValue) idx, err := argToIndex(index) diff --git a/cmd/labels.go b/cmd/labels.go index 248c964..c5f769c 100644 --- a/cmd/labels.go +++ b/cmd/labels.go @@ -163,7 +163,6 @@ func runLabelCreate(ctx *cli.Context) error { scanner := bufio.NewScanner(f) var i = 1 - // FIXME: if Gitea's API support create multiple labels once, we should move to that API. for scanner.Scan() { line := scanner.Text() color, name, description := splitLabelLine(line) diff --git a/cmd/milestones.go b/cmd/milestones.go index cfe6c8e..5e05e48 100644 --- a/cmd/milestones.go +++ b/cmd/milestones.go @@ -53,12 +53,12 @@ var CmdMilestonesList = cli.Command{ func runMilestones(ctx *cli.Context) error { if ctx.Args().Len() == 1 { - return runMilestoneDetail(ctx, ctx.Args().First()) + return runMilestoneDetail(ctx.Args().First()) } return runMilestonesList(ctx) } -func runMilestoneDetail(ctx *cli.Context, name string) error { +func runMilestoneDetail(name string) error { login, owner, repo := intern.InitCommand(globalRepoValue, globalLoginValue, globalRemoteValue) client := login.Client() @@ -177,7 +177,7 @@ func runMilestonesCreate(ctx *cli.Context) error { log.Fatal(err) } - return runMilestoneDetail(ctx, mile.Title) + return runMilestoneDetail(mile.Title) } // CmdMilestonesClose represents a sub command of milestones to close an milestone diff --git a/cmd/repos.go b/cmd/repos.go index 0b7c404..ccfb4f2 100644 --- a/cmd/repos.go +++ b/cmd/repos.go @@ -134,7 +134,7 @@ var CmdRepoCreate = cli.Command{ func runRepos(ctx *cli.Context) error { if ctx.Args().Len() == 1 { - return runRepoDetail(ctx, ctx.Args().First()) + return runRepoDetail(ctx.Args().First()) } return runReposList(ctx) } @@ -239,7 +239,7 @@ func runReposList(ctx *cli.Context) error { return nil } -func runRepoDetail(_ *cli.Context, path string) error { +func runRepoDetail(path string) error { login := intern.InitCommandLoginOnly(globalLoginValue) client := login.Client() repoOwner, repoName := intern.GetOwnerAndRepo(path, login.User) @@ -282,7 +282,7 @@ func runRepoCreate(ctx *cli.Context) error { if err != nil { return err } - if err = runRepoDetail(ctx, repo.FullName); err != nil { + if err = runRepoDetail(repo.FullName); err != nil { return err } fmt.Printf("%s\n", repo.HTMLURL) diff --git a/modules/intern/config.go b/modules/intern/config.go index a49abec..c9f1951 100644 --- a/modules/intern/config.go +++ b/modules/intern/config.go @@ -44,8 +44,12 @@ func init() { yamlConfigPath = filepath.Join(dir, "tea.yml") } +func GetConfigPath() string { + return yamlConfigPath +} + func LoadConfig() error { - ymlPath := yamlConfigPath + ymlPath := GetConfigPath() exist, _ := utils.FileExist(ymlPath) if exist { fmt.Println("Found config file", ymlPath) @@ -64,7 +68,7 @@ func LoadConfig() error { } func SaveConfig() error { - ymlPath := yamlConfigPath + ymlPath := GetConfigPath() bs, err := yaml.Marshal(Config) if err != nil { return err @@ -137,10 +141,6 @@ func curGitRepoPath(repoValue, remoteValue string) (*Login, string, error) { return nil, "", errors.New("No Gitea login found. You might want to specify --repo (and --login) to work outside of a repository") } -func GetConfigPath() string { - return yamlConfigPath -} - func GetOwnerAndRepo(repoPath, user string) (string, string) { if len(repoPath) == 0 { return "", "" -- 2.40.1 From 84f02526c4e5de3c972eb06f4640af13f93c471e Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Mon, 28 Sep 2020 01:14:59 +0200 Subject: [PATCH 04/22] lint: add description to exported func --- modules/intern/config.go | 8 +++++++- modules/intern/login.go | 3 +++ modules/print/issue.go | 1 + modules/print/milestone.go | 1 + modules/print/print.go | 4 ++-- modules/print/pull.go | 1 + modules/print/repo.go | 3 ++- 7 files changed, 17 insertions(+), 4 deletions(-) diff --git a/modules/intern/config.go b/modules/intern/config.go index c9f1951..1d9e2c7 100644 --- a/modules/intern/config.go +++ b/modules/intern/config.go @@ -19,12 +19,13 @@ import ( "gopkg.in/yaml.v2" ) -// Config reprensents local configurations +// LocalConfig represents local configurations type LocalConfig struct { Logins []Login `yaml:"logins"` } var ( + // Config contain if loaded local tea config Config LocalConfig yamlConfigPath string ) @@ -44,10 +45,12 @@ func init() { yamlConfigPath = filepath.Join(dir, "tea.yml") } +// GetConfigPath return path to tea config file func GetConfigPath() string { return yamlConfigPath } +// LoadConfig load config into global Config var func LoadConfig() error { ymlPath := GetConfigPath() exist, _ := utils.FileExist(ymlPath) @@ -67,6 +70,7 @@ func LoadConfig() error { return nil } +// SaveConfig save config from global Config var into config file func SaveConfig() error { ymlPath := GetConfigPath() bs, err := yaml.Marshal(Config) @@ -141,6 +145,8 @@ func curGitRepoPath(repoValue, remoteValue string) (*Login, string, error) { return nil, "", errors.New("No Gitea login found. You might want to specify --repo (and --login) to work outside of a repository") } +// GetOwnerAndRepo return repoOwner and repoName +// based on relative path and default owner (if not in path) func GetOwnerAndRepo(repoPath, user string) (string, string) { if len(repoPath) == 0 { return "", "" diff --git a/modules/intern/login.go b/modules/intern/login.go index 28b78c1..b5d3644 100644 --- a/modules/intern/login.go +++ b/modules/intern/login.go @@ -68,6 +68,7 @@ func (l *Login) GetSSHHost() string { return u.Hostname() } +// GetDefaultLogin return the default login func GetDefaultLogin() (*Login, error) { if len(Config.Logins) == 0 { return nil, errors.New("No available login") @@ -81,6 +82,7 @@ func GetDefaultLogin() (*Login, error) { return &Config.Logins[0], nil } +// GetLoginByName get login by name func GetLoginByName(name string) *Login { for _, l := range Config.Logins { if l.Name == name { @@ -90,6 +92,7 @@ func GetLoginByName(name string) *Login { return nil } +// AddLogin add a login to global Config var func AddLogin(login Login) error { for _, l := range Config.Logins { if l.Name == login.Name { diff --git a/modules/print/issue.go b/modules/print/issue.go index fccc276..dc7a908 100644 --- a/modules/print/issue.go +++ b/modules/print/issue.go @@ -11,6 +11,7 @@ import ( "github.com/charmbracelet/glamour" ) +// IssueDetails print an issue rendered to stdout func IssueDetails(issue *gitea.Issue) { in := fmt.Sprintf("# #%d %s (%s)\n%s created %s\n\n%s\n", issue.Index, diff --git a/modules/print/milestone.go b/modules/print/milestone.go index 131101c..cfed6d4 100644 --- a/modules/print/milestone.go +++ b/modules/print/milestone.go @@ -10,6 +10,7 @@ import ( "code.gitea.io/sdk/gitea" ) +// MilestoneDetails print an milestone formatted to stdout func MilestoneDetails(milestone *gitea.Milestone) { fmt.Printf("%s\n", milestone.Title, diff --git a/modules/print/print.go b/modules/print/print.go index 8bc5025..21b4b87 100644 --- a/modules/print/print.go +++ b/modules/print/print.go @@ -17,8 +17,8 @@ func getGlamourTheme() string { return "light" } -// FormatSize get kb in int and return string -func FormatSize(kb int64) string { +// formatSize get kb in int and return string +func formatSize(kb int64) string { if kb < 1024 { return fmt.Sprintf("%d Kb", kb) } diff --git a/modules/print/pull.go b/modules/print/pull.go index 58c8d1d..97c4cc8 100644 --- a/modules/print/pull.go +++ b/modules/print/pull.go @@ -11,6 +11,7 @@ import ( "github.com/charmbracelet/glamour" ) +// PullDetails print an pull rendered to stdout func PullDetails(pr *gitea.PullRequest) { in := fmt.Sprintf("# #%d %s (%s)\n%s created %s\n\n%s\n", pr.Index, diff --git a/modules/print/repo.go b/modules/print/repo.go index 3b246ef..96ee374 100644 --- a/modules/print/repo.go +++ b/modules/print/repo.go @@ -11,6 +11,7 @@ import ( "code.gitea.io/sdk/gitea" ) +// RepoDetails print an repo formatted to stdout func RepoDetails(repo *gitea.Repository, topics []string) { output := repo.FullName if repo.Mirror { @@ -36,7 +37,7 @@ func RepoDetails(repo *gitea.Repository, topics []string) { repo.OpenIssues, repo.Stars, repo.Forks, - FormatSize(int64(repo.Size)), + formatSize(int64(repo.Size)), ) fmt.Print(output) -- 2.40.1 From c0aa8194425e71bec2d819df78b54742cef4d47c Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Mon, 28 Sep 2020 01:22:50 +0200 Subject: [PATCH 05/22] apply from review --- modules/intern/config.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/intern/config.go b/modules/intern/config.go index 1d9e2c7..1440fc7 100644 --- a/modules/intern/config.go +++ b/modules/intern/config.go @@ -1,4 +1,4 @@ -// Copyright 2018 The Gitea Authors. All rights reserved. +// Copyright 2020 The Gitea Authors. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. @@ -30,6 +30,7 @@ var ( yamlConfigPath string ) +// TODO: do not use init function to detect the tea configuration, use GetConfigPath() func init() { homeDir, err := utils.Home() if err != nil { -- 2.40.1 From 258ca869a23cac4142e79c875adb305f9565c43e Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Mon, 28 Sep 2020 01:38:59 +0200 Subject: [PATCH 06/22] Move Add Login function to intern/login.go --- cmd/login.go | 108 +--------------------------------------- modules/intern/login.go | 107 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 107 insertions(+), 108 deletions(-) diff --git a/cmd/login.go b/cmd/login.go index 8aa8c66..1c65351 100644 --- a/cmd/login.go +++ b/cmd/login.go @@ -5,19 +5,13 @@ package cmd import ( - "crypto/tls" "fmt" "log" - "net/http" - "net/http/cookiejar" "net/url" - "os" "strings" - "time" "code.gitea.io/tea/modules/intern" - "code.gitea.io/sdk/gitea" "github.com/skratchdot/open-golang/open" "github.com/urfave/cli/v2" ) @@ -141,7 +135,7 @@ var cmdLoginAdd = cli.Command{ } func runLoginAdd(ctx *cli.Context) error { - return runLoginAddMain( + return intern.AddLogin( ctx.String("name"), ctx.String("token"), ctx.String("user"), @@ -221,105 +215,7 @@ func runLoginAddInteractive(ctx *cli.Context) error { insecure = len(stdin) != 0 && strings.ToLower(stdin[:1]) == "y" } - return runLoginAddMain(name, token, user, passwd, sshKey, giteaURL, insecure) -} - -func runLoginAddMain(name, token, user, passwd, sshKey, giteaURL string, insecure bool) error { - - if len(giteaURL) == 0 { - log.Fatal("You have to input Gitea server URL") - } - if len(token) == 0 && (len(user)+len(passwd)) == 0 { - log.Fatal("No token set") - } else if len(user) != 0 && len(passwd) == 0 { - log.Fatal("No password set") - } else if len(user) == 0 && len(passwd) != 0 { - log.Fatal("No user set") - } - - err := intern.LoadConfig() - if err != nil { - log.Fatal(err) - } - - httpClient := &http.Client{} - if insecure { - cookieJar, _ := cookiejar.New(nil) - httpClient = &http.Client{ - Jar: cookieJar, - Transport: &http.Transport{ - TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, - }} - } - client, err := gitea.NewClient(giteaURL, - gitea.SetToken(token), - gitea.SetBasicAuth(user, passwd), - gitea.SetHTTPClient(httpClient), - ) - if err != nil { - log.Fatal(err) - } - - u, _, err := client.GetMyUserInfo() - if err != nil { - log.Fatal(err) - } - - if len(token) == 0 { - // create token - host, _ := os.Hostname() - tl, _, err := client.ListAccessTokens(gitea.ListAccessTokensOptions{}) - if err != nil { - return err - } - tokenName := host + "-tea" - for i := range tl { - if tl[i].Name == tokenName { - tokenName += time.Now().Format("2006-01-02_15-04-05") - break - } - } - t, _, err := client.CreateAccessToken(gitea.CreateAccessTokenOption{Name: tokenName}) - if err != nil { - return err - } - token = t.Token - } - - fmt.Println("Login successful! Login name " + u.UserName) - - if len(name) == 0 { - parsedURL, err := url.Parse(giteaURL) - if err != nil { - return err - } - name = strings.ReplaceAll(strings.Title(parsedURL.Host), ".", "") - for _, l := range intern.Config.Logins { - if l.Name == name { - name += "_" + u.UserName - break - } - } - } - - err = intern.AddLogin(intern.Login{ - Name: name, - URL: giteaURL, - Token: token, - Insecure: insecure, - SSHKey: sshKey, - User: u.UserName, - }) - if err != nil { - log.Fatal(err) - } - - err = intern.SaveConfig() - if err != nil { - log.Fatal(err) - } - - return nil + return intern.AddLogin(name, token, user, passwd, sshKey, giteaURL, insecure) } // CmdLogin represents to login a gitea server. diff --git a/modules/intern/login.go b/modules/intern/login.go index b5d3644..fc4b1c0 100644 --- a/modules/intern/login.go +++ b/modules/intern/login.go @@ -7,10 +7,14 @@ package intern import ( "crypto/tls" "errors" + "fmt" "log" "net/http" "net/http/cookiejar" "net/url" + "os" + "strings" + "time" "code.gitea.io/tea/modules/utils" @@ -92,8 +96,107 @@ func GetLoginByName(name string) *Login { return nil } -// AddLogin add a login to global Config var -func AddLogin(login Login) error { +// AddLogin add login to config ( global var & file) +func AddLogin(name, token, user, passwd, sshKey, giteaURL string, insecure bool) error { + + if len(giteaURL) == 0 { + log.Fatal("You have to input Gitea server URL") + } + if len(token) == 0 && (len(user)+len(passwd)) == 0 { + log.Fatal("No token set") + } else if len(user) != 0 && len(passwd) == 0 { + log.Fatal("No password set") + } else if len(user) == 0 && len(passwd) != 0 { + log.Fatal("No user set") + } + + err := LoadConfig() + if err != nil { + log.Fatal(err) + } + + httpClient := &http.Client{} + if insecure { + cookieJar, _ := cookiejar.New(nil) + httpClient = &http.Client{ + Jar: cookieJar, + Transport: &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + }} + } + client, err := gitea.NewClient(giteaURL, + gitea.SetToken(token), + gitea.SetBasicAuth(user, passwd), + gitea.SetHTTPClient(httpClient), + ) + if err != nil { + log.Fatal(err) + } + + u, _, err := client.GetMyUserInfo() + if err != nil { + log.Fatal(err) + } + + if len(token) == 0 { + // create token + host, _ := os.Hostname() + tl, _, err := client.ListAccessTokens(gitea.ListAccessTokensOptions{}) + if err != nil { + return err + } + tokenName := host + "-tea" + for i := range tl { + if tl[i].Name == tokenName { + tokenName += time.Now().Format("2006-01-02_15-04-05") + break + } + } + t, _, err := client.CreateAccessToken(gitea.CreateAccessTokenOption{Name: tokenName}) + if err != nil { + return err + } + token = t.Token + } + + fmt.Println("Login successful! Login name " + u.UserName) + + if len(name) == 0 { + parsedURL, err := url.Parse(giteaURL) + if err != nil { + return err + } + name = strings.ReplaceAll(strings.Title(parsedURL.Host), ".", "") + for _, l := range Config.Logins { + if l.Name == name { + name += "_" + u.UserName + break + } + } + } + + err = addLoginToConfig(Login{ + Name: name, + URL: giteaURL, + Token: token, + Insecure: insecure, + SSHKey: sshKey, + User: u.UserName, + }) + if err != nil { + log.Fatal(err) + } + + err = SaveConfig() + if err != nil { + log.Fatal(err) + } + + return nil +} + +// addLoginToConfig add a login to global Config var +func addLoginToConfig(login Login) error { for _, l := range Config.Logins { if l.Name == login.Name { if l.URL == login.URL && l.Token == login.Token { -- 2.40.1 From dd7727a55f74fc6d4a75cf5b6f0b3f4804881d47 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Mon, 28 Sep 2020 01:43:18 +0200 Subject: [PATCH 07/22] Move Interactive Login Creation to interact package --- cmd/login.go | 76 ++-------------------------------- modules/interact/login.go | 87 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+), 73 deletions(-) create mode 100644 modules/interact/login.go diff --git a/cmd/login.go b/cmd/login.go index 1c65351..f9c7c06 100644 --- a/cmd/login.go +++ b/cmd/login.go @@ -7,9 +7,8 @@ package cmd import ( "fmt" "log" - "net/url" - "strings" + "code.gitea.io/tea/modules/interact" "code.gitea.io/tea/modules/intern" "github.com/skratchdot/open-golang/open" @@ -145,77 +144,8 @@ func runLoginAdd(ctx *cli.Context) error { ctx.Bool("insecure")) } -func runLoginAddInteractive(ctx *cli.Context) error { - var stdin, name, token, user, passwd, sshKey, giteaURL string - var insecure = false - - fmt.Print("URL of Gitea instance: ") - if _, err := fmt.Scanln(&stdin); err != nil { - stdin = "" - } - giteaURL = strings.TrimSpace(stdin) - if len(giteaURL) == 0 { - fmt.Println("URL is required!") - return nil - } - - parsedURL, err := url.Parse(giteaURL) - if err != nil { - return err - } - name = strings.ReplaceAll(strings.Title(parsedURL.Host), ".", "") - - fmt.Print("Name of new Login [" + name + "]: ") - if _, err := fmt.Scanln(&stdin); err != nil { - stdin = "" - } - if len(strings.TrimSpace(stdin)) != 0 { - name = strings.TrimSpace(stdin) - } - - fmt.Print("Do you have a token [Yes/no]: ") - if _, err := fmt.Scanln(&stdin); err != nil { - stdin = "" - } - if len(stdin) != 0 && strings.ToLower(stdin[:1]) == "n" { - fmt.Print("Username: ") - if _, err := fmt.Scanln(&stdin); err != nil { - stdin = "" - } - user = strings.TrimSpace(stdin) - - fmt.Print("Password: ") - if _, err := fmt.Scanln(&stdin); err != nil { - stdin = "" - } - passwd = strings.TrimSpace(stdin) - } else { - fmt.Print("Token: ") - if _, err := fmt.Scanln(&stdin); err != nil { - stdin = "" - } - token = strings.TrimSpace(stdin) - } - - fmt.Print("Set Optional settings [yes/No]: ") - if _, err := fmt.Scanln(&stdin); err != nil { - stdin = "" - } - if len(stdin) != 0 && strings.ToLower(stdin[:1]) == "y" { - fmt.Print("SSH Key Path: ") - if _, err := fmt.Scanln(&stdin); err != nil { - stdin = "" - } - sshKey = strings.TrimSpace(stdin) - - fmt.Print("Allow Insecure connections [yes/No]: ") - if _, err := fmt.Scanln(&stdin); err != nil { - stdin = "" - } - insecure = len(stdin) != 0 && strings.ToLower(stdin[:1]) == "y" - } - - return intern.AddLogin(name, token, user, passwd, sshKey, giteaURL, insecure) +func runLoginAddInteractive(_ *cli.Context) error { + return interact.CreateLogin() } // CmdLogin represents to login a gitea server. diff --git a/modules/interact/login.go b/modules/interact/login.go new file mode 100644 index 0000000..5a9d726 --- /dev/null +++ b/modules/interact/login.go @@ -0,0 +1,87 @@ +// Copyright 2020 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package interact + +import ( + "fmt" + "net/url" + "strings" + + "code.gitea.io/tea/modules/intern" +) + +// CreateLogin create an login interactive +func CreateLogin() error { + var stdin, name, token, user, passwd, sshKey, giteaURL string + var insecure = false + + fmt.Print("URL of Gitea instance: ") + if _, err := fmt.Scanln(&stdin); err != nil { + stdin = "" + } + giteaURL = strings.TrimSpace(stdin) + if len(giteaURL) == 0 { + fmt.Println("URL is required!") + return nil + } + + parsedURL, err := url.Parse(giteaURL) + if err != nil { + return err + } + name = strings.ReplaceAll(strings.Title(parsedURL.Host), ".", "") + + fmt.Print("Name of new Login [" + name + "]: ") + if _, err := fmt.Scanln(&stdin); err != nil { + stdin = "" + } + if len(strings.TrimSpace(stdin)) != 0 { + name = strings.TrimSpace(stdin) + } + + fmt.Print("Do you have a token [Yes/no]: ") + if _, err := fmt.Scanln(&stdin); err != nil { + stdin = "" + } + if len(stdin) != 0 && strings.ToLower(stdin[:1]) == "n" { + fmt.Print("Username: ") + if _, err := fmt.Scanln(&stdin); err != nil { + stdin = "" + } + user = strings.TrimSpace(stdin) + + fmt.Print("Password: ") + if _, err := fmt.Scanln(&stdin); err != nil { + stdin = "" + } + passwd = strings.TrimSpace(stdin) + } else { + fmt.Print("Token: ") + if _, err := fmt.Scanln(&stdin); err != nil { + stdin = "" + } + token = strings.TrimSpace(stdin) + } + + fmt.Print("Set Optional settings [yes/No]: ") + if _, err := fmt.Scanln(&stdin); err != nil { + stdin = "" + } + if len(stdin) != 0 && strings.ToLower(stdin[:1]) == "y" { + fmt.Print("SSH Key Path: ") + if _, err := fmt.Scanln(&stdin); err != nil { + stdin = "" + } + sshKey = strings.TrimSpace(stdin) + + fmt.Print("Allow Insecure connections [yes/No]: ") + if _, err := fmt.Scanln(&stdin); err != nil { + stdin = "" + } + insecure = len(stdin) != 0 && strings.ToLower(stdin[:1]) == "y" + } + + return intern.AddLogin(name, token, user, passwd, sshKey, giteaURL, insecure) +} -- 2.40.1 From 1f379527f04e8658ec6ce447b22878c149865a4d Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Mon, 28 Sep 2020 01:49:01 +0200 Subject: [PATCH 08/22] mv --- cmd/config.go | 22 ---------------------- cmd/flags.go | 14 ++++++++++++++ 2 files changed, 14 insertions(+), 22 deletions(-) delete mode 100644 cmd/config.go diff --git a/cmd/config.go b/cmd/config.go deleted file mode 100644 index f107069..0000000 --- a/cmd/config.go +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2018 The Gitea Authors. All rights reserved. -// Use of this source code is governed by a MIT-style -// license that can be found in the LICENSE file. - -package cmd - -import ( - "code.gitea.io/sdk/gitea" - "github.com/urfave/cli/v2" -) - -func getListOptions(ctx *cli.Context) gitea.ListOptions { - page := ctx.Int("page") - limit := ctx.Int("limit") - if limit != 0 && page == 0 { - page = 1 - } - return gitea.ListOptions{ - Page: page, - PageSize: limit, - } -} diff --git a/cmd/flags.go b/cmd/flags.go index 9e1dfc6..916d4a6 100644 --- a/cmd/flags.go +++ b/cmd/flags.go @@ -5,6 +5,7 @@ package cmd import ( + "code.gitea.io/sdk/gitea" "github.com/urfave/cli/v2" ) @@ -104,3 +105,16 @@ var IssuePRFlags = append([]cli.Flag{ &PaginationPageFlag, &PaginationLimitFlag, }, AllDefaultFlags...) + +// getListOptions return ListOptions based on PaginationFlags +func getListOptions(ctx *cli.Context) gitea.ListOptions { + page := ctx.Int("page") + limit := ctx.Int("limit") + if limit != 0 && page == 0 { + page = 1 + } + return gitea.ListOptions{ + Page: page, + PageSize: limit, + } +} -- 2.40.1 From 82453b928be456bc044c348411001716343d22bc Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Mon, 28 Sep 2020 01:57:14 +0200 Subject: [PATCH 09/22] split issues subcomands into own sourcefiles --- cmd/issues.go | 145 +------------------------------------------ cmd/issues_close.go | 23 +++++++ cmd/issues_create.go | 58 +++++++++++++++++ cmd/issues_list.go | 88 ++++++++++++++++++++++++++ cmd/issues_reopen.go | 24 +++++++ 5 files changed, 194 insertions(+), 144 deletions(-) create mode 100644 cmd/issues_close.go create mode 100644 cmd/issues_create.go create mode 100644 cmd/issues_list.go create mode 100644 cmd/issues_reopen.go diff --git a/cmd/issues.go b/cmd/issues.go index d50541c..5088c1f 100644 --- a/cmd/issues.go +++ b/cmd/issues.go @@ -6,7 +6,6 @@ package cmd import ( "log" - "strconv" "code.gitea.io/tea/modules/intern" "code.gitea.io/tea/modules/print" @@ -31,15 +30,6 @@ var CmdIssues = cli.Command{ Flags: IssuePRFlags, } -// CmdIssuesList represents a sub command of issues to list issues -var CmdIssuesList = cli.Command{ - Name: "ls", - Usage: "List issues of the repository", - Description: `List issues of the repository`, - Action: runIssuesList, - Flags: IssuePRFlags, -} - func runIssues(ctx *cli.Context) error { if ctx.Args().Len() == 1 { return runIssueDetail(ctx.Args().First()) @@ -62,140 +52,6 @@ func runIssueDetail(index string) error { return nil } -func runIssuesList(ctx *cli.Context) error { - login, owner, repo := intern.InitCommand(globalRepoValue, globalLoginValue, globalRemoteValue) - - state := gitea.StateOpen - switch ctx.String("state") { - case "all": - state = gitea.StateAll - case "open": - state = gitea.StateOpen - case "closed": - state = gitea.StateClosed - } - - issues, _, err := login.Client().ListRepoIssues(owner, repo, gitea.ListIssueOption{ - ListOptions: getListOptions(ctx), - State: state, - Type: gitea.IssueTypeIssue, - }) - - if err != nil { - log.Fatal(err) - } - - headers := []string{ - "Index", - "Title", - "State", - "Author", - "Milestone", - "Updated", - } - - var values [][]string - - if len(issues) == 0 { - Output(globalOutputValue, headers, values) - return nil - } - - for _, issue := range issues { - author := issue.Poster.FullName - if len(author) == 0 { - author = issue.Poster.UserName - } - mile := "" - if issue.Milestone != nil { - mile = issue.Milestone.Title - } - values = append( - values, - []string{ - strconv.FormatInt(issue.Index, 10), - issue.Title, - string(issue.State), - author, - mile, - issue.Updated.Format("2006-01-02 15:04:05"), - }, - ) - } - Output(globalOutputValue, headers, values) - - return nil -} - -// CmdIssuesCreate represents a sub command of issues to create issue -var CmdIssuesCreate = cli.Command{ - Name: "create", - Usage: "Create an issue on repository", - Description: `Create an issue on repository`, - Action: runIssuesCreate, - Flags: append([]cli.Flag{ - &cli.StringFlag{ - Name: "title", - Aliases: []string{"t"}, - Usage: "issue title to create", - }, - &cli.StringFlag{ - Name: "body", - Aliases: []string{"b"}, - Usage: "issue body to create", - }, - }, LoginRepoFlags...), -} - -func runIssuesCreate(ctx *cli.Context) error { - login, owner, repo := intern.InitCommand(globalRepoValue, globalLoginValue, globalRemoteValue) - - _, _, err := login.Client().CreateIssue(owner, repo, gitea.CreateIssueOption{ - Title: ctx.String("title"), - Body: ctx.String("body"), - // TODO: - //Assignee string `json:"assignee"` - //Assignees []string `json:"assignees"` - //Deadline *time.Time `json:"due_date"` - //Milestone int64 `json:"milestone"` - //Labels []int64 `json:"labels"` - //Closed bool `json:"closed"` - }) - - if err != nil { - log.Fatal(err) - } - - return nil -} - -// CmdIssuesReopen represents a sub command of issues to open an issue -var CmdIssuesReopen = cli.Command{ - Name: "reopen", - Aliases: []string{"open"}, - Usage: "Change state of an issue to 'open'", - Description: `Change state of an issue to 'open'`, - ArgsUsage: "", - Action: func(ctx *cli.Context) error { - var s = gitea.StateOpen - return editIssueState(ctx, gitea.EditIssueOption{State: &s}) - }, - Flags: AllDefaultFlags, -} - -// CmdIssuesClose represents a sub command of issues to close an issue -var CmdIssuesClose = cli.Command{ - Name: "close", - Usage: "Change state of an issue to 'closed'", - Description: `Change state of an issue to 'closed'`, - ArgsUsage: "", - Action: func(ctx *cli.Context) error { - var s = gitea.StateClosed - return editIssueState(ctx, gitea.EditIssueOption{State: &s}) - }, - Flags: AllDefaultFlags, -} - // editIssueState abstracts the arg parsing to edit the given issue func editIssueState(ctx *cli.Context, opts gitea.EditIssueOption) error { login, owner, repo := intern.InitCommand(globalRepoValue, globalLoginValue, globalRemoteValue) @@ -209,5 +65,6 @@ func editIssueState(ctx *cli.Context, opts gitea.EditIssueOption) error { } _, _, err = login.Client().EditIssue(owner, repo, index, opts) + // TODO: print (short)IssueDetails return err } diff --git a/cmd/issues_close.go b/cmd/issues_close.go new file mode 100644 index 0000000..fff75c5 --- /dev/null +++ b/cmd/issues_close.go @@ -0,0 +1,23 @@ +// Copyright 2018 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package cmd + +import ( + "code.gitea.io/sdk/gitea" + "github.com/urfave/cli/v2" +) + +// CmdIssuesClose represents a sub command of issues to close an issue +var CmdIssuesClose = cli.Command{ + Name: "close", + Usage: "Change state of an issue to 'closed'", + Description: `Change state of an issue to 'closed'`, + ArgsUsage: "", + Action: func(ctx *cli.Context) error { + var s = gitea.StateClosed + return editIssueState(ctx, gitea.EditIssueOption{State: &s}) + }, + Flags: AllDefaultFlags, +} diff --git a/cmd/issues_create.go b/cmd/issues_create.go new file mode 100644 index 0000000..fea0224 --- /dev/null +++ b/cmd/issues_create.go @@ -0,0 +1,58 @@ +// Copyright 2020 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package cmd + +import ( + "log" + + "code.gitea.io/tea/modules/intern" + + "code.gitea.io/sdk/gitea" + "github.com/urfave/cli/v2" +) + +// CmdIssuesCreate represents a sub command of issues to create issue +var CmdIssuesCreate = cli.Command{ + Name: "create", + Usage: "Create an issue on repository", + Description: `Create an issue on repository`, + Action: runIssuesCreate, + Flags: append([]cli.Flag{ + &cli.StringFlag{ + Name: "title", + Aliases: []string{"t"}, + Usage: "issue title to create", + }, + &cli.StringFlag{ + Name: "body", + Aliases: []string{"b"}, + Usage: "issue body to create", + }, + }, LoginRepoFlags...), +} + +func runIssuesCreate(ctx *cli.Context) error { + login, owner, repo := intern.InitCommand(globalRepoValue, globalLoginValue, globalRemoteValue) + + _, _, err := login.Client().CreateIssue(owner, repo, gitea.CreateIssueOption{ + Title: ctx.String("title"), + Body: ctx.String("body"), + // TODO: + //Assignee string `json:"assignee"` + //Assignees []string `json:"assignees"` + //Deadline *time.Time `json:"due_date"` + //Milestone int64 `json:"milestone"` + //Labels []int64 `json:"labels"` + //Closed bool `json:"closed"` + }) + + if err != nil { + log.Fatal(err) + } + + // TODO: Print IssueDetails + + return nil +} diff --git a/cmd/issues_list.go b/cmd/issues_list.go new file mode 100644 index 0000000..69a8671 --- /dev/null +++ b/cmd/issues_list.go @@ -0,0 +1,88 @@ +// Copyright 2020 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package cmd + +import ( + "log" + "strconv" + + "code.gitea.io/sdk/gitea" + "code.gitea.io/tea/modules/intern" + "github.com/urfave/cli/v2" +) + +// CmdIssuesList represents a sub command of issues to list issues +var CmdIssuesList = cli.Command{ + Name: "ls", + Usage: "List issues of the repository", + Description: `List issues of the repository`, + Action: runIssuesList, + Flags: IssuePRFlags, +} + +func runIssuesList(ctx *cli.Context) error { + login, owner, repo := intern.InitCommand(globalRepoValue, globalLoginValue, globalRemoteValue) + + state := gitea.StateOpen + switch ctx.String("state") { + case "all": + state = gitea.StateAll + case "open": + state = gitea.StateOpen + case "closed": + state = gitea.StateClosed + } + + issues, _, err := login.Client().ListRepoIssues(owner, repo, gitea.ListIssueOption{ + ListOptions: getListOptions(ctx), + State: state, + Type: gitea.IssueTypeIssue, + }) + + if err != nil { + log.Fatal(err) + } + + headers := []string{ + "Index", + "Title", + "State", + "Author", + "Milestone", + "Updated", + } + + var values [][]string + + if len(issues) == 0 { + Output(globalOutputValue, headers, values) + return nil + } + + for _, issue := range issues { + author := issue.Poster.FullName + if len(author) == 0 { + author = issue.Poster.UserName + } + mile := "" + if issue.Milestone != nil { + mile = issue.Milestone.Title + } + values = append( + values, + []string{ + strconv.FormatInt(issue.Index, 10), + issue.Title, + string(issue.State), + author, + mile, + issue.Updated.Format("2006-01-02 15:04:05"), + }, + ) + } + Output(globalOutputValue, headers, values) + + return nil +} diff --git a/cmd/issues_reopen.go b/cmd/issues_reopen.go new file mode 100644 index 0000000..2b7fd26 --- /dev/null +++ b/cmd/issues_reopen.go @@ -0,0 +1,24 @@ +// Copyright 2020 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package cmd + +import ( + "code.gitea.io/sdk/gitea" + "github.com/urfave/cli/v2" +) + +// CmdIssuesReopen represents a sub command of issues to open an issue +var CmdIssuesReopen = cli.Command{ + Name: "reopen", + Aliases: []string{"open"}, + Usage: "Change state of an issue to 'open'", + Description: `Change state of an issue to 'open'`, + ArgsUsage: "", + Action: func(ctx *cli.Context) error { + var s = gitea.StateOpen + return editIssueState(ctx, gitea.EditIssueOption{State: &s}) + }, + Flags: AllDefaultFlags, +} -- 2.40.1 From 26a61e04ac205de37f0e129f8571cb0cc4d58675 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Mon, 28 Sep 2020 02:02:30 +0200 Subject: [PATCH 10/22] Split labels subcomands into own sourcefiles --- cmd/labels.go | 156 ------------------------------------------- cmd/labels_create.go | 86 ++++++++++++++++++++++++ cmd/labels_delete.go | 38 +++++++++++ cmd/labels_update.go | 74 ++++++++++++++++++++ 4 files changed, 198 insertions(+), 156 deletions(-) create mode 100644 cmd/labels_create.go create mode 100644 cmd/labels_delete.go create mode 100644 cmd/labels_update.go diff --git a/cmd/labels.go b/cmd/labels.go index c5f769c..6022780 100644 --- a/cmd/labels.go +++ b/cmd/labels.go @@ -5,7 +5,6 @@ package cmd import ( - "bufio" "fmt" "log" "os" @@ -96,32 +95,6 @@ func runLabels(ctx *cli.Context) error { return nil } -// CmdLabelCreate represents a sub command of labels to create label. -var CmdLabelCreate = cli.Command{ - Name: "create", - Usage: "Create a label", - Description: `Create a label`, - Action: runLabelCreate, - Flags: []cli.Flag{ - &cli.StringFlag{ - Name: "name", - Usage: "label name", - }, - &cli.StringFlag{ - Name: "color", - Usage: "label color value", - }, - &cli.StringFlag{ - Name: "description", - Usage: "label description", - }, - &cli.StringFlag{ - Name: "file", - Usage: "indicate a label file", - }, - }, -} - func splitLabelLine(line string) (string, string, string) { fields := strings.SplitN(line, ";", 2) var color, name, description string @@ -142,132 +115,3 @@ func splitLabelLine(line string) (string, string, string) { } return color, name, description } - -func runLabelCreate(ctx *cli.Context) error { - login, owner, repo := intern.InitCommand(globalRepoValue, globalLoginValue, globalRemoteValue) - - labelFile := ctx.String("file") - var err error - if len(labelFile) == 0 { - _, _, err = login.Client().CreateLabel(owner, repo, gitea.CreateLabelOption{ - Name: ctx.String("name"), - Color: ctx.String("color"), - Description: ctx.String("description"), - }) - } else { - f, err := os.Open(labelFile) - if err != nil { - return err - } - defer f.Close() - - scanner := bufio.NewScanner(f) - var i = 1 - for scanner.Scan() { - line := scanner.Text() - color, name, description := splitLabelLine(line) - if color == "" || name == "" { - log.Printf("Line %d ignored because lack of enough fields: %s\n", i, line) - } else { - _, _, err = login.Client().CreateLabel(owner, repo, gitea.CreateLabelOption{ - Name: name, - Color: color, - Description: description, - }) - } - - i++ - } - } - - if err != nil { - log.Fatal(err) - } - - return nil -} - -// CmdLabelUpdate represents a sub command of labels to update label. -var CmdLabelUpdate = cli.Command{ - Name: "update", - Usage: "Update a label", - Description: `Update a label`, - Action: runLabelUpdate, - Flags: []cli.Flag{ - &cli.IntFlag{ - Name: "id", - Usage: "label id", - }, - &cli.StringFlag{ - Name: "name", - Usage: "label name", - }, - &cli.StringFlag{ - Name: "color", - Usage: "label color value", - }, - &cli.StringFlag{ - Name: "description", - Usage: "label description", - }, - }, -} - -func runLabelUpdate(ctx *cli.Context) error { - login, owner, repo := intern.InitCommand(globalRepoValue, globalLoginValue, globalRemoteValue) - - id := ctx.Int64("id") - var pName, pColor, pDescription *string - name := ctx.String("name") - if name != "" { - pName = &name - } - - color := ctx.String("color") - if color != "" { - pColor = &color - } - - description := ctx.String("description") - if description != "" { - pDescription = &description - } - - var err error - _, _, err = login.Client().EditLabel(owner, repo, id, gitea.EditLabelOption{ - Name: pName, - Color: pColor, - Description: pDescription, - }) - - if err != nil { - log.Fatal(err) - } - - return nil -} - -// CmdLabelDelete represents a sub command of labels to delete label. -var CmdLabelDelete = cli.Command{ - Name: "delete", - Usage: "Delete a label", - Description: `Delete a label`, - Action: runLabelDelete, - Flags: []cli.Flag{ - &cli.IntFlag{ - Name: "id", - Usage: "label id", - }, - }, -} - -func runLabelDelete(ctx *cli.Context) error { - login, owner, repo := intern.InitCommand(globalRepoValue, globalLoginValue, globalRemoteValue) - - _, err := login.Client().DeleteLabel(owner, repo, ctx.Int64("id")) - if err != nil { - log.Fatal(err) - } - - return nil -} diff --git a/cmd/labels_create.go b/cmd/labels_create.go new file mode 100644 index 0000000..68dc922 --- /dev/null +++ b/cmd/labels_create.go @@ -0,0 +1,86 @@ +// Copyright 2020 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package cmd + +import ( + "bufio" + "log" + "os" + + "code.gitea.io/tea/modules/intern" + + "code.gitea.io/sdk/gitea" + "github.com/urfave/cli/v2" +) + +// CmdLabelCreate represents a sub command of labels to create label. +var CmdLabelCreate = cli.Command{ + Name: "create", + Usage: "Create a label", + Description: `Create a label`, + Action: runLabelCreate, + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "name", + Usage: "label name", + }, + &cli.StringFlag{ + Name: "color", + Usage: "label color value", + }, + &cli.StringFlag{ + Name: "description", + Usage: "label description", + }, + &cli.StringFlag{ + Name: "file", + Usage: "indicate a label file", + }, + }, +} + +func runLabelCreate(ctx *cli.Context) error { + login, owner, repo := intern.InitCommand(globalRepoValue, globalLoginValue, globalRemoteValue) + + labelFile := ctx.String("file") + var err error + if len(labelFile) == 0 { + _, _, err = login.Client().CreateLabel(owner, repo, gitea.CreateLabelOption{ + Name: ctx.String("name"), + Color: ctx.String("color"), + Description: ctx.String("description"), + }) + } else { + f, err := os.Open(labelFile) + if err != nil { + return err + } + defer f.Close() + + scanner := bufio.NewScanner(f) + var i = 1 + for scanner.Scan() { + line := scanner.Text() + color, name, description := splitLabelLine(line) + if color == "" || name == "" { + log.Printf("Line %d ignored because lack of enough fields: %s\n", i, line) + } else { + _, _, err = login.Client().CreateLabel(owner, repo, gitea.CreateLabelOption{ + Name: name, + Color: color, + Description: description, + }) + } + + i++ + } + } + + if err != nil { + log.Fatal(err) + } + + return nil +} diff --git a/cmd/labels_delete.go b/cmd/labels_delete.go new file mode 100644 index 0000000..6229915 --- /dev/null +++ b/cmd/labels_delete.go @@ -0,0 +1,38 @@ +// Copyright 2020 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package cmd + +import ( + "log" + + "code.gitea.io/tea/modules/intern" + + "github.com/urfave/cli/v2" +) + +// CmdLabelDelete represents a sub command of labels to delete label. +var CmdLabelDelete = cli.Command{ + Name: "delete", + Usage: "Delete a label", + Description: `Delete a label`, + Action: runLabelDelete, + Flags: []cli.Flag{ + &cli.IntFlag{ + Name: "id", + Usage: "label id", + }, + }, +} + +func runLabelDelete(ctx *cli.Context) error { + login, owner, repo := intern.InitCommand(globalRepoValue, globalLoginValue, globalRemoteValue) + + _, err := login.Client().DeleteLabel(owner, repo, ctx.Int64("id")) + if err != nil { + log.Fatal(err) + } + + return nil +} diff --git a/cmd/labels_update.go b/cmd/labels_update.go new file mode 100644 index 0000000..044662c --- /dev/null +++ b/cmd/labels_update.go @@ -0,0 +1,74 @@ +// Copyright 2020 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package cmd + +import ( + "log" + + "code.gitea.io/tea/modules/intern" + + "code.gitea.io/sdk/gitea" + "github.com/urfave/cli/v2" +) + +// CmdLabelUpdate represents a sub command of labels to update label. +var CmdLabelUpdate = cli.Command{ + Name: "update", + Usage: "Update a label", + Description: `Update a label`, + Action: runLabelUpdate, + Flags: []cli.Flag{ + &cli.IntFlag{ + Name: "id", + Usage: "label id", + }, + &cli.StringFlag{ + Name: "name", + Usage: "label name", + }, + &cli.StringFlag{ + Name: "color", + Usage: "label color value", + }, + &cli.StringFlag{ + Name: "description", + Usage: "label description", + }, + }, +} + +func runLabelUpdate(ctx *cli.Context) error { + login, owner, repo := intern.InitCommand(globalRepoValue, globalLoginValue, globalRemoteValue) + + id := ctx.Int64("id") + var pName, pColor, pDescription *string + name := ctx.String("name") + if name != "" { + pName = &name + } + + color := ctx.String("color") + if color != "" { + pColor = &color + } + + description := ctx.String("description") + if description != "" { + pDescription = &description + } + + var err error + _, _, err = login.Client().EditLabel(owner, repo, id, gitea.EditLabelOption{ + Name: pName, + Color: pColor, + Description: pDescription, + }) + + if err != nil { + log.Fatal(err) + } + + return nil +} -- 2.40.1 From 734909f46293b43ed6b80299d55699a60e960e46 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Mon, 28 Sep 2020 02:09:27 +0200 Subject: [PATCH 11/22] Split login subcomands into own sourcefiles --- cmd/login.go | 168 +------------------------------------------ cmd/login_add.go | 81 +++++++++++++++++++++ cmd/login_default.go | 51 +++++++++++++ cmd/login_edit.go | 25 +++++++ cmd/login_list.go | 54 ++++++++++++++ 5 files changed, 212 insertions(+), 167 deletions(-) create mode 100644 cmd/login_add.go create mode 100644 cmd/login_default.go create mode 100644 cmd/login_edit.go create mode 100644 cmd/login_list.go diff --git a/cmd/login.go b/cmd/login.go index f9c7c06..c0491ea 100644 --- a/cmd/login.go +++ b/cmd/login.go @@ -5,13 +5,6 @@ package cmd import ( - "fmt" - "log" - - "code.gitea.io/tea/modules/interact" - "code.gitea.io/tea/modules/intern" - - "github.com/skratchdot/open-golang/open" "github.com/urfave/cli/v2" ) @@ -20,7 +13,7 @@ var CmdLogin = cli.Command{ Name: "login", Usage: "Log in to a Gitea server", Description: `Log in to a Gitea server`, - Action: runLoginAddInteractive, + Action: runLoginAddInteractive, // TODO show list if no arg & detail if login as arg Subcommands: []*cli.Command{ &cmdLoginList, &cmdLoginAdd, @@ -28,162 +21,3 @@ var CmdLogin = cli.Command{ &cmdLoginSetDefault, }, } - -// cmdLoginEdit represents to login a gitea server. -var cmdLoginEdit = cli.Command{ - Name: "edit", - Usage: "Edit Gitea logins", - Description: `Edit Gitea logins`, - Action: runLoginEdit, - Flags: []cli.Flag{&OutputFlag}, -} - -func runLoginEdit(ctx *cli.Context) error { - return open.Start(intern.GetConfigPath()) -} - -// cmdLoginSetDefault represents to login a gitea server. -var cmdLoginSetDefault = cli.Command{ - Name: "default", - Usage: "Get or Set Default Login", - Description: `Get or Set Default Login`, - ArgsUsage: "", - Action: runLoginSetDefault, - Flags: []cli.Flag{&OutputFlag}, -} - -func runLoginSetDefault(ctx *cli.Context) error { - if err := intern.LoadConfig(); err != nil { - return err - } - if ctx.Args().Len() == 0 { - l, err := intern.GetDefaultLogin() - if err != nil { - return err - } - fmt.Printf("Default Login: %s\n", l.Name) - return nil - } - loginExist := false - for i := range intern.Config.Logins { - intern.Config.Logins[i].Default = false - if intern.Config.Logins[i].Name == ctx.Args().First() { - intern.Config.Logins[i].Default = true - loginExist = true - } - } - - if !loginExist { - return fmt.Errorf("login '%s' not found", ctx.Args().First()) - } - - return intern.SaveConfig() -} - -// CmdLogin represents to login a gitea server. -var cmdLoginAdd = cli.Command{ - Name: "add", - Usage: "Add a Gitea login", - Description: `Add a Gitea login`, - Flags: []cli.Flag{ - &cli.StringFlag{ - Name: "name", - Aliases: []string{"n"}, - Usage: "Login name", - }, - &cli.StringFlag{ - Name: "url", - Aliases: []string{"u"}, - Value: "https://try.gitea.io", - EnvVars: []string{"GITEA_SERVER_URL"}, - Usage: "Server URL", - Required: true, - }, - &cli.StringFlag{ - Name: "token", - Aliases: []string{"t"}, - Value: "", - EnvVars: []string{"GITEA_SERVER_TOKEN"}, - Usage: "Access token. Can be obtained from Settings > Applications", - }, - &cli.StringFlag{ - Name: "user", - Value: "", - EnvVars: []string{"GITEA_SERVER_USER"}, - Usage: "User for basic auth (will create token)", - }, - &cli.StringFlag{ - Name: "password", - Aliases: []string{"pwd"}, - Value: "", - EnvVars: []string{"GITEA_SERVER_PASSWORD"}, - Usage: "Password for basic auth (will create token)", - }, - &cli.StringFlag{ - Name: "ssh-key", - Aliases: []string{"s"}, - Usage: "Path to a SSH key to use for pull/push operations", - }, - &cli.BoolFlag{ - Name: "insecure", - Aliases: []string{"i"}, - Usage: "Disable TLS verification", - }, - }, - Action: runLoginAdd, -} - -func runLoginAdd(ctx *cli.Context) error { - return intern.AddLogin( - ctx.String("name"), - ctx.String("token"), - ctx.String("user"), - ctx.String("password"), - ctx.String("ssh-key"), - ctx.String("url"), - ctx.Bool("insecure")) -} - -func runLoginAddInteractive(_ *cli.Context) error { - return interact.CreateLogin() -} - -// CmdLogin represents to login a gitea server. -var cmdLoginList = cli.Command{ - Name: "ls", - Usage: "List Gitea logins", - Description: `List Gitea logins`, - Action: runLoginList, - Flags: []cli.Flag{&OutputFlag}, -} - -func runLoginList(ctx *cli.Context) error { - err := intern.LoadConfig() - if err != nil { - log.Fatal(err) - } - - headers := []string{ - "Name", - "URL", - "SSHHost", - "User", - "Default", - } - - var values [][]string - - for _, l := range intern.Config.Logins { - values = append(values, []string{ - l.Name, - l.URL, - l.GetSSHHost(), - l.User, - fmt.Sprint(l.Default), - }) - } - - Output(globalOutputValue, headers, values) - - return nil -} diff --git a/cmd/login_add.go b/cmd/login_add.go new file mode 100644 index 0000000..13cc28c --- /dev/null +++ b/cmd/login_add.go @@ -0,0 +1,81 @@ +// Copyright 2020 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package cmd + +import ( + "code.gitea.io/tea/modules/interact" + "code.gitea.io/tea/modules/intern" + + "github.com/urfave/cli/v2" +) + +// CmdLogin represents to login a gitea server. +var cmdLoginAdd = cli.Command{ + Name: "add", + Usage: "Add a Gitea login", + Description: `Add a Gitea login`, + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "name", + Aliases: []string{"n"}, + Usage: "Login name", + }, + &cli.StringFlag{ + Name: "url", + Aliases: []string{"u"}, + Value: "https://try.gitea.io", + EnvVars: []string{"GITEA_SERVER_URL"}, + Usage: "Server URL", + Required: true, + }, + &cli.StringFlag{ + Name: "token", + Aliases: []string{"t"}, + Value: "", + EnvVars: []string{"GITEA_SERVER_TOKEN"}, + Usage: "Access token. Can be obtained from Settings > Applications", + }, + &cli.StringFlag{ + Name: "user", + Value: "", + EnvVars: []string{"GITEA_SERVER_USER"}, + Usage: "User for basic auth (will create token)", + }, + &cli.StringFlag{ + Name: "password", + Aliases: []string{"pwd"}, + Value: "", + EnvVars: []string{"GITEA_SERVER_PASSWORD"}, + Usage: "Password for basic auth (will create token)", + }, + &cli.StringFlag{ + Name: "ssh-key", + Aliases: []string{"s"}, + Usage: "Path to a SSH key to use for pull/push operations", + }, + &cli.BoolFlag{ + Name: "insecure", + Aliases: []string{"i"}, + Usage: "Disable TLS verification", + }, + }, + Action: runLoginAdd, +} + +func runLoginAdd(ctx *cli.Context) error { + // TODO: if no args -> interactive + return intern.AddLogin( + ctx.String("name"), + ctx.String("token"), + ctx.String("user"), + ctx.String("password"), + ctx.String("ssh-key"), + ctx.String("url"), + ctx.Bool("insecure")) +} + +func runLoginAddInteractive(_ *cli.Context) error { + return interact.CreateLogin() +} diff --git a/cmd/login_default.go b/cmd/login_default.go new file mode 100644 index 0000000..7023267 --- /dev/null +++ b/cmd/login_default.go @@ -0,0 +1,51 @@ +// Copyright 2020 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package cmd + +import ( + "fmt" + + "code.gitea.io/tea/modules/intern" + + "github.com/urfave/cli/v2" +) + +// cmdLoginSetDefault represents to login a gitea server. +var cmdLoginSetDefault = cli.Command{ + Name: "default", + Usage: "Get or Set Default Login", + Description: `Get or Set Default Login`, + ArgsUsage: "", + Action: runLoginSetDefault, + Flags: []cli.Flag{&OutputFlag}, +} + +func runLoginSetDefault(ctx *cli.Context) error { + if err := intern.LoadConfig(); err != nil { + return err + } + if ctx.Args().Len() == 0 { + l, err := intern.GetDefaultLogin() + if err != nil { + return err + } + fmt.Printf("Default Login: %s\n", l.Name) + return nil + } + loginExist := false + for i := range intern.Config.Logins { + intern.Config.Logins[i].Default = false + if intern.Config.Logins[i].Name == ctx.Args().First() { + intern.Config.Logins[i].Default = true + loginExist = true + } + } + + if !loginExist { + return fmt.Errorf("login '%s' not found", ctx.Args().First()) + } + + return intern.SaveConfig() +} diff --git a/cmd/login_edit.go b/cmd/login_edit.go new file mode 100644 index 0000000..86ba78b --- /dev/null +++ b/cmd/login_edit.go @@ -0,0 +1,25 @@ +// Copyright 2018 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package cmd + +import ( + "code.gitea.io/tea/modules/intern" + + "github.com/skratchdot/open-golang/open" + "github.com/urfave/cli/v2" +) + +// cmdLoginEdit represents to login a gitea server. +var cmdLoginEdit = cli.Command{ + Name: "edit", + Usage: "Edit Gitea logins", + Description: `Edit Gitea logins`, + Action: runLoginEdit, + Flags: []cli.Flag{&OutputFlag}, +} + +func runLoginEdit(ctx *cli.Context) error { + return open.Start(intern.GetConfigPath()) +} diff --git a/cmd/login_list.go b/cmd/login_list.go new file mode 100644 index 0000000..0eb15f8 --- /dev/null +++ b/cmd/login_list.go @@ -0,0 +1,54 @@ +// Copyright 2020 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package cmd + +import ( + "fmt" + "log" + + "code.gitea.io/tea/modules/intern" + + "github.com/urfave/cli/v2" +) + +// CmdLogin represents to login a gitea server. +var cmdLoginList = cli.Command{ + Name: "ls", + Usage: "List Gitea logins", + Description: `List Gitea logins`, + Action: runLoginList, + Flags: []cli.Flag{&OutputFlag}, +} + +func runLoginList(ctx *cli.Context) error { + err := intern.LoadConfig() + if err != nil { + log.Fatal(err) + } + + headers := []string{ + "Name", + "URL", + "SSHHost", + "User", + "Default", + } + + var values [][]string + + for _, l := range intern.Config.Logins { + values = append(values, []string{ + l.Name, + l.URL, + l.GetSSHHost(), + l.User, + fmt.Sprint(l.Default), + }) + } + + Output(globalOutputValue, headers, values) + + return nil +} -- 2.40.1 From 3f8e96f2a63f6bc6268ba3204a36b3476fce1c6b Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Mon, 28 Sep 2020 02:20:14 +0200 Subject: [PATCH 12/22] Split milestones subcomands into own sourcefiles --- cmd/milestones.go | 182 ------------------ cmd/milestones_close.go | 30 +++ cmd/milestones_create.go | 66 +++++++ cmd/milestones_delete.go | 30 +++ ...lestone_issues.go => milestones_issues.go} | 0 cmd/milestones_list.go | 90 +++++++++ cmd/milestones_reopen.go | 22 +++ 7 files changed, 238 insertions(+), 182 deletions(-) create mode 100644 cmd/milestones_close.go create mode 100644 cmd/milestones_create.go create mode 100644 cmd/milestones_delete.go rename cmd/{milestone_issues.go => milestones_issues.go} (100%) create mode 100644 cmd/milestones_list.go create mode 100644 cmd/milestones_reopen.go diff --git a/cmd/milestones.go b/cmd/milestones.go index 5e05e48..57131eb 100644 --- a/cmd/milestones.go +++ b/cmd/milestones.go @@ -5,9 +5,6 @@ package cmd import ( - "fmt" - "log" - "code.gitea.io/tea/modules/intern" "code.gitea.io/tea/modules/print" @@ -34,23 +31,6 @@ var CmdMilestones = cli.Command{ Flags: AllDefaultFlags, } -// CmdMilestonesList represents a sub command of milestones to list milestones -var CmdMilestonesList = cli.Command{ - Name: "ls", - Usage: "List milestones of the repository", - Description: `List milestones of the repository`, - Action: runMilestonesList, - Flags: append([]cli.Flag{ - &cli.StringFlag{ - Name: "state", - Usage: "Filter by milestone state (all|open|closed)", - DefaultText: "open", - }, - &PaginationPageFlag, - &PaginationLimitFlag, - }, AllDefaultFlags...), -} - func runMilestones(ctx *cli.Context) error { if ctx.Args().Len() == 1 { return runMilestoneDetail(ctx.Args().First()) @@ -71,136 +51,6 @@ func runMilestoneDetail(name string) error { return nil } -func runMilestonesList(ctx *cli.Context) error { - login, owner, repo := intern.InitCommand(globalRepoValue, globalLoginValue, globalRemoteValue) - - state := gitea.StateOpen - switch ctx.String("state") { - case "all": - state = gitea.StateAll - case "closed": - state = gitea.StateClosed - } - - milestones, _, err := login.Client().ListRepoMilestones(owner, repo, gitea.ListMilestoneOption{ - ListOptions: getListOptions(ctx), - State: state, - }) - - if err != nil { - log.Fatal(err) - } - - headers := []string{ - "Title", - } - if state == gitea.StateAll { - headers = append(headers, "State") - } - headers = append(headers, - "Open/Closed Issues", - "DueDate", - ) - - var values [][]string - - for _, m := range milestones { - var deadline = "" - - if m.Deadline != nil && !m.Deadline.IsZero() { - deadline = m.Deadline.Format("2006-01-02 15:04:05") - } - - item := []string{ - m.Title, - } - if state == gitea.StateAll { - item = append(item, string(m.State)) - } - item = append(item, - fmt.Sprintf("%d/%d", m.OpenIssues, m.ClosedIssues), - deadline, - ) - - values = append(values, item) - } - Output(globalOutputValue, headers, values) - - return nil -} - -// CmdMilestonesCreate represents a sub command of milestones to create milestone -var CmdMilestonesCreate = cli.Command{ - Name: "create", - Usage: "Create an milestone on repository", - Description: `Create an milestone on repository`, - Action: runMilestonesCreate, - Flags: append([]cli.Flag{ - &cli.StringFlag{ - Name: "title", - Aliases: []string{"t"}, - Usage: "milestone title to create", - }, - &cli.StringFlag{ - Name: "description", - Aliases: []string{"d"}, - Usage: "milestone description to create", - }, - &cli.StringFlag{ - Name: "state", - Usage: "set milestone state (default is open)", - DefaultText: "open", - }, - }, AllDefaultFlags...), -} - -func runMilestonesCreate(ctx *cli.Context) error { - login, owner, repo := intern.InitCommand(globalRepoValue, globalLoginValue, globalRemoteValue) - - title := ctx.String("title") - if len(title) == 0 { - fmt.Printf("Title is required\n") - return nil - } - - state := gitea.StateOpen - if ctx.String("state") == "closed" { - state = gitea.StateClosed - } - - mile, _, err := login.Client().CreateMilestone(owner, repo, gitea.CreateMilestoneOption{ - Title: title, - Description: ctx.String("description"), - State: state, - }) - if err != nil { - log.Fatal(err) - } - - return runMilestoneDetail(mile.Title) -} - -// CmdMilestonesClose represents a sub command of milestones to close an milestone -var CmdMilestonesClose = cli.Command{ - Name: "close", - Usage: "Change state of an milestone to 'closed'", - Description: `Change state of an milestone to 'closed'`, - ArgsUsage: "", - Action: func(ctx *cli.Context) error { - if ctx.Bool("force") { - return deleteMilestone(ctx) - } - return editMilestoneStatus(ctx, true) - }, - Flags: append([]cli.Flag{ - &cli.BoolFlag{ - Name: "force", - Aliases: []string{"f"}, - Usage: "delete milestone", - }, - }, AllDefaultFlags...), -} - func editMilestoneStatus(ctx *cli.Context, close bool) error { login, owner, repo := intern.InitCommand(globalRepoValue, globalLoginValue, globalRemoteValue) client := login.Client() @@ -216,35 +66,3 @@ func editMilestoneStatus(ctx *cli.Context, close bool) error { return err } - -// CmdMilestonesDelete represents a sub command of milestones to delete an milestone -var CmdMilestonesDelete = cli.Command{ - Name: "delete", - Aliases: []string{"rm"}, - Usage: "delete a milestone", - Description: "delete a milestone", - ArgsUsage: "", - Action: deleteMilestone, - Flags: AllDefaultFlags, -} - -func deleteMilestone(ctx *cli.Context) error { - login, owner, repo := intern.InitCommand(globalRepoValue, globalLoginValue, globalRemoteValue) - client := login.Client() - - _, err := client.DeleteMilestoneByName(owner, repo, ctx.Args().First()) - return err -} - -// CmdMilestonesReopen represents a sub command of milestones to open an milestone -var CmdMilestonesReopen = cli.Command{ - Name: "reopen", - Aliases: []string{"open"}, - Usage: "Change state of an milestone to 'open'", - Description: `Change state of an milestone to 'open'`, - ArgsUsage: "", - Action: func(ctx *cli.Context) error { - return editMilestoneStatus(ctx, false) - }, - Flags: AllDefaultFlags, -} diff --git a/cmd/milestones_close.go b/cmd/milestones_close.go new file mode 100644 index 0000000..0815046 --- /dev/null +++ b/cmd/milestones_close.go @@ -0,0 +1,30 @@ +// Copyright 2020 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package cmd + +import ( + "github.com/urfave/cli/v2" +) + +// CmdMilestonesClose represents a sub command of milestones to close an milestone +var CmdMilestonesClose = cli.Command{ + Name: "close", + Usage: "Change state of an milestone to 'closed'", + Description: `Change state of an milestone to 'closed'`, + ArgsUsage: "", + Action: func(ctx *cli.Context) error { + if ctx.Bool("force") { + return deleteMilestone(ctx) + } + return editMilestoneStatus(ctx, true) + }, + Flags: append([]cli.Flag{ + &cli.BoolFlag{ + Name: "force", + Aliases: []string{"f"}, + Usage: "delete milestone", + }, + }, AllDefaultFlags...), +} diff --git a/cmd/milestones_create.go b/cmd/milestones_create.go new file mode 100644 index 0000000..1757940 --- /dev/null +++ b/cmd/milestones_create.go @@ -0,0 +1,66 @@ +// Copyright 2020 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package cmd + +import ( + "fmt" + "log" + + "code.gitea.io/tea/modules/intern" + + "code.gitea.io/sdk/gitea" + "github.com/urfave/cli/v2" +) + +// CmdMilestonesCreate represents a sub command of milestones to create milestone +var CmdMilestonesCreate = cli.Command{ + Name: "create", + Usage: "Create an milestone on repository", + Description: `Create an milestone on repository`, + Action: runMilestonesCreate, + Flags: append([]cli.Flag{ + &cli.StringFlag{ + Name: "title", + Aliases: []string{"t"}, + Usage: "milestone title to create", + }, + &cli.StringFlag{ + Name: "description", + Aliases: []string{"d"}, + Usage: "milestone description to create", + }, + &cli.StringFlag{ + Name: "state", + Usage: "set milestone state (default is open)", + DefaultText: "open", + }, + }, AllDefaultFlags...), +} + +func runMilestonesCreate(ctx *cli.Context) error { + login, owner, repo := intern.InitCommand(globalRepoValue, globalLoginValue, globalRemoteValue) + + title := ctx.String("title") + if len(title) == 0 { + fmt.Printf("Title is required\n") + return nil + } + + state := gitea.StateOpen + if ctx.String("state") == "closed" { + state = gitea.StateClosed + } + + mile, _, err := login.Client().CreateMilestone(owner, repo, gitea.CreateMilestoneOption{ + Title: title, + Description: ctx.String("description"), + State: state, + }) + if err != nil { + log.Fatal(err) + } + + return runMilestoneDetail(mile.Title) +} diff --git a/cmd/milestones_delete.go b/cmd/milestones_delete.go new file mode 100644 index 0000000..a074f3f --- /dev/null +++ b/cmd/milestones_delete.go @@ -0,0 +1,30 @@ +// Copyright 2020 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package cmd + +import ( + "code.gitea.io/tea/modules/intern" + + "github.com/urfave/cli/v2" +) + +// CmdMilestonesDelete represents a sub command of milestones to delete an milestone +var CmdMilestonesDelete = cli.Command{ + Name: "delete", + Aliases: []string{"rm"}, + Usage: "delete a milestone", + Description: "delete a milestone", + ArgsUsage: "", + Action: deleteMilestone, + Flags: AllDefaultFlags, +} + +func deleteMilestone(ctx *cli.Context) error { + login, owner, repo := intern.InitCommand(globalRepoValue, globalLoginValue, globalRemoteValue) + client := login.Client() + + _, err := client.DeleteMilestoneByName(owner, repo, ctx.Args().First()) + return err +} diff --git a/cmd/milestone_issues.go b/cmd/milestones_issues.go similarity index 100% rename from cmd/milestone_issues.go rename to cmd/milestones_issues.go diff --git a/cmd/milestones_list.go b/cmd/milestones_list.go new file mode 100644 index 0000000..c67b84e --- /dev/null +++ b/cmd/milestones_list.go @@ -0,0 +1,90 @@ +// Copyright 2020 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package cmd + +import ( + "fmt" + "log" + + "code.gitea.io/tea/modules/intern" + + "code.gitea.io/sdk/gitea" + "github.com/urfave/cli/v2" +) + +// CmdMilestonesList represents a sub command of milestones to list milestones +var CmdMilestonesList = cli.Command{ + Name: "ls", + Usage: "List milestones of the repository", + Description: `List milestones of the repository`, + Action: runMilestonesList, + Flags: append([]cli.Flag{ + &cli.StringFlag{ + Name: "state", + Usage: "Filter by milestone state (all|open|closed)", + DefaultText: "open", + }, + &PaginationPageFlag, + &PaginationLimitFlag, + }, AllDefaultFlags...), +} + +func runMilestonesList(ctx *cli.Context) error { + login, owner, repo := intern.InitCommand(globalRepoValue, globalLoginValue, globalRemoteValue) + + state := gitea.StateOpen + switch ctx.String("state") { + case "all": + state = gitea.StateAll + case "closed": + state = gitea.StateClosed + } + + milestones, _, err := login.Client().ListRepoMilestones(owner, repo, gitea.ListMilestoneOption{ + ListOptions: getListOptions(ctx), + State: state, + }) + + if err != nil { + log.Fatal(err) + } + + headers := []string{ + "Title", + } + if state == gitea.StateAll { + headers = append(headers, "State") + } + headers = append(headers, + "Open/Closed Issues", + "DueDate", + ) + + var values [][]string + + for _, m := range milestones { + var deadline = "" + + if m.Deadline != nil && !m.Deadline.IsZero() { + deadline = m.Deadline.Format("2006-01-02 15:04:05") + } + + item := []string{ + m.Title, + } + if state == gitea.StateAll { + item = append(item, string(m.State)) + } + item = append(item, + fmt.Sprintf("%d/%d", m.OpenIssues, m.ClosedIssues), + deadline, + ) + + values = append(values, item) + } + Output(globalOutputValue, headers, values) + + return nil +} diff --git a/cmd/milestones_reopen.go b/cmd/milestones_reopen.go new file mode 100644 index 0000000..0606b0a --- /dev/null +++ b/cmd/milestones_reopen.go @@ -0,0 +1,22 @@ +// Copyright 2020 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package cmd + +import ( + "github.com/urfave/cli/v2" +) + +// CmdMilestonesReopen represents a sub command of milestones to open an milestone +var CmdMilestonesReopen = cli.Command{ + Name: "reopen", + Aliases: []string{"open"}, + Usage: "Change state of an milestone to 'open'", + Description: `Change state of an milestone to 'open'`, + ArgsUsage: "", + Action: func(ctx *cli.Context) error { + return editMilestoneStatus(ctx, false) + }, + Flags: AllDefaultFlags, +} -- 2.40.1 From e2f7a3e3b5a1f14514b3a8a28c6ce72f10ddf8a3 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Mon, 28 Sep 2020 02:27:59 +0200 Subject: [PATCH 13/22] Split pulls subcomands into own sourcefiles --- cmd/pulls.go | 377 +----------------------------------------- cmd/pulls_checkout.go | 95 +++++++++++ cmd/pulls_clean.go | 103 ++++++++++++ cmd/pulls_create.go | 144 ++++++++++++++++ cmd/pulls_list.go | 90 ++++++++++ 5 files changed, 434 insertions(+), 375 deletions(-) create mode 100644 cmd/pulls_checkout.go create mode 100644 cmd/pulls_clean.go create mode 100644 cmd/pulls_create.go create mode 100644 cmd/pulls_list.go diff --git a/cmd/pulls.go b/cmd/pulls.go index 4e655ab..0860c92 100644 --- a/cmd/pulls.go +++ b/cmd/pulls.go @@ -6,17 +6,10 @@ package cmd import ( "fmt" - "log" "strconv" "strings" - local_git "code.gitea.io/tea/modules/git" "code.gitea.io/tea/modules/intern" - "code.gitea.io/tea/modules/print" - - "code.gitea.io/sdk/gitea" - "github.com/go-git/go-git/v5" - git_config "github.com/go-git/go-git/v5/config" "github.com/urfave/cli/v2" ) @@ -39,21 +32,12 @@ var CmdPulls = cli.Command{ func runPulls(ctx *cli.Context) error { if ctx.Args().Len() == 1 { - return runPullDetail(ctx, ctx.Args().First()) + return runPullDetail(ctx.Args().First()) } return runPullsList(ctx) } -// CmdPullsList represents a sub command of issues to list pulls -var CmdPullsList = cli.Command{ - Name: "ls", - Usage: "List pull requests of the repository", - Description: `List pull requests of the repository`, - Action: runPullsList, - Flags: IssuePRFlags, -} - -func runPullDetail(ctx *cli.Context, index string) error { +func runPullDetail(index string) error { login, owner, repo := intern.InitCommand(globalRepoValue, globalLoginValue, globalRemoteValue) idx, err := argToIndex(index) @@ -75,363 +59,6 @@ func runPullDetail(ctx *cli.Context, index string) error { return nil } -func runPullsList(ctx *cli.Context) error { - login, owner, repo := intern.InitCommand(globalRepoValue, globalLoginValue, globalRemoteValue) - - state := gitea.StateOpen - switch ctx.String("state") { - case "all": - state = gitea.StateAll - case "open": - state = gitea.StateOpen - case "closed": - state = gitea.StateClosed - } - - prs, _, err := login.Client().ListRepoPullRequests(owner, repo, gitea.ListPullRequestsOptions{ - State: state, - }) - - if err != nil { - log.Fatal(err) - } - - headers := []string{ - "Index", - "Title", - "State", - "Author", - "Milestone", - "Updated", - } - - var values [][]string - - if len(prs) == 0 { - Output(globalOutputValue, headers, values) - return nil - } - - for _, pr := range prs { - if pr == nil { - continue - } - author := pr.Poster.FullName - if len(author) == 0 { - author = pr.Poster.UserName - } - mile := "" - if pr.Milestone != nil { - mile = pr.Milestone.Title - } - values = append( - values, - []string{ - strconv.FormatInt(pr.Index, 10), - pr.Title, - string(pr.State), - author, - mile, - pr.Updated.Format("2006-01-02 15:04:05"), - }, - ) - } - Output(globalOutputValue, headers, values) - - return nil -} - -// CmdPullsCheckout is a command to locally checkout the given PR -var CmdPullsCheckout = cli.Command{ - Name: "checkout", - Usage: "Locally check out the given PR", - Description: `Locally check out the given PR`, - Action: runPullsCheckout, - ArgsUsage: "", - Flags: AllDefaultFlags, -} - -func runPullsCheckout(ctx *cli.Context) error { - login, owner, repo := intern.InitCommand(globalRepoValue, globalLoginValue, globalRemoteValue) - if ctx.Args().Len() != 1 { - log.Fatal("Must specify a PR index") - } - - // fetch PR source-repo & -branch from gitea - idx, err := argToIndex(ctx.Args().First()) - if err != nil { - return err - } - pr, _, err := login.Client().GetPullRequest(owner, repo, idx) - if err != nil { - return err - } - remoteURL := pr.Head.Repository.CloneURL - remoteBranchName := pr.Head.Ref - - // open local git repo - localRepo, err := local_git.RepoForWorkdir() - if err != nil { - return nil - } - - // verify related remote is in local repo, otherwise add it - newRemoteName := fmt.Sprintf("pulls/%v", pr.Head.Repository.Owner.UserName) - localRemote, err := localRepo.GetOrCreateRemote(remoteURL, newRemoteName) - if err != nil { - return err - } - - localRemoteName := localRemote.Config().Name - localBranchName := fmt.Sprintf("pulls/%v-%v", idx, remoteBranchName) - - // fetch remote - fmt.Printf("Fetching PR %v (head %s:%s) from remote '%s'\n", - idx, remoteURL, remoteBranchName, localRemoteName) - - url, err := local_git.ParseURL(localRemote.Config().URLs[0]) - if err != nil { - return err - } - auth, err := local_git.GetAuthForURL(url, login.User, login.SSHKey) - if err != nil { - return err - } - - err = localRemote.Fetch(&git.FetchOptions{Auth: auth}) - if err == git.NoErrAlreadyUpToDate { - fmt.Println(err) - } else if err != nil { - return err - } - - // checkout local branch - fmt.Printf("Creating branch '%s'\n", localBranchName) - err = localRepo.TeaCreateBranch(localBranchName, remoteBranchName, localRemoteName) - if err == git.ErrBranchExists { - fmt.Println(err) - } else if err != nil { - return err - } - - fmt.Printf("Checking out PR %v\n", idx) - err = localRepo.TeaCheckout(localBranchName) - - return err -} - -// CmdPullsClean removes the remote and local feature branches, if a PR is merged. -var CmdPullsClean = cli.Command{ - Name: "clean", - Usage: "Deletes local & remote feature-branches for a closed pull request", - Description: `Deletes local & remote feature-branches for a closed pull request`, - ArgsUsage: "", - Action: runPullsClean, - Flags: append([]cli.Flag{ - &cli.BoolFlag{ - Name: "ignore-sha", - Usage: "Find the local branch by name instead of commit hash (less precise)", - }, - }, AllDefaultFlags...), -} - -func runPullsClean(ctx *cli.Context) error { - login, owner, repo := intern.InitCommand(globalRepoValue, globalLoginValue, globalRemoteValue) - if ctx.Args().Len() != 1 { - return fmt.Errorf("Must specify a PR index") - } - - // fetch PR source-repo & -branch from gitea - idx, err := argToIndex(ctx.Args().First()) - if err != nil { - return err - } - pr, _, err := login.Client().GetPullRequest(owner, repo, idx) - if err != nil { - return err - } - if pr.State == gitea.StateOpen { - return fmt.Errorf("PR is still open, won't delete branches") - } - - // IDEA: abort if PR.Head.Repository.CloneURL does not match login.URL? - - r, err := local_git.RepoForWorkdir() - if err != nil { - return err - } - - // find a branch with matching sha or name, that has a remote matching the repo url - var branch *git_config.Branch - if ctx.Bool("ignore-sha") { - branch, err = r.TeaFindBranchByName(pr.Head.Ref, pr.Head.Repository.CloneURL) - } else { - branch, err = r.TeaFindBranchBySha(pr.Head.Sha, pr.Head.Repository.CloneURL) - } - if err != nil { - return err - } - if branch == nil { - if ctx.Bool("ignore-sha") { - return fmt.Errorf("Remote branch %s not found in local repo", pr.Head.Ref) - } - return fmt.Errorf(`Remote branch %s not found in local repo. -Either you don't track this PR, or the local branch has diverged from the remote. -If you still want to continue & are sure you don't loose any important commits, -call me again with the --ignore-sha flag`, pr.Head.Ref) - } - - // prepare deletion of local branch: - headRef, err := r.Head() - if err != nil { - return err - } - if headRef.Name().Short() == branch.Name { - fmt.Printf("Checking out 'master' to delete local branch '%s'\n", branch.Name) - err = r.TeaCheckout("master") - if err != nil { - return err - } - } - - // remove local & remote branch - fmt.Printf("Deleting local branch %s and remote branch %s\n", branch.Name, pr.Head.Ref) - url, err := r.TeaRemoteURL(branch.Remote) - if err != nil { - return err - } - auth, err := local_git.GetAuthForURL(url, login.User, login.SSHKey) - if err != nil { - return err - } - return r.TeaDeleteBranch(branch, pr.Head.Ref, auth) -} - -// CmdPullsCreate creates a pull request -var CmdPullsCreate = cli.Command{ - Name: "create", - Usage: "Create a pull-request", - Description: "Create a pull-request", - Action: runPullsCreate, - Flags: append([]cli.Flag{ - &cli.StringFlag{ - Name: "head", - Usage: "Set head branch (default is current one)", - }, - &cli.StringFlag{ - Name: "base", - Aliases: []string{"b"}, - Usage: "Set base branch (default is default branch)", - }, - &cli.StringFlag{ - Name: "title", - Aliases: []string{"t"}, - Usage: "Set title of pull (default is head branch name)", - }, - &cli.StringFlag{ - Name: "description", - Aliases: []string{"d"}, - Usage: "Set body of new pull", - }, - }, AllDefaultFlags...), -} - -func runPullsCreate(ctx *cli.Context) error { - login, ownerArg, repoArg := intern.InitCommand(globalRepoValue, globalLoginValue, globalRemoteValue) - client := login.Client() - - repo, _, err := client.GetRepo(ownerArg, repoArg) - if err != nil { - log.Fatal("could not fetch repo meta: ", err) - } - - // open local git repo - localRepo, err := local_git.RepoForWorkdir() - if err != nil { - log.Fatal("could not open local repo: ", err) - } - - // push if possible - log.Println("git push") - err = localRepo.Push(&git.PushOptions{}) - if err != nil && err != git.NoErrAlreadyUpToDate { - log.Printf("Error occurred during 'git push':\n%s\n", err.Error()) - } - - base := ctx.String("base") - // default is default branch - if len(base) == 0 { - base = repo.DefaultBranch - } - - head := ctx.String("head") - // default is current one - if len(head) == 0 { - headBranch, err := localRepo.Head() - if err != nil { - log.Fatal(err) - } - sha := headBranch.Hash().String() - - remote, err := localRepo.TeaFindBranchRemote("", sha) - if err != nil { - log.Fatal("could not determine remote for current branch: ", err) - } - - if remote == nil { - // if no remote branch is found for the local hash, we abort: - // user has probably not configured a remote for the local branch, - // or local branch does not represent remote state. - log.Fatal("no matching remote found for this branch. try git push -u ") - } - - branchName, err := localRepo.TeaGetCurrentBranchName() - if err != nil { - log.Fatal(err) - } - - url, err := local_git.ParseURL(remote.Config().URLs[0]) - if err != nil { - log.Fatal(err) - } - owner, _ := intern.GetOwnerAndRepo(strings.TrimLeft(url.Path, "/"), "") - head = fmt.Sprintf("%s:%s", owner, branchName) - } - - title := ctx.String("title") - // default is head branch name - if len(title) == 0 { - title = head - if strings.Contains(title, ":") { - title = strings.SplitN(title, ":", 2)[1] - } - title = strings.Replace(title, "-", " ", -1) - title = strings.Replace(title, "_", " ", -1) - title = strings.Title(strings.ToLower(title)) - } - // title is required - if len(title) == 0 { - fmt.Printf("Title is required") - return nil - } - - pr, _, err := client.CreatePullRequest(ownerArg, repoArg, gitea.CreatePullRequestOption{ - Head: head, - Base: base, - Title: title, - Body: ctx.String("description"), - }) - - if err != nil { - log.Fatalf("could not create PR from %s to %s:%s: %s", head, ownerArg, base, err) - } - - print.PullDetails(pr) - - fmt.Println(pr.HTMLURL) - return err -} - func argToIndex(arg string) (int64, error) { if strings.HasPrefix(arg, "#") { arg = arg[1:] diff --git a/cmd/pulls_checkout.go b/cmd/pulls_checkout.go new file mode 100644 index 0000000..ff2783b --- /dev/null +++ b/cmd/pulls_checkout.go @@ -0,0 +1,95 @@ +// Copyright 2020 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package cmd + +import ( + "fmt" + "log" + + local_git "code.gitea.io/tea/modules/git" + "code.gitea.io/tea/modules/intern" + + "github.com/go-git/go-git/v5" + "github.com/urfave/cli/v2" +) + +// CmdPullsCheckout is a command to locally checkout the given PR +var CmdPullsCheckout = cli.Command{ + Name: "checkout", + Usage: "Locally check out the given PR", + Description: `Locally check out the given PR`, + Action: runPullsCheckout, + ArgsUsage: "", + Flags: AllDefaultFlags, +} + +func runPullsCheckout(ctx *cli.Context) error { + login, owner, repo := intern.InitCommand(globalRepoValue, globalLoginValue, globalRemoteValue) + if ctx.Args().Len() != 1 { + log.Fatal("Must specify a PR index") + } + + // fetch PR source-repo & -branch from gitea + idx, err := argToIndex(ctx.Args().First()) + if err != nil { + return err + } + pr, _, err := login.Client().GetPullRequest(owner, repo, idx) + if err != nil { + return err + } + remoteURL := pr.Head.Repository.CloneURL + remoteBranchName := pr.Head.Ref + + // open local git repo + localRepo, err := local_git.RepoForWorkdir() + if err != nil { + return nil + } + + // verify related remote is in local repo, otherwise add it + newRemoteName := fmt.Sprintf("pulls/%v", pr.Head.Repository.Owner.UserName) + localRemote, err := localRepo.GetOrCreateRemote(remoteURL, newRemoteName) + if err != nil { + return err + } + + localRemoteName := localRemote.Config().Name + localBranchName := fmt.Sprintf("pulls/%v-%v", idx, remoteBranchName) + + // fetch remote + fmt.Printf("Fetching PR %v (head %s:%s) from remote '%s'\n", + idx, remoteURL, remoteBranchName, localRemoteName) + + url, err := local_git.ParseURL(localRemote.Config().URLs[0]) + if err != nil { + return err + } + auth, err := local_git.GetAuthForURL(url, login.User, login.SSHKey) + if err != nil { + return err + } + + err = localRemote.Fetch(&git.FetchOptions{Auth: auth}) + if err == git.NoErrAlreadyUpToDate { + fmt.Println(err) + } else if err != nil { + return err + } + + // checkout local branch + fmt.Printf("Creating branch '%s'\n", localBranchName) + err = localRepo.TeaCreateBranch(localBranchName, remoteBranchName, localRemoteName) + if err == git.ErrBranchExists { + fmt.Println(err) + } else if err != nil { + return err + } + + fmt.Printf("Checking out PR %v\n", idx) + err = localRepo.TeaCheckout(localBranchName) + + return err +} diff --git a/cmd/pulls_clean.go b/cmd/pulls_clean.go new file mode 100644 index 0000000..06fc5f1 --- /dev/null +++ b/cmd/pulls_clean.go @@ -0,0 +1,103 @@ +// Copyright 2020 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package cmd + +import ( + "fmt" + + local_git "code.gitea.io/tea/modules/git" + "code.gitea.io/tea/modules/intern" + + "code.gitea.io/sdk/gitea" + git_config "github.com/go-git/go-git/v5/config" + "github.com/urfave/cli/v2" +) + +// CmdPullsClean removes the remote and local feature branches, if a PR is merged. +var CmdPullsClean = cli.Command{ + Name: "clean", + Usage: "Deletes local & remote feature-branches for a closed pull request", + Description: `Deletes local & remote feature-branches for a closed pull request`, + ArgsUsage: "", + Action: runPullsClean, + Flags: append([]cli.Flag{ + &cli.BoolFlag{ + Name: "ignore-sha", + Usage: "Find the local branch by name instead of commit hash (less precise)", + }, + }, AllDefaultFlags...), +} + +func runPullsClean(ctx *cli.Context) error { + login, owner, repo := intern.InitCommand(globalRepoValue, globalLoginValue, globalRemoteValue) + if ctx.Args().Len() != 1 { + return fmt.Errorf("Must specify a PR index") + } + + // fetch PR source-repo & -branch from gitea + idx, err := argToIndex(ctx.Args().First()) + if err != nil { + return err + } + pr, _, err := login.Client().GetPullRequest(owner, repo, idx) + if err != nil { + return err + } + if pr.State == gitea.StateOpen { + return fmt.Errorf("PR is still open, won't delete branches") + } + + // IDEA: abort if PR.Head.Repository.CloneURL does not match login.URL? + + r, err := local_git.RepoForWorkdir() + if err != nil { + return err + } + + // find a branch with matching sha or name, that has a remote matching the repo url + var branch *git_config.Branch + if ctx.Bool("ignore-sha") { + branch, err = r.TeaFindBranchByName(pr.Head.Ref, pr.Head.Repository.CloneURL) + } else { + branch, err = r.TeaFindBranchBySha(pr.Head.Sha, pr.Head.Repository.CloneURL) + } + if err != nil { + return err + } + if branch == nil { + if ctx.Bool("ignore-sha") { + return fmt.Errorf("Remote branch %s not found in local repo", pr.Head.Ref) + } + return fmt.Errorf(`Remote branch %s not found in local repo. +Either you don't track this PR, or the local branch has diverged from the remote. +If you still want to continue & are sure you don't loose any important commits, +call me again with the --ignore-sha flag`, pr.Head.Ref) + } + + // prepare deletion of local branch: + headRef, err := r.Head() + if err != nil { + return err + } + if headRef.Name().Short() == branch.Name { + fmt.Printf("Checking out 'master' to delete local branch '%s'\n", branch.Name) + err = r.TeaCheckout("master") + if err != nil { + return err + } + } + + // remove local & remote branch + fmt.Printf("Deleting local branch %s and remote branch %s\n", branch.Name, pr.Head.Ref) + url, err := r.TeaRemoteURL(branch.Remote) + if err != nil { + return err + } + auth, err := local_git.GetAuthForURL(url, login.User, login.SSHKey) + if err != nil { + return err + } + return r.TeaDeleteBranch(branch, pr.Head.Ref, auth) +} diff --git a/cmd/pulls_create.go b/cmd/pulls_create.go new file mode 100644 index 0000000..af81876 --- /dev/null +++ b/cmd/pulls_create.go @@ -0,0 +1,144 @@ +// Copyright 2020 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package cmd + +import ( + "fmt" + "log" + "strings" + + local_git "code.gitea.io/tea/modules/git" + "code.gitea.io/tea/modules/intern" + "code.gitea.io/tea/modules/print" + + "code.gitea.io/sdk/gitea" + "github.com/go-git/go-git/v5" + "github.com/urfave/cli/v2" +) + +// CmdPullsCreate creates a pull request +var CmdPullsCreate = cli.Command{ + Name: "create", + Usage: "Create a pull-request", + Description: "Create a pull-request", + Action: runPullsCreate, + Flags: append([]cli.Flag{ + &cli.StringFlag{ + Name: "head", + Usage: "Set head branch (default is current one)", + }, + &cli.StringFlag{ + Name: "base", + Aliases: []string{"b"}, + Usage: "Set base branch (default is default branch)", + }, + &cli.StringFlag{ + Name: "title", + Aliases: []string{"t"}, + Usage: "Set title of pull (default is head branch name)", + }, + &cli.StringFlag{ + Name: "description", + Aliases: []string{"d"}, + Usage: "Set body of new pull", + }, + }, AllDefaultFlags...), +} + +func runPullsCreate(ctx *cli.Context) error { + login, ownerArg, repoArg := intern.InitCommand(globalRepoValue, globalLoginValue, globalRemoteValue) + client := login.Client() + + repo, _, err := client.GetRepo(ownerArg, repoArg) + if err != nil { + log.Fatal("could not fetch repo meta: ", err) + } + + // open local git repo + localRepo, err := local_git.RepoForWorkdir() + if err != nil { + log.Fatal("could not open local repo: ", err) + } + + // push if possible + log.Println("git push") + err = localRepo.Push(&git.PushOptions{}) + if err != nil && err != git.NoErrAlreadyUpToDate { + log.Printf("Error occurred during 'git push':\n%s\n", err.Error()) + } + + base := ctx.String("base") + // default is default branch + if len(base) == 0 { + base = repo.DefaultBranch + } + + head := ctx.String("head") + // default is current one + if len(head) == 0 { + headBranch, err := localRepo.Head() + if err != nil { + log.Fatal(err) + } + sha := headBranch.Hash().String() + + remote, err := localRepo.TeaFindBranchRemote("", sha) + if err != nil { + log.Fatal("could not determine remote for current branch: ", err) + } + + if remote == nil { + // if no remote branch is found for the local hash, we abort: + // user has probably not configured a remote for the local branch, + // or local branch does not represent remote state. + log.Fatal("no matching remote found for this branch. try git push -u ") + } + + branchName, err := localRepo.TeaGetCurrentBranchName() + if err != nil { + log.Fatal(err) + } + + url, err := local_git.ParseURL(remote.Config().URLs[0]) + if err != nil { + log.Fatal(err) + } + owner, _ := intern.GetOwnerAndRepo(strings.TrimLeft(url.Path, "/"), "") + head = fmt.Sprintf("%s:%s", owner, branchName) + } + + title := ctx.String("title") + // default is head branch name + if len(title) == 0 { + title = head + if strings.Contains(title, ":") { + title = strings.SplitN(title, ":", 2)[1] + } + title = strings.Replace(title, "-", " ", -1) + title = strings.Replace(title, "_", " ", -1) + title = strings.Title(strings.ToLower(title)) + } + // title is required + if len(title) == 0 { + fmt.Printf("Title is required") + return nil + } + + pr, _, err := client.CreatePullRequest(ownerArg, repoArg, gitea.CreatePullRequestOption{ + Head: head, + Base: base, + Title: title, + Body: ctx.String("description"), + }) + + if err != nil { + log.Fatalf("could not create PR from %s to %s:%s: %s", head, ownerArg, base, err) + } + + print.PullDetails(pr) + + fmt.Println(pr.HTMLURL) + return err +} diff --git a/cmd/pulls_list.go b/cmd/pulls_list.go new file mode 100644 index 0000000..8770cd8 --- /dev/null +++ b/cmd/pulls_list.go @@ -0,0 +1,90 @@ +// Copyright 2020 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package cmd + +import ( + "log" + "strconv" + + "code.gitea.io/tea/modules/intern" + + "code.gitea.io/sdk/gitea" + "github.com/urfave/cli/v2" +) + +// CmdPullsList represents a sub command of issues to list pulls +var CmdPullsList = cli.Command{ + Name: "ls", + Usage: "List pull requests of the repository", + Description: `List pull requests of the repository`, + Action: runPullsList, + Flags: IssuePRFlags, +} + +func runPullsList(ctx *cli.Context) error { + login, owner, repo := intern.InitCommand(globalRepoValue, globalLoginValue, globalRemoteValue) + + state := gitea.StateOpen + switch ctx.String("state") { + case "all": + state = gitea.StateAll + case "open": + state = gitea.StateOpen + case "closed": + state = gitea.StateClosed + } + + prs, _, err := login.Client().ListRepoPullRequests(owner, repo, gitea.ListPullRequestsOptions{ + State: state, + }) + + if err != nil { + log.Fatal(err) + } + + headers := []string{ + "Index", + "Title", + "State", + "Author", + "Milestone", + "Updated", + } + + var values [][]string + + if len(prs) == 0 { + Output(globalOutputValue, headers, values) + return nil + } + + for _, pr := range prs { + if pr == nil { + continue + } + author := pr.Poster.FullName + if len(author) == 0 { + author = pr.Poster.UserName + } + mile := "" + if pr.Milestone != nil { + mile = pr.Milestone.Title + } + values = append( + values, + []string{ + strconv.FormatInt(pr.Index, 10), + pr.Title, + string(pr.State), + author, + mile, + pr.Updated.Format("2006-01-02 15:04:05"), + }, + ) + } + Output(globalOutputValue, headers, values) + + return nil +} -- 2.40.1 From 57479823451582e5b3878bacfe367f097fd7a8e3 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Mon, 28 Sep 2020 02:33:54 +0200 Subject: [PATCH 14/22] Split releases subcomands into own sourcefiles --- cmd/releases.go | 271 +---------------------------------------- cmd/releases_create.go | 101 +++++++++++++++ cmd/releases_delete.go | 45 +++++++ cmd/releases_edit.go | 101 +++++++++++++++ cmd/releases_list.go | 72 +++++++++++ 5 files changed, 321 insertions(+), 269 deletions(-) create mode 100644 cmd/releases_create.go create mode 100644 cmd/releases_delete.go create mode 100644 cmd/releases_edit.go create mode 100644 cmd/releases_list.go diff --git a/cmd/releases.go b/cmd/releases.go index a95a890..0ee9f20 100644 --- a/cmd/releases.go +++ b/cmd/releases.go @@ -6,25 +6,19 @@ package cmd import ( "fmt" - "log" - "net/http" - "os" - "path/filepath" - "strings" - - "code.gitea.io/tea/modules/intern" "code.gitea.io/sdk/gitea" "github.com/urfave/cli/v2" ) // CmdReleases represents to login a gitea server. +// ToDo: ReleaseDetails var CmdReleases = cli.Command{ Name: "release", Aliases: []string{"releases"}, Usage: "Manage releases", Description: "Manage releases", - Action: runReleases, + Action: runReleasesList, Subcommands: []*cli.Command{ &CmdReleaseList, &CmdReleaseCreate, @@ -34,181 +28,6 @@ var CmdReleases = cli.Command{ Flags: AllDefaultFlags, } -// CmdReleaseList represents a sub command of Release to list releases -var CmdReleaseList = cli.Command{ - Name: "ls", - Usage: "List Releases", - Description: "List Releases", - Action: runReleases, - Flags: append([]cli.Flag{ - &PaginationPageFlag, - &PaginationLimitFlag, - }, AllDefaultFlags...), -} - -func runReleases(ctx *cli.Context) error { - login, owner, repo := intern.InitCommand(globalRepoValue, globalLoginValue, globalRemoteValue) - - // ToDo: ReleaseDetails - - releases, _, err := login.Client().ListReleases(owner, repo, gitea.ListReleasesOptions{ListOptions: getListOptions(ctx)}) - if err != nil { - log.Fatal(err) - } - - headers := []string{ - "Tag-Name", - "Title", - "Published At", - "Status", - "Tar URL", - } - - var values [][]string - - if len(releases) == 0 { - Output(globalOutputValue, headers, values) - return nil - } - - for _, release := range releases { - status := "released" - if release.IsDraft { - status = "draft" - } else if release.IsPrerelease { - status = "prerelease" - } - values = append( - values, - []string{ - release.TagName, - release.Title, - release.PublishedAt.Format("2006-01-02 15:04:05"), - status, - release.TarURL, - }, - ) - } - Output(globalOutputValue, headers, values) - - return nil -} - -// CmdReleaseCreate represents a sub command of Release to create release -var CmdReleaseCreate = cli.Command{ - Name: "create", - Usage: "Create a release", - Description: `Create a release`, - Action: runReleaseCreate, - Flags: append([]cli.Flag{ - &cli.StringFlag{ - Name: "tag", - Usage: "Tag name", - }, - &cli.StringFlag{ - Name: "target", - Usage: "Target refs, branch name or commit id", - }, - &cli.StringFlag{ - Name: "title", - Aliases: []string{"t"}, - Usage: "Release title", - }, - &cli.StringFlag{ - Name: "note", - Aliases: []string{"n"}, - Usage: "Release notes", - }, - &cli.BoolFlag{ - Name: "draft", - Aliases: []string{"d"}, - Usage: "Is a draft", - }, - &cli.BoolFlag{ - Name: "prerelease", - Aliases: []string{"p"}, - Usage: "Is a pre-release", - }, - &cli.StringSliceFlag{ - Name: "asset", - Aliases: []string{"a"}, - Usage: "List of files to attach", - }, - }, AllDefaultFlags...), -} - -func runReleaseCreate(ctx *cli.Context) error { - login, owner, repo := intern.InitCommand(globalRepoValue, globalLoginValue, globalRemoteValue) - - release, resp, err := login.Client().CreateRelease(owner, repo, gitea.CreateReleaseOption{ - TagName: ctx.String("tag"), - Target: ctx.String("target"), - Title: ctx.String("title"), - Note: ctx.String("note"), - IsDraft: ctx.Bool("draft"), - IsPrerelease: ctx.Bool("prerelease"), - }) - - if err != nil { - if resp != nil && resp.StatusCode == http.StatusConflict { - fmt.Println("error: There already is a release for this tag") - return nil - } - log.Fatal(err) - } - - for _, asset := range ctx.StringSlice("asset") { - var file *os.File - - if file, err = os.Open(asset); err != nil { - log.Fatal(err) - } - - filePath := filepath.Base(asset) - - if _, _, err = login.Client().CreateReleaseAttachment(owner, repo, release.ID, file, filePath); err != nil { - file.Close() - log.Fatal(err) - } - - file.Close() - } - - return nil -} - -// CmdReleaseDelete represents a sub command of Release to delete a release -var CmdReleaseDelete = cli.Command{ - Name: "delete", - Usage: "Delete a release", - Description: `Delete a release`, - ArgsUsage: "", - Action: runReleaseDelete, - Flags: AllDefaultFlags, -} - -func runReleaseDelete(ctx *cli.Context) error { - login, owner, repo := intern.InitCommand(globalRepoValue, globalLoginValue, globalRemoteValue) - client := login.Client() - - tag := ctx.Args().First() - if len(tag) == 0 { - fmt.Println("Release tag needed to delete") - return nil - } - - release, err := getReleaseByTag(owner, repo, tag, client) - if err != nil { - return err - } - if release == nil { - return nil - } - - _, err = client.DeleteRelease(owner, repo, release.ID) - return err -} - func getReleaseByTag(owner, repo, tag string, client *gitea.Client) (*gitea.Release, error) { rl, _, err := client.ListReleases(owner, repo, gitea.ListReleasesOptions{}) if err != nil { @@ -226,89 +45,3 @@ func getReleaseByTag(owner, repo, tag string, client *gitea.Client) (*gitea.Rele fmt.Println("Release tag does not exist") return nil, nil } - -// CmdReleaseEdit represents a sub command of Release to edit releases -var CmdReleaseEdit = cli.Command{ - Name: "edit", - Usage: "Edit a release", - Description: `Edit a release`, - ArgsUsage: "", - Action: runReleaseEdit, - Flags: append([]cli.Flag{ - &cli.StringFlag{ - Name: "tag", - Usage: "Change Tag", - }, - &cli.StringFlag{ - Name: "target", - Usage: "Change Target", - }, - &cli.StringFlag{ - Name: "title", - Aliases: []string{"t"}, - Usage: "Change Title", - }, - &cli.StringFlag{ - Name: "note", - Aliases: []string{"n"}, - Usage: "Change Notes", - }, - &cli.StringFlag{ - Name: "draft", - Aliases: []string{"d"}, - Usage: "Mark as Draft [True/false]", - DefaultText: "true", - }, - &cli.StringFlag{ - Name: "prerelease", - Aliases: []string{"p"}, - Usage: "Mark as Pre-Release [True/false]", - DefaultText: "true", - }, - }, AllDefaultFlags...), -} - -func runReleaseEdit(ctx *cli.Context) error { - login, owner, repo := intern.InitCommand(globalRepoValue, globalLoginValue, globalRemoteValue) - client := login.Client() - - tag := ctx.Args().First() - if len(tag) == 0 { - fmt.Println("Release tag needed to edit") - return nil - } - - release, err := getReleaseByTag(owner, repo, tag, client) - if err != nil { - return err - } - if release == nil { - return nil - } - - var isDraft, isPre *bool - bTrue := true - bFalse := false - if ctx.IsSet("draft") { - isDraft = &bFalse - if strings.ToLower(ctx.String("draft"))[:1] == "t" { - isDraft = &bTrue - } - } - if ctx.IsSet("prerelease") { - isPre = &bFalse - if strings.ToLower(ctx.String("prerelease"))[:1] == "t" { - isPre = &bTrue - } - } - - _, _, err = client.EditRelease(owner, repo, release.ID, gitea.EditReleaseOption{ - TagName: ctx.String("tag"), - Target: ctx.String("target"), - Title: ctx.String("title"), - Note: ctx.String("note"), - IsDraft: isDraft, - IsPrerelease: isPre, - }) - return err -} diff --git a/cmd/releases_create.go b/cmd/releases_create.go new file mode 100644 index 0000000..1fe0ff7 --- /dev/null +++ b/cmd/releases_create.go @@ -0,0 +1,101 @@ +// Copyright 2020 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package cmd + +import ( + "fmt" + "log" + "net/http" + "os" + "path/filepath" + + "code.gitea.io/tea/modules/intern" + + "code.gitea.io/sdk/gitea" + "github.com/urfave/cli/v2" +) + +// CmdReleaseCreate represents a sub command of Release to create release +var CmdReleaseCreate = cli.Command{ + Name: "create", + Usage: "Create a release", + Description: `Create a release`, + Action: runReleaseCreate, + Flags: append([]cli.Flag{ + &cli.StringFlag{ + Name: "tag", + Usage: "Tag name", + }, + &cli.StringFlag{ + Name: "target", + Usage: "Target refs, branch name or commit id", + }, + &cli.StringFlag{ + Name: "title", + Aliases: []string{"t"}, + Usage: "Release title", + }, + &cli.StringFlag{ + Name: "note", + Aliases: []string{"n"}, + Usage: "Release notes", + }, + &cli.BoolFlag{ + Name: "draft", + Aliases: []string{"d"}, + Usage: "Is a draft", + }, + &cli.BoolFlag{ + Name: "prerelease", + Aliases: []string{"p"}, + Usage: "Is a pre-release", + }, + &cli.StringSliceFlag{ + Name: "asset", + Aliases: []string{"a"}, + Usage: "List of files to attach", + }, + }, AllDefaultFlags...), +} + +func runReleaseCreate(ctx *cli.Context) error { + login, owner, repo := intern.InitCommand(globalRepoValue, globalLoginValue, globalRemoteValue) + + release, resp, err := login.Client().CreateRelease(owner, repo, gitea.CreateReleaseOption{ + TagName: ctx.String("tag"), + Target: ctx.String("target"), + Title: ctx.String("title"), + Note: ctx.String("note"), + IsDraft: ctx.Bool("draft"), + IsPrerelease: ctx.Bool("prerelease"), + }) + + if err != nil { + if resp != nil && resp.StatusCode == http.StatusConflict { + fmt.Println("error: There already is a release for this tag") + return nil + } + log.Fatal(err) + } + + for _, asset := range ctx.StringSlice("asset") { + var file *os.File + + if file, err = os.Open(asset); err != nil { + log.Fatal(err) + } + + filePath := filepath.Base(asset) + + if _, _, err = login.Client().CreateReleaseAttachment(owner, repo, release.ID, file, filePath); err != nil { + file.Close() + log.Fatal(err) + } + + file.Close() + } + + return nil +} diff --git a/cmd/releases_delete.go b/cmd/releases_delete.go new file mode 100644 index 0000000..4272882 --- /dev/null +++ b/cmd/releases_delete.go @@ -0,0 +1,45 @@ +// Copyright 2020 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package cmd + +import ( + "fmt" + + "code.gitea.io/tea/modules/intern" + + "github.com/urfave/cli/v2" +) + +// CmdReleaseDelete represents a sub command of Release to delete a release +var CmdReleaseDelete = cli.Command{ + Name: "delete", + Usage: "Delete a release", + Description: `Delete a release`, + ArgsUsage: "", + Action: runReleaseDelete, + Flags: AllDefaultFlags, +} + +func runReleaseDelete(ctx *cli.Context) error { + login, owner, repo := intern.InitCommand(globalRepoValue, globalLoginValue, globalRemoteValue) + client := login.Client() + + tag := ctx.Args().First() + if len(tag) == 0 { + fmt.Println("Release tag needed to delete") + return nil + } + + release, err := getReleaseByTag(owner, repo, tag, client) + if err != nil { + return err + } + if release == nil { + return nil + } + + _, err = client.DeleteRelease(owner, repo, release.ID) + return err +} diff --git a/cmd/releases_edit.go b/cmd/releases_edit.go new file mode 100644 index 0000000..22ee37a --- /dev/null +++ b/cmd/releases_edit.go @@ -0,0 +1,101 @@ +// Copyright 2020 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package cmd + +import ( + "fmt" + "strings" + + "code.gitea.io/tea/modules/intern" + + "code.gitea.io/sdk/gitea" + "github.com/urfave/cli/v2" +) + +// CmdReleaseEdit represents a sub command of Release to edit releases +var CmdReleaseEdit = cli.Command{ + Name: "edit", + Usage: "Edit a release", + Description: `Edit a release`, + ArgsUsage: "", + Action: runReleaseEdit, + Flags: append([]cli.Flag{ + &cli.StringFlag{ + Name: "tag", + Usage: "Change Tag", + }, + &cli.StringFlag{ + Name: "target", + Usage: "Change Target", + }, + &cli.StringFlag{ + Name: "title", + Aliases: []string{"t"}, + Usage: "Change Title", + }, + &cli.StringFlag{ + Name: "note", + Aliases: []string{"n"}, + Usage: "Change Notes", + }, + &cli.StringFlag{ + Name: "draft", + Aliases: []string{"d"}, + Usage: "Mark as Draft [True/false]", + DefaultText: "true", + }, + &cli.StringFlag{ + Name: "prerelease", + Aliases: []string{"p"}, + Usage: "Mark as Pre-Release [True/false]", + DefaultText: "true", + }, + }, AllDefaultFlags...), +} + +func runReleaseEdit(ctx *cli.Context) error { + login, owner, repo := intern.InitCommand(globalRepoValue, globalLoginValue, globalRemoteValue) + client := login.Client() + + tag := ctx.Args().First() + if len(tag) == 0 { + fmt.Println("Release tag needed to edit") + return nil + } + + release, err := getReleaseByTag(owner, repo, tag, client) + if err != nil { + return err + } + if release == nil { + return nil + } + + var isDraft, isPre *bool + bTrue := true + bFalse := false + if ctx.IsSet("draft") { + isDraft = &bFalse + if strings.ToLower(ctx.String("draft"))[:1] == "t" { + isDraft = &bTrue + } + } + if ctx.IsSet("prerelease") { + isPre = &bFalse + if strings.ToLower(ctx.String("prerelease"))[:1] == "t" { + isPre = &bTrue + } + } + + _, _, err = client.EditRelease(owner, repo, release.ID, gitea.EditReleaseOption{ + TagName: ctx.String("tag"), + Target: ctx.String("target"), + Title: ctx.String("title"), + Note: ctx.String("note"), + IsDraft: isDraft, + IsPrerelease: isPre, + }) + return err +} diff --git a/cmd/releases_list.go b/cmd/releases_list.go new file mode 100644 index 0000000..15a90c9 --- /dev/null +++ b/cmd/releases_list.go @@ -0,0 +1,72 @@ +// Copyright 2020 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package cmd + +import ( + "log" + + "code.gitea.io/tea/modules/intern" + + "code.gitea.io/sdk/gitea" + "github.com/urfave/cli/v2" +) + +// CmdReleaseList represents a sub command of Release to list releases +var CmdReleaseList = cli.Command{ + Name: "ls", + Usage: "List Releases", + Description: "List Releases", + Action: runReleasesList, + Flags: append([]cli.Flag{ + &PaginationPageFlag, + &PaginationLimitFlag, + }, AllDefaultFlags...), +} + +func runReleasesList(ctx *cli.Context) error { + login, owner, repo := intern.InitCommand(globalRepoValue, globalLoginValue, globalRemoteValue) + + releases, _, err := login.Client().ListReleases(owner, repo, gitea.ListReleasesOptions{ListOptions: getListOptions(ctx)}) + if err != nil { + log.Fatal(err) + } + + headers := []string{ + "Tag-Name", + "Title", + "Published At", + "Status", + "Tar URL", + } + + var values [][]string + + if len(releases) == 0 { + Output(globalOutputValue, headers, values) + return nil + } + + for _, release := range releases { + status := "released" + if release.IsDraft { + status = "draft" + } else if release.IsPrerelease { + status = "prerelease" + } + values = append( + values, + []string{ + release.TagName, + release.Title, + release.PublishedAt.Format("2006-01-02 15:04:05"), + status, + release.TarURL, + }, + ) + } + Output(globalOutputValue, headers, values) + + return nil +} -- 2.40.1 From 7dba99cdd9631b08f290c486c5524f1d8458806b Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Mon, 28 Sep 2020 02:38:21 +0200 Subject: [PATCH 15/22] Split repos subcomands into own sourcefiles --- cmd/repos.go | 239 -------------------------------------------- cmd/repos_create.go | 114 +++++++++++++++++++++ cmd/repos_list.go | 150 +++++++++++++++++++++++++++ 3 files changed, 264 insertions(+), 239 deletions(-) create mode 100644 cmd/repos_create.go create mode 100644 cmd/repos_list.go diff --git a/cmd/repos.go b/cmd/repos.go index ccfb4f2..2534299 100644 --- a/cmd/repos.go +++ b/cmd/repos.go @@ -5,11 +5,6 @@ package cmd import ( - "fmt" - "log" - "net/http" - "strings" - "code.gitea.io/tea/modules/intern" "code.gitea.io/tea/modules/print" @@ -31,107 +26,6 @@ var CmdRepos = cli.Command{ Flags: LoginOutputFlags, } -// CmdReposList represents a sub command of repos to list them -var CmdReposList = cli.Command{ - Name: "ls", - Usage: "List available repositories", - Description: `List available repositories`, - Action: runReposList, - Flags: append([]cli.Flag{ - &cli.StringFlag{ - Name: "mode", - Aliases: []string{"m"}, - Required: false, - Usage: "Filter by mode: fork, mirror, source", - }, - &cli.StringFlag{ - Name: "owner", - Aliases: []string{"O"}, - Required: false, - Usage: "Filter by owner", - }, - &cli.StringFlag{ - Name: "private", - Required: false, - Usage: "Filter private repos (true|false)", - }, - &cli.StringFlag{ - Name: "archived", - Required: false, - Usage: "Filter archived repos (true|false)", - }, - &PaginationPageFlag, - &PaginationLimitFlag, - }, LoginOutputFlags...), -} - -// CmdRepoCreate represents a sub command of repos to create one -var CmdRepoCreate = cli.Command{ - Name: "create", - Aliases: []string{"c"}, - Usage: "Create a repository", - Description: "Create a repository", - Action: runRepoCreate, - Flags: append([]cli.Flag{ - &cli.StringFlag{ - Name: "name", - Aliases: []string{""}, - Required: true, - Usage: "name of new repo", - }, - &cli.StringFlag{ - Name: "owner", - Aliases: []string{"O"}, - Required: false, - Usage: "name of repo owner", - }, - &cli.BoolFlag{ - Name: "private", - Required: false, - Value: false, - Usage: "make repo private", - }, - &cli.StringFlag{ - Name: "description", - Aliases: []string{"desc"}, - Required: false, - Usage: "add description to repo", - }, - &cli.BoolFlag{ - Name: "init", - Required: false, - Value: false, - Usage: "initialize repo", - }, - &cli.StringFlag{ - Name: "labels", - Required: false, - Usage: "name of label set to add", - }, - &cli.StringFlag{ - Name: "gitignores", - Aliases: []string{"git"}, - Required: false, - Usage: "list of gitignore templates (need --init)", - }, - &cli.StringFlag{ - Name: "license", - Required: false, - Usage: "add license (need --init)", - }, - &cli.StringFlag{ - Name: "readme", - Required: false, - Usage: "use readme template (need --init)", - }, - &cli.StringFlag{ - Name: "branch", - Required: false, - Usage: "use custom default branch (need --init)", - }, - }, LoginOutputFlags...), -} - func runRepos(ctx *cli.Context) error { if ctx.Args().Len() == 1 { return runRepoDetail(ctx.Args().First()) @@ -139,106 +33,6 @@ func runRepos(ctx *cli.Context) error { return runReposList(ctx) } -// runReposList list repositories -func runReposList(ctx *cli.Context) error { - login := intern.InitCommandLoginOnly(globalLoginValue) - client := login.Client() - - var ownerID int64 - if ctx.IsSet("owner") { - // test if owner is a organisation - org, resp, err := client.GetOrg(ctx.String("owner")) - if err != nil { - if resp == nil || resp.StatusCode != http.StatusNotFound { - return err - } - // if owner is no org, its a user - user, _, err := client.GetUserInfo(ctx.String("owner")) - if err != nil { - return err - } - ownerID = user.ID - } else { - ownerID = org.ID - } - } else { - me, _, err := client.GetMyUserInfo() - if err != nil { - return err - } - ownerID = me.ID - } - - var isArchived *bool - if ctx.IsSet("archived") { - archived := strings.ToLower(ctx.String("archived"))[:1] == "t" - isArchived = &archived - } - - var isPrivate *bool - if ctx.IsSet("private") { - private := strings.ToLower(ctx.String("private"))[:1] == "t" - isArchived = &private - } - - mode := gitea.RepoTypeNone - switch ctx.String("mode") { - case "fork": - mode = gitea.RepoTypeFork - case "mirror": - mode = gitea.RepoTypeMirror - case "source": - mode = gitea.RepoTypeSource - } - - rps, _, err := client.SearchRepos(gitea.SearchRepoOptions{ - ListOptions: getListOptions(ctx), - OwnerID: ownerID, - IsPrivate: isPrivate, - IsArchived: isArchived, - Type: mode, - }) - if err != nil { - return err - } - - if len(rps) == 0 { - log.Fatal("No repositories found", rps) - return nil - } - - headers := []string{ - "Name", - "Type", - "SSH", - "Owner", - } - var values [][]string - - for _, rp := range rps { - var mode = "source" - if rp.Fork { - mode = "fork" - } - if rp.Mirror { - mode = "mirror" - } - - values = append( - values, - []string{ - rp.FullName, - mode, - rp.SSHURL, - rp.Owner.UserName, - }, - ) - } - Output(globalOutputValue, headers, values) - - return nil -} - func runRepoDetail(path string) error { login := intern.InitCommandLoginOnly(globalLoginValue) client := login.Client() @@ -255,36 +49,3 @@ func runRepoDetail(path string) error { print.RepoDetails(repo, topics) return nil } - -func runRepoCreate(ctx *cli.Context) error { - login := intern.InitCommandLoginOnly(globalLoginValue) - client := login.Client() - var ( - repo *gitea.Repository - err error - ) - opts := gitea.CreateRepoOption{ - Name: ctx.String("name"), - Description: ctx.String("description"), - Private: ctx.Bool("private"), - AutoInit: ctx.Bool("init"), - IssueLabels: ctx.String("labels"), - Gitignores: ctx.String("gitignores"), - License: ctx.String("license"), - Readme: ctx.String("readme"), - DefaultBranch: ctx.String("branch"), - } - if len(ctx.String("owner")) != 0 { - repo, _, err = client.CreateOrgRepo(ctx.String("owner"), opts) - } else { - repo, _, err = client.CreateRepo(opts) - } - if err != nil { - return err - } - if err = runRepoDetail(repo.FullName); err != nil { - return err - } - fmt.Printf("%s\n", repo.HTMLURL) - return nil -} diff --git a/cmd/repos_create.go b/cmd/repos_create.go new file mode 100644 index 0000000..495218b --- /dev/null +++ b/cmd/repos_create.go @@ -0,0 +1,114 @@ +// Copyright 2020 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package cmd + +import ( + "fmt" + + "code.gitea.io/tea/modules/intern" + + "code.gitea.io/sdk/gitea" + "github.com/urfave/cli/v2" +) + +// CmdRepoCreate represents a sub command of repos to create one +var CmdRepoCreate = cli.Command{ + Name: "create", + Aliases: []string{"c"}, + Usage: "Create a repository", + Description: "Create a repository", + Action: runRepoCreate, + Flags: append([]cli.Flag{ + &cli.StringFlag{ + Name: "name", + Aliases: []string{""}, + Required: true, + Usage: "name of new repo", + }, + &cli.StringFlag{ + Name: "owner", + Aliases: []string{"O"}, + Required: false, + Usage: "name of repo owner", + }, + &cli.BoolFlag{ + Name: "private", + Required: false, + Value: false, + Usage: "make repo private", + }, + &cli.StringFlag{ + Name: "description", + Aliases: []string{"desc"}, + Required: false, + Usage: "add description to repo", + }, + &cli.BoolFlag{ + Name: "init", + Required: false, + Value: false, + Usage: "initialize repo", + }, + &cli.StringFlag{ + Name: "labels", + Required: false, + Usage: "name of label set to add", + }, + &cli.StringFlag{ + Name: "gitignores", + Aliases: []string{"git"}, + Required: false, + Usage: "list of gitignore templates (need --init)", + }, + &cli.StringFlag{ + Name: "license", + Required: false, + Usage: "add license (need --init)", + }, + &cli.StringFlag{ + Name: "readme", + Required: false, + Usage: "use readme template (need --init)", + }, + &cli.StringFlag{ + Name: "branch", + Required: false, + Usage: "use custom default branch (need --init)", + }, + }, LoginOutputFlags...), +} + +func runRepoCreate(ctx *cli.Context) error { + login := intern.InitCommandLoginOnly(globalLoginValue) + client := login.Client() + var ( + repo *gitea.Repository + err error + ) + opts := gitea.CreateRepoOption{ + Name: ctx.String("name"), + Description: ctx.String("description"), + Private: ctx.Bool("private"), + AutoInit: ctx.Bool("init"), + IssueLabels: ctx.String("labels"), + Gitignores: ctx.String("gitignores"), + License: ctx.String("license"), + Readme: ctx.String("readme"), + DefaultBranch: ctx.String("branch"), + } + if len(ctx.String("owner")) != 0 { + repo, _, err = client.CreateOrgRepo(ctx.String("owner"), opts) + } else { + repo, _, err = client.CreateRepo(opts) + } + if err != nil { + return err + } + if err = runRepoDetail(repo.FullName); err != nil { + return err + } + fmt.Printf("%s\n", repo.HTMLURL) + return nil +} diff --git a/cmd/repos_list.go b/cmd/repos_list.go new file mode 100644 index 0000000..4a20476 --- /dev/null +++ b/cmd/repos_list.go @@ -0,0 +1,150 @@ +// Copyright 2020 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package cmd + +import ( + "log" + "net/http" + "strings" + + "code.gitea.io/tea/modules/intern" + + "code.gitea.io/sdk/gitea" + "github.com/urfave/cli/v2" +) + +// CmdReposList represents a sub command of repos to list them +var CmdReposList = cli.Command{ + Name: "ls", + Usage: "List available repositories", + Description: `List available repositories`, + Action: runReposList, + Flags: append([]cli.Flag{ + &cli.StringFlag{ + Name: "mode", + Aliases: []string{"m"}, + Required: false, + Usage: "Filter by mode: fork, mirror, source", + }, + &cli.StringFlag{ + Name: "owner", + Aliases: []string{"O"}, + Required: false, + Usage: "Filter by owner", + }, + &cli.StringFlag{ + Name: "private", + Required: false, + Usage: "Filter private repos (true|false)", + }, + &cli.StringFlag{ + Name: "archived", + Required: false, + Usage: "Filter archived repos (true|false)", + }, + &PaginationPageFlag, + &PaginationLimitFlag, + }, LoginOutputFlags...), +} + +// runReposList list repositories +func runReposList(ctx *cli.Context) error { + login := intern.InitCommandLoginOnly(globalLoginValue) + client := login.Client() + + var ownerID int64 + if ctx.IsSet("owner") { + // test if owner is a organisation + org, resp, err := client.GetOrg(ctx.String("owner")) + if err != nil { + if resp == nil || resp.StatusCode != http.StatusNotFound { + return err + } + // if owner is no org, its a user + user, _, err := client.GetUserInfo(ctx.String("owner")) + if err != nil { + return err + } + ownerID = user.ID + } else { + ownerID = org.ID + } + } else { + me, _, err := client.GetMyUserInfo() + if err != nil { + return err + } + ownerID = me.ID + } + + var isArchived *bool + if ctx.IsSet("archived") { + archived := strings.ToLower(ctx.String("archived"))[:1] == "t" + isArchived = &archived + } + + var isPrivate *bool + if ctx.IsSet("private") { + private := strings.ToLower(ctx.String("private"))[:1] == "t" + isArchived = &private + } + + mode := gitea.RepoTypeNone + switch ctx.String("mode") { + case "fork": + mode = gitea.RepoTypeFork + case "mirror": + mode = gitea.RepoTypeMirror + case "source": + mode = gitea.RepoTypeSource + } + + rps, _, err := client.SearchRepos(gitea.SearchRepoOptions{ + ListOptions: getListOptions(ctx), + OwnerID: ownerID, + IsPrivate: isPrivate, + IsArchived: isArchived, + Type: mode, + }) + if err != nil { + return err + } + + if len(rps) == 0 { + log.Fatal("No repositories found", rps) + return nil + } + + headers := []string{ + "Name", + "Type", + "SSH", + "Owner", + } + var values [][]string + + for _, rp := range rps { + var mode = "source" + if rp.Fork { + mode = "fork" + } + if rp.Mirror { + mode = "mirror" + } + + values = append( + values, + []string{ + rp.FullName, + mode, + rp.SSHURL, + rp.Owner.UserName, + }, + ) + } + Output(globalOutputValue, headers, values) + + return nil +} -- 2.40.1 From 58351487749fc9ad23bbc5d3675fe7d43300f6b6 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Mon, 28 Sep 2020 02:43:05 +0200 Subject: [PATCH 16/22] Split times subcomands into own sourcefiles --- cmd/times.go | 115 -------------------------------------------- cmd/times_add.go | 57 ++++++++++++++++++++++ cmd/times_delete.go | 55 +++++++++++++++++++++ cmd/times_reset.go | 49 +++++++++++++++++++ 4 files changed, 161 insertions(+), 115 deletions(-) create mode 100644 cmd/times_add.go create mode 100644 cmd/times_delete.go create mode 100644 cmd/times_reset.go diff --git a/cmd/times.go b/cmd/times.go index 19daf79..3369491 100644 --- a/cmd/times.go +++ b/cmd/times.go @@ -155,118 +155,3 @@ func printTrackedTimes(times []*gitea.TrackedTime, outputType string, from, unti } Output(outputType, headers, outputValues) } - -// CmdTrackedTimesAdd represents a sub command of times to add time to an issue -var CmdTrackedTimesAdd = cli.Command{ - Name: "add", - Usage: "Track spent time on an issue", - UsageText: "tea times add ", - Description: `Track spent time on an issue - Example: - tea times add 1 1h25m - `, - Action: runTrackedTimesAdd, - Flags: LoginRepoFlags, -} - -func runTrackedTimesAdd(ctx *cli.Context) error { - login, owner, repo := intern.InitCommand(globalRepoValue, globalLoginValue, globalRemoteValue) - - if ctx.Args().Len() < 2 { - return fmt.Errorf("No issue or duration specified.\nUsage:\t%s", ctx.Command.UsageText) - } - - issue, err := argToIndex(ctx.Args().First()) - if err != nil { - log.Fatal(err) - } - - duration, err := time.ParseDuration(strings.Join(ctx.Args().Tail(), "")) - if err != nil { - log.Fatal(err) - } - - _, _, err = login.Client().AddTime(owner, repo, issue, gitea.AddTimeOption{ - Time: int64(duration.Seconds()), - }) - if err != nil { - log.Fatal(err) - } - - return nil -} - -// CmdTrackedTimesDelete is a sub command of CmdTrackedTimes, and removes time from an issue -var CmdTrackedTimesDelete = cli.Command{ - Name: "delete", - Aliases: []string{"rm"}, - Usage: "Delete a single tracked time on an issue", - UsageText: "tea times delete