Refactor: apply new internal structurs #206

Merged
lunny merged 22 commits from 6543/tea:refactor_new-internal-structure into master 2020-09-30 05:11:35 +00:00
17 changed files with 448 additions and 405 deletions
Showing only changes of commit 6bad01c328 - Show all commits

View File

@ -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")

View File

@ -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
}

View File

@ -9,6 +9,8 @@ import (
"log"
"strconv"
6543 marked this conversation as resolved Outdated
Outdated
Review

We need a blank line.

We need a blank line.
Outdated
Review

between internal modues ?!?

between internal modues ?!?
"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)
}

View File

@ -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 {

View File

@ -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
}

View File

@ -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)
}
}

View File

@ -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")

View File

@ -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())

View File

@ -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
}

View File

@ -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)

View File

@ -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)
}

View File

@ -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()

View File

@ -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

View File

@ -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 {

153
modules/intern/config.go Normal file
View File

@ -0,0 +1,153 @@
// Copyright 2018 The Gitea Authors. All rights reserved.
6543 marked this conversation as resolved Outdated
Outdated
Review

2020

2020
// 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
6543 marked this conversation as resolved Outdated
Outdated
Review

LocalConfig

LocalConfig
type LocalConfig struct {
Logins []Login `yaml:"logins"`
}
var (
Config LocalConfig
yamlConfigPath string
)
func init() {
6543 marked this conversation as resolved Outdated
Outdated
Review

// TODO: dont rely on intransparent init func to detect tea config

// TODO: dont rely on intransparent init func to detect tea config
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
}

176
modules/intern/login.go Normal file
View File

@ -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
}

View File

@ -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
}