diff --git a/README.md b/README.md index 9844a1a..0f33733 100644 --- a/README.md +++ b/README.md @@ -10,13 +10,12 @@ If no `upstream` repository is found, `upstream` becomes synonymous with `origin * Configuration `sip config` * Change the default `origin` remote name `sip config origin` * Change the default `upstream` remote name `sip config upstrea` -* Login `sip login` - * Add a user token for API usage - * Generate a new token from CLI `sip login auto` +* Tokens `sip tokens` + * List current available access tokens + * Generate a new token from CLI `sip token create ` + * If `` is present, add new access token, otherwise... * Authenticate with username/password to get a new token without leaving the terminal - * List available logins `sip login list` -* Logout `sip logout` - * Remove user tokens + * Remove access tokens `sip tokens remove` * Repository status `sip repo` * Get basic information about the `upstream` repository * Issue search `sip issues` diff --git a/cmd/cmd.go b/cmd/cmd.go index fef9b78..22ea32b 100644 --- a/cmd/cmd.go +++ b/cmd/cmd.go @@ -43,9 +43,9 @@ var ( Value: getUpstreamRepo()[2], }, &cli.StringFlag{ - Name: "login", - Aliases: []string{"l"}, - Usage: "The login token to use (by name)", + Name: "token", + Aliases: []string{"t"}, + Usage: "The access token to use (by name)", }, } originOnce sync.Once @@ -55,38 +55,38 @@ var ( ) func requireToken(ctx *cli.Context) (string, error) { - if ctx.IsSet("login") { - return getToken(ctx.String("login")), nil + if ctx.IsSet("token") { + return getToken(ctx.String("token")), nil } - loginMap := make(map[string]config.Login) - opts := make([]string, len(config.Logins)) - for idx, login := range config.Logins { - key := fmt.Sprintf("%s (%s)", login.Name, login.URL) - loginMap[key] = login + tokenMap := make(map[string]config.Token) + opts := make([]string, len(config.Tokens)) + for idx, token := range config.Tokens { + key := fmt.Sprintf("%s (%s)", token.Name, token.URL) + tokenMap[key] = token opts[idx] = key } - question := &survey.Select{Message: "This action requires a login token", Options: opts} + question := &survey.Select{Message: "This action requires an access token", Options: opts} var answer string if err := survey.AskOne(question, &answer); err != nil { return "", err } - return loginMap[answer].Token, nil + return tokenMap[answer].Token, nil } func getToken(name string) string { - for _, login := range config.Logins { - if name == login.Name { - return login.Token + for _, token := range config.Tokens { + if name == token.Name { + return token.Token } } return "" } func getClient(ctx *cli.Context) *gitea.Client { - return gitea.NewClient(ctx.String("url"), getToken(ctx.String("login"))) + return gitea.NewClient(ctx.String("url"), getToken(ctx.String("token"))) } func getUpstreamRepo() []string { diff --git a/cmd/login.go b/cmd/login.go deleted file mode 100644 index 3211d7c..0000000 --- a/cmd/login.go +++ /dev/null @@ -1,152 +0,0 @@ -package cmd - -import ( - "code.gitea.io/sdk/gitea" - "errors" - "gitea.com/jolheiser/beaver" - "gitea.com/jolheiser/sip/modules/config" - "github.com/AlecAivazis/survey/v2" - "github.com/urfave/cli/v2" -) - -var ( - Login = cli.Command{ - Name: "login", - Usage: "Log in to a Gitea server", - Action: doLogin, - Subcommands: []*cli.Command{ - { - Name: "auto", - Usage: "Create a new token via API", - Action: doLoginAuto, - }, - { - Name: "list", - Usage: "List available tokens", - Action: doLoginList, - }, - }, - } -) - -func doLogin(ctx *cli.Context) error { - questions := []*survey.Question{ - { - Name: "name", - Prompt: &survey.Input{Message: "Name for this login"}, - Validate: validateLoginName, - }, - { - Name: "url", - Prompt: &survey.Input{Message: "URL for the Gitea instance", Default: ctx.String("url")}, - Validate: survey.Required, - }, - { - Name: "token", - Prompt: &survey.Input{Message: "API Token"}, - Validate: survey.Required, - }, - } - answers := struct { - Name string - URL string - Token string - }{} - - if err := survey.Ask(questions, &answers); err != nil { - return err - } - - config.Logins = append(config.Logins, config.Login{ - Name: answers.Name, - URL: answers.URL, - Token: answers.Token, - }) - - if err := config.Save(); err != nil { - return err - } - - beaver.Infof("Login saved! You can refer to it by using `--login %s` with commands!", answers.Name) - - return nil -} - -func doLoginAuto(ctx *cli.Context) error { - questions := []*survey.Question{ - { - Name: "name", - Prompt: &survey.Input{Message: "Name for this login", Default: "gitea"}, - Validate: validateLoginName, - }, - { - Name: "token", - Prompt: &survey.Input{Message: "Name for this token", Default: "sip"}, - Validate: survey.Required, - }, - { - Name: "url", - Prompt: &survey.Input{Message: "URL for the Gitea instance", Default: ctx.String("url")}, - Validate: survey.Required, - }, - { - Name: "username", - Prompt: &survey.Input{Message: "Username the Gitea instance"}, - Validate: survey.Required, - }, - { - Name: "password", - Prompt: &survey.Password{Message: "Password for the Gitea instance"}, - Validate: survey.Required, - }, - } - answers := struct { - Name string - Token string - URL string - Username string - Password string - }{} - - if err := survey.Ask(questions, &answers); err != nil { - return err - } - - client := gitea.NewClient(answers.URL, "") - - token, err := client.CreateAccessToken(answers.Username, answers.Password, gitea.CreateAccessTokenOption{Name: answers.Token}) - if err != nil { - return err - } - - config.Logins = append(config.Logins, config.Login{ - Name: answers.Name, - URL: answers.URL, - Token: token.Token, - }) - - if err := config.Save(); err != nil { - return err - } - - beaver.Infof("Login saved! You can refer to it by using `--login %s` with commands!", answers.Name) - - return nil -} - -func doLoginList(ctx *cli.Context) error { - for idx, login := range config.Logins { - beaver.Infof("%d. %s (%s)", idx+1, login.Name, login.URL) - } - return nil -} - -func validateLoginName(ans interface{}) error { - name := ans.(string) - for _, login := range config.Logins { - if name == login.Name { - return errors.New("login name already exists") - } - } - return survey.Required(ans) -} diff --git a/cmd/token.go b/cmd/token.go new file mode 100644 index 0000000..6f32e9d --- /dev/null +++ b/cmd/token.go @@ -0,0 +1,25 @@ +package cmd + +import ( + "gitea.com/jolheiser/beaver" + "gitea.com/jolheiser/sip/modules/config" + "github.com/urfave/cli/v2" +) + +var Tokens = cli.Command{ + Name: "tokens", + Aliases: []string{"token"}, + Usage: "Manage access tokens", + Action: doTokenList, + Subcommands: []*cli.Command{ + &TokensAdd, + &TokensRemove, + }, +} + +func doTokenList(ctx *cli.Context) error { + for idx, token := range config.Tokens { + beaver.Infof("%d. %s (%s)", idx+1, token.Name, token.URL) + } + return nil +} diff --git a/cmd/token_add.go b/cmd/token_add.go new file mode 100644 index 0000000..744c376 --- /dev/null +++ b/cmd/token_add.go @@ -0,0 +1,101 @@ +package cmd + +import ( + "code.gitea.io/sdk/gitea" + "errors" + "gitea.com/jolheiser/beaver" + "gitea.com/jolheiser/sip/modules/config" + "github.com/AlecAivazis/survey/v2" + "github.com/urfave/cli/v2" +) + +var TokensAdd = cli.Command{ + Name: "add", + Usage: "Add a new access token", + Action: doTokenAdd, +} + +func doTokenAdd(ctx *cli.Context) error { + token := ctx.Args().First() + questions := []*survey.Question{ + { + Name: "name", + Prompt: &survey.Input{Message: "Local nickname for this token", Default: "gitea"}, + Validate: validateTokenName, + }, + { + Name: "token", + Prompt: &survey.Input{Message: "Name for this token in Gitea", Default: "sip"}, + Validate: survey.Required, + }, + { + Name: "url", + Prompt: &survey.Input{Message: "URL for the Gitea instance", Default: ctx.String("url")}, + Validate: survey.Required, + }, + } + answers := struct { + Name string + Token string + URL string + }{} + + if err := survey.Ask(questions, &answers); err != nil { + return err + } + + if token == "" { + authQuestions := []*survey.Question{ + { + Name: "username", + Prompt: &survey.Input{Message: "Username the Gitea instance"}, + Validate: survey.Required, + }, + { + Name: "password", + Prompt: &survey.Password{Message: "Password for the Gitea instance"}, + Validate: survey.Required, + }, + } + authAnswers := struct { + Username string + Password string + }{} + + if err := survey.Ask(authQuestions, &authAnswers); err != nil { + return err + } + + client := gitea.NewClient(answers.URL, "") + + t, err := client.CreateAccessToken(authAnswers.Username, authAnswers.Password, gitea.CreateAccessTokenOption{Name: answers.Token}) + if err != nil { + return err + } + token = t.Token + } + + config.Tokens = append(config.Tokens, config.Token{ + Name: answers.Name, + URL: answers.URL, + Token: token, + }) + + if err := config.Save(); err != nil { + return err + } + + beaver.Infof("Token saved! You can refer to it by using `--token %s` with commands!", answers.Name) + + return nil +} + +func validateTokenName(ans interface{}) error { + name := ans.(string) + for _, token := range config.Tokens { + if name == token.Name { + return errors.New("token already exists") + } + } + return survey.Required(ans) +} diff --git a/cmd/logout.go b/cmd/token_remove.go similarity index 52% rename from cmd/logout.go rename to cmd/token_remove.go index 614ba98..5d9fbba 100644 --- a/cmd/logout.go +++ b/cmd/token_remove.go @@ -8,18 +8,16 @@ import ( "github.com/urfave/cli/v2" ) -var ( - Logout = cli.Command{ - Name: "logout", - Usage: "Log out of a Gitea server", - Action: doLogout, - } -) +var TokensRemove = cli.Command{ + Name: "remove", + Usage: "Remove access tokens", + Action: doTokenRemove, +} -func doLogout(ctx *cli.Context) error { - opts := make([]string, len(config.Logins)) - for idx, login := range config.Logins { - opts[idx] = fmt.Sprintf("%s (%s)", login.Name, login.URL) +func doTokenRemove(ctx *cli.Context) error { + opts := make([]string, len(config.Tokens)) + for idx, token := range config.Tokens { + opts[idx] = fmt.Sprintf("%s (%s)", token.Name, token.URL) } question := &survey.MultiSelect{ Message: "Which would you like to remove?", @@ -34,22 +32,22 @@ func doLogout(ctx *cli.Context) error { idxs := make([]int, len(answers)) for idx, answer := range answers { - for idy, login := range config.Logins { - if answer == fmt.Sprintf("%s (%s)", login.Name, login.URL) { + for idy, token := range config.Tokens { + if answer == fmt.Sprintf("%s (%s)", token.Name, token.URL) { idxs[len(answers)-idx-1] = idy } } } for _, idx := range idxs { - config.Logins = append(config.Logins[:idx], config.Logins[idx+1:]...) + config.Tokens = append(config.Tokens[:idx], config.Tokens[idx+1:]...) } if err := config.Save(); err != nil { return err } - beaver.Infof("Logged out of %d accounts! Remember to clean up your tokens!", len(answers)) + beaver.Infof("Removed %d token(s)! Remember to clean up in Gitea if necessary!", len(answers)) return nil } diff --git a/main.go b/main.go index da204ce..1cb3dfb 100644 --- a/main.go +++ b/main.go @@ -16,8 +16,7 @@ func main() { app.Version = Version app.Commands = []*cli.Command{ &cmd.Config, - &cmd.Login, - &cmd.Logout, + &cmd.Tokens, &cmd.Repo, &cmd.Issues, &cmd.Pulls, diff --git a/modules/config/config.go b/modules/config/config.go index bbe1a74..ffda306 100644 --- a/modules/config/config.go +++ b/modules/config/config.go @@ -15,16 +15,16 @@ var ( Origin string Upstream string - Logins []Login + Tokens []Token ) type config struct { Origin string `toml:"origin"` Upstream string `toml:"upstream"` - Logins []Login `toml:"login"` + Tokens []Token `toml:"token"` } -type Login struct { +type Token struct { Name string `toml:"name"` URL string `toml:"url"` Token string `toml:"token"` @@ -53,13 +53,13 @@ func init() { Origin = cfg.Origin Upstream = cfg.Upstream - Logins = cfg.Logins + Tokens = cfg.Tokens } func Save() error { cfg.Origin = Origin cfg.Upstream = Upstream - cfg.Logins = Logins + cfg.Tokens = Tokens fi, err := os.Create(configPath) if err != nil {