Refactor: apply new internal structurs #206

Merged
lunny merged 22 commits from 6543/tea:refactor_new-internal-structure into master 2 years ago

@ -1,286 +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 (
"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"
}
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")
if limit != 0 && page == 0 {
page = 1
}
return gitea.ListOptions{
Page: page,
PageSize: limit,
}
}

@ -2,23 +2,24 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package cmd
package flags
import (
"log"
"code.gitea.io/tea/modules/utils"
"code.gitea.io/sdk/gitea"
"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 contain value of --login|-l arg
GlobalLoginValue string
// GlobalRepoValue contain value of --repo|-r arg
GlobalRepoValue string
// GlobalOutputValue contain value of --output|-o arg
GlobalOutputValue string
// GlobalRemoteValue contain value of --remote|-R arg
GlobalRemoteValue string
)
// LoginFlag provides flag to specify tea login profile
@ -26,7 +27,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 +35,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 +43,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 +51,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"
@ -109,61 +110,15 @@ var IssuePRFlags = append([]cli.Flag{
&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)
// 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
}
if login, err = getDefaultLogin(); err != nil {
log.Fatal(err.Error())
return gitea.ListOptions{
Page: page,
PageSize: limit,
}
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
}

@ -5,12 +5,12 @@
package cmd
import (
"fmt"
"log"
"strconv"
"code.gitea.io/tea/cmd/flags"
"code.gitea.io/tea/cmd/issues"
"code.gitea.io/tea/modules/config"
"code.gitea.io/tea/modules/print"
"code.gitea.io/tea/modules/utils"
"code.gitea.io/sdk/gitea"
"github.com/charmbracelet/glamour"
"github.com/urfave/cli/v2"
)
@ -22,34 +22,25 @@ var CmdIssues = cli.Command{
ArgsUsage: "[<issue index>]",
Action: runIssues,
Subcommands: []*cli.Command{
&CmdIssuesList,
&CmdIssuesCreate,
&CmdIssuesReopen,
&CmdIssuesClose,
&issues.CmdIssuesList,
&issues.CmdIssuesCreate,
&issues.CmdIssuesReopen,
&issues.CmdIssuesClose,
},
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,
Flags: flags.IssuePRFlags,
}
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)
return issues.RunIssuesList(ctx)
}
func runIssueDetail(ctx *cli.Context, index string) error {
login, owner, repo := initCommand()
func runIssueDetail(index string) error {
login, owner, repo := config.InitCommand(flags.GlobalRepoValue, flags.GlobalLoginValue, flags.GlobalRemoteValue)
idx, err := argToIndex(index)
idx, err := utils.ArgToIndex(index)
if err != nil {
return err
}
@ -57,165 +48,6 @@ 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)
return nil
}
func runIssuesList(ctx *cli.Context) error {
login, owner, repo := initCommand()
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(outputValue, 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(outputValue, 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 := initCommand()
_, _, 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)
}
print.IssueDetails(issue)
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: "<issue index>",
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: "<issue index>",
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 := initCommand()
if ctx.Args().Len() == 0 {
log.Fatal(ctx.Command.ArgsUsage)
}
index, err := argToIndex(ctx.Args().First())
if err != nil {
return err
}
_, _, err = login.Client().EditIssue(owner, repo, index, opts)
return err
}

@ -0,0 +1,46 @@
// 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 issues
import (
"log"
"code.gitea.io/tea/cmd/flags"
"code.gitea.io/tea/modules/config"
"code.gitea.io/tea/modules/utils"
"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: "<issue index>",
Action: func(ctx *cli.Context) error {
var s = gitea.StateClosed
return editIssueState(ctx, gitea.EditIssueOption{State: &s})
},
Flags: flags.AllDefaultFlags,
}
// editIssueState abstracts the arg parsing to edit the given issue
func editIssueState(ctx *cli.Context, opts gitea.EditIssueOption) error {
login, owner, repo := config.InitCommand(flags.GlobalRepoValue, flags.GlobalLoginValue, flags.GlobalRemoteValue)
if ctx.Args().Len() == 0 {
log.Fatal(ctx.Command.ArgsUsage)
}
index, err := utils.ArgToIndex(ctx.Args().First())
if err != nil {
return err
}
_, _, err = login.Client().EditIssue(owner, repo, index, opts)
// TODO: print (short)IssueDetails
return err
}

@ -0,0 +1,59 @@
// 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 issues
import (
"log"
"code.gitea.io/tea/cmd/flags"
"code.gitea.io/tea/modules/config"
"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",
},
}, flags.LoginRepoFlags...),
}
func runIssuesCreate(ctx *cli.Context) error {
login, owner, repo := config.InitCommand(flags.GlobalRepoValue, flags.GlobalLoginValue, flags.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
}

@ -0,0 +1,92 @@
// 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 issues
import (
"log"
"strconv"
"code.gitea.io/tea/cmd/flags"
"code.gitea.io/tea/modules/config"
"code.gitea.io/tea/modules/print"
"code.gitea.io/sdk/gitea"
"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: flags.IssuePRFlags,
}
// RunIssuesList list issues
func RunIssuesList(ctx *cli.Context) error {
login, owner, repo := config.InitCommand(flags.GlobalRepoValue, flags.GlobalLoginValue, flags.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: flags.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 {
print.OutputList(flags.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"),
},
)
}
print.OutputList(flags.GlobalOutputValue, headers, values)
return nil
}

@ -0,0 +1,26 @@
// 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 issues
import (
"code.gitea.io/tea/cmd/flags"
"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: "<issue index>",
Action: func(ctx *cli.Context) error {
var s = gitea.StateOpen
return editIssueState(ctx, gitea.EditIssueOption{State: &s})
},
Flags: flags.AllDefaultFlags,
}

@ -5,15 +5,17 @@
package cmd
import (
"bufio"
"fmt"
"log"
"os"
"strconv"
"strings"
"code.gitea.io/sdk/gitea"
"code.gitea.io/tea/cmd/flags"
"code.gitea.io/tea/cmd/labels"
"code.gitea.io/tea/modules/config"
"code.gitea.io/tea/modules/print"
"code.gitea.io/sdk/gitea"
"github.com/muesli/termenv"
"github.com/urfave/cli/v2"
)
@ -25,9 +27,9 @@ var CmdLabels = cli.Command{
Description: `Manage issue labels`,
Action: runLabels,
Subcommands: []*cli.Command{
&CmdLabelCreate,
&CmdLabelUpdate,
&CmdLabelDelete,
&labels.CmdLabelCreate,
&labels.CmdLabelUpdate,
&labels.CmdLabelDelete,
},
Flags: append([]cli.Flag{
&cli.StringFlag{
@ -35,13 +37,13 @@ var CmdLabels = cli.Command{
Aliases: []string{"s"},
Usage: "Save all the labels as a file",
},
&PaginationPageFlag,
&PaginationLimitFlag,
}, AllDefaultFlags...),
&flags.PaginationPageFlag,
&flags.PaginationLimitFlag,
}, flags.AllDefaultFlags...),
}
func runLabels(ctx *cli.Context) error {
login, owner, repo := initCommand()
login, owner, repo := config.InitCommand(flags.GlobalRepoValue, flags.GlobalLoginValue, flags.GlobalRemoteValue)
headers := []string{
"Index",
@ -52,13 +54,13 @@ func runLabels(ctx *cli.Context) error {
var values [][]string
labels, _, err := login.Client().ListRepoLabels(owner, repo, gitea.ListLabelsOptions{ListOptions: getListOptions(ctx)})
labels, _, err := login.Client().ListRepoLabels(owner, repo, gitea.ListLabelsOptions{ListOptions: flags.GetListOptions(ctx)})
if err != nil {
log.Fatal(err)
}
if len(labels) == 0 {
Output(outputValue, headers, values)
print.OutputList(flags.GlobalOutputValue, headers, values)
return nil
}
@ -89,184 +91,7 @@ func runLabels(ctx *cli.Context) error {
},
)
}
Output(outputValue, headers, values)
}
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
if len(fields) < 1 {
return "", "", ""
} else if len(fields) >= 2 {
description = strings.TrimSpace(fields[1])
}
fields = strings.Fields(fields[0])
if len(fields) <= 0 {
return "", "", ""
}
color = fields[0]
if len(fields) == 2 {
name = fields[1]
} else if len(fields) > 2 {
name = strings.Join(fields[1:], " ")
}
return color, name, description
}
func runLabelCreate(ctx *cli.Context) error {
login, owner, repo := initCommand()
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
// 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)
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 := initCommand()
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 := initCommand()
_, err := login.Client().DeleteLabel(owner, repo, ctx.Int64("id"))
if err != nil {
log.Fatal(err)
print.OutputList(flags.GlobalOutputValue, headers, values)
}
return nil

@ -0,0 +1,109 @@
// 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 labels
import (
"bufio"
"log"
"os"
"strings"
"code.gitea.io/tea/cmd/flags"
"code.gitea.io/tea/modules/config"
"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 := config.InitCommand(flags.GlobalRepoValue, flags.GlobalLoginValue, flags.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
}
func splitLabelLine(line string) (string, string, string) {
fields := strings.SplitN(line, ";", 2)
var color, name, description string
if len(fields) < 1 {
return "", "", ""
} else if len(fields) >= 2 {
description = strings.TrimSpace(fields[1])
}
fields = strings.Fields(fields[0])
if len(fields) <= 0 {
return "", "", ""
}
color = fields[0]
if len(fields) == 2 {
name = fields[1]
} else if len(fields) > 2 {
name = strings.Join(fields[1:], " ")
}
return color, name, description
}

@ -2,7 +2,7 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package cmd
package labels
import (
"bufio"

@ -0,0 +1,39 @@
// 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 labels
import (
"log"
"code.gitea.io/tea/cmd/flags"
"code.gitea.io/tea/modules/config"
"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 := config.InitCommand(flags.GlobalRepoValue, flags.GlobalLoginValue, flags.GlobalRemoteValue)
_, err := login.Client().DeleteLabel(owner, repo, ctx.Int64("id"))
if err != nil {
log.Fatal(err)
}
return nil
}

@ -0,0 +1,75 @@
// 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 labels
import (
"log"
"code.gitea.io/tea/cmd/flags"
"code.gitea.io/tea/modules/config"
"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 := config.InitCommand(flags.GlobalRepoValue, flags.GlobalLoginValue, flags.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
}

@ -5,19 +5,8 @@
package cmd
import (
"crypto/tls"
"fmt"
"log"
"net/http"
"net/http/cookiejar"
"net/url"
"os"
"strings"
"time"
"code.gitea.io/tea/cmd/login"
"code.gitea.io/sdk/gitea"
"github.com/skratchdot/open-golang/open"
"github.com/urfave/cli/v2"
)
@ -26,337 +15,11 @@ var CmdLogin = cli.Command{
Name: "login",
Usage: "Log in to a Gitea server",
Description: `Log in to a Gitea server`,
Action: runLoginAddInteractive,
Action: login.RunLoginAddInteractive, // TODO show list if no arg & detail if login as arg
Subcommands: []*cli.Command{
&cmdLoginList,
&cmdLoginAdd,
&cmdLoginEdit,
&cmdLoginSetDefault,
&login.CmdLoginList,
&login.CmdLoginAdd,
&login.CmdLoginEdit,
&login.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(yamlConfigPath)
}
// 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: "<Login>",
Action: runLoginSetDefault,
Flags: []cli.Flag{&OutputFlag},
}
func runLoginSetDefault(ctx *cli.Context) error {
if err := loadConfig(yamlConfigPath); err != nil {
return err
}
if ctx.Args().Len() == 0 {
l, err := getDefaultLogin()
if err != nil {
return err
}
fmt.Printf("Default Login: %s\n", l.Name)
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
loginExist = true
}
}
if !loginExist {
return fmt.Errorf("login '%s' not found", ctx.Args().First())
}
return saveConfig(yamlConfigPath)
}
// 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: "",