From 26bd1dd11010b4d86cebe2510ad7085a6b316334 Mon Sep 17 00:00:00 2001 From: Michael Muré Date: Sun, 28 Jun 2020 18:26:29 +0200 Subject: commands: refactor to avoid globals --- commands/add.go | 80 +++++++-------- commands/bridge.go | 42 +++++--- commands/bridge_auth.go | 40 +++++--- commands/bridge_auth_addtoken.go | 90 ++++++++-------- commands/bridge_auth_rm.go | 38 +++---- commands/bridge_auth_show.go | 48 +++++---- commands/bridge_configure.go | 216 ++++++++++++++++++++------------------- commands/bridge_pull.go | 71 +++++++------ commands/bridge_push.go | 41 ++++---- commands/bridge_rm.go | 36 ++++--- commands/commands.go | 69 +++++++------ commands/comment.go | 57 +++++------ commands/comment_add.go | 73 ++++++------- commands/deselect.go | 44 ++++---- commands/env.go | 40 ++++++++ commands/label.go | 40 ++++---- commands/label_add.go | 35 ++++--- commands/label_rm.go | 34 +++--- commands/ls-id.go | 33 +++--- commands/ls-labels.go | 41 ++++---- commands/ls.go | 186 +++++++++++++++++---------------- commands/pull.go | 42 ++++---- commands/push.go | 37 ++++--- commands/root.go | 133 +++++++++++++++--------- commands/select.go | 60 +++++------ commands/show.go | 153 +++++++++++++-------------- commands/status.go | 38 ++++--- commands/status_close.go | 33 +++--- commands/status_open.go | 33 +++--- commands/termui.go | 35 ++++--- commands/title.go | 39 +++---- commands/title_edit.go | 65 ++++++------ commands/user.go | 89 +++++++++------- commands/user_adopt.go | 38 +++---- commands/user_create.go | 39 +++---- commands/user_ls.go | 62 ++++++----- commands/version.go | 89 ++++++++-------- commands/webui.go | 100 +++++++++--------- 38 files changed, 1328 insertions(+), 1111 deletions(-) create mode 100644 commands/env.go (limited to 'commands') diff --git a/commands/add.go b/commands/add.go index e656a262..8b5facaf 100644 --- a/commands/add.go +++ b/commands/add.go @@ -1,42 +1,65 @@ package commands import ( - "fmt" + "github.com/spf13/cobra" "github.com/MichaelMure/git-bug/cache" "github.com/MichaelMure/git-bug/input" "github.com/MichaelMure/git-bug/util/interrupt" - "github.com/spf13/cobra" ) -var ( - addTitle string - addMessage string - addMessageFile string -) +type addOptions struct { + title string + message string + messageFile string +} + +func newAddCommand() *cobra.Command { + env := newEnv() + options := addOptions{} + + cmd := &cobra.Command{ + Use: "add", + Short: "Create a new bug.", + PreRunE: loadRepoEnsureUser(env), + RunE: func(cmd *cobra.Command, args []string) error { + return runAdd(env, options) + }, + } + + flags := cmd.Flags() + flags.SortFlags = false -func runAddBug(cmd *cobra.Command, args []string) error { - var err error + flags.StringVarP(&options.title, "title", "t", "", + "Provide a title to describe the issue") + flags.StringVarP(&options.message, "message", "m", "", + "Provide a message to describe the issue") + flags.StringVarP(&options.messageFile, "file", "F", "", + "Take the message from the given file. Use - to read the message from the standard input") - backend, err := cache.NewRepoCache(repo) + return cmd +} + +func runAdd(env *Env, opts addOptions) error { + backend, err := cache.NewRepoCache(env.repo) if err != nil { return err } defer backend.Close() interrupt.RegisterCleaner(backend.Close) - if addMessageFile != "" && addMessage == "" { - addTitle, addMessage, err = input.BugCreateFileInput(addMessageFile) + if opts.messageFile != "" && opts.message == "" { + opts.title, opts.message, err = input.BugCreateFileInput(opts.messageFile) if err != nil { return err } } - if addMessageFile == "" && (addMessage == "" || addTitle == "") { - addTitle, addMessage, err = input.BugCreateEditorInput(backend, addTitle, addMessage) + if opts.messageFile == "" && (opts.message == "" || opts.title == "") { + opts.title, opts.message, err = input.BugCreateEditorInput(backend, opts.title, opts.message) if err == input.ErrEmptyTitle { - fmt.Println("Empty title, aborting.") + env.out.Println("Empty title, aborting.") return nil } if err != nil { @@ -44,35 +67,12 @@ func runAddBug(cmd *cobra.Command, args []string) error { } } - b, _, err := backend.NewBug(addTitle, addMessage) + b, _, err := backend.NewBug(opts.title, opts.message) if err != nil { return err } - fmt.Printf("%s created\n", b.Id().Human()) + env.out.Printf("%s created\n", b.Id().Human()) return nil } - -var addCmd = &cobra.Command{ - Use: "add", - Short: "Create a new bug.", - PreRunE: loadRepoEnsureUser, - RunE: runAddBug, -} - -func init() { - RootCmd.AddCommand(addCmd) - - addCmd.Flags().SortFlags = false - - addCmd.Flags().StringVarP(&addTitle, "title", "t", "", - "Provide a title to describe the issue", - ) - addCmd.Flags().StringVarP(&addMessage, "message", "m", "", - "Provide a message to describe the issue", - ) - addCmd.Flags().StringVarP(&addMessageFile, "file", "F", "", - "Take the message from the given file. Use - to read the message from the standard input", - ) -} diff --git a/commands/bridge.go b/commands/bridge.go index 3c398e6b..8a2cf38b 100644 --- a/commands/bridge.go +++ b/commands/bridge.go @@ -1,8 +1,6 @@ package commands import ( - "fmt" - "github.com/spf13/cobra" "github.com/MichaelMure/git-bug/bridge" @@ -10,8 +8,30 @@ import ( "github.com/MichaelMure/git-bug/util/interrupt" ) -func runBridge(cmd *cobra.Command, args []string) error { - backend, err := cache.NewRepoCache(repo) +func newBridgeCommand() *cobra.Command { + env := newEnv() + + cmd := &cobra.Command{ + Use: "bridge", + Short: "Configure and use bridges to other bug trackers.", + PreRunE: loadRepo(env), + RunE: func(cmd *cobra.Command, args []string) error { + return runBridge(env) + }, + Args: cobra.NoArgs, + } + + cmd.AddCommand(newBridgeAuthCommand()) + cmd.AddCommand(newBridgeConfigureCommand()) + cmd.AddCommand(newBridgePullCommand()) + cmd.AddCommand(newBridgePushCommand()) + cmd.AddCommand(newBridgeRm()) + + return cmd +} + +func runBridge(env *Env) error { + backend, err := cache.NewRepoCache(env.repo) if err != nil { return err } @@ -24,20 +44,8 @@ func runBridge(cmd *cobra.Command, args []string) error { } for _, c := range configured { - fmt.Println(c) + env.out.Println(c) } return nil } - -var bridgeCmd = &cobra.Command{ - Use: "bridge", - Short: "Configure and use bridges to other bug trackers.", - PreRunE: loadRepo, - RunE: runBridge, - Args: cobra.NoArgs, -} - -func init() { - RootCmd.AddCommand(bridgeCmd) -} diff --git a/commands/bridge_auth.go b/commands/bridge_auth.go index 3a0e0c29..e51b9b9d 100644 --- a/commands/bridge_auth.go +++ b/commands/bridge_auth.go @@ -1,7 +1,6 @@ package commands import ( - "fmt" "sort" "strings" @@ -15,8 +14,28 @@ import ( "github.com/MichaelMure/git-bug/util/interrupt" ) -func runBridgeAuth(cmd *cobra.Command, args []string) error { - backend, err := cache.NewRepoCache(repo) +func newBridgeAuthCommand() *cobra.Command { + env := newEnv() + + cmd := &cobra.Command{ + Use: "auth", + Short: "List all known bridge authentication credentials.", + PreRunE: loadRepo(env), + RunE: func(cmd *cobra.Command, args []string) error { + return runBridgeAuth(env) + }, + Args: cobra.NoArgs, + } + + cmd.AddCommand(newBridgeAuthAddTokenCommand()) + cmd.AddCommand(newBridgeAuthRm()) + cmd.AddCommand(newBridgeAuthShow()) + + return cmd +} + +func runBridgeAuth(env *Env) error { + backend, err := cache.NewRepoCache(env.repo) if err != nil { return err } @@ -44,7 +63,7 @@ func runBridgeAuth(cmd *cobra.Command, args []string) error { sort.Strings(meta) metaFmt := strings.Join(meta, ",") - fmt.Printf("%s %s %s %s %s\n", + env.out.Printf("%s %s %s %s %s\n", colors.Cyan(cred.ID().Human()), colors.Yellow(targetFmt), colors.Magenta(cred.Kind()), @@ -55,16 +74,3 @@ func runBridgeAuth(cmd *cobra.Command, args []string) error { return nil } - -var bridgeAuthCmd = &cobra.Command{ - Use: "auth", - Short: "List all known bridge authentication credentials.", - PreRunE: loadRepo, - RunE: runBridgeAuth, - Args: cobra.NoArgs, -} - -func init() { - bridgeCmd.AddCommand(bridgeAuthCmd) - bridgeAuthCmd.Flags().SortFlags = false -} diff --git a/commands/bridge_auth_addtoken.go b/commands/bridge_auth_addtoken.go index 8eda28ac..dde7a6dd 100644 --- a/commands/bridge_auth_addtoken.go +++ b/commands/bridge_auth_addtoken.go @@ -17,34 +17,61 @@ import ( "github.com/MichaelMure/git-bug/util/interrupt" ) -var ( - bridgeAuthAddTokenTarget string - bridgeAuthAddTokenLogin string - bridgeAuthAddTokenUser string -) +type bridgeAuthAddTokenOptions struct { + target string + login string + user string +} + +func newBridgeAuthAddTokenCommand() *cobra.Command { + env := newEnv() + options := bridgeAuthAddTokenOptions{} + + cmd := &cobra.Command{ + Use: "add-token []", + Short: "Store a new token", + PreRunE: loadRepoEnsureUser(env), + RunE: func(cmd *cobra.Command, args []string) error { + return runBridgeAuthAddToken(env, options, args) + }, + Args: cobra.MaximumNArgs(1), + } + + flags := cmd.Flags() + flags.SortFlags = false + + flags.StringVarP(&options.target, "target", "t", "", + fmt.Sprintf("The target of the bridge. Valid values are [%s]", strings.Join(bridge.Targets(), ","))) + flags.StringVarP(&options.login, + "login", "l", "", "The login in the remote bug-tracker") + flags.StringVarP(&options.user, + "user", "u", "", "The user to add the token to. Default is the current user") -func runBridgeTokenAdd(cmd *cobra.Command, args []string) error { + return cmd +} + +func runBridgeAuthAddToken(env *Env, opts bridgeAuthAddTokenOptions, args []string) error { // Note: as bridgeAuthAddTokenLogin is not checked against the remote bug-tracker, // it's possible to register a credential with an incorrect login (including bad case). // The consequence is that it will not get picked later by the bridge. I find that // checking it would require a cumbersome UX (need to provide a base URL for some bridges, ...) // so it's probably not worth it, unless we refactor that entirely. - if bridgeAuthAddTokenTarget == "" { + if opts.target == "" { return fmt.Errorf("flag --target is required") } - if bridgeAuthAddTokenLogin == "" { + if opts.login == "" { return fmt.Errorf("flag --login is required") } - backend, err := cache.NewRepoCache(repo) + backend, err := cache.NewRepoCache(env.repo) if err != nil { return err } defer backend.Close() interrupt.RegisterCleaner(backend.Close) - if !core.TargetExist(bridgeAuthAddTokenTarget) { + if !core.TargetExist(opts.target) { return fmt.Errorf("unknown target") } @@ -55,7 +82,7 @@ func runBridgeTokenAdd(cmd *cobra.Command, args []string) error { } else { // Read from Stdin if isatty.IsTerminal(os.Stdin.Fd()) { - fmt.Println("Enter the token:") + env.err.Println("Enter the token:") } reader := bufio.NewReader(os.Stdin) raw, err := reader.ReadString('\n') @@ -67,62 +94,43 @@ func runBridgeTokenAdd(cmd *cobra.Command, args []string) error { var user *cache.IdentityCache - if bridgeAuthAddTokenUser == "" { + if opts.user == "" { user, err = backend.GetUserIdentity() } else { - user, err = backend.ResolveIdentityPrefix(bridgeAuthAddTokenUser) + user, err = backend.ResolveIdentityPrefix(opts.user) } if err != nil { return err } - metaKey, _ := bridge.LoginMetaKey(bridgeAuthAddTokenTarget) + metaKey, _ := bridge.LoginMetaKey(opts.target) login, ok := user.ImmutableMetadata()[metaKey] switch { - case ok && login == bridgeAuthAddTokenLogin: + case ok && login == opts.login: // nothing to do - case ok && login != bridgeAuthAddTokenLogin: - return fmt.Errorf("this user is already tagged with a different %s login", bridgeAuthAddTokenTarget) + case ok && login != opts.login: + return fmt.Errorf("this user is already tagged with a different %s login", opts.target) default: - user.SetMetadata(metaKey, bridgeAuthAddTokenLogin) + user.SetMetadata(metaKey, opts.login) err = user.Commit() if err != nil { return err } } - token := auth.NewToken(bridgeAuthAddTokenTarget, value) - token.SetMetadata(auth.MetaKeyLogin, bridgeAuthAddTokenLogin) + token := auth.NewToken(opts.target, value) + token.SetMetadata(auth.MetaKeyLogin, opts.login) if err := token.Validate(); err != nil { return errors.Wrap(err, "invalid token") } - err = auth.Store(repo, token) + err = auth.Store(env.repo, token) if err != nil { return err } - fmt.Printf("token %s added\n", token.ID()) + env.out.Printf("token %s added\n", token.ID()) return nil } - -var bridgeAuthAddTokenCmd = &cobra.Command{ - Use: "add-token []", - Short: "Store a new token", - PreRunE: loadRepoEnsureUser, - RunE: runBridgeTokenAdd, - Args: cobra.MaximumNArgs(1), -} - -func init() { - bridgeAuthCmd.AddCommand(bridgeAuthAddTokenCmd) - bridgeAuthAddTokenCmd.Flags().StringVarP(&bridgeAuthAddTokenTarget, "target", "t", "", - fmt.Sprintf("The target of the bridge. Valid values are [%s]", strings.Join(bridge.Targets(), ","))) - bridgeAuthAddTokenCmd.Flags().StringVarP(&bridgeAuthAddTokenLogin, - "login", "l", "", "The login in the remote bug-tracker") - bridgeAuthAddTokenCmd.Flags().StringVarP(&bridgeAuthAddTokenUser, - "user", "u", "", "The user to add the token to. Default is the current user") - bridgeAuthAddTokenCmd.Flags().SortFlags = false -} diff --git a/commands/bridge_auth_rm.go b/commands/bridge_auth_rm.go index 17e70625..b2a44e92 100644 --- a/commands/bridge_auth_rm.go +++ b/commands/bridge_auth_rm.go @@ -1,36 +1,38 @@ package commands import ( - "fmt" - "github.com/spf13/cobra" "github.com/MichaelMure/git-bug/bridge/core/auth" ) -func runBridgeAuthRm(cmd *cobra.Command, args []string) error { - cred, err := auth.LoadWithPrefix(repo, args[0]) +func newBridgeAuthRm() *cobra.Command { + env := newEnv() + + cmd := &cobra.Command{ + Use: "rm ", + Short: "Remove a credential.", + PreRunE: loadRepo(env), + RunE: func(cmd *cobra.Command, args []string) error { + return runBridgeAuthRm(env, args) + }, + Args: cobra.ExactArgs(1), + } + + return cmd +} + +func runBridgeAuthRm(env *Env, args []string) error { + cred, err := auth.LoadWithPrefix(env.repo, args[0]) if err != nil { return err } - err = auth.Remove(repo, cred.ID()) + err = auth.Remove(env.repo, cred.ID()) if err != nil { return err } - fmt.Printf("credential %s removed\n", cred.ID()) + env.out.Printf("credential %s removed\n", cred.ID()) return nil } - -var bridgeAuthRmCmd = &cobra.Command{ - Use: "rm ", - Short: "Remove a credential.", - PreRunE: loadRepo, - RunE: runBridgeAuthRm, - Args: cobra.ExactArgs(1), -} - -func init() { - bridgeAuthCmd.AddCommand(bridgeAuthRmCmd) -} diff --git a/commands/bridge_auth_show.go b/commands/bridge_auth_show.go index fbbf60a7..f8b15c9a 100644 --- a/commands/bridge_auth_show.go +++ b/commands/bridge_auth_show.go @@ -13,30 +13,46 @@ import ( "github.com/MichaelMure/git-bug/util/interrupt" ) -func runBridgeAuthShow(cmd *cobra.Command, args []string) error { - backend, err := cache.NewRepoCache(repo) +func newBridgeAuthShow() *cobra.Command { + env := newEnv() + + cmd := &cobra.Command{ + Use: "show", + Short: "Display an authentication credential.", + PreRunE: loadRepo(env), + RunE: func(cmd *cobra.Command, args []string) error { + return runBridgeAuthShow(env, args) + }, + Args: cobra.ExactArgs(1), + } + + return cmd +} + +func runBridgeAuthShow(env *Env, args []string) error { + backend, err := cache.NewRepoCache(env.repo) if err != nil { return err } defer backend.Close() interrupt.RegisterCleaner(backend.Close) - cred, err := auth.LoadWithPrefix(repo, args[0]) + cred, err := auth.LoadWithPrefix(env.repo, args[0]) if err != nil { return err } - fmt.Printf("Id: %s\n", cred.ID()) - fmt.Printf("Target: %s\n", cred.Target()) - fmt.Printf("Kind: %s\n", cred.Kind()) - fmt.Printf("Creation: %s\n", cred.CreateTime().Format(time.RFC822)) + env.out.Printf("Id: %s\n", cred.ID()) + env.out.Printf("Target: %s\n", cred.Target()) + env.out.Printf("Kind: %s\n", cred.Kind()) + env.out.Printf("Creation: %s\n", cred.CreateTime().Format(time.RFC822)) switch cred := cred.(type) { case *auth.Token: - fmt.Printf("Value: %s\n", cred.Value) + env.out.Printf("Value: %s\n", cred.Value) } - fmt.Println("Metadata:") + env.out.Println("Metadata:") meta := make([]string, 0, len(cred.Metadata())) for key, value := range cred.Metadata() { @@ -44,19 +60,7 @@ func runBridgeAuthShow(cmd *cobra.Command, args []string) error { } sort.Strings(meta) - fmt.Print(strings.Join(meta, "")) + env.out.Print(strings.Join(meta, "")) return nil } - -var bridgeAuthShowCmd = &cobra.Command{ - Use: "show", - Short: "Display an authentication credential.", - PreRunE: loadRepo, - RunE: runBridgeAuthShow, - Args: cobra.ExactArgs(1), -} - -func init() { - bridgeAuthCmd.AddCommand(bridgeAuthShowCmd) -} diff --git a/commands/bridge_configure.go b/commands/bridge_configure.go index 89553633..83555b0c 100644 --- a/commands/bridge_configure.go +++ b/commands/bridge_configure.go @@ -17,79 +17,161 @@ import ( "github.com/MichaelMure/git-bug/util/interrupt" ) -const ( - defaultName = "default" -) +type bridgeConfigureOptions struct { + name string + target string + params core.BridgeParams + token string + tokenStdin bool +} -var ( - bridgeConfigureName string - bridgeConfigureTarget string - bridgeConfigureParams core.BridgeParams - bridgeConfigureToken string - bridgeConfigureTokenStdin bool -) +func newBridgeConfigureCommand() *cobra.Command { + env := newEnv() + options := bridgeConfigureOptions{} + + cmd := &cobra.Command{ + Use: "configure", + Short: "Configure a new bridge.", + Long: ` Configure a new bridge by passing flags or/and using interactive terminal prompts. You can avoid all the terminal prompts by passing all the necessary flags to configure your bridge.`, + Example: `# Interactive example +[1]: github +[2]: gitlab +[3]: jira +[4]: launchpad-preview + +target: 1 +name [default]: default + +Detected projects: +[1]: github.com/a-hilaly/git-bug +[2]: github.com/MichaelMure/git-bug + +[0]: Another project + +Select option: 1 + +[1]: user provided token +[2]: interactive token creation +Select option: 1 + +You can generate a new token by visiting https://github.com/settings/tokens. +Choose 'Generate new token' and set the necessary access scope for your repository. + +The access scope depend on the type of repository. +Public: + - 'public_repo': to be able to read public repositories +Private: + - 'repo' : to be able to read private repositories + +Enter token: 87cf5c03b64029f18ea5f9ca5679daa08ccbd700 +Successfully configured bridge: default + +# For GitHub +git bug bridge configure \ + --name=default \ + --target=github \ + --owner=$(OWNER) \ + --project=$(PROJECT) \ + --token=$(TOKEN) + +# For Launchpad +git bug bridge configure \ + --name=default \ + --target=launchpad-preview \ + --url=https://bugs.launchpad.net/ubuntu/ -func runBridgeConfigure(cmd *cobra.Command, args []string) error { - backend, err := cache.NewRepoCache(repo) +# For Gitlab +git bug bridge configure \ + --name=default \ + --target=github \ + --url=https://github.com/michaelmure/git-bug \ + --token=$(TOKEN)`, + PreRunE: loadRepo(env), + RunE: func(cmd *cobra.Command, args []string) error { + return runBridgeConfigure(env, options) + }, + } + + flags := cmd.Flags() + flags.SortFlags = false + + flags.StringVarP(&options.name, "name", "n", "", "A distinctive name to identify the bridge") + flags.StringVarP(&options.target, "target", "t", "", + fmt.Sprintf("The target of the bridge. Valid values are [%s]", strings.Join(bridge.Targets(), ","))) + flags.StringVarP(&options.params.URL, "url", "u", "", "The URL of the remote repository") + flags.StringVarP(&options.params.BaseURL, "base-url", "b", "", "The base URL of your remote issue tracker") + flags.StringVarP(&options.params.Login, "login", "l", "", "The login on your remote issue tracker") + flags.StringVarP(&options.params.CredPrefix, "credential", "c", "", "The identifier or prefix of an already known credential for your remote issue tracker (see \"git-bug bridge auth\")") + flags.StringVar(&options.token, "token", "", "A raw authentication token for the remote issue tracker") + flags.BoolVar(&options.tokenStdin, "token-stdin", false, "Will read the token from stdin and ignore --token") + flags.StringVarP(&options.params.Owner, "owner", "o", "", "The owner of the remote repository") + flags.StringVarP(&options.params.Project, "project", "p", "", "The name of the remote repository") + + return cmd +} + +func runBridgeConfigure(env *Env, opts bridgeConfigureOptions) error { + backend, err := cache.NewRepoCache(env.repo) if err != nil { return err } defer backend.Close() interrupt.RegisterCleaner(backend.Close) - if (bridgeConfigureTokenStdin || bridgeConfigureToken != "" || bridgeConfigureParams.CredPrefix != "") && - (bridgeConfigureName == "" || bridgeConfigureTarget == "") { + if (opts.tokenStdin || opts.token != "" || opts.params.CredPrefix != "") && + (opts.name == "" || opts.target == "") { return fmt.Errorf("you must provide a bridge name and target to configure a bridge with a credential") } // early fail - if bridgeConfigureParams.CredPrefix != "" { - if _, err := auth.LoadWithPrefix(repo, bridgeConfigureParams.CredPrefix); err != nil { + if opts.params.CredPrefix != "" { + if _, err := auth.LoadWithPrefix(env.repo, opts.params.CredPrefix); err != nil { return err } } switch { - case bridgeConfigureTokenStdin: + case opts.tokenStdin: reader := bufio.NewReader(os.Stdin) token, err := reader.ReadString('\n') if err != nil { return fmt.Errorf("reading from stdin: %v", err) } - bridgeConfigureParams.TokenRaw = strings.TrimSpace(token) - case bridgeConfigureToken != "": - bridgeConfigureParams.TokenRaw = bridgeConfigureToken + opts.params.TokenRaw = strings.TrimSpace(token) + case opts.token != "": + opts.params.TokenRaw = opts.token } - if bridgeConfigureTarget == "" { - bridgeConfigureTarget, err = promptTarget() + if opts.target == "" { + opts.target, err = promptTarget() if err != nil { return err } } - if bridgeConfigureName == "" { - bridgeConfigureName, err = promptName(repo) + if opts.name == "" { + opts.name, err = promptName(env.repo) if err != nil { return err } } - b, err := bridge.NewBridge(backend, bridgeConfigureTarget, bridgeConfigureName) + b, err := bridge.NewBridge(backend, opts.target, opts.name) if err != nil { return err } - err = b.Configure(bridgeConfigureParams) + err = b.Configure(opts.params) if err != nil { return err } - fmt.Printf("Successfully configured bridge: %s\n", bridgeConfigureName) + env.out.Printf("Successfully configured bridge: %s\n", opts.name) return nil } func promptTarget() (string, error) { + // TODO: use the reusable prompt from the input package targets := bridge.Targets() for { @@ -117,6 +199,9 @@ func promptTarget() (string, error) { } func promptName(repo repository.RepoConfig) (string, error) { + // TODO: use the reusable prompt from the input package + const defaultName = "default" + defaultExist := core.BridgeExist(repo, defaultName) for { @@ -149,80 +234,3 @@ func promptName(repo repository.RepoConfig) (string, error) { fmt.Println("a bridge with the same name already exist") } } - -var bridgeConfigureCmd = &cobra.Command{ - Use: "configure", - Short: "Configure a new bridge.", - Long: ` Configure a new bridge by passing flags or/and using interactive terminal prompts. You can avoid all the terminal prompts by passing all the necessary flags to configure your bridge.`, - Example: `# Interactive example -[1]: github -[2]: gitlab -[3]: jira -[4]: launchpad-preview - -target: 1 -name [default]: default - -Detected projects: -[1]: github.com/a-hilaly/git-bug -[2]: github.com/MichaelMure/git-bug - -[0]: Another project - -Select option: 1 - -[1]: user provided token -[2]: interactive token creation -Select option: 1 - -You can generate a new token by visiting https://github.com/settings/tokens. -Choose 'Generate new token' and set the necessary access scope for your repository. - -The access scope depend on the type of repository. -Public: - - 'public_repo': to be able to read public repositories -Private: - - 'repo' : to be able to read private repositories - -Enter token: 87cf5c03b64029f18ea5f9ca5679daa08ccbd700 -Successfully configured bridge: default - -# For GitHub -git bug bridge configure \ - --name=default \ - --target=github \ - --owner=$(OWNER) \ - --project=$(PROJECT) \ - --token=$(TOKEN) - -# For Launchpad -git bug bridge configure \ - --name=default \ - --target=launchpad-preview \ - --url=https://bugs.launchpad.net/ubuntu/ - -# For Gitlab -git bug bridge configure \ - --name=default \ - --target=github \ - --url=https://github.com/michaelmure/git-bug \ - --token=$(TOKEN)`, - PreRunE: loadRepo, - RunE: runBridgeConfigure, -} - -func init() { - bridgeCmd.AddCommand(bridgeConfigureCmd) - bridgeConfigureCmd.Flags().StringVarP(&bridgeConfigureName, "name", "n", "", "A distinctive name to identify the bridge") - bridgeConfigureCmd.Flags().StringVarP(&bridgeConfigureTarget, "target", "t", "", - fmt.Sprintf("The target of the bridge. Valid values are [%s]", strings.Join(bridge.Targets(), ","))) - bridgeConfigureCmd.Flags().StringVarP(&bridgeConfigureParams.URL, "url", "u", "", "The URL of the remote repository") - bridgeConfigureCmd.Flags().StringVarP(&bridgeConfigureParams.BaseURL, "base-url", "b", "", "The base URL of your remote issue tracker") - bridgeConfigureCmd.Flags().StringVarP(&bridgeConfigureParams.Login, "login", "l", "", "The login on your remote issue tracker") - bridgeConfigureCmd.Flags().StringVarP(&bridgeConfigureParams.CredPrefix, "credential", "c", "", "The identifier or prefix of an already known credential for your remote issue tracker (see \"git-bug bridge auth\")") - bridgeConfigureCmd.Flags().StringVar(&bridgeConfigureToken, "token", "", "A raw authentication token for the remote issue tracker") - bridgeConfigureCmd.Flags().BoolVar(&bridgeConfigureTokenStdin, "token-stdin", false, "Will read the token from stdin and ignore --token") - bridgeConfigureCmd.Flags().StringVarP(&bridgeConfigureParams.Owner, "owner", "o", "", "The owner of the remote repository") - bridgeConfigureCmd.Flags().StringVarP(&bridgeConfigureParams.Project, "project", "p", "", "The name of the remote repository") - bridgeConfigureCmd.Flags().SortFlags = false -} diff --git a/commands/bridge_pull.go b/commands/bridge_pull.go index 2dd3d93e..dc8825fa 100644 --- a/commands/bridge_pull.go +++ b/commands/bridge_pull.go @@ -17,17 +17,40 @@ import ( "github.com/MichaelMure/git-bug/util/interrupt" ) -var ( - bridgePullImportSince string - bridgePullNoResume bool -) +type bridgePullOptions struct { + importSince string + noResume bool +} + +func newBridgePullCommand() *cobra.Command { + env := newEnv() + options := bridgePullOptions{} + + cmd := &cobra.Command{ + Use: "pull []", + Short: "Pull updates.", + PreRunE: loadRepo(env), + RunE: func(cmd *cobra.Command, args []string) error { + return runBridgePull(env, options, args) + }, + Args: cobra.MaximumNArgs(1), + } + + flags := cmd.Flags() + flags.SortFlags = false + + flags.BoolVarP(&options.noResume, "no-resume", "n", false, "force importing all bugs") + flags.StringVarP(&options.importSince, "since", "s", "", "import only bugs updated after the given date (ex: \"200h\" or \"june 2 2019\")") -func runBridgePull(cmd *cobra.Command, args []string) error { - if bridgePullNoResume && bridgePullImportSince != "" { + return cmd +} + +func runBridgePull(env *Env, opts bridgePullOptions, args []string) error { + if opts.noResume && opts.importSince != "" { return fmt.Errorf("only one of --no-resume and --since flags should be used") } - backend, err := cache.NewRepoCache(repo) + backend, err := cache.NewRepoCache(env.repo) if err != nil { return err } @@ -58,14 +81,14 @@ func runBridgePull(cmd *cobra.Command, args []string) error { interrupt.RegisterCleaner(func() error { mu.Lock() if interruptCount > 0 { - fmt.Println("Received another interrupt before graceful stop, terminating...") + env.err.Println("Received another interrupt before graceful stop, terminating...") os.Exit(0) } interruptCount++ mu.Unlock() - fmt.Println("Received interrupt signal, stopping the import...\n(Hit ctrl-c again to kill the process.)") + env.err.Println("Received interrupt signal, stopping the import...\n(Hit ctrl-c again to kill the process.)") // send signal to stop the importer cancel() @@ -77,10 +100,10 @@ func runBridgePull(cmd *cobra.Command, args []string) error { var events <-chan core.ImportResult switch { - case bridgePullNoResume: + case opts.noResume: events, err = b.ImportAllSince(ctx, time.Time{}) - case bridgePullImportSince != "": - since, err2 := parseSince(bridgePullImportSince) + case opts.importSince != "": + since, err2 := parseSince(opts.importSince) if err2 != nil { return errors.Wrap(err2, "import time parsing") } @@ -102,23 +125,23 @@ func runBridgePull(cmd *cobra.Command, args []string) error { case core.ImportEventBug: importedIssues++ - fmt.Println(result.String()) + env.out.Println(result.String()) case core.ImportEventIdentity: importedIdentities++ - fmt.Println(result.String()) + env.out.Println(result.String()) case core.ImportEventError: if result.Err != context.Canceled { - fmt.Println(result.String()) + env.out.Println(result.String()) } default: - fmt.Println(result.String()) + env.out.Println(result.String()) } } - fmt.Printf("imported %d issues and %d identities with %s bridge\n", importedIssues, importedIdentities, b.Name) + env.out.Printf("imported %d issues and %d identities with %s bridge\n", importedIssues, importedIdentities, b.Name) // send done signal close(done) @@ -134,17 +157,3 @@ func parseSince(since string) (time.Time, error) { return dateparse.ParseLocal(since) } - -var bridgePullCmd = &cobra.Command{ - Use: "pull []", - Short: "Pull updates.", - PreRunE: loadRepo, - RunE: runBridgePull, - Args: cobra.MaximumNArgs(1), -} - -func init() { - bridgeCmd.AddCommand(bridgePullCmd) - bridgePullCmd.Flags().BoolVarP(&bridgePullNoResume, "no-resume", "n", false, "force importing all bugs") - bridgePullCmd.Flags().StringVarP(&bridgePullImportSince, "since", "s", "", "import only bugs updated after the given date (ex: \"200h\" or \"june 2 2019\")") -} diff --git a/commands/bridge_push.go b/commands/bridge_push.go index 52d23a97..73df87fb 100644 --- a/commands/bridge_push.go +++ b/commands/bridge_push.go @@ -2,7 +2,6 @@ package commands import ( "context" - "fmt" "os" "sync" "time" @@ -15,8 +14,24 @@ import ( "github.com/MichaelMure/git-bug/util/interrupt" ) -func runBridgePush(cmd *cobra.Command, args []string) error { - backend, err := cache.NewRepoCache(repo) +func newBridgePushCommand() *cobra.Command { + env := newEnv() + + cmd := &cobra.Command{ + Use: "push []", + Short: "Push updates.", + PreRunE: loadRepoEnsureUser(env), + RunE: func(cmd *cobra.Command, args []string) error { + return runBridgePush(env, args) + }, + Args: cobra.MaximumNArgs(1), + } + + return cmd +} + +func runBridgePush(env *Env, args []string) error { + backend, err := cache.NewRepoCache(env.repo) if err != nil { return err } @@ -46,14 +61,14 @@ func runBridgePush(cmd *cobra.Command, args []string) error { interrupt.RegisterCleaner(func() error { mu.Lock() if interruptCount > 0 { - fmt.Println("Received another interrupt before graceful stop, terminating...") + env.err.Println("Received another interrupt before graceful stop, terminating...") os.Exit(0) } interruptCount++ mu.Unlock() - fmt.Println("Received interrupt signal, stopping the import...\n(Hit ctrl-c again to kill the process.)") + env.err.Println("Received interrupt signal, stopping the import...\n(Hit ctrl-c again to kill the process.)") // send signal to stop the importer cancel() @@ -71,7 +86,7 @@ func runBridgePush(cmd *cobra.Command, args []string) error { exportedIssues := 0 for result := range events { if result.Event != core.ExportEventNothing { - fmt.Println(result.String()) + env.out.Println(result.String()) } switch result.Event { @@ -80,21 +95,9 @@ func runBridgePush(cmd *cobra.Command, args []string) error { } } - fmt.Printf("exported %d issues with %s bridge\n", exportedIssues, b.Name) + env.out.Printf("exported %d issues with %s bridge\n", exportedIssues, b.Name) // send done signal close(done) return nil } - -var bridgePushCmd = &cobra.Command{ - Use: "push []", - Short: "Push updates.", - PreRunE: loadRepoEnsureUser, - RunE: runBridgePush, - Args: cobra.MaximumNArgs(1), -} - -func init() { - bridgeCmd.AddCommand(bridgePushCmd) -} diff --git a/commands/bridge_rm.go b/commands/bridge_rm.go index 76ad5949..4ff963db 100644 --- a/commands/bridge_rm.go +++ b/commands/bridge_rm.go @@ -1,8 +1,6 @@ package commands import ( - "fmt" - "github.com/spf13/cobra" "github.com/MichaelMure/git-bug/bridge" @@ -10,8 +8,24 @@ import ( "github.com/MichaelMure/git-bug/util/interrupt" ) -func runBridgeRm(cmd *cobra.Command, args []string) error { - backend, err := cache.NewRepoCache(repo) +func newBridgeRm() *cobra.Command { + env := newEnv() + + cmd := &cobra.Command{ + Use: "rm ", + Short: "Delete a configured bridge.", + PreRunE: loadRepo(env), + RunE: func(cmd *cobra.Command, args []string) error { + return runBridgeRm(env, args) + }, + Args: cobra.ExactArgs(1), + } + + return cmd +} + +func runBridgeRm(env *Env, args []string) error { + backend, err := cache.NewRepoCache(env.repo) if err != nil { return err } @@ -23,18 +37,6 @@ func runBridgeRm(cmd *cobra.Command, args []string) error { return err } - fmt.Printf("Successfully removed bridge configuration %v\n", args[0]) + env.out.Printf("Successfully removed bridge configuration %v\n", args[0]) return nil } - -var bridgeRmCmd = &cobra.Command{ - Use: "rm ", - Short: "Delete a configured bridge.", - PreRunE: loadRepo, - RunE: runBridgeRm, - Args: cobra.ExactArgs(1), -} - -func init() { - bridgeCmd.AddCommand(bridgeRmCmd) -} diff --git a/commands/commands.go b/commands/commands.go index bd8cb14d..103d5412 100644 --- a/commands/commands.go +++ b/commands/commands.go @@ -1,27 +1,42 @@ package commands import ( - "fmt" "sort" "github.com/spf13/cobra" ) -var ( - commandsDesc bool -) +type commandOptions struct { + desc bool +} -type commandSorterByName []*cobra.Command +func newCommandsCommand() *cobra.Command { + env := newEnv() + options := commandOptions{} -func (c commandSorterByName) Len() int { return len(c) } -func (c commandSorterByName) Swap(i, j int) { c[i], c[j] = c[j], c[i] } -func (c commandSorterByName) Less(i, j int) bool { return c[i].CommandPath() < c[j].CommandPath() } + cmd := &cobra.Command{ + Use: "commands [