diff --git a/commands/add.go b/commands/add.go index e656a26256a65e89b892481dd3196a92e09263a3..17fbbc935e0dc6c824e7855d0f33d2a5bf9dbcc4 100644 --- a/commands/add.go +++ b/commands/add.go @@ -1,42 +1,58 @@ 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 runAddBug(cmd *cobra.Command, args []string) error { - var err error +func newAddCommand() *cobra.Command { + env := newEnv() + options := addOptions{} - backend, err := cache.NewRepoCache(repo) - if err != nil { - return err + cmd := &cobra.Command{ + Use: "add", + Short: "Create a new bug.", + PreRunE: loadBackendEnsureUser(env), + PostRunE: closeBackend(env), + RunE: func(cmd *cobra.Command, args []string) error { + return runAdd(env, options) + }, } - defer backend.Close() - interrupt.RegisterCleaner(backend.Close) - if addMessageFile != "" && addMessage == "" { - addTitle, addMessage, err = input.BugCreateFileInput(addMessageFile) + flags := cmd.Flags() + flags.SortFlags = false + + 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") + + return cmd +} + +func runAdd(env *Env, opts addOptions) error { + var err error + 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(env.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 +60,12 @@ func runAddBug(cmd *cobra.Command, args []string) error { } } - b, _, err := backend.NewBug(addTitle, addMessage) + b, _, err := env.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 3c398e6b0bae08704d2f72637a53ec12b61bec5d..46b3d9ecdcaaecb6fc3ccaa23f408f4c7c555e8b 100644 --- a/commands/bridge.go +++ b/commands/bridge.go @@ -1,43 +1,43 @@ package commands import ( - "fmt" - "github.com/spf13/cobra" "github.com/MichaelMure/git-bug/bridge" - "github.com/MichaelMure/git-bug/cache" - "github.com/MichaelMure/git-bug/util/interrupt" ) -func runBridge(cmd *cobra.Command, args []string) error { - backend, err := cache.NewRepoCache(repo) - if err != nil { - return err +func newBridgeCommand() *cobra.Command { + env := newEnv() + + cmd := &cobra.Command{ + Use: "bridge", + Short: "Configure and use bridges to other bug trackers.", + PreRunE: loadBackend(env), + PostRunE: closeBackend(env), + RunE: func(cmd *cobra.Command, args []string) error { + return runBridge(env) + }, + Args: cobra.NoArgs, } - defer backend.Close() - interrupt.RegisterCleaner(backend.Close) - configured, err := bridge.ConfiguredBridges(backend) + cmd.AddCommand(newBridgeAuthCommand()) + cmd.AddCommand(newBridgeConfigureCommand()) + cmd.AddCommand(newBridgePullCommand()) + cmd.AddCommand(newBridgePushCommand()) + cmd.AddCommand(newBridgeRm()) + + return cmd +} + +func runBridge(env *Env) error { + configured, err := bridge.ConfiguredBridges(env.backend) if err != nil { return err } 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 3a0e0c294d0e7770756eeecae9deb316d81b09cf..db4c5212cce5a7e46c95b3bf24e1b9a388b3025a 100644 --- a/commands/bridge_auth.go +++ b/commands/bridge_auth.go @@ -1,7 +1,6 @@ package commands import ( - "fmt" "sort" "strings" @@ -10,20 +9,32 @@ import ( text "github.com/MichaelMure/go-term-text" "github.com/MichaelMure/git-bug/bridge/core/auth" - "github.com/MichaelMure/git-bug/cache" "github.com/MichaelMure/git-bug/util/colors" - "github.com/MichaelMure/git-bug/util/interrupt" ) -func runBridgeAuth(cmd *cobra.Command, args []string) error { - backend, err := cache.NewRepoCache(repo) - if err != nil { - return err +func newBridgeAuthCommand() *cobra.Command { + env := newEnv() + + cmd := &cobra.Command{ + Use: "auth", + Short: "List all known bridge authentication credentials.", + PreRunE: loadBackend(env), + PostRunE: closeBackend(env), + RunE: func(cmd *cobra.Command, args []string) error { + return runBridgeAuth(env) + }, + Args: cobra.NoArgs, } - defer backend.Close() - interrupt.RegisterCleaner(backend.Close) - creds, err := auth.List(backend) + cmd.AddCommand(newBridgeAuthAddTokenCommand()) + cmd.AddCommand(newBridgeAuthRm()) + cmd.AddCommand(newBridgeAuthShow()) + + return cmd +} + +func runBridgeAuth(env *Env) error { + creds, err := auth.List(env.backend) if err != nil { return err } @@ -44,7 +55,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 +66,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 8eda28acd58e3c3a50383aa5e2ae80fcf97b7bb3..55edd919b0243c3e86e41119335cbc98723b03b7 100644 --- a/commands/bridge_auth_addtoken.go +++ b/commands/bridge_auth_addtoken.go @@ -14,37 +14,57 @@ import ( "github.com/MichaelMure/git-bug/bridge/core" "github.com/MichaelMure/git-bug/bridge/core/auth" "github.com/MichaelMure/git-bug/cache" - "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: loadBackendEnsureUser(env), + PostRunE: closeBackend(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") + + return cmd +} -func runBridgeTokenAdd(cmd *cobra.Command, args []string) error { +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) - 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 +75,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') @@ -66,63 +86,45 @@ func runBridgeTokenAdd(cmd *cobra.Command, args []string) error { } var user *cache.IdentityCache + var err error - if bridgeAuthAddTokenUser == "" { - user, err = backend.GetUserIdentity() + if opts.user == "" { + user, err = env.backend.GetUserIdentity() } else { - user, err = backend.ResolveIdentityPrefix(bridgeAuthAddTokenUser) + user, err = env.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 17e70625f1b52591ddab1c48108fb60f8c9a7d88..b2a44e92d063a92a4da6b1e918cf0d6db7c733b3 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 fbbf60a72b5e30b095f73c4779020fc2e177f3af..408a6cf172554267ad11ea8f6ef6ede1871bbec6 100644 --- a/commands/bridge_auth_show.go +++ b/commands/bridge_auth_show.go @@ -9,34 +9,42 @@ import ( "github.com/spf13/cobra" "github.com/MichaelMure/git-bug/bridge/core/auth" - "github.com/MichaelMure/git-bug/cache" - "github.com/MichaelMure/git-bug/util/interrupt" ) -func runBridgeAuthShow(cmd *cobra.Command, args []string) error { - backend, err := cache.NewRepoCache(repo) - if err != nil { - return err +func newBridgeAuthShow() *cobra.Command { + env := newEnv() + + cmd := &cobra.Command{ + Use: "show", + Short: "Display an authentication credential.", + PreRunE: loadBackend(env), + PostRunE: closeBackend(env), + RunE: func(cmd *cobra.Command, args []string) error { + return runBridgeAuthShow(env, args) + }, + Args: cobra.ExactArgs(1), } - defer backend.Close() - interrupt.RegisterCleaner(backend.Close) - cred, err := auth.LoadWithPrefix(repo, args[0]) + return cmd +} + +func runBridgeAuthShow(env *Env, args []string) error { + 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 +52,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 8955363383e146a0a4c214ac365be0c859af3668..ecdb6502cd3ee8fa90c739d88033a9c6edd67ec5 100644 --- a/commands/bridge_configure.go +++ b/commands/bridge_configure.go @@ -12,84 +12,160 @@ import ( "github.com/MichaelMure/git-bug/bridge" "github.com/MichaelMure/git-bug/bridge/core" "github.com/MichaelMure/git-bug/bridge/core/auth" - "github.com/MichaelMure/git-bug/cache" "github.com/MichaelMure/git-bug/repository" - "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{} -func runBridgeConfigure(cmd *cobra.Command, args []string) error { - backend, err := cache.NewRepoCache(repo) - if err != nil { - return err + 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/ + +# For Gitlab +git bug bridge configure \ + --name=default \ + --target=github \ + --url=https://github.com/michaelmure/git-bug \ + --token=$(TOKEN)`, + PreRunE: loadBackend(env), + PostRunE: closeBackend(env), + RunE: func(cmd *cobra.Command, args []string) error { + return runBridgeConfigure(env, options) + }, } - defer backend.Close() - interrupt.RegisterCleaner(backend.Close) - if (bridgeConfigureTokenStdin || bridgeConfigureToken != "" || bridgeConfigureParams.CredPrefix != "") && - (bridgeConfigureName == "" || bridgeConfigureTarget == "") { + 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 { + var err error + + 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(env.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 +193,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 +228,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 2dd3d93e716a587b16dba34de5de7a8102ddc5b6..bb7055824aae77af0d382fa2a7cf6b7ea646eb95 100644 --- a/commands/bridge_pull.go +++ b/commands/bridge_pull.go @@ -13,33 +13,50 @@ import ( "github.com/MichaelMure/git-bug/bridge" "github.com/MichaelMure/git-bug/bridge/core" - "github.com/MichaelMure/git-bug/cache" "github.com/MichaelMure/git-bug/util/interrupt" ) -var ( - bridgePullImportSince string - bridgePullNoResume bool -) +type bridgePullOptions struct { + importSince string + noResume bool +} -func runBridgePull(cmd *cobra.Command, args []string) error { - if bridgePullNoResume && bridgePullImportSince != "" { - return fmt.Errorf("only one of --no-resume and --since flags should be used") +func newBridgePullCommand() *cobra.Command { + env := newEnv() + options := bridgePullOptions{} + + cmd := &cobra.Command{ + Use: "pull []", + Short: "Pull updates.", + PreRunE: loadBackend(env), + PostRunE: closeBackend(env), + RunE: func(cmd *cobra.Command, args []string) error { + return runBridgePull(env, options, args) + }, + Args: cobra.MaximumNArgs(1), } - backend, err := cache.NewRepoCache(repo) - if err != nil { - return err + 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\")") + + 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") } - defer backend.Close() - interrupt.RegisterCleaner(backend.Close) var b *core.Bridge + var err error if len(args) == 0 { - b, err = bridge.DefaultBridge(backend) + b, err = bridge.DefaultBridge(env.backend) } else { - b, err = bridge.LoadBridge(backend, args[0]) + b, err = bridge.LoadBridge(env.backend, args[0]) } if err != nil { @@ -58,14 +75,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 +94,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 +119,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 +151,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 52d23a974218a20ff6e7bdf77461b31f165cc841..7061a5ca2c91db389a99f526ffa90cb8e2fb03dd 100644 --- a/commands/bridge_push.go +++ b/commands/bridge_push.go @@ -2,7 +2,6 @@ package commands import ( "context" - "fmt" "os" "sync" "time" @@ -11,24 +10,34 @@ import ( "github.com/MichaelMure/git-bug/bridge" "github.com/MichaelMure/git-bug/bridge/core" - "github.com/MichaelMure/git-bug/cache" "github.com/MichaelMure/git-bug/util/interrupt" ) -func runBridgePush(cmd *cobra.Command, args []string) error { - backend, err := cache.NewRepoCache(repo) - if err != nil { - return err +func newBridgePushCommand() *cobra.Command { + env := newEnv() + + cmd := &cobra.Command{ + Use: "push []", + Short: "Push updates.", + PreRunE: loadBackendEnsureUser(env), + PostRunE: closeBackend(env), + RunE: func(cmd *cobra.Command, args []string) error { + return runBridgePush(env, args) + }, + Args: cobra.MaximumNArgs(1), } - defer backend.Close() - interrupt.RegisterCleaner(backend.Close) + return cmd +} + +func runBridgePush(env *Env, args []string) error { var b *core.Bridge + var err error if len(args) == 0 { - b, err = bridge.DefaultBridge(backend) + b, err = bridge.DefaultBridge(env.backend) } else { - b, err = bridge.LoadBridge(backend, args[0]) + b, err = bridge.LoadBridge(env.backend, args[0]) } if err != nil { @@ -46,14 +55,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 +80,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 +89,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 76ad59498e6e901fad023a38cec13520c2af7d99..9f93c37a0aece5cf20e8ab83c33b2c2dcbc111ab 100644 --- a/commands/bridge_rm.go +++ b/commands/bridge_rm.go @@ -1,40 +1,34 @@ package commands import ( - "fmt" - "github.com/spf13/cobra" "github.com/MichaelMure/git-bug/bridge" - "github.com/MichaelMure/git-bug/cache" - "github.com/MichaelMure/git-bug/util/interrupt" ) -func runBridgeRm(cmd *cobra.Command, args []string) error { - backend, err := cache.NewRepoCache(repo) - if err != nil { - return err +func newBridgeRm() *cobra.Command { + env := newEnv() + + cmd := &cobra.Command{ + Use: "rm ", + Short: "Delete a configured bridge.", + PreRunE: loadBackend(env), + PostRunE: closeBackend(env), + RunE: func(cmd *cobra.Command, args []string) error { + return runBridgeRm(env, args) + }, + Args: cobra.ExactArgs(1), } - defer backend.Close() - interrupt.RegisterCleaner(backend.Close) - err = bridge.RemoveBridge(backend, args[0]) + return cmd +} + +func runBridgeRm(env *Env, args []string) error { + err := bridge.RemoveBridge(env.backend, args[0]) if err != nil { 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 bd8cb14d38f8d0e301da7d9d9460a8656bf4bbec..103d5412b8aa515ae098daa04f59f6637320c756 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 [