diff --git a/cache/repo_cache_identity.go b/cache/repo_cache_identity.go index eb30687cd583ed4dc6f47d0a6fd3a7a09a867a4f..4f6122809c76da6254e520ca9955324f1f76b1b7 100644 --- a/cache/repo_cache_identity.go +++ b/cache/repo_cache_identity.go @@ -147,7 +147,7 @@ func (c *RepoCache) ResolveIdentityPrefix(prefix string) (*IdentityCache, error) } // ResolveIdentityImmutableMetadata retrieve an Identity that has the exact given metadata on -// one of it's version. If multiple version have the same key, the first defined take precedence. +// one of its version. If multiple version have the same key, the first defined take precedence. func (c *RepoCache) ResolveIdentityImmutableMetadata(key string, value string) (*IdentityCache, error) { return c.ResolveIdentityMatcher(func(excerpt *IdentityExcerpt) bool { return excerpt.ImmutableMetadata[key] == value diff --git a/commands/add_test.go b/commands/add_test.go deleted file mode 100644 index 077995a6df5f5f9c2f8de37c4830fb6743671e89..0000000000000000000000000000000000000000 --- a/commands/add_test.go +++ /dev/null @@ -1,32 +0,0 @@ -package commands - -import ( - "strings" - "testing" - - "github.com/stretchr/testify/require" -) - -func newTestEnvAndBug(t *testing.T) (*testEnv, string) { - t.Helper() - - testEnv, _ := newTestEnvAndUser(t) - opts := addOptions{ - title: "this is a bug title", - message: "this is a bug message", - messageFile: "", - nonInteractive: true, - } - - require.NoError(t, runAdd(testEnv.env, opts)) - require.Regexp(t, "^[0-9A-Fa-f]{7} created\n$", testEnv.out) - bugID := strings.Split(testEnv.out.String(), " ")[0] - testEnv.out.Reset() - - return testEnv, bugID -} - -func TestAdd(t *testing.T) { - _, bugID := newTestEnvAndBug(t) - require.Regexp(t, "^[0-9A-Fa-f]{7}$", bugID) -} diff --git a/commands/bridge.go b/commands/bridge.go deleted file mode 100644 index 8ce35aa364cf9eaf77174668c55b10b146371bb2..0000000000000000000000000000000000000000 --- a/commands/bridge.go +++ /dev/null @@ -1,42 +0,0 @@ -package commands - -import ( - "github.com/spf13/cobra" - - "github.com/MichaelMure/git-bug/bridge" -) - -func newBridgeCommand() *cobra.Command { - env := newEnv() - - cmd := &cobra.Command{ - Use: "bridge", - Short: "Configure and use bridges to other bug trackers.", - PreRunE: loadBackend(env), - RunE: closeBackend(env, 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 { - configured, err := bridge.ConfiguredBridges(env.backend) - if err != nil { - return err - } - - for _, c := range configured { - env.out.Println(c) - } - - return nil -} diff --git a/commands/bridge/bridge.go b/commands/bridge/bridge.go new file mode 100644 index 0000000000000000000000000000000000000000..980a38e27647b6122f785c0ca56f5bda610e8b54 --- /dev/null +++ b/commands/bridge/bridge.go @@ -0,0 +1,43 @@ +package bridgecmd + +import ( + "github.com/spf13/cobra" + + "github.com/MichaelMure/git-bug/bridge" + "github.com/MichaelMure/git-bug/commands/execenv" +) + +func NewBridgeCommand() *cobra.Command { + env := execenv.NewEnv() + + cmd := &cobra.Command{ + Use: "bridge", + Short: "List bridges to other bug trackers", + PreRunE: execenv.LoadBackend(env), + RunE: execenv.CloseBackend(env, func(cmd *cobra.Command, args []string) error { + return runBridge(env) + }), + Args: cobra.NoArgs, + } + + cmd.AddCommand(newBridgeAuthCommand()) + cmd.AddCommand(newBridgeNewCommand()) + cmd.AddCommand(newBridgePullCommand()) + cmd.AddCommand(newBridgePushCommand()) + cmd.AddCommand(newBridgeRm()) + + return cmd +} + +func runBridge(env *execenv.Env) error { + configured, err := bridge.ConfiguredBridges(env.Backend) + if err != nil { + return err + } + + for _, c := range configured { + env.Out.Println(c) + } + + return nil +} diff --git a/commands/bridge_auth.go b/commands/bridge/bridge_auth.go similarity index 72% rename from commands/bridge_auth.go rename to commands/bridge/bridge_auth.go index 50306b8da70ee0d2e815c791d8590b9b2adcdb7a..52e063e67c5349c2721b52cb9f58c066b9e428d8 100644 --- a/commands/bridge_auth.go +++ b/commands/bridge/bridge_auth.go @@ -1,25 +1,25 @@ -package commands +package bridgecmd import ( "sort" "strings" - "github.com/spf13/cobra" - text "github.com/MichaelMure/go-term-text" + "github.com/spf13/cobra" "github.com/MichaelMure/git-bug/bridge/core/auth" + "github.com/MichaelMure/git-bug/commands/execenv" "github.com/MichaelMure/git-bug/util/colors" ) func newBridgeAuthCommand() *cobra.Command { - env := newEnv() + env := execenv.NewEnv() cmd := &cobra.Command{ Use: "auth", - Short: "List all known bridge authentication credentials.", - PreRunE: loadBackend(env), - RunE: closeBackend(env, func(cmd *cobra.Command, args []string) error { + Short: "List all known bridge authentication credentials", + PreRunE: execenv.LoadBackend(env), + RunE: execenv.CloseBackend(env, func(cmd *cobra.Command, args []string) error { return runBridgeAuth(env) }), Args: cobra.NoArgs, @@ -32,8 +32,8 @@ func newBridgeAuthCommand() *cobra.Command { return cmd } -func runBridgeAuth(env *Env) error { - creds, err := auth.List(env.backend) +func runBridgeAuth(env *execenv.Env) error { + creds, err := auth.List(env.Backend) if err != nil { return err } @@ -54,7 +54,7 @@ func runBridgeAuth(env *Env) error { sort.Strings(meta) metaFmt := strings.Join(meta, ",") - env.out.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()), diff --git a/commands/bridge_auth_addtoken.go b/commands/bridge/bridge_auth_addtoken.go similarity index 79% rename from commands/bridge_auth_addtoken.go rename to commands/bridge/bridge_auth_addtoken.go index dfdc66b69d35b43947c7cc14bea0217f21a3f154..bcab7fc3b8fe24754708f438a5efcd1cd2a9817c 100644 --- a/commands/bridge_auth_addtoken.go +++ b/commands/bridge/bridge_auth_addtoken.go @@ -1,4 +1,4 @@ -package commands +package bridgecmd import ( "bufio" @@ -14,6 +14,8 @@ 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/commands/completion" + "github.com/MichaelMure/git-bug/commands/execenv" ) type bridgeAuthAddTokenOptions struct { @@ -23,14 +25,14 @@ type bridgeAuthAddTokenOptions struct { } func newBridgeAuthAddTokenCommand() *cobra.Command { - env := newEnv() + env := execenv.NewEnv() options := bridgeAuthAddTokenOptions{} cmd := &cobra.Command{ Use: "add-token [TOKEN]", Short: "Store a new token", - PreRunE: loadBackendEnsureUser(env), - RunE: closeBackend(env, func(cmd *cobra.Command, args []string) error { + PreRunE: execenv.LoadBackendEnsureUser(env), + RunE: execenv.CloseBackend(env, func(cmd *cobra.Command, args []string) error { return runBridgeAuthAddToken(env, options, args) }), Args: cobra.MaximumNArgs(1), @@ -41,17 +43,17 @@ func newBridgeAuthAddTokenCommand() *cobra.Command { flags.StringVarP(&options.target, "target", "t", "", fmt.Sprintf("The target of the bridge. Valid values are [%s]", strings.Join(bridge.Targets(), ","))) - cmd.RegisterFlagCompletionFunc("target", completeFrom(bridge.Targets())) + cmd.RegisterFlagCompletionFunc("target", completion.From(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") - cmd.RegisterFlagCompletionFunc("user", completeUser(env)) + cmd.RegisterFlagCompletionFunc("user", completion.User(env)) return cmd } -func runBridgeAuthAddToken(env *Env, opts bridgeAuthAddTokenOptions, args []string) error { +func runBridgeAuthAddToken(env *execenv.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 @@ -76,7 +78,7 @@ func runBridgeAuthAddToken(env *Env, opts bridgeAuthAddTokenOptions, args []stri } else { // Read from Stdin if isatty.IsTerminal(os.Stdin.Fd()) { - env.err.Println("Enter the token:") + env.Err.Println("Enter the token:") } reader := bufio.NewReader(os.Stdin) raw, err := reader.ReadString('\n') @@ -90,9 +92,9 @@ func runBridgeAuthAddToken(env *Env, opts bridgeAuthAddTokenOptions, args []stri var err error if opts.user == "" { - user, err = env.backend.GetUserIdentity() + user, err = env.Backend.GetUserIdentity() } else { - user, err = env.backend.ResolveIdentityPrefix(opts.user) + user, err = env.Backend.ResolveIdentityPrefix(opts.user) } if err != nil { return err @@ -121,11 +123,11 @@ func runBridgeAuthAddToken(env *Env, opts bridgeAuthAddTokenOptions, args []stri return errors.Wrap(err, "invalid token") } - err = auth.Store(env.repo, token) + err = auth.Store(env.Repo, token) if err != nil { return err } - env.out.Printf("token %s added\n", token.ID()) + env.Out.Printf("token %s added\n", token.ID()) return nil } diff --git a/commands/bridge/bridge_auth_rm.go b/commands/bridge/bridge_auth_rm.go new file mode 100644 index 0000000000000000000000000000000000000000..d58ca63e8603b082c869bfcbccd51a8bd1ab0e50 --- /dev/null +++ b/commands/bridge/bridge_auth_rm.go @@ -0,0 +1,41 @@ +package bridgecmd + +import ( + "github.com/spf13/cobra" + + "github.com/MichaelMure/git-bug/bridge/core/auth" + "github.com/MichaelMure/git-bug/commands/completion" + "github.com/MichaelMure/git-bug/commands/execenv" +) + +func newBridgeAuthRm() *cobra.Command { + env := execenv.NewEnv() + + cmd := &cobra.Command{ + Use: "rm BRIDGE_ID", + Short: "Remove a credential", + PreRunE: execenv.LoadRepo(env), + RunE: func(cmd *cobra.Command, args []string) error { + return runBridgeAuthRm(env, args) + }, + Args: cobra.ExactArgs(1), + ValidArgsFunction: completion.BridgeAuth(env), + } + + return cmd +} + +func runBridgeAuthRm(env *execenv.Env, args []string) error { + cred, err := auth.LoadWithPrefix(env.Repo, args[0]) + if err != nil { + return err + } + + err = auth.Remove(env.Repo, cred.ID()) + if err != nil { + return err + } + + env.Out.Printf("credential %s removed\n", cred.ID()) + return nil +} diff --git a/commands/bridge/bridge_auth_show.go b/commands/bridge/bridge_auth_show.go new file mode 100644 index 0000000000000000000000000000000000000000..d373273d6e4790b8ccc2defa856cd4369e5f0c38 --- /dev/null +++ b/commands/bridge/bridge_auth_show.go @@ -0,0 +1,60 @@ +package bridgecmd + +import ( + "fmt" + "sort" + "strings" + "time" + + "github.com/spf13/cobra" + + "github.com/MichaelMure/git-bug/bridge/core/auth" + "github.com/MichaelMure/git-bug/commands/completion" + "github.com/MichaelMure/git-bug/commands/execenv" +) + +func newBridgeAuthShow() *cobra.Command { + env := execenv.NewEnv() + + cmd := &cobra.Command{ + Use: "show", + Short: "Display an authentication credential", + PreRunE: execenv.LoadBackend(env), + RunE: execenv.CloseBackend(env, func(cmd *cobra.Command, args []string) error { + return runBridgeAuthShow(env, args) + }), + Args: cobra.ExactArgs(1), + ValidArgsFunction: completion.BridgeAuth(env), + } + + return cmd +} + +func runBridgeAuthShow(env *execenv.Env, args []string) error { + cred, err := auth.LoadWithPrefix(env.Repo, args[0]) + if err != nil { + return err + } + + 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: + env.Out.Printf("Value: %s\n", cred.Value) + } + + env.Out.Println("Metadata:") + + meta := make([]string, 0, len(cred.Metadata())) + for key, value := range cred.Metadata() { + meta = append(meta, fmt.Sprintf(" %s --> %s\n", key, value)) + } + sort.Strings(meta) + + env.Out.Print(strings.Join(meta, "")) + + return nil +} diff --git a/commands/bridge_configure.go b/commands/bridge/bridge_new.go similarity index 85% rename from commands/bridge_configure.go rename to commands/bridge/bridge_new.go index d5b40dfdfc46e58dc9158307f3bf4fdddc26a246..4cfc903dee05aa9a216169bb71f46ccb90ae4fba 100644 --- a/commands/bridge_configure.go +++ b/commands/bridge/bridge_new.go @@ -1,4 +1,4 @@ -package commands +package bridgecmd import ( "bufio" @@ -12,10 +12,12 @@ 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/commands/completion" + "github.com/MichaelMure/git-bug/commands/execenv" "github.com/MichaelMure/git-bug/repository" ) -type bridgeConfigureOptions struct { +type bridgeNewOptions struct { name string target string params core.BridgeParams @@ -24,13 +26,13 @@ type bridgeConfigureOptions struct { nonInteractive bool } -func newBridgeConfigureCommand() *cobra.Command { - env := newEnv() - options := bridgeConfigureOptions{} +func newBridgeNewCommand() *cobra.Command { + env := execenv.NewEnv() + options := bridgeNewOptions{} cmd := &cobra.Command{ - Use: "configure", - Short: "Configure a new bridge.", + Use: "new", + 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 @@ -66,7 +68,7 @@ Enter token: 87cf5c03b64029f18ea5f9ca5679daa08ccbd700 Successfully configured bridge: default # For GitHub -git bug bridge configure \ +git bug bridge new \ --name=default \ --target=github \ --owner=$(OWNER) \ @@ -74,20 +76,20 @@ git bug bridge configure \ --token=$(TOKEN) # For Launchpad -git bug bridge configure \ +git bug bridge new \ --name=default \ --target=launchpad-preview \ --url=https://bugs.launchpad.net/ubuntu/ # For Gitlab -git bug bridge configure \ +git bug bridge new \ --name=default \ --target=github \ --url=https://github.com/michaelmure/git-bug \ --token=$(TOKEN)`, - PreRunE: loadBackend(env), - RunE: closeBackend(env, func(cmd *cobra.Command, args []string) error { - return runBridgeConfigure(env, options) + PreRunE: execenv.LoadBackend(env), + RunE: execenv.CloseBackend(env, func(cmd *cobra.Command, args []string) error { + return runBridgeNew(env, options) }), } @@ -97,7 +99,7 @@ git bug bridge configure \ 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(), ","))) - cmd.RegisterFlagCompletionFunc("target", completeFrom(bridge.Targets())) + cmd.RegisterFlagCompletionFunc("target", completion.From(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") @@ -111,7 +113,7 @@ git bug bridge configure \ return cmd } -func runBridgeConfigure(env *Env, opts bridgeConfigureOptions) error { +func runBridgeNew(env *execenv.Env, opts bridgeNewOptions) error { var err error if (opts.tokenStdin || opts.token != "" || opts.params.CredPrefix != "") && @@ -121,7 +123,7 @@ func runBridgeConfigure(env *Env, opts bridgeConfigureOptions) error { // early fail if opts.params.CredPrefix != "" { - if _, err := auth.LoadWithPrefix(env.repo, opts.params.CredPrefix); err != nil { + if _, err := auth.LoadWithPrefix(env.Repo, opts.params.CredPrefix); err != nil { return err } } @@ -146,13 +148,13 @@ func runBridgeConfigure(env *Env, opts bridgeConfigureOptions) error { } if !opts.nonInteractive && opts.name == "" { - opts.name, err = promptName(env.repo) + opts.name, err = promptName(env.Repo) if err != nil { return err } } - b, err := bridge.NewBridge(env.backend, opts.target, opts.name) + b, err := bridge.NewBridge(env.Backend, opts.target, opts.name) if err != nil { return err } @@ -162,7 +164,7 @@ func runBridgeConfigure(env *Env, opts bridgeConfigureOptions) error { return err } - env.out.Printf("Successfully configured bridge: %s\n", opts.name) + env.Out.Printf("Successfully configured bridge: %s\n", opts.name) return nil } diff --git a/commands/bridge_pull.go b/commands/bridge/bridge_pull.go similarity index 75% rename from commands/bridge_pull.go rename to commands/bridge/bridge_pull.go index 9370e0886bf1f963d76e506c0d32aba7580e1a0b..d1fc279a89199c24e91907ec0b58c3160dff9e36 100644 --- a/commands/bridge_pull.go +++ b/commands/bridge/bridge_pull.go @@ -1,4 +1,4 @@ -package commands +package bridgecmd import ( "context" @@ -13,6 +13,8 @@ import ( "github.com/MichaelMure/git-bug/bridge" "github.com/MichaelMure/git-bug/bridge/core" + "github.com/MichaelMure/git-bug/commands/completion" + "github.com/MichaelMure/git-bug/commands/execenv" "github.com/MichaelMure/git-bug/util/interrupt" ) @@ -22,18 +24,18 @@ type bridgePullOptions struct { } func newBridgePullCommand() *cobra.Command { - env := newEnv() + env := execenv.NewEnv() options := bridgePullOptions{} cmd := &cobra.Command{ Use: "pull [NAME]", - Short: "Pull updates.", - PreRunE: loadBackend(env), - RunE: closeBackend(env, func(cmd *cobra.Command, args []string) error { + Short: "Pull updates from a remote bug tracker", + PreRunE: execenv.LoadBackend(env), + RunE: execenv.CloseBackend(env, func(cmd *cobra.Command, args []string) error { return runBridgePull(env, options, args) }), Args: cobra.MaximumNArgs(1), - ValidArgsFunction: completeBridge(env), + ValidArgsFunction: completion.Bridge(env), } flags := cmd.Flags() @@ -45,7 +47,7 @@ func newBridgePullCommand() *cobra.Command { return cmd } -func runBridgePull(env *Env, opts bridgePullOptions, args []string) error { +func runBridgePull(env *execenv.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") } @@ -54,9 +56,9 @@ func runBridgePull(env *Env, opts bridgePullOptions, args []string) error { var err error if len(args) == 0 { - b, err = bridge.DefaultBridge(env.backend) + b, err = bridge.DefaultBridge(env.Backend) } else { - b, err = bridge.LoadBridge(env.backend, args[0]) + b, err = bridge.LoadBridge(env.Backend, args[0]) } if err != nil { @@ -75,14 +77,14 @@ func runBridgePull(env *Env, opts bridgePullOptions, args []string) error { interrupt.RegisterCleaner(func() error { mu.Lock() if interruptCount > 0 { - env.err.Println("Received another interrupt before graceful stop, terminating...") + env.Err.Println("Received another interrupt before graceful stop, terminating...") os.Exit(0) } interruptCount++ mu.Unlock() - env.err.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() @@ -119,23 +121,23 @@ func runBridgePull(env *Env, opts bridgePullOptions, args []string) error { case core.ImportEventBug: importedIssues++ - env.out.Println(result.String()) + env.Out.Println(result.String()) case core.ImportEventIdentity: importedIdentities++ - env.out.Println(result.String()) + env.Out.Println(result.String()) case core.ImportEventError: if result.Err != context.Canceled { - env.out.Println(result.String()) + env.Out.Println(result.String()) } default: - env.out.Println(result.String()) + env.Out.Println(result.String()) } } - env.out.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) diff --git a/commands/bridge_push.go b/commands/bridge/bridge_push.go similarity index 63% rename from commands/bridge_push.go rename to commands/bridge/bridge_push.go index ef1f2d3ed98d5cac3b012ff1e92085dfb23095e3..51baed4dfe8a6c13f922963b8d68122926388702 100644 --- a/commands/bridge_push.go +++ b/commands/bridge/bridge_push.go @@ -1,4 +1,4 @@ -package commands +package bridgecmd import ( "context" @@ -10,34 +10,36 @@ import ( "github.com/MichaelMure/git-bug/bridge" "github.com/MichaelMure/git-bug/bridge/core" + "github.com/MichaelMure/git-bug/commands/completion" + "github.com/MichaelMure/git-bug/commands/execenv" "github.com/MichaelMure/git-bug/util/interrupt" ) func newBridgePushCommand() *cobra.Command { - env := newEnv() + env := execenv.NewEnv() cmd := &cobra.Command{ Use: "push [NAME]", - Short: "Push updates.", - PreRunE: loadBackendEnsureUser(env), - RunE: closeBackend(env, func(cmd *cobra.Command, args []string) error { + Short: "Push updates to remote bug tracker", + PreRunE: execenv.LoadBackendEnsureUser(env), + RunE: execenv.CloseBackend(env, func(cmd *cobra.Command, args []string) error { return runBridgePush(env, args) }), Args: cobra.MaximumNArgs(1), - ValidArgsFunction: completeBridge(env), + ValidArgsFunction: completion.Bridge(env), } return cmd } -func runBridgePush(env *Env, args []string) error { +func runBridgePush(env *execenv.Env, args []string) error { var b *core.Bridge var err error if len(args) == 0 { - b, err = bridge.DefaultBridge(env.backend) + b, err = bridge.DefaultBridge(env.Backend) } else { - b, err = bridge.LoadBridge(env.backend, args[0]) + b, err = bridge.LoadBridge(env.Backend, args[0]) } if err != nil { @@ -55,14 +57,14 @@ func runBridgePush(env *Env, args []string) error { interrupt.RegisterCleaner(func() error { mu.Lock() if interruptCount > 0 { - env.err.Println("Received another interrupt before graceful stop, terminating...") + env.Err.Println("Received another interrupt before graceful stop, terminating...") os.Exit(0) } interruptCount++ mu.Unlock() - env.err.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() @@ -80,7 +82,7 @@ func runBridgePush(env *Env, args []string) error { exportedIssues := 0 for result := range events { if result.Event != core.ExportEventNothing { - env.out.Println(result.String()) + env.Out.Println(result.String()) } switch result.Event { @@ -89,7 +91,7 @@ func runBridgePush(env *Env, args []string) error { } } - env.out.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) diff --git a/commands/bridge/bridge_rm.go b/commands/bridge/bridge_rm.go new file mode 100644 index 0000000000000000000000000000000000000000..5d8d23c5f060ce68b2a80dab7f02ce5c8f9f8d77 --- /dev/null +++ b/commands/bridge/bridge_rm.go @@ -0,0 +1,36 @@ +package bridgecmd + +import ( + "github.com/spf13/cobra" + + "github.com/MichaelMure/git-bug/bridge" + "github.com/MichaelMure/git-bug/commands/completion" + "github.com/MichaelMure/git-bug/commands/execenv" +) + +func newBridgeRm() *cobra.Command { + env := execenv.NewEnv() + + cmd := &cobra.Command{ + Use: "rm NAME", + Short: "Delete a configured bridge", + PreRunE: execenv.LoadBackend(env), + RunE: execenv.CloseBackend(env, func(cmd *cobra.Command, args []string) error { + return runBridgeRm(env, args) + }), + Args: cobra.ExactArgs(1), + ValidArgsFunction: completion.Bridge(env), + } + + return cmd +} + +func runBridgeRm(env *execenv.Env, args []string) error { + err := bridge.RemoveBridge(env.Backend, args[0]) + if err != nil { + return err + } + + env.Out.Printf("Successfully removed bridge configuration %v\n", args[0]) + return nil +} diff --git a/commands/bridge_auth_rm.go b/commands/bridge_auth_rm.go deleted file mode 100644 index a28057dea621fc65d2b30af80b5197bb00296306..0000000000000000000000000000000000000000 --- a/commands/bridge_auth_rm.go +++ /dev/null @@ -1,39 +0,0 @@ -package commands - -import ( - "github.com/spf13/cobra" - - "github.com/MichaelMure/git-bug/bridge/core/auth" -) - -func newBridgeAuthRm() *cobra.Command { - env := newEnv() - - cmd := &cobra.Command{ - Use: "rm ID", - Short: "Remove a credential.", - PreRunE: loadRepo(env), - RunE: func(cmd *cobra.Command, args []string) error { - return runBridgeAuthRm(env, args) - }, - Args: cobra.ExactArgs(1), - ValidArgsFunction: completeBridgeAuth(env), - } - - 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(env.repo, cred.ID()) - if err != nil { - return err - } - - env.out.Printf("credential %s removed\n", cred.ID()) - return nil -} diff --git a/commands/bridge_auth_show.go b/commands/bridge_auth_show.go deleted file mode 100644 index 7233bb51529c17fdd118508550ad0f695ff3b8fd..0000000000000000000000000000000000000000 --- a/commands/bridge_auth_show.go +++ /dev/null @@ -1,58 +0,0 @@ -package commands - -import ( - "fmt" - "sort" - "strings" - "time" - - "github.com/spf13/cobra" - - "github.com/MichaelMure/git-bug/bridge/core/auth" -) - -func newBridgeAuthShow() *cobra.Command { - env := newEnv() - - cmd := &cobra.Command{ - Use: "show", - Short: "Display an authentication credential.", - PreRunE: loadBackend(env), - RunE: closeBackend(env, func(cmd *cobra.Command, args []string) error { - return runBridgeAuthShow(env, args) - }), - Args: cobra.ExactArgs(1), - ValidArgsFunction: completeBridgeAuth(env), - } - - return cmd -} - -func runBridgeAuthShow(env *Env, args []string) error { - cred, err := auth.LoadWithPrefix(env.repo, args[0]) - if err != nil { - return err - } - - 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: - env.out.Printf("Value: %s\n", cred.Value) - } - - env.out.Println("Metadata:") - - meta := make([]string, 0, len(cred.Metadata())) - for key, value := range cred.Metadata() { - meta = append(meta, fmt.Sprintf(" %s --> %s\n", key, value)) - } - sort.Strings(meta) - - env.out.Print(strings.Join(meta, "")) - - return nil -} diff --git a/commands/bridge_rm.go b/commands/bridge_rm.go deleted file mode 100644 index 0306944eeb0a6c9751e3cbc1504b6737b56ec57c..0000000000000000000000000000000000000000 --- a/commands/bridge_rm.go +++ /dev/null @@ -1,34 +0,0 @@ -package commands - -import ( - "github.com/spf13/cobra" - - "github.com/MichaelMure/git-bug/bridge" -) - -func newBridgeRm() *cobra.Command { - env := newEnv() - - cmd := &cobra.Command{ - Use: "rm NAME", - Short: "Delete a configured bridge.", - PreRunE: loadBackend(env), - RunE: closeBackend(env, func(cmd *cobra.Command, args []string) error { - return runBridgeRm(env, args) - }), - Args: cobra.ExactArgs(1), - ValidArgsFunction: completeBridge(env), - } - - return cmd -} - -func runBridgeRm(env *Env, args []string) error { - err := bridge.RemoveBridge(env.backend, args[0]) - if err != nil { - return err - } - - env.out.Printf("Successfully removed bridge configuration %v\n", args[0]) - return nil -} diff --git a/commands/ls.go b/commands/bug/bug.go similarity index 63% rename from commands/ls.go rename to commands/bug/bug.go index 7ed897fae08bf8e7ea01401f267d865492f627b5..04bf898078e26f9771b2da72c4dd907182e72a2a 100644 --- a/commands/ls.go +++ b/commands/bug/bug.go @@ -1,4 +1,4 @@ -package commands +package bugcmd import ( "encoding/json" @@ -11,13 +11,16 @@ import ( "github.com/spf13/cobra" "github.com/MichaelMure/git-bug/cache" + "github.com/MichaelMure/git-bug/commands/cmdjson" + "github.com/MichaelMure/git-bug/commands/completion" + "github.com/MichaelMure/git-bug/commands/execenv" "github.com/MichaelMure/git-bug/entities/bug" "github.com/MichaelMure/git-bug/entities/common" "github.com/MichaelMure/git-bug/query" "github.com/MichaelMure/git-bug/util/colors" ) -type lsOptions struct { +type bugOptions struct { statusQuery []string authorQuery []string metadataQuery []string @@ -31,33 +34,33 @@ type lsOptions struct { outputFormat string } -func newLsCommand() *cobra.Command { - env := newEnv() - options := lsOptions{} +func NewBugCommand() *cobra.Command { + env := execenv.NewEnv() + options := bugOptions{} cmd := &cobra.Command{ - Use: "ls [QUERY]", - Short: "List bugs.", + Use: "bug [QUERY]", + Short: "List bugs", Long: `Display a summary of each bugs. You can pass an additional query to filter and order the list. This query can be expressed either with a simple query language, flags, a natural language full text search, or a combination of the aforementioned.`, Example: `List open bugs sorted by last edition with a query: -git bug ls status:open sort:edit-desc +git bug status:open sort:edit-desc List closed bugs sorted by creation with flags: -git bug ls --status closed --by creation +git bug --status closed --by creation Do a full text search of all bugs: -git bug ls "foo bar" baz +git bug "foo bar" baz Use queries, flags, and full text search: -git bug ls status:open --by creation "foo bar" baz +git bug status:open --by creation "foo bar" baz `, - PreRunE: loadBackend(env), - RunE: closeBackend(env, func(cmd *cobra.Command, args []string) error { - return runLs(env, options, args) + PreRunE: execenv.LoadBackend(env), + RunE: execenv.CloseBackend(env, func(cmd *cobra.Command, args []string) error { + return runBug(env, options, args) }), - ValidArgsFunction: completeLs(env), + ValidArgsFunction: completion.Ls(env), } flags := cmd.Flags() @@ -65,41 +68,60 @@ git bug ls status:open --by creation "foo bar" baz flags.StringSliceVarP(&options.statusQuery, "status", "s", nil, "Filter by status. Valid values are [open,closed]") - cmd.RegisterFlagCompletionFunc("status", completeFrom([]string{"open", "closed"})) + cmd.RegisterFlagCompletionFunc("status", completion.From([]string{"open", "closed"})) flags.StringSliceVarP(&options.authorQuery, "author", "a", nil, "Filter by author") flags.StringSliceVarP(&options.metadataQuery, "metadata", "m", nil, "Filter by metadata. Example: github-url=URL") - cmd.RegisterFlagCompletionFunc("author", completeUserForQuery(env)) + cmd.RegisterFlagCompletionFunc("author", completion.UserForQuery(env)) flags.StringSliceVarP(&options.participantQuery, "participant", "p", nil, "Filter by participant") - cmd.RegisterFlagCompletionFunc("participant", completeUserForQuery(env)) + cmd.RegisterFlagCompletionFunc("participant", completion.UserForQuery(env)) flags.StringSliceVarP(&options.actorQuery, "actor", "A", nil, "Filter by actor") - cmd.RegisterFlagCompletionFunc("actor", completeUserForQuery(env)) + cmd.RegisterFlagCompletionFunc("actor", completion.UserForQuery(env)) flags.StringSliceVarP(&options.labelQuery, "label", "l", nil, "Filter by label") - cmd.RegisterFlagCompletionFunc("label", completeLabel(env)) + cmd.RegisterFlagCompletionFunc("label", completion.Label(env)) flags.StringSliceVarP(&options.titleQuery, "title", "t", nil, "Filter by title") flags.StringSliceVarP(&options.noQuery, "no", "n", nil, "Filter by absence of something. Valid values are [label]") - cmd.RegisterFlagCompletionFunc("no", completeLabel(env)) + cmd.RegisterFlagCompletionFunc("no", completion.Label(env)) flags.StringVarP(&options.sortBy, "by", "b", "creation", "Sort the results by a characteristic. Valid values are [id,creation,edit]") - cmd.RegisterFlagCompletionFunc("by", completeFrom([]string{"id", "creation", "edit"})) + cmd.RegisterFlagCompletionFunc("by", completion.From([]string{"id", "creation", "edit"})) flags.StringVarP(&options.sortDirection, "direction", "d", "asc", "Select the sorting direction. Valid values are [asc,desc]") - cmd.RegisterFlagCompletionFunc("direction", completeFrom([]string{"asc", "desc"})) + cmd.RegisterFlagCompletionFunc("direction", completion.From([]string{"asc", "desc"})) flags.StringVarP(&options.outputFormat, "format", "f", "default", "Select the output formatting style. Valid values are [default,plain,compact,id,json,org-mode]") cmd.RegisterFlagCompletionFunc("format", - completeFrom([]string{"default", "plain", "compact", "id", "json", "org-mode"})) + completion.From([]string{"default", "plain", "compact", "id", "json", "org-mode"})) + + const selectGroup = "select" + cmd.AddGroup(&cobra.Group{ID: selectGroup, Title: "Implicit selection"}) + + addCmdWithGroup := func(child *cobra.Command, groupID string) { + cmd.AddCommand(child) + child.GroupID = groupID + } + + addCmdWithGroup(newBugDeselectCommand(), selectGroup) + addCmdWithGroup(newBugSelectCommand(), selectGroup) + + cmd.AddCommand(newBugCommentCommand()) + cmd.AddCommand(newBugLabelCommand()) + cmd.AddCommand(newBugNewCommand()) + cmd.AddCommand(newBugRmCommand()) + cmd.AddCommand(newBugShowCommand()) + cmd.AddCommand(newBugStatusCommand()) + cmd.AddCommand(newBugTitleCommand()) return cmd } -func runLs(env *Env, opts lsOptions, args []string) error { +func runBug(env *execenv.Env, opts bugOptions, args []string) error { var q *query.Query var err error @@ -120,14 +142,14 @@ func runLs(env *Env, opts lsOptions, args []string) error { return err } - allIds, err := env.backend.QueryBugs(q) + allIds, err := env.Backend.QueryBugs(q) if err != nil { return err } bugExcerpt := make([]*cache.BugExcerpt, len(allIds)) for i, id := range allIds { - b, err := env.backend.ResolveBugExcerpt(id) + b, err := env.Backend.ResolveBugExcerpt(id) if err != nil { return err } @@ -136,17 +158,17 @@ func runLs(env *Env, opts lsOptions, args []string) error { switch opts.outputFormat { case "org-mode": - return lsOrgmodeFormatter(env, bugExcerpt) + return bugsOrgmodeFormatter(env, bugExcerpt) case "plain": - return lsPlainFormatter(env, bugExcerpt) + return bugsPlainFormatter(env, bugExcerpt) case "json": - return lsJsonFormatter(env, bugExcerpt) + return bugsJsonFormatter(env, bugExcerpt) case "compact": - return lsCompactFormatter(env, bugExcerpt) + return bugsCompactFormatter(env, bugExcerpt) case "id": - return lsIDFormatter(env, bugExcerpt) + return bugsIDFormatter(env, bugExcerpt) case "default": - return lsDefaultFormatter(env, bugExcerpt) + return bugsDefaultFormatter(env, bugExcerpt) default: return fmt.Errorf("unknown format %s", opts.outputFormat) } @@ -166,30 +188,30 @@ func repairQuery(args []string) string { } type JSONBugExcerpt struct { - Id string `json:"id"` - HumanId string `json:"human_id"` - CreateTime JSONTime `json:"create_time"` - EditTime JSONTime `json:"edit_time"` - - Status string `json:"status"` - Labels []bug.Label `json:"labels"` - Title string `json:"title"` - Actors []JSONIdentity `json:"actors"` - Participants []JSONIdentity `json:"participants"` - Author JSONIdentity `json:"author"` + Id string `json:"id"` + HumanId string `json:"human_id"` + CreateTime cmdjson.Time `json:"create_time"` + EditTime cmdjson.Time `json:"edit_time"` + + Status string `json:"status"` + Labels []bug.Label `json:"labels"` + Title string `json:"title"` + Actors []cmdjson.Identity `json:"actors"` + Participants []cmdjson.Identity `json:"participants"` + Author cmdjson.Identity `json:"author"` Comments int `json:"comments"` Metadata map[string]string `json:"metadata"` } -func lsJsonFormatter(env *Env, bugExcerpts []*cache.BugExcerpt) error { +func bugsJsonFormatter(env *execenv.Env, bugExcerpts []*cache.BugExcerpt) error { jsonBugs := make([]JSONBugExcerpt, len(bugExcerpts)) for i, b := range bugExcerpts { jsonBug := JSONBugExcerpt{ Id: b.Id.String(), HumanId: b.Id.Human(), - CreateTime: NewJSONTime(b.CreateTime(), b.CreateLamportTime), - EditTime: NewJSONTime(b.EditTime(), b.EditLamportTime), + CreateTime: cmdjson.NewTime(b.CreateTime(), b.CreateLamportTime), + EditTime: cmdjson.NewTime(b.EditTime(), b.EditLamportTime), Status: b.Status.String(), Labels: b.Labels, Title: b.Title, @@ -197,40 +219,40 @@ func lsJsonFormatter(env *Env, bugExcerpts []*cache.BugExcerpt) error { Metadata: b.CreateMetadata, } - author, err := env.backend.ResolveIdentityExcerpt(b.AuthorId) + author, err := env.Backend.ResolveIdentityExcerpt(b.AuthorId) if err != nil { return err } - jsonBug.Author = NewJSONIdentityFromExcerpt(author) + jsonBug.Author = cmdjson.NewIdentityFromExcerpt(author) - jsonBug.Actors = make([]JSONIdentity, len(b.Actors)) + jsonBug.Actors = make([]cmdjson.Identity, len(b.Actors)) for i, element := range b.Actors { - actor, err := env.backend.ResolveIdentityExcerpt(element) + actor, err := env.Backend.ResolveIdentityExcerpt(element) if err != nil { return err } - jsonBug.Actors[i] = NewJSONIdentityFromExcerpt(actor) + jsonBug.Actors[i] = cmdjson.NewIdentityFromExcerpt(actor) } - jsonBug.Participants = make([]JSONIdentity, len(b.Participants)) + jsonBug.Participants = make([]cmdjson.Identity, len(b.Participants)) for i, element := range b.Participants { - participant, err := env.backend.ResolveIdentityExcerpt(element) + participant, err := env.Backend.ResolveIdentityExcerpt(element) if err != nil { return err } - jsonBug.Participants[i] = NewJSONIdentityFromExcerpt(participant) + jsonBug.Participants[i] = cmdjson.NewIdentityFromExcerpt(participant) } jsonBugs[i] = jsonBug } jsonObject, _ := json.MarshalIndent(jsonBugs, "", " ") - env.out.Printf("%s\n", jsonObject) + env.Out.Printf("%s\n", jsonObject) return nil } -func lsCompactFormatter(env *Env, bugExcerpts []*cache.BugExcerpt) error { +func bugsCompactFormatter(env *execenv.Env, bugExcerpts []*cache.BugExcerpt) error { for _, b := range bugExcerpts { - author, err := env.backend.ResolveIdentityExcerpt(b.AuthorId) + author, err := env.Backend.ResolveIdentityExcerpt(b.AuthorId) if err != nil { return err } @@ -243,7 +265,7 @@ func lsCompactFormatter(env *Env, bugExcerpts []*cache.BugExcerpt) error { labelsTxt.WriteString(lc256.Unescape()) } - env.out.Printf("%s %s %s %s %s\n", + env.Out.Printf("%s %s %s %s %s\n", colors.Cyan(b.Id.Human()), colors.Yellow(b.Status), text.LeftPadMaxLine(strings.TrimSpace(b.Title), 40, 0), @@ -254,17 +276,17 @@ func lsCompactFormatter(env *Env, bugExcerpts []*cache.BugExcerpt) error { return nil } -func lsIDFormatter(env *Env, bugExcerpts []*cache.BugExcerpt) error { +func bugsIDFormatter(env *execenv.Env, bugExcerpts []*cache.BugExcerpt) error { for _, b := range bugExcerpts { - env.out.Println(b.Id.String()) + env.Out.Println(b.Id.String()) } return nil } -func lsDefaultFormatter(env *Env, bugExcerpts []*cache.BugExcerpt) error { +func bugsDefaultFormatter(env *execenv.Env, bugExcerpts []*cache.BugExcerpt) error { for _, b := range bugExcerpts { - author, err := env.backend.ResolveIdentityExcerpt(b.AuthorId) + author, err := env.Backend.ResolveIdentityExcerpt(b.AuthorId) if err != nil { return err } @@ -290,7 +312,7 @@ func lsDefaultFormatter(env *Env, bugExcerpts []*cache.BugExcerpt) error { comments = " ∞ 💬" } - env.out.Printf("%s\t%s\t%s\t%s\t%s\n", + env.Out.Printf("%s\t%s\t%s\t%s\t%s\n", colors.Cyan(b.Id.Human()), colors.Yellow(b.Status), titleFmt+labelsFmt, @@ -301,14 +323,14 @@ func lsDefaultFormatter(env *Env, bugExcerpts []*cache.BugExcerpt) error { return nil } -func lsPlainFormatter(env *Env, bugExcerpts []*cache.BugExcerpt) error { +func bugsPlainFormatter(env *execenv.Env, bugExcerpts []*cache.BugExcerpt) error { for _, b := range bugExcerpts { - env.out.Printf("%s [%s] %s\n", b.Id.Human(), b.Status, strings.TrimSpace(b.Title)) + env.Out.Printf("%s [%s] %s\n", b.Id.Human(), b.Status, strings.TrimSpace(b.Title)) } return nil } -func lsOrgmodeFormatter(env *Env, bugExcerpts []*cache.BugExcerpt) error { +func bugsOrgmodeFormatter(env *execenv.Env, bugExcerpts []*cache.BugExcerpt) error { // see https://orgmode.org/manual/Tags.html orgTagRe := regexp.MustCompile("[^[:alpha:]_@]") formatTag := func(l bug.Label) string { @@ -319,7 +341,7 @@ func lsOrgmodeFormatter(env *Env, bugExcerpts []*cache.BugExcerpt) error { return time.Format("[2006-01-02 Mon 15:05]") } - env.out.Println("#+TODO: OPEN | CLOSED") + env.Out.Println("#+TODO: OPEN | CLOSED") for _, b := range bugExcerpts { status := strings.ToUpper(b.Status.String()) @@ -331,7 +353,7 @@ func lsOrgmodeFormatter(env *Env, bugExcerpts []*cache.BugExcerpt) error { title = b.Title } - author, err := env.backend.ResolveIdentityExcerpt(b.AuthorId) + author, err := env.Backend.ResolveIdentityExcerpt(b.AuthorId) if err != nil { return err } @@ -346,7 +368,7 @@ func lsOrgmodeFormatter(env *Env, bugExcerpts []*cache.BugExcerpt) error { } labels.WriteString(":") - env.out.Printf("* %-6s %s %s %s: %s %s\n", + env.Out.Printf("* %-6s %s %s %s: %s %s\n", status, b.Id.Human(), formatTime(b.CreateTime()), @@ -355,29 +377,29 @@ func lsOrgmodeFormatter(env *Env, bugExcerpts []*cache.BugExcerpt) error { labels.String(), ) - env.out.Printf("** Last Edited: %s\n", formatTime(b.EditTime())) + env.Out.Printf("** Last Edited: %s\n", formatTime(b.EditTime())) - env.out.Printf("** Actors:\n") + env.Out.Printf("** Actors:\n") for _, element := range b.Actors { - actor, err := env.backend.ResolveIdentityExcerpt(element) + actor, err := env.Backend.ResolveIdentityExcerpt(element) if err != nil { return err } - env.out.Printf(": %s %s\n", + env.Out.Printf(": %s %s\n", actor.Id.Human(), actor.DisplayName(), ) } - env.out.Printf("** Participants:\n") + env.Out.Printf("** Participants:\n") for _, element := range b.Participants { - participant, err := env.backend.ResolveIdentityExcerpt(element) + participant, err := env.Backend.ResolveIdentityExcerpt(element) if err != nil { return err } - env.out.Printf(": %s %s\n", + env.Out.Printf(": %s %s\n", participant.Id.Human(), participant.DisplayName(), ) @@ -388,7 +410,7 @@ func lsOrgmodeFormatter(env *Env, bugExcerpts []*cache.BugExcerpt) error { } // Finish the command flags transformation into the query.Query -func completeQuery(q *query.Query, opts lsOptions) error { +func completeQuery(q *query.Query, opts bugOptions) error { for _, str := range opts.statusQuery { status, err := common.StatusFromString(str) if err != nil { diff --git a/commands/bug/bug_comment.go b/commands/bug/bug_comment.go new file mode 100644 index 0000000000000000000000000000000000000000..bc665f0db74af57e7cd207b7f750073db0b4e34a --- /dev/null +++ b/commands/bug/bug_comment.go @@ -0,0 +1,52 @@ +package bugcmd + +import ( + text "github.com/MichaelMure/go-term-text" + "github.com/spf13/cobra" + + "github.com/MichaelMure/git-bug/commands/bug/select" + "github.com/MichaelMure/git-bug/commands/completion" + "github.com/MichaelMure/git-bug/commands/execenv" + "github.com/MichaelMure/git-bug/util/colors" +) + +func newBugCommentCommand() *cobra.Command { + env := execenv.NewEnv() + + cmd := &cobra.Command{ + Use: "comment [BUG_ID]", + Short: "List a bug's comments", + PreRunE: execenv.LoadBackend(env), + RunE: execenv.CloseBackend(env, func(cmd *cobra.Command, args []string) error { + return runBugComment(env, args) + }), + ValidArgsFunction: completion.Bug(env), + } + + cmd.AddCommand(newBugCommentNewCommand()) + cmd.AddCommand(newBugCommentEditCommand()) + + return cmd +} + +func runBugComment(env *execenv.Env, args []string) error { + b, args, err := _select.ResolveBug(env.Backend, args) + if err != nil { + return err + } + + snap := b.Snapshot() + + for i, comment := range snap.Comments { + if i != 0 { + env.Out.Println() + } + + env.Out.Printf("Author: %s\n", colors.Magenta(comment.Author.DisplayName())) + env.Out.Printf("Id: %s\n", colors.Cyan(comment.CombinedId().Human())) + env.Out.Printf("Date: %s\n\n", comment.FormatTime()) + env.Out.Println(text.LeftPadLines(comment.Message, 4)) + } + + return nil +} diff --git a/commands/comment_add.go b/commands/bug/bug_comment_add.go similarity index 56% rename from commands/comment_add.go rename to commands/bug/bug_comment_add.go index acac7994d364543aff17d995aefe43a330bf8d7e..b676db3ac7b7c04b0daf6e7255362bff31a5a283 100644 --- a/commands/comment_add.go +++ b/commands/bug/bug_comment_add.go @@ -1,31 +1,33 @@ -package commands +package bugcmd import ( "github.com/spf13/cobra" + "github.com/MichaelMure/git-bug/commands/bug/select" + "github.com/MichaelMure/git-bug/commands/completion" + "github.com/MichaelMure/git-bug/commands/execenv" "github.com/MichaelMure/git-bug/commands/input" - _select "github.com/MichaelMure/git-bug/commands/select" "github.com/MichaelMure/git-bug/util/text" ) -type commentAddOptions struct { +type bugCommentNewOptions struct { messageFile string message string nonInteractive bool } -func newCommentAddCommand() *cobra.Command { - env := newEnv() - options := commentAddOptions{} +func newBugCommentNewCommand() *cobra.Command { + env := execenv.NewEnv() + options := bugCommentNewOptions{} cmd := &cobra.Command{ - Use: "add [ID]", - Short: "Add a new comment to a bug.", - PreRunE: loadBackendEnsureUser(env), - RunE: closeBackend(env, func(cmd *cobra.Command, args []string) error { - return runCommentAdd(env, options, args) + Use: "new [BUG_ID]", + Short: "Add a new comment to a bug", + PreRunE: execenv.LoadBackendEnsureUser(env), + RunE: execenv.CloseBackend(env, func(cmd *cobra.Command, args []string) error { + return runBugCommentNew(env, options, args) }), - ValidArgsFunction: completeBug(env), + ValidArgsFunction: completion.Bug(env), } flags := cmd.Flags() @@ -41,8 +43,8 @@ func newCommentAddCommand() *cobra.Command { return cmd } -func runCommentAdd(env *Env, opts commentAddOptions, args []string) error { - b, args, err := _select.ResolveBug(env.backend, args) +func runBugCommentNew(env *execenv.Env, opts bugCommentNewOptions, args []string) error { + b, args, err := _select.ResolveBug(env.Backend, args) if err != nil { return err } @@ -56,12 +58,12 @@ func runCommentAdd(env *Env, opts commentAddOptions, args []string) error { if opts.messageFile == "" && opts.message == "" { if opts.nonInteractive { - env.err.Println("No message given. Use -m or -F option to specify a message. Aborting.") + env.Err.Println("No message given. Use -m or -F option to specify a message. Aborting.") return nil } - opts.message, err = input.BugCommentEditorInput(env.backend, "") + opts.message, err = input.BugCommentEditorInput(env.Backend, "") if err == input.ErrEmptyMessage { - env.err.Println("Empty message, aborting.") + env.Err.Println("Empty message, aborting.") return nil } if err != nil { diff --git a/commands/bug/bug_comment_add_test.go b/commands/bug/bug_comment_add_test.go new file mode 100644 index 0000000000000000000000000000000000000000..55e285f445e052dee203acf7d3b2cffbaaead541 --- /dev/null +++ b/commands/bug/bug_comment_add_test.go @@ -0,0 +1,18 @@ +package bugcmd + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/MichaelMure/git-bug/commands/bug/testenv" +) + +func TestBugCommentNew(t *testing.T) { + const golden = "testdata/comment/add" + + env, bugID, _ := testenv.NewTestEnvAndBugWithComment(t) + + require.NoError(t, runBugComment(env, []string{bugID.String()})) + requireCommentsEqual(t, golden, env) +} diff --git a/commands/comment_edit.go b/commands/bug/bug_comment_edit.go similarity index 60% rename from commands/comment_edit.go rename to commands/bug/bug_comment_edit.go index 91c6d80977a14824b93b7e62d8bfa55d684d7f17..8be7cb80c53652c9723b2cc8742816841af5b114 100644 --- a/commands/comment_edit.go +++ b/commands/bug/bug_comment_edit.go @@ -1,28 +1,29 @@ -package commands +package bugcmd import ( "github.com/spf13/cobra" + "github.com/MichaelMure/git-bug/commands/execenv" "github.com/MichaelMure/git-bug/commands/input" ) -type commentEditOptions struct { +type bugCommentEditOptions struct { messageFile string message string nonInteractive bool } -func newCommentEditCommand() *cobra.Command { - env := newEnv() - options := commentEditOptions{} +func newBugCommentEditCommand() *cobra.Command { + env := execenv.NewEnv() + options := bugCommentEditOptions{} cmd := &cobra.Command{ Use: "edit [COMMENT_ID]", - Short: "Edit an existing comment on a bug.", + Short: "Edit an existing comment on a bug", Args: cobra.ExactArgs(1), - PreRunE: loadBackendEnsureUser(env), - RunE: closeBackend(env, func(cmd *cobra.Command, args []string) error { - return runCommentEdit(env, options, args) + PreRunE: execenv.LoadBackendEnsureUser(env), + RunE: execenv.CloseBackend(env, func(cmd *cobra.Command, args []string) error { + return runBugCommentEdit(env, options, args) }), } @@ -39,8 +40,8 @@ func newCommentEditCommand() *cobra.Command { return cmd } -func runCommentEdit(env *Env, opts commentEditOptions, args []string) error { - b, commentId, err := env.backend.ResolveComment(args[0]) +func runBugCommentEdit(env *execenv.Env, opts bugCommentEditOptions, args []string) error { + b, commentId, err := env.Backend.ResolveComment(args[0]) if err != nil { return err } @@ -54,12 +55,12 @@ func runCommentEdit(env *Env, opts commentEditOptions, args []string) error { if opts.messageFile == "" && opts.message == "" { if opts.nonInteractive { - env.err.Println("No message given. Use -m or -F option to specify a message. Aborting.") + env.Err.Println("No message given. Use -m or -F option to specify a message. Aborting.") return nil } - opts.message, err = input.BugCommentEditorInput(env.backend, "") + opts.message, err = input.BugCommentEditorInput(env.Backend, "") if err == input.ErrEmptyMessage { - env.err.Println("Empty message, aborting.") + env.Err.Println("Empty message, aborting.") return nil } if err != nil { diff --git a/commands/bug/bug_comment_edit_test.go b/commands/bug/bug_comment_edit_test.go new file mode 100644 index 0000000000000000000000000000000000000000..9e110a3b31b51a47d9410e7869d18b4756677e20 --- /dev/null +++ b/commands/bug/bug_comment_edit_test.go @@ -0,0 +1,23 @@ +package bugcmd + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/MichaelMure/git-bug/commands/bug/testenv" +) + +func TestBugCommentEdit(t *testing.T) { + const golden = "testdata/comment/edit" + + env, bugID, commentID := testenv.NewTestEnvAndBugWithComment(t) + + opts := bugCommentEditOptions{ + message: "this is an altered bug comment", + } + require.NoError(t, runBugCommentEdit(env, opts, []string{commentID.Human()})) + + require.NoError(t, runBugComment(env, []string{bugID.Human()})) + requireCommentsEqual(t, golden, env) +} diff --git a/commands/comment_test.go b/commands/bug/bug_comment_test.go similarity index 81% rename from commands/comment_test.go rename to commands/bug/bug_comment_test.go index 43062ed0caf1ec8a79b1112cad107a3282f30cbb..c1dc9952db22f25284fcdf1e45f6090deacb00a3 100644 --- a/commands/comment_test.go +++ b/commands/bug/bug_comment_test.go @@ -1,4 +1,4 @@ -package commands +package bugcmd import ( "fmt" @@ -8,14 +8,18 @@ import ( "time" "github.com/stretchr/testify/require" + + "github.com/MichaelMure/git-bug/commands/bug/testenv" + "github.com/MichaelMure/git-bug/commands/cmdtest" + "github.com/MichaelMure/git-bug/commands/execenv" ) -func TestComment(t *testing.T) { +func TestBugComment(t *testing.T) { const golden = "testdata/comment/message-only" - env, bug := newTestEnvAndBug(t) + env, bug := testenv.NewTestEnvAndBug(t) - require.NoError(t, runComment(env.env, []string{bug})) + require.NoError(t, runBugComment(env, []string{bug.Human()})) requireCommentsEqual(t, golden, env) } @@ -37,7 +41,7 @@ type commentParser struct { comments []parsedComment } -func parseComments(t *testing.T, env *testEnv) []parsedComment { +func parseComments(t *testing.T, env *execenv.Env) []parsedComment { t.Helper() parser := &commentParser{ @@ -48,7 +52,7 @@ func parseComments(t *testing.T, env *testEnv) []parsedComment { comment := &parsedComment{} parser.fn = parser.parseAuthor - for _, line := range strings.Split(env.out.String(), "\n") { + for _, line := range strings.Split(env.Out.String(), "\n") { parser.fn(comment, line) } @@ -116,7 +120,7 @@ func normalizeParsedComments(t *testing.T, comments []parsedComment) []parsedCom date, err := time.Parse(gitDateFormat, "Fri Aug 19 07:00:00 2022 +1900") require.NoError(t, err) - out := []parsedComment{} + var out []parsedComment for i, comment := range comments { comment.id = fmt.Sprintf("%7x", prefix+i) @@ -127,18 +131,18 @@ func normalizeParsedComments(t *testing.T, comments []parsedComment) []parsedCom return out } -func requireCommentsEqual(t *testing.T, golden string, env *testEnv) { +func requireCommentsEqual(t *testing.T, golden string, env *execenv.Env) { t.Helper() - const goldenFilePatter = "%s-%d-golden.txt" + const goldenFilePattern = "%s-%d-golden.txt" comments := parseComments(t, env) comments = normalizeParsedComments(t, comments) - if *update { + if *cmdtest.Update { t.Log("Got here") for i, comment := range comments { - fileName := fmt.Sprintf(goldenFilePatter, golden, i) + fileName := fmt.Sprintf(goldenFilePattern, golden, i) require.NoError(t, ioutil.WriteFile(fileName, []byte(comment.message), 0644)) } } @@ -152,7 +156,7 @@ func requireCommentsEqual(t *testing.T, golden string, env *testEnv) { require.Equal(t, fmt.Sprintf("%7x", prefix+i), comment.id) require.Equal(t, date.Add(time.Duration(i)*time.Minute), comment.date) - fileName := fmt.Sprintf(goldenFilePatter, golden, i) + fileName := fmt.Sprintf(goldenFilePattern, golden, i) exp, err := ioutil.ReadFile(fileName) require.NoError(t, err) require.Equal(t, strings.ReplaceAll(string(exp), "\r", ""), strings.ReplaceAll(comment.message, "\r", "")) diff --git a/commands/bug/bug_deselect.go b/commands/bug/bug_deselect.go new file mode 100644 index 0000000000000000000000000000000000000000..7e2a86c980994564717aef8580f38923b626de09 --- /dev/null +++ b/commands/bug/bug_deselect.go @@ -0,0 +1,37 @@ +package bugcmd + +import ( + "github.com/spf13/cobra" + + "github.com/MichaelMure/git-bug/commands/bug/select" + "github.com/MichaelMure/git-bug/commands/execenv" +) + +func newBugDeselectCommand() *cobra.Command { + env := execenv.NewEnv() + + cmd := &cobra.Command{ + Use: "deselect", + Short: "Clear the implicitly selected bug", + Example: `git bug select 2f15 +git bug comment +git bug status +git bug deselect +`, + PreRunE: execenv.LoadBackend(env), + RunE: execenv.CloseBackend(env, func(cmd *cobra.Command, args []string) error { + return runBugDeselect(env) + }), + } + + return cmd +} + +func runBugDeselect(env *execenv.Env) error { + err := _select.Clear(env.Backend) + if err != nil { + return err + } + + return nil +} diff --git a/commands/bug/bug_label.go b/commands/bug/bug_label.go new file mode 100644 index 0000000000000000000000000000000000000000..657fa2ca73c4eb25a05be8e523415822fc4e19b9 --- /dev/null +++ b/commands/bug/bug_label.go @@ -0,0 +1,43 @@ +package bugcmd + +import ( + "github.com/spf13/cobra" + + "github.com/MichaelMure/git-bug/commands/bug/select" + "github.com/MichaelMure/git-bug/commands/completion" + "github.com/MichaelMure/git-bug/commands/execenv" +) + +func newBugLabelCommand() *cobra.Command { + env := execenv.NewEnv() + + cmd := &cobra.Command{ + Use: "label [BUG_ID]", + Short: "Display labels of a bug", + PreRunE: execenv.LoadBackend(env), + RunE: execenv.CloseBackend(env, func(cmd *cobra.Command, args []string) error { + return runBugLabel(env, args) + }), + ValidArgsFunction: completion.Bug(env), + } + + cmd.AddCommand(newBugLabelNewCommand()) + cmd.AddCommand(newBugLabelRmCommand()) + + return cmd +} + +func runBugLabel(env *execenv.Env, args []string) error { + b, args, err := _select.ResolveBug(env.Backend, args) + if err != nil { + return err + } + + snap := b.Snapshot() + + for _, l := range snap.Labels { + env.Out.Println(l) + } + + return nil +} diff --git a/commands/bug/bug_label_new.go b/commands/bug/bug_label_new.go new file mode 100644 index 0000000000000000000000000000000000000000..f94d3dc85defc2a7095d5db8add0a14e77e9c712 --- /dev/null +++ b/commands/bug/bug_label_new.go @@ -0,0 +1,47 @@ +package bugcmd + +import ( + "github.com/spf13/cobra" + + "github.com/MichaelMure/git-bug/commands/bug/select" + "github.com/MichaelMure/git-bug/commands/completion" + "github.com/MichaelMure/git-bug/commands/execenv" + "github.com/MichaelMure/git-bug/util/text" +) + +func newBugLabelNewCommand() *cobra.Command { + env := execenv.NewEnv() + + cmd := &cobra.Command{ + Use: "new [BUG_ID] LABEL...", + Short: "Add a label to a bug", + PreRunE: execenv.LoadBackendEnsureUser(env), + RunE: execenv.CloseBackend(env, func(cmd *cobra.Command, args []string) error { + return runBugLabelNew(env, args) + }), + ValidArgsFunction: completion.BugAndLabels(env, true), + } + + return cmd +} + +func runBugLabelNew(env *execenv.Env, args []string) error { + b, args, err := _select.ResolveBug(env.Backend, args) + if err != nil { + return err + } + + added := args + + changes, _, err := b.ChangeLabels(text.CleanupOneLineArray(added), nil) + + for _, change := range changes { + env.Out.Println(change) + } + + if err != nil { + return err + } + + return b.Commit() +} diff --git a/commands/bug/bug_label_rm.go b/commands/bug/bug_label_rm.go new file mode 100644 index 0000000000000000000000000000000000000000..13ce4b81ebb1423bcda7bfc55931cb5ec6d5ef16 --- /dev/null +++ b/commands/bug/bug_label_rm.go @@ -0,0 +1,47 @@ +package bugcmd + +import ( + "github.com/spf13/cobra" + + "github.com/MichaelMure/git-bug/commands/bug/select" + "github.com/MichaelMure/git-bug/commands/completion" + "github.com/MichaelMure/git-bug/commands/execenv" + "github.com/MichaelMure/git-bug/util/text" +) + +func newBugLabelRmCommand() *cobra.Command { + env := execenv.NewEnv() + + cmd := &cobra.Command{ + Use: "rm [BUG_ID] LABEL...", + Short: "Remove a label from a bug", + PreRunE: execenv.LoadBackend(env), + RunE: execenv.CloseBackend(env, func(cmd *cobra.Command, args []string) error { + return runBugLabelRm(env, args) + }), + ValidArgsFunction: completion.BugAndLabels(env, false), + } + + return cmd +} + +func runBugLabelRm(env *execenv.Env, args []string) error { + b, args, err := _select.ResolveBug(env.Backend, args) + if err != nil { + return err + } + + removed := args + + changes, _, err := b.ChangeLabels(nil, text.CleanupOneLineArray(removed)) + + for _, change := range changes { + env.Out.Println(change) + } + + if err != nil { + return err + } + + return b.Commit() +} diff --git a/commands/add.go b/commands/bug/bug_new.go similarity index 67% rename from commands/add.go rename to commands/bug/bug_new.go index b43eda3625bbd2877dec9613f48c7fa5b7eac19e..4f73a09c2b4251cf22d43fd5f7eb4dbca16da3c1 100644 --- a/commands/add.go +++ b/commands/bug/bug_new.go @@ -1,29 +1,30 @@ -package commands +package bugcmd import ( "github.com/spf13/cobra" + "github.com/MichaelMure/git-bug/commands/execenv" "github.com/MichaelMure/git-bug/commands/input" "github.com/MichaelMure/git-bug/util/text" ) -type addOptions struct { +type bugNewOptions struct { title string message string messageFile string nonInteractive bool } -func newAddCommand() *cobra.Command { - env := newEnv() - options := addOptions{} +func newBugNewCommand() *cobra.Command { + env := execenv.NewEnv() + options := bugNewOptions{} cmd := &cobra.Command{ - Use: "add", - Short: "Create a new bug.", - PreRunE: loadBackendEnsureUser(env), - RunE: closeBackend(env, func(cmd *cobra.Command, args []string) error { - return runAdd(env, options) + Use: "new", + Short: "Create a new bug", + PreRunE: execenv.LoadBackendEnsureUser(env), + RunE: execenv.CloseBackend(env, func(cmd *cobra.Command, args []string) error { + return runBugNew(env, options) }), } @@ -41,7 +42,7 @@ func newAddCommand() *cobra.Command { return cmd } -func runAdd(env *Env, opts addOptions) error { +func runBugNew(env *execenv.Env, opts bugNewOptions) error { var err error if opts.messageFile != "" && opts.message == "" { opts.title, opts.message, err = input.BugCreateFileInput(opts.messageFile) @@ -51,10 +52,10 @@ func runAdd(env *Env, opts addOptions) error { } if !opts.nonInteractive && opts.messageFile == "" && (opts.message == "" || opts.title == "") { - opts.title, opts.message, err = input.BugCreateEditorInput(env.backend, opts.title, opts.message) + opts.title, opts.message, err = input.BugCreateEditorInput(env.Backend, opts.title, opts.message) if err == input.ErrEmptyTitle { - env.out.Println("Empty title, aborting.") + env.Out.Println("Empty title, aborting.") return nil } if err != nil { @@ -62,7 +63,7 @@ func runAdd(env *Env, opts addOptions) error { } } - b, _, err := env.backend.NewBug( + b, _, err := env.Backend.NewBug( text.CleanupOneLine(opts.title), text.Cleanup(opts.message), ) @@ -70,7 +71,7 @@ func runAdd(env *Env, opts addOptions) error { return err } - env.out.Printf("%s created\n", b.Id().Human()) + env.Out.Printf("%s created\n", b.Id().Human()) return nil } diff --git a/commands/bug/bug_new_test.go b/commands/bug/bug_new_test.go new file mode 100644 index 0000000000000000000000000000000000000000..210a4b0b439a2d798c0ad72bbade890b886c5101 --- /dev/null +++ b/commands/bug/bug_new_test.go @@ -0,0 +1,21 @@ +package bugcmd + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/MichaelMure/git-bug/commands/bug/testenv" +) + +func TestBugNew(t *testing.T) { + env, _ := testenv.NewTestEnvAndUser(t) + + err := runBugNew(env, bugNewOptions{ + nonInteractive: true, + message: "message", + title: "title", + }) + require.NoError(t, err) + require.Regexp(t, "^[0-9A-Fa-f]{7} created\n$", env.Out.String()) +} diff --git a/commands/bug/bug_rm.go b/commands/bug/bug_rm.go new file mode 100644 index 0000000000000000000000000000000000000000..1d2a7524ec71efa158b8b4f1efffdd2c998aed28 --- /dev/null +++ b/commands/bug/bug_rm.go @@ -0,0 +1,46 @@ +package bugcmd + +import ( + "errors" + + "github.com/spf13/cobra" + + "github.com/MichaelMure/git-bug/commands/completion" + "github.com/MichaelMure/git-bug/commands/execenv" +) + +func newBugRmCommand() *cobra.Command { + env := execenv.NewEnv() + + cmd := &cobra.Command{ + Use: "rm BUG_ID", + Short: "Remove an existing bug", + Long: "Remove an existing bug in the local repository. Note removing bugs that were imported from bridges will not remove the bug on the remote, and will only remove the local copy of the bug.", + PreRunE: execenv.LoadBackendEnsureUser(env), + RunE: execenv.CloseBackend(env, func(cmd *cobra.Command, args []string) error { + return runBugRm(env, args) + }), + ValidArgsFunction: completion.Bug(env), + } + + flags := cmd.Flags() + flags.SortFlags = false + + return cmd +} + +func runBugRm(env *execenv.Env, args []string) (err error) { + if len(args) == 0 { + return errors.New("you must provide a bug prefix to remove") + } + + err = env.Backend.RemoveBug(args[0]) + + if err != nil { + return + } + + env.Out.Printf("bug %s removed\n", args[0]) + + return +} diff --git a/commands/bug/bug_rm_test.go b/commands/bug/bug_rm_test.go new file mode 100644 index 0000000000000000000000000000000000000000..e0c2bbc579cd0eb44228c002fa73272d3e6169dc --- /dev/null +++ b/commands/bug/bug_rm_test.go @@ -0,0 +1,19 @@ +package bugcmd + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/MichaelMure/git-bug/commands/bug/testenv" +) + +func TestBugRm(t *testing.T) { + env, bugID := testenv.NewTestEnvAndBug(t) + + exp := "bug " + bugID.Human() + " removed\n" + + require.NoError(t, runBugRm(env, []string{bugID.Human()})) + require.Equal(t, exp, env.Out.String()) + env.Out.Reset() +} diff --git a/commands/bug/bug_select.go b/commands/bug/bug_select.go new file mode 100644 index 0000000000000000000000000000000000000000..0b1cb15ca32a449e2e7417034961522d10ad32c7 --- /dev/null +++ b/commands/bug/bug_select.go @@ -0,0 +1,62 @@ +package bugcmd + +import ( + "errors" + + "github.com/spf13/cobra" + + "github.com/MichaelMure/git-bug/commands/bug/select" + "github.com/MichaelMure/git-bug/commands/completion" + "github.com/MichaelMure/git-bug/commands/execenv" +) + +func newBugSelectCommand() *cobra.Command { + env := execenv.NewEnv() + + cmd := &cobra.Command{ + Use: "select BUG_ID", + Short: "Select a bug for implicit use in future commands", + Example: `git bug select 2f15 +git bug comment +git bug status +`, + Long: `Select a bug for implicit use in future commands. + +This command allows you to omit any bug ID argument, for example: + git bug show +instead of + git bug show 2f153ca + +The complementary command is "git bug deselect" performing the opposite operation. +`, + PreRunE: execenv.LoadBackend(env), + RunE: execenv.CloseBackend(env, func(cmd *cobra.Command, args []string) error { + return runBugSelect(env, args) + }), + ValidArgsFunction: completion.Bug(env), + } + + return cmd +} + +func runBugSelect(env *execenv.Env, args []string) error { + if len(args) == 0 { + return errors.New("You must provide a bug id") + } + + prefix := args[0] + + b, err := env.Backend.ResolveBugPrefix(prefix) + if err != nil { + return err + } + + err = _select.Select(env.Backend, b.Id()) + if err != nil { + return err + } + + env.Out.Printf("selected bug %s: %s\n", b.Id().Human(), b.Snapshot().Title) + + return nil +} diff --git a/commands/show.go b/commands/bug/bug_show.go similarity index 55% rename from commands/show.go rename to commands/bug/bug_show.go index 1491372e38cf160f69fc00cf7e91808deb86684a..105b1150afe770db46ab925a30ba3d4466ddbab3 100644 --- a/commands/show.go +++ b/commands/bug/bug_show.go @@ -1,4 +1,4 @@ -package commands +package bugcmd import ( "encoding/json" @@ -8,28 +8,31 @@ import ( "github.com/spf13/cobra" - _select "github.com/MichaelMure/git-bug/commands/select" + "github.com/MichaelMure/git-bug/commands/bug/select" + "github.com/MichaelMure/git-bug/commands/cmdjson" + "github.com/MichaelMure/git-bug/commands/completion" + "github.com/MichaelMure/git-bug/commands/execenv" "github.com/MichaelMure/git-bug/entities/bug" "github.com/MichaelMure/git-bug/util/colors" ) -type showOptions struct { +type bugShowOptions struct { fields string format string } -func newShowCommand() *cobra.Command { - env := newEnv() - options := showOptions{} +func newBugShowCommand() *cobra.Command { + env := execenv.NewEnv() + options := bugShowOptions{} cmd := &cobra.Command{ - Use: "show [ID]", - Short: "Display the details of a bug.", - PreRunE: loadBackend(env), - RunE: closeBackend(env, func(cmd *cobra.Command, args []string) error { - return runShow(env, options, args) + Use: "show [BUG_ID]", + Short: "Display the details of a bug", + PreRunE: execenv.LoadBackend(env), + RunE: execenv.CloseBackend(env, func(cmd *cobra.Command, args []string) error { + return runBugShow(env, options, args) }), - ValidArgsFunction: completeBug(env), + ValidArgsFunction: completion.Bug(env), } flags := cmd.Flags() @@ -39,15 +42,15 @@ func newShowCommand() *cobra.Command { "id", "labels", "shortId", "status", "title", "actors", "participants"} flags.StringVarP(&options.fields, "field", "", "", "Select field to display. Valid values are ["+strings.Join(fields, ",")+"]") - cmd.RegisterFlagCompletionFunc("by", completeFrom(fields)) + cmd.RegisterFlagCompletionFunc("by", completion.From(fields)) flags.StringVarP(&options.format, "format", "f", "default", "Select the output formatting style. Valid values are [default,json,org-mode]") return cmd } -func runShow(env *Env, opts showOptions, args []string) error { - b, args, err := _select.ResolveBug(env.backend, args) +func runBugShow(env *execenv.Env, opts bugShowOptions, args []string) error { + b, args, err := _select.ResolveBug(env.Backend, args) if err != nil { return err } @@ -61,35 +64,35 @@ func runShow(env *Env, opts showOptions, args []string) error { if opts.fields != "" { switch opts.fields { case "author": - env.out.Printf("%s\n", snap.Author.DisplayName()) + env.Out.Printf("%s\n", snap.Author.DisplayName()) case "authorEmail": - env.out.Printf("%s\n", snap.Author.Email()) + env.Out.Printf("%s\n", snap.Author.Email()) case "createTime": - env.out.Printf("%s\n", snap.CreateTime.String()) + env.Out.Printf("%s\n", snap.CreateTime.String()) case "lastEdit": - env.out.Printf("%s\n", snap.EditTime().String()) + env.Out.Printf("%s\n", snap.EditTime().String()) case "humanId": - env.out.Printf("%s\n", snap.Id().Human()) + env.Out.Printf("%s\n", snap.Id().Human()) case "id": - env.out.Printf("%s\n", snap.Id()) + env.Out.Printf("%s\n", snap.Id()) case "labels": for _, l := range snap.Labels { - env.out.Printf("%s\n", l.String()) + env.Out.Printf("%s\n", l.String()) } case "actors": for _, a := range snap.Actors { - env.out.Printf("%s\n", a.DisplayName()) + env.Out.Printf("%s\n", a.DisplayName()) } case "participants": for _, p := range snap.Participants { - env.out.Printf("%s\n", p.DisplayName()) + env.Out.Printf("%s\n", p.DisplayName()) } case "shortId": - env.out.Printf("%s\n", snap.Id().Human()) + env.Out.Printf("%s\n", snap.Id().Human()) case "status": - env.out.Printf("%s\n", snap.Status) + env.Out.Printf("%s\n", snap.Status) case "title": - env.out.Printf("%s\n", snap.Title) + env.Out.Printf("%s\n", snap.Title) default: return fmt.Errorf("\nUnsupported field: %s\n", opts.fields) } @@ -109,20 +112,20 @@ func runShow(env *Env, opts showOptions, args []string) error { } } -func showDefaultFormatter(env *Env, snapshot *bug.Snapshot) error { +func showDefaultFormatter(env *execenv.Env, snapshot *bug.Snapshot) error { // Header - env.out.Printf("%s [%s] %s\n\n", + env.Out.Printf("%s [%s] %s\n\n", colors.Cyan(snapshot.Id().Human()), colors.Yellow(snapshot.Status), snapshot.Title, ) - env.out.Printf("%s opened this issue %s\n", + env.Out.Printf("%s opened this issue %s\n", colors.Magenta(snapshot.Author.DisplayName()), snapshot.CreateTime.String(), ) - env.out.Printf("This was last edited at %s\n\n", + env.Out.Printf("This was last edited at %s\n\n", snapshot.EditTime().String(), ) @@ -132,7 +135,7 @@ func showDefaultFormatter(env *Env, snapshot *bug.Snapshot) error { labels[i] = string(snapshot.Labels[i]) } - env.out.Printf("labels: %s\n", + env.Out.Printf("labels: %s\n", strings.Join(labels, ", "), ) @@ -142,7 +145,7 @@ func showDefaultFormatter(env *Env, snapshot *bug.Snapshot) error { actors[i] = snapshot.Actors[i].DisplayName() } - env.out.Printf("actors: %s\n", + env.Out.Printf("actors: %s\n", strings.Join(actors, ", "), ) @@ -152,7 +155,7 @@ func showDefaultFormatter(env *Env, snapshot *bug.Snapshot) error { participants[i] = snapshot.Participants[i].DisplayName() } - env.out.Printf("participants: %s\n\n", + env.Out.Printf("participants: %s\n\n", strings.Join(participants, ", "), ) @@ -161,7 +164,7 @@ func showDefaultFormatter(env *Env, snapshot *bug.Snapshot) error { for i, comment := range snapshot.Comments { var message string - env.out.Printf("%s%s #%d %s <%s>\n\n", + env.Out.Printf("%s%s #%d %s <%s>\n\n", indent, comment.CombinedId().Human(), i, @@ -175,7 +178,7 @@ func showDefaultFormatter(env *Env, snapshot *bug.Snapshot) error { message = comment.Message } - env.out.Printf("%s%s\n\n\n", + env.Out.Printf("%s%s\n\n\n", indent, message, ) @@ -185,85 +188,85 @@ func showDefaultFormatter(env *Env, snapshot *bug.Snapshot) error { } type JSONBugSnapshot struct { - Id string `json:"id"` - HumanId string `json:"human_id"` - CreateTime JSONTime `json:"create_time"` - EditTime JSONTime `json:"edit_time"` - Status string `json:"status"` - Labels []bug.Label `json:"labels"` - Title string `json:"title"` - Author JSONIdentity `json:"author"` - Actors []JSONIdentity `json:"actors"` - Participants []JSONIdentity `json:"participants"` - Comments []JSONComment `json:"comments"` + Id string `json:"id"` + HumanId string `json:"human_id"` + CreateTime cmdjson.Time `json:"create_time"` + EditTime cmdjson.Time `json:"edit_time"` + Status string `json:"status"` + Labels []bug.Label `json:"labels"` + Title string `json:"title"` + Author cmdjson.Identity `json:"author"` + Actors []cmdjson.Identity `json:"actors"` + Participants []cmdjson.Identity `json:"participants"` + Comments []JSONBugComment `json:"comments"` } -type JSONComment struct { - Id string `json:"id"` - HumanId string `json:"human_id"` - Author JSONIdentity `json:"author"` - Message string `json:"message"` +type JSONBugComment struct { + Id string `json:"id"` + HumanId string `json:"human_id"` + Author cmdjson.Identity `json:"author"` + Message string `json:"message"` } -func NewJSONComment(comment bug.Comment) JSONComment { - return JSONComment{ +func NewJSONComment(comment bug.Comment) JSONBugComment { + return JSONBugComment{ Id: comment.CombinedId().String(), HumanId: comment.CombinedId().Human(), - Author: NewJSONIdentity(comment.Author), + Author: cmdjson.NewIdentity(comment.Author), Message: comment.Message, } } -func showJsonFormatter(env *Env, snapshot *bug.Snapshot) error { +func showJsonFormatter(env *execenv.Env, snapshot *bug.Snapshot) error { jsonBug := JSONBugSnapshot{ Id: snapshot.Id().String(), HumanId: snapshot.Id().Human(), - CreateTime: NewJSONTime(snapshot.CreateTime, 0), - EditTime: NewJSONTime(snapshot.EditTime(), 0), + CreateTime: cmdjson.NewTime(snapshot.CreateTime, 0), + EditTime: cmdjson.NewTime(snapshot.EditTime(), 0), Status: snapshot.Status.String(), Labels: snapshot.Labels, Title: snapshot.Title, - Author: NewJSONIdentity(snapshot.Author), + Author: cmdjson.NewIdentity(snapshot.Author), } - jsonBug.Actors = make([]JSONIdentity, len(snapshot.Actors)) + jsonBug.Actors = make([]cmdjson.Identity, len(snapshot.Actors)) for i, element := range snapshot.Actors { - jsonBug.Actors[i] = NewJSONIdentity(element) + jsonBug.Actors[i] = cmdjson.NewIdentity(element) } - jsonBug.Participants = make([]JSONIdentity, len(snapshot.Participants)) + jsonBug.Participants = make([]cmdjson.Identity, len(snapshot.Participants)) for i, element := range snapshot.Participants { - jsonBug.Participants[i] = NewJSONIdentity(element) + jsonBug.Participants[i] = cmdjson.NewIdentity(element) } - jsonBug.Comments = make([]JSONComment, len(snapshot.Comments)) + jsonBug.Comments = make([]JSONBugComment, len(snapshot.Comments)) for i, comment := range snapshot.Comments { jsonBug.Comments[i] = NewJSONComment(comment) } jsonObject, _ := json.MarshalIndent(jsonBug, "", " ") - env.out.Printf("%s\n", jsonObject) + env.Out.Printf("%s\n", jsonObject) return nil } -func showOrgModeFormatter(env *Env, snapshot *bug.Snapshot) error { +func showOrgModeFormatter(env *execenv.Env, snapshot *bug.Snapshot) error { // Header - env.out.Printf("%s [%s] %s\n", + env.Out.Printf("%s [%s] %s\n", snapshot.Id().Human(), snapshot.Status, snapshot.Title, ) - env.out.Printf("* Author: %s\n", + env.Out.Printf("* Author: %s\n", snapshot.Author.DisplayName(), ) - env.out.Printf("* Creation Time: %s\n", + env.Out.Printf("* Creation Time: %s\n", snapshot.CreateTime.String(), ) - env.out.Printf("* Last Edit: %s\n", + env.Out.Printf("* Last Edit: %s\n", snapshot.EditTime().String(), ) @@ -273,9 +276,9 @@ func showOrgModeFormatter(env *Env, snapshot *bug.Snapshot) error { labels[i] = string(label) } - env.out.Printf("* Labels:\n") + env.Out.Printf("* Labels:\n") if len(labels) > 0 { - env.out.Printf("** %s\n", + env.Out.Printf("** %s\n", strings.Join(labels, "\n** "), ) } @@ -289,7 +292,7 @@ func showOrgModeFormatter(env *Env, snapshot *bug.Snapshot) error { ) } - env.out.Printf("* Actors:\n** %s\n", + env.Out.Printf("* Actors:\n** %s\n", strings.Join(actors, "\n** "), ) @@ -302,15 +305,15 @@ func showOrgModeFormatter(env *Env, snapshot *bug.Snapshot) error { ) } - env.out.Printf("* Participants:\n** %s\n", + env.Out.Printf("* Participants:\n** %s\n", strings.Join(participants, "\n** "), ) - env.out.Printf("* Comments:\n") + env.Out.Printf("* Comments:\n") for i, comment := range snapshot.Comments { var message string - env.out.Printf("** #%d %s\n", + env.Out.Printf("** #%d %s\n", i, comment.Author.DisplayName()) if comment.Message == "" { @@ -319,7 +322,7 @@ func showOrgModeFormatter(env *Env, snapshot *bug.Snapshot) error { message = strings.ReplaceAll(comment.Message, "\n", "\n: ") } - env.out.Printf(": %s\n", message) + env.Out.Printf(": %s\n", message) } return nil diff --git a/commands/bug/bug_status.go b/commands/bug/bug_status.go new file mode 100644 index 0000000000000000000000000000000000000000..b05f862cd9ba19ecefbc5ef3b6d179f88fe72adc --- /dev/null +++ b/commands/bug/bug_status.go @@ -0,0 +1,41 @@ +package bugcmd + +import ( + "github.com/spf13/cobra" + + "github.com/MichaelMure/git-bug/commands/bug/select" + "github.com/MichaelMure/git-bug/commands/completion" + "github.com/MichaelMure/git-bug/commands/execenv" +) + +func newBugStatusCommand() *cobra.Command { + env := execenv.NewEnv() + + cmd := &cobra.Command{ + Use: "status [BUG_ID]", + Short: "Display the status of a bug", + PreRunE: execenv.LoadBackend(env), + RunE: execenv.CloseBackend(env, func(cmd *cobra.Command, args []string) error { + return runBugStatus(env, args) + }), + ValidArgsFunction: completion.Bug(env), + } + + cmd.AddCommand(newBugStatusCloseCommand()) + cmd.AddCommand(newBugStatusOpenCommand()) + + return cmd +} + +func runBugStatus(env *execenv.Env, args []string) error { + b, args, err := _select.ResolveBug(env.Backend, args) + if err != nil { + return err + } + + snap := b.Snapshot() + + env.Out.Println(snap.Status) + + return nil +} diff --git a/commands/bug/bug_status_close.go b/commands/bug/bug_status_close.go new file mode 100644 index 0000000000000000000000000000000000000000..fcd4792206e8f43b562ee86b489009b625c8766a --- /dev/null +++ b/commands/bug/bug_status_close.go @@ -0,0 +1,39 @@ +package bugcmd + +import ( + "github.com/spf13/cobra" + + "github.com/MichaelMure/git-bug/commands/bug/select" + "github.com/MichaelMure/git-bug/commands/completion" + "github.com/MichaelMure/git-bug/commands/execenv" +) + +func newBugStatusCloseCommand() *cobra.Command { + env := execenv.NewEnv() + + cmd := &cobra.Command{ + Use: "close [BUG_ID]", + Short: "Mark a bug as closed", + PreRunE: execenv.LoadBackendEnsureUser(env), + RunE: execenv.CloseBackend(env, func(cmd *cobra.Command, args []string) error { + return runBugStatusClose(env, args) + }), + ValidArgsFunction: completion.Bug(env), + } + + return cmd +} + +func runBugStatusClose(env *execenv.Env, args []string) error { + b, args, err := _select.ResolveBug(env.Backend, args) + if err != nil { + return err + } + + _, err = b.Close() + if err != nil { + return err + } + + return b.Commit() +} diff --git a/commands/bug/bug_status_open.go b/commands/bug/bug_status_open.go new file mode 100644 index 0000000000000000000000000000000000000000..e686add1ad473fc83f8933e2b41272ae6ee81355 --- /dev/null +++ b/commands/bug/bug_status_open.go @@ -0,0 +1,39 @@ +package bugcmd + +import ( + "github.com/spf13/cobra" + + "github.com/MichaelMure/git-bug/commands/bug/select" + "github.com/MichaelMure/git-bug/commands/completion" + "github.com/MichaelMure/git-bug/commands/execenv" +) + +func newBugStatusOpenCommand() *cobra.Command { + env := execenv.NewEnv() + + cmd := &cobra.Command{ + Use: "open [BUG_ID]", + Short: "Mark a bug as open", + PreRunE: execenv.LoadBackendEnsureUser(env), + RunE: execenv.CloseBackend(env, func(cmd *cobra.Command, args []string) error { + return runBugStatusOpen(env, args) + }), + ValidArgsFunction: completion.Bug(env), + } + + return cmd +} + +func runBugStatusOpen(env *execenv.Env, args []string) error { + b, args, err := _select.ResolveBug(env.Backend, args) + if err != nil { + return err + } + + _, err = b.Open() + if err != nil { + return err + } + + return b.Commit() +} diff --git a/commands/ls_test.go b/commands/bug/bug_test.go similarity index 77% rename from commands/ls_test.go rename to commands/bug/bug_test.go index 22adc1ce1e46b274d55afcd23cbd23766f774efc..aef0346d1938228be9502857b23239c5ef10c68a 100644 --- a/commands/ls_test.go +++ b/commands/bug/bug_test.go @@ -1,4 +1,4 @@ -package commands +package bugcmd import ( "encoding/json" @@ -6,6 +6,8 @@ import ( "testing" "github.com/stretchr/testify/require" + + "github.com/MichaelMure/git-bug/commands/bug/testenv" ) func Test_repairQuery(t *testing.T) { @@ -44,7 +46,7 @@ func Test_repairQuery(t *testing.T) { } } -func TestLs_Format(t *testing.T) { +func TestBug_Format(t *testing.T) { const expOrgMode = `^#+TODO: OPEN | CLOSED [*] OPEN [0-9a-f]{7} \[\d\d\d\d-\d\d-\d\d [[:alpha:]]{3} \d\d:\d\d\] John Doe: this is a bug title :: [*]{2} Last Edited: \[\d\d\d\d-\d\d-\d\d [[:alpha:]]{3} \d\d:\d\d\] @@ -66,7 +68,7 @@ $` } for _, testcase := range cases { - opts := lsOptions{ + opts := bugOptions{ sortDirection: "asc", sortBy: "creation", outputFormat: testcase.format, @@ -75,26 +77,26 @@ $` name := fmt.Sprintf("with %s format", testcase.format) t.Run(name, func(t *testing.T) { - env, _ := newTestEnvAndBug(t) + env, _ := testenv.NewTestEnvAndBug(t) - require.NoError(t, runLs(env.env, opts, []string{})) - require.Regexp(t, testcase.exp, env.out.String()) + require.NoError(t, runBug(env, opts, []string{})) + require.Regexp(t, testcase.exp, env.Out.String()) }) } t.Run("with JSON format", func(t *testing.T) { - opts := lsOptions{ + opts := bugOptions{ sortDirection: "asc", sortBy: "creation", outputFormat: "json", } - env, _ := newTestEnvAndBug(t) + env, _ := testenv.NewTestEnvAndBug(t) - require.NoError(t, runLs(env.env, opts, []string{})) + require.NoError(t, runBug(env, opts, []string{})) - bugs := []JSONBugExcerpt{} - require.NoError(t, json.Unmarshal(env.out.Bytes(), &bugs)) + var bugs []JSONBugExcerpt + require.NoError(t, json.Unmarshal(env.Out.Bytes(), &bugs)) require.Len(t, bugs, 1) }) diff --git a/commands/bug/bug_title.go b/commands/bug/bug_title.go new file mode 100644 index 0000000000000000000000000000000000000000..98809b6041ce082d61792861fa5d286bab541f7a --- /dev/null +++ b/commands/bug/bug_title.go @@ -0,0 +1,40 @@ +package bugcmd + +import ( + "github.com/spf13/cobra" + + "github.com/MichaelMure/git-bug/commands/bug/select" + "github.com/MichaelMure/git-bug/commands/completion" + "github.com/MichaelMure/git-bug/commands/execenv" +) + +func newBugTitleCommand() *cobra.Command { + env := execenv.NewEnv() + + cmd := &cobra.Command{ + Use: "title [BUG_ID]", + Short: "Display the title of a bug", + PreRunE: execenv.LoadBackend(env), + RunE: execenv.CloseBackend(env, func(cmd *cobra.Command, args []string) error { + return runBugTitle(env, args) + }), + ValidArgsFunction: completion.Bug(env), + } + + cmd.AddCommand(newBugTitleEditCommand()) + + return cmd +} + +func runBugTitle(env *execenv.Env, args []string) error { + b, args, err := _select.ResolveBug(env.Backend, args) + if err != nil { + return err + } + + snap := b.Snapshot() + + env.Out.Println(snap.Title) + + return nil +} diff --git a/commands/bug/bug_title_edit.go b/commands/bug/bug_title_edit.go new file mode 100644 index 0000000000000000000000000000000000000000..e71330a1e6b4b75a285b0f358e932313d3efd579 --- /dev/null +++ b/commands/bug/bug_title_edit.go @@ -0,0 +1,76 @@ +package bugcmd + +import ( + "github.com/spf13/cobra" + + "github.com/MichaelMure/git-bug/commands/bug/select" + "github.com/MichaelMure/git-bug/commands/completion" + "github.com/MichaelMure/git-bug/commands/execenv" + "github.com/MichaelMure/git-bug/commands/input" + "github.com/MichaelMure/git-bug/util/text" +) + +type bugTitleEditOptions struct { + title string + nonInteractive bool +} + +func newBugTitleEditCommand() *cobra.Command { + env := execenv.NewEnv() + options := bugTitleEditOptions{} + + cmd := &cobra.Command{ + Use: "edit [BUG_ID]", + Short: "Edit a title of a bug", + PreRunE: execenv.LoadBackendEnsureUser(env), + RunE: execenv.CloseBackend(env, func(cmd *cobra.Command, args []string) error { + return runBugTitleEdit(env, options, args) + }), + ValidArgsFunction: completion.Bug(env), + } + + flags := cmd.Flags() + flags.SortFlags = false + + flags.StringVarP(&options.title, "title", "t", "", + "Provide a title to describe the issue", + ) + flags.BoolVar(&options.nonInteractive, "non-interactive", false, "Do not ask for user input") + + return cmd +} + +func runBugTitleEdit(env *execenv.Env, opts bugTitleEditOptions, args []string) error { + b, args, err := _select.ResolveBug(env.Backend, args) + if err != nil { + return err + } + + snap := b.Snapshot() + + if opts.title == "" { + if opts.nonInteractive { + env.Err.Println("No title given. Use -m or -F option to specify a title. Aborting.") + return nil + } + opts.title, err = input.BugTitleEditorInput(env.Repo, snap.Title) + if err == input.ErrEmptyTitle { + env.Out.Println("Empty title, aborting.") + return nil + } + if err != nil { + return err + } + } + + if opts.title == snap.Title { + env.Err.Println("No change, aborting.") + } + + _, err = b.SetTitle(text.CleanupOneLine(opts.title)) + if err != nil { + return err + } + + return b.Commit() +} diff --git a/commands/select/select.go b/commands/bug/select/select.go similarity index 100% rename from commands/select/select.go rename to commands/bug/select/select.go diff --git a/commands/select/select_test.go b/commands/bug/select/select_test.go similarity index 100% rename from commands/select/select_test.go rename to commands/bug/select/select_test.go diff --git a/commands/testdata/comment/add-0-golden.txt b/commands/bug/testdata/comment/add-0-golden.txt similarity index 100% rename from commands/testdata/comment/add-0-golden.txt rename to commands/bug/testdata/comment/add-0-golden.txt diff --git a/commands/testdata/comment/add-1-golden.txt b/commands/bug/testdata/comment/add-1-golden.txt similarity index 100% rename from commands/testdata/comment/add-1-golden.txt rename to commands/bug/testdata/comment/add-1-golden.txt diff --git a/commands/testdata/comment/edit-0-golden.txt b/commands/bug/testdata/comment/edit-0-golden.txt similarity index 100% rename from commands/testdata/comment/edit-0-golden.txt rename to commands/bug/testdata/comment/edit-0-golden.txt diff --git a/commands/testdata/comment/edit-1-golden.txt b/commands/bug/testdata/comment/edit-1-golden.txt similarity index 100% rename from commands/testdata/comment/edit-1-golden.txt rename to commands/bug/testdata/comment/edit-1-golden.txt diff --git a/commands/testdata/comment/message-only-0-golden.txt b/commands/bug/testdata/comment/message-only-0-golden.txt similarity index 100% rename from commands/testdata/comment/message-only-0-golden.txt rename to commands/bug/testdata/comment/message-only-0-golden.txt diff --git a/commands/bug/testenv/testenv.go b/commands/bug/testenv/testenv.go new file mode 100644 index 0000000000000000000000000000000000000000..10f20950ddf817049f1af62f6ec5c35718d62368 --- /dev/null +++ b/commands/bug/testenv/testenv.go @@ -0,0 +1,63 @@ +package testenv + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/MichaelMure/git-bug/commands/execenv" + "github.com/MichaelMure/git-bug/entity" +) + +const ( + testUserName = "John Doe" + testUserEmail = "jdoe@example.com" +) + +func NewTestEnvAndUser(t *testing.T) (*execenv.Env, entity.Id) { + t.Helper() + + testEnv := execenv.NewTestEnv(t) + + i, err := testEnv.Backend.NewIdentity(testUserName, testUserEmail) + require.NoError(t, err) + + err = testEnv.Backend.SetUserIdentity(i) + require.NoError(t, err) + + return testEnv, i.Id() +} + +const ( + testBugTitle = "this is a bug title" + testBugMessage = "this is a bug message" +) + +func NewTestEnvAndBug(t *testing.T) (*execenv.Env, entity.Id) { + t.Helper() + + testEnv, _ := NewTestEnvAndUser(t) + + b, _, err := testEnv.Backend.NewBug(testBugTitle, testBugMessage) + require.NoError(t, err) + + return testEnv, b.Id() +} + +const ( + testCommentMessage = "this is a bug comment" +) + +func NewTestEnvAndBugWithComment(t *testing.T) (*execenv.Env, entity.Id, entity.CombinedId) { + t.Helper() + + env, bugID := NewTestEnvAndBug(t) + + b, err := env.Backend.ResolveBug(bugID) + require.NoError(t, err) + + commentId, _, err := b.AddComment(testCommentMessage) + require.NoError(t, err) + + return env, bugID, commentId +} diff --git a/commands/json_common.go b/commands/cmdjson/json_common.go similarity index 59% rename from commands/json_common.go rename to commands/cmdjson/json_common.go index 3ceee1ec0bb7277824014bbd05ba429f7026a1c9..60e6e751a7fe99c3b72707a5fe381d2639c2bbd6 100644 --- a/commands/json_common.go +++ b/commands/cmdjson/json_common.go @@ -1,4 +1,4 @@ -package commands +package cmdjson import ( "time" @@ -8,15 +8,15 @@ import ( "github.com/MichaelMure/git-bug/util/lamport" ) -type JSONIdentity struct { +type Identity struct { Id string `json:"id"` HumanId string `json:"human_id"` Name string `json:"name"` Login string `json:"login"` } -func NewJSONIdentity(i identity.Interface) JSONIdentity { - return JSONIdentity{ +func NewIdentity(i identity.Interface) Identity { + return Identity{ Id: i.Id().String(), HumanId: i.Id().Human(), Name: i.Name(), @@ -24,8 +24,8 @@ func NewJSONIdentity(i identity.Interface) JSONIdentity { } } -func NewJSONIdentityFromExcerpt(excerpt *cache.IdentityExcerpt) JSONIdentity { - return JSONIdentity{ +func NewIdentityFromExcerpt(excerpt *cache.IdentityExcerpt) Identity { + return Identity{ Id: excerpt.Id.String(), HumanId: excerpt.Id.Human(), Name: excerpt.Name, @@ -33,21 +33,14 @@ func NewJSONIdentityFromExcerpt(excerpt *cache.IdentityExcerpt) JSONIdentity { } } -func NewJSONIdentityFromLegacyExcerpt(excerpt *cache.LegacyAuthorExcerpt) JSONIdentity { - return JSONIdentity{ - Name: excerpt.Name, - Login: excerpt.Login, - } -} - -type JSONTime struct { +type Time struct { Timestamp int64 `json:"timestamp"` Time time.Time `json:"time"` Lamport lamport.Time `json:"lamport,omitempty"` } -func NewJSONTime(t time.Time, l lamport.Time) JSONTime { - return JSONTime{ +func NewTime(t time.Time, l lamport.Time) Time { + return Time{ Timestamp: t.Unix(), Time: t, Lamport: l, diff --git a/commands/cmdtest/golden.go b/commands/cmdtest/golden.go new file mode 100644 index 0000000000000000000000000000000000000000..c9a21f73be5e9d1866950910e81729f7b9318faa --- /dev/null +++ b/commands/cmdtest/golden.go @@ -0,0 +1,5 @@ +package cmdtest + +import "flag" + +var Update = flag.Bool("Update", false, "Update golden files") diff --git a/commands/commands.go b/commands/commands.go index 49c960abb4da73f4823aa246ad11cfd3d849a62d..7d2fc37d7feef4edc198734198a74d6238e982e5 100644 --- a/commands/commands.go +++ b/commands/commands.go @@ -4,6 +4,8 @@ import ( "sort" "github.com/spf13/cobra" + + "github.com/MichaelMure/git-bug/commands/execenv" ) type commandOptions struct { @@ -11,7 +13,7 @@ type commandOptions struct { } func newCommandsCommand() *cobra.Command { - env := newEnv() + env := execenv.NewEnv() options := commandOptions{} cmd := &cobra.Command{ @@ -32,7 +34,7 @@ func newCommandsCommand() *cobra.Command { return cmd } -func runCommands(env *Env, opts commandOptions) error { +func runCommands(env *execenv.Env, opts commandOptions) error { first := true var allCmds []*cobra.Command @@ -49,24 +51,24 @@ func runCommands(env *Env, opts commandOptions) error { for _, cmd := range allCmds { if !first { - env.out.Println() + env.Out.Println() } first = false if opts.desc { - env.out.Printf("# %s\n", cmd.Short) + env.Out.Printf("# %s\n", cmd.Short) } - env.out.Print(cmd.UseLine()) + env.Out.Print(cmd.UseLine()) if opts.desc { - env.out.Println() + env.Out.Println() } } if !opts.desc { - env.out.Println() + env.Out.Println() } return nil diff --git a/commands/comment.go b/commands/comment.go deleted file mode 100644 index 7cab447cc7c0144e824bf26bafba9ef78df641db..0000000000000000000000000000000000000000 --- a/commands/comment.go +++ /dev/null @@ -1,50 +0,0 @@ -package commands - -import ( - text "github.com/MichaelMure/go-term-text" - "github.com/spf13/cobra" - - _select "github.com/MichaelMure/git-bug/commands/select" - "github.com/MichaelMure/git-bug/util/colors" -) - -func newCommentCommand() *cobra.Command { - env := newEnv() - - cmd := &cobra.Command{ - Use: "comment [ID]", - Short: "Display or add comments to a bug.", - PreRunE: loadBackend(env), - RunE: closeBackend(env, func(cmd *cobra.Command, args []string) error { - return runComment(env, args) - }), - ValidArgsFunction: completeBug(env), - } - - cmd.AddCommand(newCommentAddCommand()) - cmd.AddCommand(newCommentEditCommand()) - - return cmd -} - -func runComment(env *Env, args []string) error { - b, args, err := _select.ResolveBug(env.backend, args) - if err != nil { - return err - } - - snap := b.Snapshot() - - for i, comment := range snap.Comments { - if i != 0 { - env.out.Println() - } - - env.out.Printf("Author: %s\n", colors.Magenta(comment.Author.DisplayName())) - env.out.Printf("Id: %s\n", colors.Cyan(comment.CombinedId().Human())) - env.out.Printf("Date: %s\n\n", comment.FormatTime()) - env.out.Println(text.LeftPadLines(comment.Message, 4)) - } - - return nil -} diff --git a/commands/comment_add_test.go b/commands/comment_add_test.go deleted file mode 100644 index 34ff3743f5dc9d26236a557014024ddd031c6067..0000000000000000000000000000000000000000 --- a/commands/comment_add_test.go +++ /dev/null @@ -1,33 +0,0 @@ -package commands - -import ( - "testing" - - "github.com/stretchr/testify/require" -) - -func newTestEnvAndBugWithComment(t *testing.T) (*testEnv, string, string) { - t.Helper() - - env, bugID := newTestEnvAndBug(t) - - opts := commentAddOptions{ - message: "this is a bug comment", - } - require.NoError(t, runCommentAdd(env.env, opts, []string{bugID})) - require.NoError(t, runComment(env.env, []string{bugID})) - comments := parseComments(t, env) - require.Len(t, comments, 2) - - env.out.Reset() - - return env, bugID, comments[1].id -} - -func TestCommentAdd(t *testing.T) { - const golden = "testdata/comment/add" - - env, bugID, _ := newTestEnvAndBugWithComment(t) - require.NoError(t, runComment(env.env, []string{bugID})) - requireCommentsEqual(t, golden, env) -} diff --git a/commands/comment_edit_test.go b/commands/comment_edit_test.go deleted file mode 100644 index 50c1850bc282733559040bbc82f51a3301f182d0..0000000000000000000000000000000000000000 --- a/commands/comment_edit_test.go +++ /dev/null @@ -1,21 +0,0 @@ -package commands - -import ( - "testing" - - "github.com/stretchr/testify/require" -) - -func TestCommentEdit(t *testing.T) { - const golden = "testdata/comment/edit" - - env, bugID, commentID := newTestEnvAndBugWithComment(t) - - opts := commentEditOptions{ - message: "this is an altered bug comment", - } - require.NoError(t, runCommentEdit(env.env, opts, []string{commentID})) - - require.NoError(t, runComment(env.env, []string{bugID})) - requireCommentsEqual(t, golden, env) -} diff --git a/commands/helper_completion.go b/commands/completion/helper_completion.go similarity index 70% rename from commands/helper_completion.go rename to commands/completion/helper_completion.go index 847a02883da4bc216ee6532b74dc9b71d0cdb55d..27fbd61597cdbd5b7922da5753337e798c4eb402 100644 --- a/commands/helper_completion.go +++ b/commands/completion/helper_completion.go @@ -1,4 +1,4 @@ -package commands +package completion import ( "fmt" @@ -10,28 +10,29 @@ import ( "github.com/MichaelMure/git-bug/bridge" "github.com/MichaelMure/git-bug/bridge/core/auth" "github.com/MichaelMure/git-bug/cache" - _select "github.com/MichaelMure/git-bug/commands/select" + "github.com/MichaelMure/git-bug/commands/bug/select" + "github.com/MichaelMure/git-bug/commands/execenv" "github.com/MichaelMure/git-bug/entities/bug" ) -type validArgsFunction func(cmd *cobra.Command, args []string, toComplete string) (completions []string, directives cobra.ShellCompDirective) +type ValidArgsFunction func(cmd *cobra.Command, args []string, toComplete string) (completions []string, directives cobra.ShellCompDirective) -func completionHandlerError(err error) (completions []string, directives cobra.ShellCompDirective) { +func handleError(err error) (completions []string, directives cobra.ShellCompDirective) { return nil, cobra.ShellCompDirectiveError } -func completeBridge(env *Env) validArgsFunction { +func Bridge(env *execenv.Env) ValidArgsFunction { return func(cmd *cobra.Command, args []string, toComplete string) (completions []string, directives cobra.ShellCompDirective) { - if err := loadBackend(env)(cmd, args); err != nil { - return completionHandlerError(err) + if err := execenv.LoadBackend(env)(cmd, args); err != nil { + return handleError(err) } defer func() { - _ = env.backend.Close() + _ = env.Backend.Close() }() - bridges, err := bridge.ConfiguredBridges(env.backend) + bridges, err := bridge.ConfiguredBridges(env.Backend) if err != nil { - return completionHandlerError(err) + return handleError(err) } completions = make([]string, len(bridges)) @@ -43,18 +44,18 @@ func completeBridge(env *Env) validArgsFunction { } } -func completeBridgeAuth(env *Env) validArgsFunction { +func BridgeAuth(env *execenv.Env) ValidArgsFunction { return func(cmd *cobra.Command, args []string, toComplete string) (completions []string, directives cobra.ShellCompDirective) { - if err := loadBackend(env)(cmd, args); err != nil { - return completionHandlerError(err) + if err := execenv.LoadBackend(env)(cmd, args); err != nil { + return handleError(err) } defer func() { - _ = env.backend.Close() + _ = env.Backend.Close() }() - creds, err := auth.List(env.backend) + creds, err := auth.List(env.Backend) if err != nil { - return completionHandlerError(err) + return handleError(err) } completions = make([]string, len(creds)) @@ -73,27 +74,27 @@ func completeBridgeAuth(env *Env) validArgsFunction { } } -func completeBug(env *Env) validArgsFunction { +func Bug(env *execenv.Env) ValidArgsFunction { return func(cmd *cobra.Command, args []string, toComplete string) (completions []string, directives cobra.ShellCompDirective) { - if err := loadBackend(env)(cmd, args); err != nil { - return completionHandlerError(err) + if err := execenv.LoadBackend(env)(cmd, args); err != nil { + return handleError(err) } defer func() { - _ = env.backend.Close() + _ = env.Backend.Close() }() - return completeBugWithBackend(env.backend, toComplete) + return bugWithBackend(env.Backend, toComplete) } } -func completeBugWithBackend(backend *cache.RepoCache, toComplete string) (completions []string, directives cobra.ShellCompDirective) { +func bugWithBackend(backend *cache.RepoCache, toComplete string) (completions []string, directives cobra.ShellCompDirective) { allIds := backend.AllBugsIds() bugExcerpt := make([]*cache.BugExcerpt, len(allIds)) for i, id := range allIds { var err error bugExcerpt[i], err = backend.ResolveBugExcerpt(id) if err != nil { - return completionHandlerError(err) + return handleError(err) } } @@ -106,22 +107,22 @@ func completeBugWithBackend(backend *cache.RepoCache, toComplete string) (comple return completions, cobra.ShellCompDirectiveNoFileComp } -func completeBugAndLabels(env *Env, addOrRemove bool) validArgsFunction { +func BugAndLabels(env *execenv.Env, addOrRemove bool) ValidArgsFunction { return func(cmd *cobra.Command, args []string, toComplete string) (completions []string, directives cobra.ShellCompDirective) { - if err := loadBackend(env)(cmd, args); err != nil { - return completionHandlerError(err) + if err := execenv.LoadBackend(env)(cmd, args); err != nil { + return handleError(err) } defer func() { - _ = env.backend.Close() + _ = env.Backend.Close() }() - b, args, err := _select.ResolveBug(env.backend, args) + b, args, err := _select.ResolveBug(env.Backend, args) if err == _select.ErrNoValidId { // we need a bug first to complete labels - return completeBugWithBackend(env.backend, toComplete) + return bugWithBackend(env.Backend, toComplete) } if err != nil { - return completionHandlerError(err) + return handleError(err) } snap := b.Snapshot() @@ -137,7 +138,7 @@ func completeBugAndLabels(env *Env, addOrRemove bool) validArgsFunction { seenLabels[label] = true } - allLabels := env.backend.ValidLabels() + allLabels := env.Backend.ValidLabels() labels = make([]bug.Label, 0, len(allLabels)) for _, label := range allLabels { if !seenLabels[label] { @@ -162,24 +163,24 @@ func completeBugAndLabels(env *Env, addOrRemove bool) validArgsFunction { } } -func completeFrom(choices []string) validArgsFunction { +func From(choices []string) ValidArgsFunction { return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { return choices, cobra.ShellCompDirectiveNoFileComp } } -func completeGitRemote(env *Env) validArgsFunction { +func GitRemote(env *execenv.Env) ValidArgsFunction { return func(cmd *cobra.Command, args []string, toComplete string) (completions []string, directives cobra.ShellCompDirective) { - if err := loadBackend(env)(cmd, args); err != nil { - return completionHandlerError(err) + if err := execenv.LoadBackend(env)(cmd, args); err != nil { + return handleError(err) } defer func() { - _ = env.backend.Close() + _ = env.Backend.Close() }() - remoteMap, err := env.backend.GetRemotes() + remoteMap, err := env.Backend.GetRemotes() if err != nil { - return completionHandlerError(err) + return handleError(err) } completions = make([]string, 0, len(remoteMap)) for remote, url := range remoteMap { @@ -190,16 +191,16 @@ func completeGitRemote(env *Env) validArgsFunction { } } -func completeLabel(env *Env) validArgsFunction { +func Label(env *execenv.Env) ValidArgsFunction { return func(cmd *cobra.Command, args []string, toComplete string) (completions []string, directives cobra.ShellCompDirective) { - if err := loadBackend(env)(cmd, args); err != nil { - return completionHandlerError(err) + if err := execenv.LoadBackend(env)(cmd, args); err != nil { + return handleError(err) } defer func() { - _ = env.backend.Close() + _ = env.Backend.Close() }() - labels := env.backend.ValidLabels() + labels := env.Backend.ValidLabels() completions = make([]string, len(labels)) for i, label := range labels { if strings.Contains(label.String(), " ") { @@ -212,7 +213,7 @@ func completeLabel(env *Env) validArgsFunction { } } -func completeLs(env *Env) validArgsFunction { +func Ls(env *execenv.Env) ValidArgsFunction { return func(cmd *cobra.Command, args []string, toComplete string) (completions []string, directives cobra.ShellCompDirective) { if strings.HasPrefix(toComplete, "status:") { completions = append(completions, "status:open\tOpen bugs") @@ -230,11 +231,11 @@ func completeLs(env *Env) validArgsFunction { } if needBackend { - if err := loadBackend(env)(cmd, args); err != nil { - return completionHandlerError(err) + if err := execenv.LoadBackend(env)(cmd, args); err != nil { + return handleError(err) } defer func() { - _ = env.backend.Close() + _ = env.Backend.Close() }() } @@ -242,12 +243,12 @@ func completeLs(env *Env) validArgsFunction { if !strings.HasPrefix(toComplete, key) { continue } - ids := env.backend.AllIdentityIds() + ids := env.Backend.AllIdentityIds() completions = make([]string, len(ids)) for i, id := range ids { - user, err := env.backend.ResolveIdentityExcerpt(id) + user, err := env.Backend.ResolveIdentityExcerpt(id) if err != nil { - return completionHandlerError(err) + return handleError(err) } var handle string if user.Login != "" { @@ -265,7 +266,7 @@ func completeLs(env *Env) validArgsFunction { if !strings.HasPrefix(toComplete, key) { continue } - labels := env.backend.ValidLabels() + labels := env.Backend.ValidLabels() completions = make([]string, len(labels)) for i, label := range labels { if strings.Contains(label.String(), " ") { @@ -290,21 +291,21 @@ func completeLs(env *Env) validArgsFunction { } } -func completeUser(env *Env) validArgsFunction { +func User(env *execenv.Env) ValidArgsFunction { return func(cmd *cobra.Command, args []string, toComplete string) (completions []string, directives cobra.ShellCompDirective) { - if err := loadBackend(env)(cmd, args); err != nil { - return completionHandlerError(err) + if err := execenv.LoadBackend(env)(cmd, args); err != nil { + return handleError(err) } defer func() { - _ = env.backend.Close() + _ = env.Backend.Close() }() - ids := env.backend.AllIdentityIds() + ids := env.Backend.AllIdentityIds() completions = make([]string, len(ids)) for i, id := range ids { - user, err := env.backend.ResolveIdentityExcerpt(id) + user, err := env.Backend.ResolveIdentityExcerpt(id) if err != nil { - return completionHandlerError(err) + return handleError(err) } completions[i] = user.Id.Human() + "\t" + user.DisplayName() } @@ -312,21 +313,21 @@ func completeUser(env *Env) validArgsFunction { } } -func completeUserForQuery(env *Env) validArgsFunction { +func UserForQuery(env *execenv.Env) ValidArgsFunction { return func(cmd *cobra.Command, args []string, toComplete string) (completions []string, directives cobra.ShellCompDirective) { - if err := loadBackend(env)(cmd, args); err != nil { - return completionHandlerError(err) + if err := execenv.LoadBackend(env)(cmd, args); err != nil { + return handleError(err) } defer func() { - _ = env.backend.Close() + _ = env.Backend.Close() }() - ids := env.backend.AllIdentityIds() + ids := env.Backend.AllIdentityIds() completions = make([]string, len(ids)) for i, id := range ids { - user, err := env.backend.ResolveIdentityExcerpt(id) + user, err := env.Backend.ResolveIdentityExcerpt(id) if err != nil { - return completionHandlerError(err) + return handleError(err) } var handle string if user.Login != "" { diff --git a/commands/deselect.go b/commands/deselect.go deleted file mode 100644 index d8c44dd75be2322bf78b29ea801020402610dc1e..0000000000000000000000000000000000000000 --- a/commands/deselect.go +++ /dev/null @@ -1,36 +0,0 @@ -package commands - -import ( - "github.com/spf13/cobra" - - "github.com/MichaelMure/git-bug/commands/select" -) - -func newDeselectCommand() *cobra.Command { - env := newEnv() - - cmd := &cobra.Command{ - Use: "deselect", - Short: "Clear the implicitly selected bug.", - Example: `git bug select 2f15 -git bug comment -git bug status -git bug deselect -`, - PreRunE: loadBackend(env), - RunE: closeBackend(env, func(cmd *cobra.Command, args []string) error { - return runDeselect(env) - }), - } - - return cmd -} - -func runDeselect(env *Env) error { - err := _select.Clear(env.backend) - if err != nil { - return err - } - - return nil -} diff --git a/commands/env_testing.go b/commands/env_testing.go deleted file mode 100644 index 1493a190d59b6ef9c58163523f9f1b0f477eebab..0000000000000000000000000000000000000000 --- a/commands/env_testing.go +++ /dev/null @@ -1,40 +0,0 @@ -package commands - -import ( - "bytes" - "testing" - - "github.com/stretchr/testify/require" - - "github.com/MichaelMure/git-bug/cache" - "github.com/MichaelMure/git-bug/repository" -) - -type testEnv struct { - env *Env - out *bytes.Buffer -} - -func newTestEnv(t *testing.T) *testEnv { - t.Helper() - - repo := repository.CreateGoGitTestRepo(t, false) - - buf := new(bytes.Buffer) - - backend, err := cache.NewRepoCache(repo) - require.NoError(t, err) - t.Cleanup(func() { - backend.Close() - }) - - return &testEnv{ - env: &Env{ - repo: repo, - backend: backend, - out: out{Writer: buf}, - err: out{Writer: buf}, - }, - out: buf, - } -} diff --git a/commands/env.go b/commands/execenv/env.go similarity index 51% rename from commands/env.go rename to commands/execenv/env.go index 11b91c4bf75179a425e6f4d80e198501aae1a0c4..a63f835a98f25e449a56ae67dd001e143826cdb5 100644 --- a/commands/env.go +++ b/commands/execenv/env.go @@ -1,4 +1,4 @@ -package commands +package execenv import ( "fmt" @@ -14,24 +14,43 @@ import ( "github.com/MichaelMure/git-bug/util/interrupt" ) +const RootCommandName = "git-bug" + const gitBugNamespace = "git-bug" // Env is the environment of a command type Env struct { - repo repository.ClockedRepo - backend *cache.RepoCache - out out - err out + Repo repository.ClockedRepo + Backend *cache.RepoCache + Out Out + Err Out } -func newEnv() *Env { +func NewEnv() *Env { return &Env{ - repo: nil, - out: out{Writer: os.Stdout}, - err: out{Writer: os.Stderr}, + Repo: nil, + Out: out{Writer: os.Stdout}, + Err: out{Writer: os.Stderr}, } } +type Out interface { + io.Writer + Printf(format string, a ...interface{}) + Print(a ...interface{}) + Println(a ...interface{}) + + // String returns what have been written in the output before, as a string. + // This only works in test scenario. + String() string + // Bytes returns what have been written in the output before, as []byte. + // This only works in test scenario. + Bytes() []byte + // Reset clear what has been recorded as written in the output before. + // This only works in test scenario. + Reset() +} + type out struct { io.Writer } @@ -48,17 +67,29 @@ func (o out) Println(a ...interface{}) { _, _ = fmt.Fprintln(o, a...) } -// loadRepo is a pre-run function that load the repository for use in a command -func loadRepo(env *Env) func(*cobra.Command, []string) error { +func (o out) String() string { + panic("only work with a test env") +} + +func (o out) Bytes() []byte { + panic("only work with a test env") +} + +func (o out) Reset() { + panic("only work with a test env") +} + +// LoadRepo is a pre-run function that load the repository for use in a command +func LoadRepo(env *Env) func(*cobra.Command, []string) error { return func(cmd *cobra.Command, args []string) error { cwd, err := os.Getwd() if err != nil { return fmt.Errorf("unable to get the current working directory: %q", err) } - env.repo, err = repository.OpenGoGitRepo(cwd, gitBugNamespace, []repository.ClockLoader{bug.ClockLoader}) + env.Repo, err = repository.OpenGoGitRepo(cwd, gitBugNamespace, []repository.ClockLoader{bug.ClockLoader}) if err == repository.ErrNotARepo { - return fmt.Errorf("%s must be run from within a git repo", rootCommandName) + return fmt.Errorf("%s must be run from within a git Repo", RootCommandName) } if err != nil { @@ -69,17 +100,17 @@ func loadRepo(env *Env) func(*cobra.Command, []string) error { } } -// loadRepoEnsureUser is the same as loadRepo, but also ensure that the user has configured +// LoadRepoEnsureUser is the same as LoadRepo, but also ensure that the user has configured // an identity. Use this pre-run function when an error after using the configured user won't // do. -func loadRepoEnsureUser(env *Env) func(*cobra.Command, []string) error { +func LoadRepoEnsureUser(env *Env) func(*cobra.Command, []string) error { return func(cmd *cobra.Command, args []string) error { - err := loadRepo(env)(cmd, args) + err := LoadRepo(env)(cmd, args) if err != nil { return err } - _, err = identity.GetUserIdentity(env.repo) + _, err = identity.GetUserIdentity(env.Repo) if err != nil { return err } @@ -88,25 +119,25 @@ func loadRepoEnsureUser(env *Env) func(*cobra.Command, []string) error { } } -// loadBackend is a pre-run function that load the repository and the backend for use in a command -// When using this function you also need to use closeBackend as a post-run -func loadBackend(env *Env) func(*cobra.Command, []string) error { +// LoadBackend is a pre-run function that load the repository and the Backend for use in a command +// When using this function you also need to use CloseBackend as a post-run +func LoadBackend(env *Env) func(*cobra.Command, []string) error { return func(cmd *cobra.Command, args []string) error { - err := loadRepo(env)(cmd, args) + err := LoadRepo(env)(cmd, args) if err != nil { return err } - env.backend, err = cache.NewRepoCache(env.repo) + env.Backend, err = cache.NewRepoCache(env.Repo) if err != nil { return err } cleaner := func(env *Env) interrupt.CleanerFunc { return func() error { - if env.backend != nil { - err := env.backend.Close() - env.backend = nil + if env.Backend != nil { + err := env.Backend.Close() + env.Backend = nil return err } return nil @@ -119,17 +150,17 @@ func loadBackend(env *Env) func(*cobra.Command, []string) error { } } -// loadBackendEnsureUser is the same as loadBackend, but also ensure that the user has configured +// LoadBackendEnsureUser is the same as LoadBackend, but also ensure that the user has configured // an identity. Use this pre-run function when an error after using the configured user won't // do. -func loadBackendEnsureUser(env *Env) func(*cobra.Command, []string) error { +func LoadBackendEnsureUser(env *Env) func(*cobra.Command, []string) error { return func(cmd *cobra.Command, args []string) error { - err := loadBackend(env)(cmd, args) + err := LoadBackend(env)(cmd, args) if err != nil { return err } - _, err = identity.GetUserIdentity(env.repo) + _, err = identity.GetUserIdentity(env.Repo) if err != nil { return err } @@ -138,18 +169,18 @@ func loadBackendEnsureUser(env *Env) func(*cobra.Command, []string) error { } } -// closeBackend is a wrapper for a RunE function that will close the backend properly +// CloseBackend is a wrapper for a RunE function that will close the Backend properly // if it has been opened. // This wrapper style is necessary because a Cobra PostE function does not run if RunE return an error. -func closeBackend(env *Env, runE func(cmd *cobra.Command, args []string) error) func(*cobra.Command, []string) error { +func CloseBackend(env *Env, runE func(cmd *cobra.Command, args []string) error) func(*cobra.Command, []string) error { return func(cmd *cobra.Command, args []string) error { errRun := runE(cmd, args) - if env.backend == nil { + if env.Backend == nil { return nil } - err := env.backend.Close() - env.backend = nil + err := env.Backend.Close() + env.Backend = nil // prioritize the RunE error if errRun != nil { diff --git a/commands/execenv/env_testing.go b/commands/execenv/env_testing.go new file mode 100644 index 0000000000000000000000000000000000000000..7d9fbd60d1d480096fd143b9fc949b01ffb6a237 --- /dev/null +++ b/commands/execenv/env_testing.go @@ -0,0 +1,48 @@ +package execenv + +import ( + "bytes" + "fmt" + "testing" + + "github.com/MichaelMure/git-bug/cache" + "github.com/MichaelMure/git-bug/repository" + "github.com/stretchr/testify/require" +) + +type TestOut struct { + *bytes.Buffer +} + +func (te *TestOut) Printf(format string, a ...interface{}) { + _, _ = fmt.Fprintf(te.Buffer, format, a...) +} + +func (te *TestOut) Print(a ...interface{}) { + _, _ = fmt.Fprint(te.Buffer, a...) +} + +func (te *TestOut) Println(a ...interface{}) { + _, _ = fmt.Fprintln(te.Buffer, a...) +} + +func NewTestEnv(t *testing.T) *Env { + t.Helper() + + repo := repository.CreateGoGitTestRepo(t, false) + + buf := new(bytes.Buffer) + + backend, err := cache.NewRepoCache(repo) + require.NoError(t, err) + t.Cleanup(func() { + backend.Close() + }) + + return &Env{ + Repo: repo, + Backend: backend, + Out: &TestOut{buf}, + Err: &TestOut{buf}, + } +} diff --git a/commands/golden_test.go b/commands/golden_test.go deleted file mode 100644 index 9fcee0d60fb3ebc0e17275d9b90b586617ab0a5b..0000000000000000000000000000000000000000 --- a/commands/golden_test.go +++ /dev/null @@ -1,5 +0,0 @@ -package commands - -import "flag" - -var update = flag.Bool("update", false, "update golden files") diff --git a/commands/input/input.go b/commands/input/input.go index e9c1be1bc438f67ac1f130ecf15c41f7d10f3f0c..ee343cd8dabaff894d81de681b0561aac7db4b71 100644 --- a/commands/input/input.go +++ b/commands/input/input.go @@ -25,7 +25,7 @@ const messageFilename = "BUG_MESSAGE_EDITMSG" // ErrEmptyMessage is returned when the required message has not been entered var ErrEmptyMessage = errors.New("empty message") -// ErrEmptyMessage is returned when the required title has not been entered +// ErrEmptyTitle is returned when the required title has not been entered var ErrEmptyTitle = errors.New("empty title") const bugTitleCommentTemplate = `%s%s diff --git a/commands/label.go b/commands/label.go index ff4d0151393bf380e5167c03771451ba5aaf0439..70090d26cba33939e15d217a1671ff0649042b8b 100644 --- a/commands/label.go +++ b/commands/label.go @@ -3,39 +3,32 @@ package commands import ( "github.com/spf13/cobra" - _select "github.com/MichaelMure/git-bug/commands/select" + "github.com/MichaelMure/git-bug/commands/execenv" ) func newLabelCommand() *cobra.Command { - env := newEnv() + env := execenv.NewEnv() cmd := &cobra.Command{ - Use: "label [ID]", - Short: "Display, add or remove labels to/from a bug.", - PreRunE: loadBackend(env), - RunE: closeBackend(env, func(cmd *cobra.Command, args []string) error { - return runLabel(env, args) + Use: "label", + Short: "List valid labels", + Long: `List valid labels. + +Note: in the future, a proper label policy could be implemented where valid labels are defined in a configuration file. Until that, the default behavior is to return the list of labels already used.`, + PreRunE: execenv.LoadBackend(env), + RunE: execenv.CloseBackend(env, func(cmd *cobra.Command, args []string) error { + return runLabel(env) }), - ValidArgsFunction: completeBug(env), } - cmd.AddCommand(newLabelAddCommand()) - cmd.AddCommand(newLabelLsCommand()) - cmd.AddCommand(newLabelRmCommand()) - return cmd } -func runLabel(env *Env, args []string) error { - b, _, err := _select.ResolveBug(env.backend, args) - if err != nil { - return err - } - - snap := b.Snapshot() +func runLabel(env *execenv.Env) error { + labels := env.Backend.ValidLabels() - for _, l := range snap.Labels { - env.out.Println(l) + for _, l := range labels { + env.Out.Println(l) } return nil diff --git a/commands/label_add.go b/commands/label_add.go deleted file mode 100644 index 65439a4ae5d4ceb9914899aaec3b199c89ec7453..0000000000000000000000000000000000000000 --- a/commands/label_add.go +++ /dev/null @@ -1,45 +0,0 @@ -package commands - -import ( - "github.com/spf13/cobra" - - _select "github.com/MichaelMure/git-bug/commands/select" - "github.com/MichaelMure/git-bug/util/text" -) - -func newLabelAddCommand() *cobra.Command { - env := newEnv() - - cmd := &cobra.Command{ - Use: "add [ID] LABEL...", - Short: "Add a label to a bug.", - PreRunE: loadBackendEnsureUser(env), - RunE: closeBackend(env, func(cmd *cobra.Command, args []string) error { - return runLabelAdd(env, args) - }), - ValidArgsFunction: completeBugAndLabels(env, true), - } - - return cmd -} - -func runLabelAdd(env *Env, args []string) error { - b, args, err := _select.ResolveBug(env.backend, args) - if err != nil { - return err - } - - added := args - - changes, _, err := b.ChangeLabels(text.CleanupOneLineArray(added), nil) - - for _, change := range changes { - env.out.Println(change) - } - - if err != nil { - return err - } - - return b.Commit() -} diff --git a/commands/label_ls.go b/commands/label_ls.go deleted file mode 100644 index 242eb00c6b479106d3565e5579a6da28fa528e88..0000000000000000000000000000000000000000 --- a/commands/label_ls.go +++ /dev/null @@ -1,33 +0,0 @@ -package commands - -import ( - "github.com/spf13/cobra" -) - -func newLabelLsCommand() *cobra.Command { - env := newEnv() - - cmd := &cobra.Command{ - Use: "ls", - Short: "List valid labels.", - Long: `List valid labels. - -Note: in the future, a proper label policy could be implemented where valid labels are defined in a configuration file. Until that, the default behavior is to return the list of labels already used.`, - PreRunE: loadBackend(env), - RunE: closeBackend(env, func(cmd *cobra.Command, args []string) error { - return runLabelLs(env) - }), - } - - return cmd -} - -func runLabelLs(env *Env) error { - labels := env.backend.ValidLabels() - - for _, l := range labels { - env.out.Println(l) - } - - return nil -} diff --git a/commands/label_rm.go b/commands/label_rm.go deleted file mode 100644 index 3f4e19581637624d5ac8c744500cb808053a7d0f..0000000000000000000000000000000000000000 --- a/commands/label_rm.go +++ /dev/null @@ -1,45 +0,0 @@ -package commands - -import ( - "github.com/spf13/cobra" - - _select "github.com/MichaelMure/git-bug/commands/select" - "github.com/MichaelMure/git-bug/util/text" -) - -func newLabelRmCommand() *cobra.Command { - env := newEnv() - - cmd := &cobra.Command{ - Use: "rm [ID] LABEL...", - Short: "Remove a label from a bug.", - PreRunE: loadBackend(env), - RunE: closeBackend(env, func(cmd *cobra.Command, args []string) error { - return runLabelRm(env, args) - }), - ValidArgsFunction: completeBugAndLabels(env, false), - } - - return cmd -} - -func runLabelRm(env *Env, args []string) error { - b, args, err := _select.ResolveBug(env.backend, args) - if err != nil { - return err - } - - removed := args - - changes, _, err := b.ChangeLabels(nil, text.CleanupOneLineArray(removed)) - - for _, change := range changes { - env.out.Println(change) - } - - if err != nil { - return err - } - - return b.Commit() -} diff --git a/commands/ls-id.go b/commands/ls-id.go deleted file mode 100644 index 31107e873c9243dc4c2f49e472321e64cd7694a9..0000000000000000000000000000000000000000 --- a/commands/ls-id.go +++ /dev/null @@ -1,42 +0,0 @@ -package commands - -import ( - "github.com/spf13/cobra" -) - -func newLsIdCommand() *cobra.Command { - env := newEnv() - - cmd := &cobra.Command{ - Use: "ls-id [PREFIX]", - Short: "List bug identifiers.", - PreRunE: loadBackend(env), - RunE: closeBackend(env, func(cmd *cobra.Command, args []string) error { - return runLsId(env, args) - }), - Deprecated: `and will be removed in v1.0. - -Please use the "ls" command which allows filtering and sorting of the resulting -list of ids. The following example would print a new-line separated list containing -the ids of all open bugs: -git-bug ls --format id --status open -`, - } - - return cmd -} - -func runLsId(env *Env, args []string) error { - var prefix = "" - if len(args) != 0 { - prefix = args[0] - } - - for _, id := range env.backend.AllBugsIds() { - if prefix == "" || id.HasPrefix(prefix) { - env.out.Println(id) - } - } - - return nil -} diff --git a/commands/ls-labels.go b/commands/ls-labels.go deleted file mode 100644 index 00fc3fe66e73a030f9c8e3d986995f2340247f1d..0000000000000000000000000000000000000000 --- a/commands/ls-labels.go +++ /dev/null @@ -1,29 +0,0 @@ -package commands - -import ( - "github.com/spf13/cobra" -) - -func newLsLabelCommand() *cobra.Command { - env := newEnv() - - cmd := &cobra.Command{ - Use: "ls-label", - Short: "List valid labels.", - Long: `List valid labels. - -Note: in the future, a proper label policy could be implemented where valid labels are defined in a configuration file. Until that, the default behavior is to return the list of labels already used.`, - PreRunE: loadBackend(env), - RunE: closeBackend(env, func(cmd *cobra.Command, args []string) error { - return runLabelLs(env) - }), - Deprecated: ` and will be removed in v1.0. - -The functionality provided by this command is now provided by -the following (equivalent) command: -git-bug label ls -`, - } - - return cmd -} diff --git a/commands/pull.go b/commands/pull.go index 29c9f034234d8553d23c9cabc80354e88f615328..2e2639e1571107c2f2fb6145e8e5e5c0d8a22f93 100644 --- a/commands/pull.go +++ b/commands/pull.go @@ -5,26 +5,28 @@ import ( "github.com/spf13/cobra" + "github.com/MichaelMure/git-bug/commands/completion" + "github.com/MichaelMure/git-bug/commands/execenv" "github.com/MichaelMure/git-bug/entity" ) func newPullCommand() *cobra.Command { - env := newEnv() + env := execenv.NewEnv() cmd := &cobra.Command{ Use: "pull [REMOTE]", - Short: "Pull bugs update from a git remote.", - PreRunE: loadBackend(env), - RunE: closeBackend(env, func(cmd *cobra.Command, args []string) error { + Short: "Pull updates from a git remote", + PreRunE: execenv.LoadBackend(env), + RunE: execenv.CloseBackend(env, func(cmd *cobra.Command, args []string) error { return runPull(env, args) }), - ValidArgsFunction: completeGitRemote(env), + ValidArgsFunction: completion.GitRemote(env), } return cmd } -func runPull(env *Env, args []string) error { +func runPull(env *execenv.Env, args []string) error { if len(args) > 1 { return errors.New("Only pulling from one remote at a time is supported") } @@ -34,24 +36,24 @@ func runPull(env *Env, args []string) error { remote = args[0] } - env.out.Println("Fetching remote ...") + env.Out.Println("Fetching remote ...") - stdout, err := env.backend.Fetch(remote) + stdout, err := env.Backend.Fetch(remote) if err != nil { return err } - env.out.Println(stdout) + env.Out.Println(stdout) - env.out.Println("Merging data ...") + env.Out.Println("Merging data ...") - for result := range env.backend.MergeAll(remote) { + for result := range env.Backend.MergeAll(remote) { if result.Err != nil { - env.err.Println(result.Err) + env.Err.Println(result.Err) } if result.Status != entity.MergeStatusNothing { - env.out.Printf("%s: %s\n", result.Id.Human(), result) + env.Out.Printf("%s: %s\n", result.Id.Human(), result) } } diff --git a/commands/push.go b/commands/push.go index adba6bef034ef96f65f169233e2bcd9d91d13ce3..d45e301a26af79c33f629f2ae2be7ff444ccf508 100644 --- a/commands/push.go +++ b/commands/push.go @@ -4,25 +4,28 @@ import ( "errors" "github.com/spf13/cobra" + + "github.com/MichaelMure/git-bug/commands/completion" + "github.com/MichaelMure/git-bug/commands/execenv" ) func newPushCommand() *cobra.Command { - env := newEnv() + env := execenv.NewEnv() cmd := &cobra.Command{ Use: "push [REMOTE]", - Short: "Push bugs update to a git remote.", - PreRunE: loadBackend(env), - RunE: closeBackend(env, func(cmd *cobra.Command, args []string) error { + Short: "Push updates to a git remote", + PreRunE: execenv.LoadBackend(env), + RunE: execenv.CloseBackend(env, func(cmd *cobra.Command, args []string) error { return runPush(env, args) }), - ValidArgsFunction: completeGitRemote(env), + ValidArgsFunction: completion.GitRemote(env), } return cmd } -func runPush(env *Env, args []string) error { +func runPush(env *execenv.Env, args []string) error { if len(args) > 1 { return errors.New("Only pushing to one remote at a time is supported") } @@ -32,12 +35,12 @@ func runPush(env *Env, args []string) error { remote = args[0] } - stdout, err := env.backend.Push(remote) + stdout, err := env.Backend.Push(remote) if err != nil { return err } - env.out.Println(stdout) + env.Out.Println(stdout) return nil } diff --git a/commands/rm.go b/commands/rm.go deleted file mode 100644 index 2e1d924d547d013c007b4a29ae0c233031c3787f..0000000000000000000000000000000000000000 --- a/commands/rm.go +++ /dev/null @@ -1,43 +0,0 @@ -package commands - -import ( - "errors" - - "github.com/spf13/cobra" -) - -func newRmCommand() *cobra.Command { - env := newEnv() - - cmd := &cobra.Command{ - Use: "rm ID", - Short: "Remove an existing bug.", - Long: "Remove an existing bug in the local repository. Note removing bugs that were imported from bridges will not remove the bug on the remote, and will only remove the local copy of the bug.", - PreRunE: loadBackendEnsureUser(env), - RunE: closeBackend(env, func(cmd *cobra.Command, args []string) error { - return runRm(env, args) - }), - ValidArgsFunction: completeBug(env), - } - - flags := cmd.Flags() - flags.SortFlags = false - - return cmd -} - -func runRm(env *Env, args []string) (err error) { - if len(args) == 0 { - return errors.New("you must provide a bug prefix to remove") - } - - err = env.backend.RemoveBug(args[0]) - - if err != nil { - return - } - - env.out.Printf("bug %s removed\n", args[0]) - - return -} diff --git a/commands/rm_test.go b/commands/rm_test.go deleted file mode 100644 index 0156bbd4d618ceb2c0dc470146998907811a3935..0000000000000000000000000000000000000000 --- a/commands/rm_test.go +++ /dev/null @@ -1,17 +0,0 @@ -package commands - -import ( - "testing" - - "github.com/stretchr/testify/require" -) - -func TestRm(t *testing.T) { - testEnv, bugID := newTestEnvAndBug(t) - - exp := "bug " + bugID + " removed\n" - - require.NoError(t, runRm(testEnv.env, []string{bugID})) - require.Equal(t, exp, testEnv.out.String()) - testEnv.out.Reset() -} diff --git a/commands/root.go b/commands/root.go index e012bd83d0b08509864b4098414f5670e833969e..b28b77b8aa1d64b68a531e8dd4b2a4799999da01 100644 --- a/commands/root.go +++ b/commands/root.go @@ -6,9 +6,13 @@ import ( "os" "github.com/spf13/cobra" -) -const rootCommandName = "git-bug" + "github.com/MichaelMure/git-bug/commands/bridge" + usercmd "github.com/MichaelMure/git-bug/commands/user" + + "github.com/MichaelMure/git-bug/commands/bug" + "github.com/MichaelMure/git-bug/commands/execenv" +) // These variables are initialized externally during the build. See the Makefile. var GitCommit string @@ -17,8 +21,8 @@ var GitExactTag string func NewRootCommand() *cobra.Command { cmd := &cobra.Command{ - Use: rootCommandName, - Short: "A bug tracker embedded in Git.", + Use: execenv.RootCommandName, + Short: "A bug tracker embedded in Git", Long: `git-bug is a bug tracker embedded in git. git-bug use git objects to store the bug tracking separated from the files @@ -52,26 +56,32 @@ the same git remote you are already using to collaborate with other people. DisableAutoGenTag: true, } - cmd.AddCommand(newAddCommand()) - cmd.AddCommand(newBridgeCommand()) + const entityGroup = "entity" + const uiGroup = "ui" + const remoteGroup = "remote" + + cmd.AddGroup(&cobra.Group{ID: entityGroup, Title: "Entities"}) + cmd.AddGroup(&cobra.Group{ID: uiGroup, Title: "User interfaces"}) + cmd.AddGroup(&cobra.Group{ID: remoteGroup, Title: "Interaction with the outside world"}) + + addCmdWithGroup := func(child *cobra.Command, groupID string) { + cmd.AddCommand(child) + child.GroupID = groupID + } + + addCmdWithGroup(bugcmd.NewBugCommand(), entityGroup) + addCmdWithGroup(usercmd.NewUserCommand(), entityGroup) + addCmdWithGroup(newLabelCommand(), entityGroup) + + addCmdWithGroup(newTermUICommand(), uiGroup) + addCmdWithGroup(newWebUICommand(), uiGroup) + + addCmdWithGroup(newPullCommand(), remoteGroup) + addCmdWithGroup(newPushCommand(), remoteGroup) + addCmdWithGroup(bridgecmd.NewBridgeCommand(), remoteGroup) + cmd.AddCommand(newCommandsCommand()) - cmd.AddCommand(newCommentCommand()) - cmd.AddCommand(newDeselectCommand()) - cmd.AddCommand(newLabelCommand()) - cmd.AddCommand(newLsCommand()) - cmd.AddCommand(newLsIdCommand()) - cmd.AddCommand(newLsLabelCommand()) - cmd.AddCommand(newPullCommand()) - cmd.AddCommand(newPushCommand()) - cmd.AddCommand(newRmCommand()) - cmd.AddCommand(newSelectCommand()) - cmd.AddCommand(newShowCommand()) - cmd.AddCommand(newStatusCommand()) - cmd.AddCommand(newTermUICommand()) - cmd.AddCommand(newTitleCommand()) - cmd.AddCommand(newUserCommand()) cmd.AddCommand(newVersionCommand()) - cmd.AddCommand(newWebUICommand()) return cmd } diff --git a/commands/select.go b/commands/select.go deleted file mode 100644 index f9e6ece7faec2cae397a72f8c35031a13b531125..0000000000000000000000000000000000000000 --- a/commands/select.go +++ /dev/null @@ -1,60 +0,0 @@ -package commands - -import ( - "errors" - - "github.com/spf13/cobra" - - _select "github.com/MichaelMure/git-bug/commands/select" -) - -func newSelectCommand() *cobra.Command { - env := newEnv() - - cmd := &cobra.Command{ - Use: "select ID", - Short: "Select a bug for implicit use in future commands.", - Example: `git bug select 2f15 -git bug comment -git bug status -`, - Long: `Select a bug for implicit use in future commands. - -This command allows you to omit any bug ID argument, for example: - git bug show -instead of - git bug show 2f153ca - -The complementary command is "git bug deselect" performing the opposite operation. -`, - PreRunE: loadBackend(env), - RunE: closeBackend(env, func(cmd *cobra.Command, args []string) error { - return runSelect(env, args) - }), - ValidArgsFunction: completeBug(env), - } - - return cmd -} - -func runSelect(env *Env, args []string) error { - if len(args) == 0 { - return errors.New("You must provide a bug id") - } - - prefix := args[0] - - b, err := env.backend.ResolveBugPrefix(prefix) - if err != nil { - return err - } - - err = _select.Select(env.backend, b.Id()) - if err != nil { - return err - } - - env.out.Printf("selected bug %s: %s\n", b.Id().Human(), b.Snapshot().Title) - - return nil -} diff --git a/commands/status.go b/commands/status.go deleted file mode 100644 index c3e860b61522b184dc3e8228a0bc222e2acdd8fc..0000000000000000000000000000000000000000 --- a/commands/status.go +++ /dev/null @@ -1,38 +0,0 @@ -package commands - -import ( - _select "github.com/MichaelMure/git-bug/commands/select" - "github.com/spf13/cobra" -) - -func newStatusCommand() *cobra.Command { - env := newEnv() - - cmd := &cobra.Command{ - Use: "status [ID]", - Short: "Display or change a bug status.", - PreRunE: loadBackend(env), - RunE: closeBackend(env, func(cmd *cobra.Command, args []string) error { - return runStatus(env, args) - }), - ValidArgsFunction: completeBug(env), - } - - cmd.AddCommand(newStatusCloseCommand()) - cmd.AddCommand(newStatusOpenCommand()) - - return cmd -} - -func runStatus(env *Env, args []string) error { - b, args, err := _select.ResolveBug(env.backend, args) - if err != nil { - return err - } - - snap := b.Snapshot() - - env.out.Println(snap.Status) - - return nil -} diff --git a/commands/status_close.go b/commands/status_close.go deleted file mode 100644 index 8541aa0b6426fa634e31548fd33d2acd80fe78e2..0000000000000000000000000000000000000000 --- a/commands/status_close.go +++ /dev/null @@ -1,35 +0,0 @@ -package commands - -import ( - _select "github.com/MichaelMure/git-bug/commands/select" - "github.com/spf13/cobra" -) - -func newStatusCloseCommand() *cobra.Command { - env := newEnv() - - cmd := &cobra.Command{ - Use: "close [ID]", - Short: "Mark a bug as closed.", - PreRunE: loadBackendEnsureUser(env), - RunE: closeBackend(env, func(cmd *cobra.Command, args []string) error { - return runStatusClose(env, args) - }), - } - - return cmd -} - -func runStatusClose(env *Env, args []string) error { - b, args, err := _select.ResolveBug(env.backend, args) - if err != nil { - return err - } - - _, err = b.Close() - if err != nil { - return err - } - - return b.Commit() -} diff --git a/commands/status_open.go b/commands/status_open.go deleted file mode 100644 index ee6bd27abdeefb074ac30752dc21bf6f313cfa89..0000000000000000000000000000000000000000 --- a/commands/status_open.go +++ /dev/null @@ -1,35 +0,0 @@ -package commands - -import ( - _select "github.com/MichaelMure/git-bug/commands/select" - "github.com/spf13/cobra" -) - -func newStatusOpenCommand() *cobra.Command { - env := newEnv() - - cmd := &cobra.Command{ - Use: "open [ID]", - Short: "Mark a bug as open.", - PreRunE: loadBackendEnsureUser(env), - RunE: closeBackend(env, func(cmd *cobra.Command, args []string) error { - return runStatusOpen(env, args) - }), - } - - return cmd -} - -func runStatusOpen(env *Env, args []string) error { - b, args, err := _select.ResolveBug(env.backend, args) - if err != nil { - return err - } - - _, err = b.Open() - if err != nil { - return err - } - - return b.Commit() -} diff --git a/commands/termui.go b/commands/termui.go index 4df6cdaf521dc9ff7bea02d6272ae836b7564eb8..1cfdd8f396cb005ad9e67372104b630c816da4b6 100644 --- a/commands/termui.go +++ b/commands/termui.go @@ -3,18 +3,19 @@ package commands import ( "github.com/spf13/cobra" + "github.com/MichaelMure/git-bug/commands/execenv" "github.com/MichaelMure/git-bug/termui" ) func newTermUICommand() *cobra.Command { - env := newEnv() + env := execenv.NewEnv() cmd := &cobra.Command{ Use: "termui", Aliases: []string{"tui"}, - Short: "Launch the terminal UI.", - PreRunE: loadBackendEnsureUser(env), - RunE: closeBackend(env, func(cmd *cobra.Command, args []string) error { + Short: "Launch the terminal UI", + PreRunE: execenv.LoadBackendEnsureUser(env), + RunE: execenv.CloseBackend(env, func(cmd *cobra.Command, args []string) error { return runTermUI(env) }), } @@ -22,6 +23,6 @@ func newTermUICommand() *cobra.Command { return cmd } -func runTermUI(env *Env) error { - return termui.Run(env.backend) +func runTermUI(env *execenv.Env) error { + return termui.Run(env.Backend) } diff --git a/commands/title.go b/commands/title.go deleted file mode 100644 index f99c6eff8c6adb36e2f8f81b2502fc185b4b0374..0000000000000000000000000000000000000000 --- a/commands/title.go +++ /dev/null @@ -1,37 +0,0 @@ -package commands - -import ( - _select "github.com/MichaelMure/git-bug/commands/select" - "github.com/spf13/cobra" -) - -func newTitleCommand() *cobra.Command { - env := newEnv() - - cmd := &cobra.Command{ - Use: "title [ID]", - Short: "Display or change a title of a bug.", - PreRunE: loadBackend(env), - RunE: closeBackend(env, func(cmd *cobra.Command, args []string) error { - return runTitle(env, args) - }), - ValidArgsFunction: completeBug(env), - } - - cmd.AddCommand(newTitleEditCommand()) - - return cmd -} - -func runTitle(env *Env, args []string) error { - b, args, err := _select.ResolveBug(env.backend, args) - if err != nil { - return err - } - - snap := b.Snapshot() - - env.out.Println(snap.Title) - - return nil -} diff --git a/commands/title_edit.go b/commands/title_edit.go deleted file mode 100644 index a1ba032422560c0e55f87f01222a608839b567d5..0000000000000000000000000000000000000000 --- a/commands/title_edit.go +++ /dev/null @@ -1,74 +0,0 @@ -package commands - -import ( - "github.com/spf13/cobra" - - "github.com/MichaelMure/git-bug/commands/input" - _select "github.com/MichaelMure/git-bug/commands/select" - "github.com/MichaelMure/git-bug/util/text" -) - -type titleEditOptions struct { - title string - nonInteractive bool -} - -func newTitleEditCommand() *cobra.Command { - env := newEnv() - options := titleEditOptions{} - - cmd := &cobra.Command{ - Use: "edit [ID]", - Short: "Edit a title of a bug.", - PreRunE: loadBackendEnsureUser(env), - RunE: closeBackend(env, func(cmd *cobra.Command, args []string) error { - return runTitleEdit(env, options, args) - }), - ValidArgsFunction: completeBug(env), - } - - flags := cmd.Flags() - flags.SortFlags = false - - flags.StringVarP(&options.title, "title", "t", "", - "Provide a title to describe the issue", - ) - flags.BoolVar(&options.nonInteractive, "non-interactive", false, "Do not ask for user input") - - return cmd -} - -func runTitleEdit(env *Env, opts titleEditOptions, args []string) error { - b, args, err := _select.ResolveBug(env.backend, args) - if err != nil { - return err - } - - snap := b.Snapshot() - - if opts.title == "" { - if opts.nonInteractive { - env.err.Println("No title given. Use -m or -F option to specify a title. Aborting.") - return nil - } - opts.title, err = input.BugTitleEditorInput(env.repo, snap.Title) - if err == input.ErrEmptyTitle { - env.out.Println("Empty title, aborting.") - return nil - } - if err != nil { - return err - } - } - - if opts.title == snap.Title { - env.err.Println("No change, aborting.") - } - - _, err = b.SetTitle(text.CleanupOneLine(opts.title)) - if err != nil { - return err - } - - return b.Commit() -} diff --git a/commands/user.go b/commands/user.go deleted file mode 100644 index 0fe3be4db8ae04322f26d5ddb7f39c08355ebbf5..0000000000000000000000000000000000000000 --- a/commands/user.go +++ /dev/null @@ -1,110 +0,0 @@ -package commands - -import ( - "errors" - "fmt" - "strings" - - "github.com/spf13/cobra" - - "github.com/MichaelMure/git-bug/cache" -) - -type userOptions struct { - fields string -} - -func newUserCommand() *cobra.Command { - env := newEnv() - options := userOptions{} - - cmd := &cobra.Command{ - Use: "user [USER-ID]", - Short: "Display or change the user identity.", - PreRunE: loadBackendEnsureUser(env), - RunE: closeBackend(env, func(cmd *cobra.Command, args []string) error { - return runUser(env, options, args) - }), - ValidArgsFunction: completeUser(env), - } - - cmd.AddCommand(newUserAdoptCommand()) - cmd.AddCommand(newUserCreateCommand()) - cmd.AddCommand(newUserLsCommand()) - - flags := cmd.Flags() - flags.SortFlags = false - - fields := []string{"email", "humanId", "id", "lastModification", "lastModificationLamports", "login", "metadata", "name"} - flags.StringVarP(&options.fields, "field", "f", "", - "Select field to display. Valid values are ["+strings.Join(fields, ",")+"]") - cmd.RegisterFlagCompletionFunc("field", completeFrom(fields)) - - return cmd -} - -func runUser(env *Env, opts userOptions, args []string) error { - if len(args) > 1 { - return errors.New("only one identity can be displayed at a time") - } - - var id *cache.IdentityCache - var err error - if len(args) == 1 { - id, err = env.backend.ResolveIdentityPrefix(args[0]) - } else { - id, err = env.backend.GetUserIdentity() - } - - if err != nil { - return err - } - - if opts.fields != "" { - switch opts.fields { - case "email": - env.out.Printf("%s\n", id.Email()) - case "login": - env.out.Printf("%s\n", id.Login()) - case "humanId": - env.out.Printf("%s\n", id.Id().Human()) - case "id": - env.out.Printf("%s\n", id.Id()) - case "lastModification": - env.out.Printf("%s\n", id.LastModification(). - Time().Format("Mon Jan 2 15:04:05 2006 +0200")) - case "lastModificationLamport": - for name, t := range id.LastModificationLamports() { - env.out.Printf("%s\n%d\n", name, t) - } - case "metadata": - for key, value := range id.ImmutableMetadata() { - env.out.Printf("%s\n%s\n", key, value) - } - case "name": - env.out.Printf("%s\n", id.Name()) - - default: - return fmt.Errorf("\nUnsupported field: %s\n", opts.fields) - } - - return nil - } - - env.out.Printf("Id: %s\n", id.Id()) - env.out.Printf("Name: %s\n", id.Name()) - env.out.Printf("Email: %s\n", id.Email()) - env.out.Printf("Login: %s\n", id.Login()) - env.out.Printf("Last modification: %s\n", id.LastModification().Time().Format("Mon Jan 2 15:04:05 2006 +0200")) - env.out.Printf("Last moditication (lamport):\n") - for name, t := range id.LastModificationLamports() { - env.out.Printf("\t%s: %d", name, t) - } - env.out.Println("Metadata:") - for key, value := range id.ImmutableMetadata() { - env.out.Printf(" %s --> %s\n", key, value) - } - // env.out.Printf("Protected: %v\n", id.IsProtected()) - - return nil -} diff --git a/commands/user/user.go b/commands/user/user.go new file mode 100644 index 0000000000000000000000000000000000000000..191fb8287cc37648562363b25676a8743a0dbd94 --- /dev/null +++ b/commands/user/user.go @@ -0,0 +1,89 @@ +package usercmd + +import ( + "encoding/json" + "fmt" + + "github.com/spf13/cobra" + + json2 "github.com/MichaelMure/git-bug/commands/cmdjson" + + "github.com/MichaelMure/git-bug/cache" + "github.com/MichaelMure/git-bug/commands/completion" + "github.com/MichaelMure/git-bug/commands/execenv" + "github.com/MichaelMure/git-bug/util/colors" +) + +type userOptions struct { + format string +} + +func NewUserCommand() *cobra.Command { + env := execenv.NewEnv() + options := userOptions{} + + cmd := &cobra.Command{ + Use: "user", + Short: "List identities", + PreRunE: execenv.LoadBackend(env), + RunE: execenv.CloseBackend(env, func(cmd *cobra.Command, args []string) error { + return runUser(env, options) + }), + } + + cmd.AddCommand(newUserNewCommand()) + cmd.AddCommand(newUserShowCommand()) + cmd.AddCommand(newUserAdoptCommand()) + + flags := cmd.Flags() + flags.SortFlags = false + + flags.StringVarP(&options.format, "format", "f", "default", + "Select the output formatting style. Valid values are [default,json]") + cmd.RegisterFlagCompletionFunc("format", completion.From([]string{"default", "json"})) + + return cmd +} + +func runUser(env *execenv.Env, opts userOptions) error { + ids := env.Backend.AllIdentityIds() + var users []*cache.IdentityExcerpt + for _, id := range ids { + user, err := env.Backend.ResolveIdentityExcerpt(id) + if err != nil { + return err + } + users = append(users, user) + } + + switch opts.format { + case "json": + return userJsonFormatter(env, users) + case "default": + return userDefaultFormatter(env, users) + default: + return fmt.Errorf("unknown format %s", opts.format) + } +} + +func userDefaultFormatter(env *execenv.Env, users []*cache.IdentityExcerpt) error { + for _, user := range users { + env.Out.Printf("%s %s\n", + colors.Cyan(user.Id.Human()), + user.DisplayName(), + ) + } + + return nil +} + +func userJsonFormatter(env *execenv.Env, users []*cache.IdentityExcerpt) error { + jsonUsers := make([]json2.Identity, len(users)) + for i, user := range users { + jsonUsers[i] = json2.NewIdentityFromExcerpt(user) + } + + jsonObject, _ := json.MarshalIndent(jsonUsers, "", " ") + env.Out.Printf("%s\n", jsonObject) + return nil +} diff --git a/commands/user/user_adopt.go b/commands/user/user_adopt.go new file mode 100644 index 0000000000000000000000000000000000000000..f59440535ae34d6239fc27fece05acff718f6edc --- /dev/null +++ b/commands/user/user_adopt.go @@ -0,0 +1,43 @@ +package usercmd + +import ( + "github.com/spf13/cobra" + + "github.com/MichaelMure/git-bug/commands/completion" + "github.com/MichaelMure/git-bug/commands/execenv" +) + +func newUserAdoptCommand() *cobra.Command { + env := execenv.NewEnv() + + cmd := &cobra.Command{ + Use: "adopt USER_ID", + Short: "Adopt an existing identity as your own", + Args: cobra.ExactArgs(1), + PreRunE: execenv.LoadBackend(env), + RunE: execenv.CloseBackend(env, func(cmd *cobra.Command, args []string) error { + return runUserAdopt(env, args) + }), + ValidArgsFunction: completion.User(env), + } + + return cmd +} + +func runUserAdopt(env *execenv.Env, args []string) error { + prefix := args[0] + + i, err := env.Backend.ResolveIdentityPrefix(prefix) + if err != nil { + return err + } + + err = env.Backend.SetUserIdentity(i) + if err != nil { + return err + } + + env.Out.Printf("Your identity is now: %s\n", i.DisplayName()) + + return nil +} diff --git a/commands/user_create.go b/commands/user/user_new.go similarity index 64% rename from commands/user_create.go rename to commands/user/user_new.go index 6941cff506b4a7bb69d1797acfcb7a54257b8c89..d7224512fe41e7449fc0c560677301ff5260fadb 100644 --- a/commands/user_create.go +++ b/commands/user/user_new.go @@ -1,28 +1,29 @@ -package commands +package usercmd import ( "github.com/spf13/cobra" + "github.com/MichaelMure/git-bug/commands/execenv" "github.com/MichaelMure/git-bug/commands/input" ) -type createUserOptions struct { +type userNewOptions struct { name string email string avatarURL string nonInteractive bool } -func newUserCreateCommand() *cobra.Command { - env := newEnv() +func newUserNewCommand() *cobra.Command { + env := execenv.NewEnv() - options := createUserOptions{} + options := userNewOptions{} cmd := &cobra.Command{ - Use: "create", - Short: "Create a new identity.", - PreRunE: loadBackend(env), - RunE: closeBackend(env, func(cmd *cobra.Command, args []string) error { - return runUserCreate(env, options) + Use: "new", + Short: "Create a new identity", + PreRunE: execenv.LoadBackend(env), + RunE: execenv.CloseBackend(env, func(cmd *cobra.Command, args []string) error { + return runUserNew(env, options) }), } @@ -35,10 +36,10 @@ func newUserCreateCommand() *cobra.Command { return cmd } -func runUserCreate(env *Env, opts createUserOptions) error { +func runUserNew(env *execenv.Env, opts userNewOptions) error { if !opts.nonInteractive && opts.name == "" { - preName, err := env.backend.GetUserName() + preName, err := env.Backend.GetUserName() if err != nil { return err } @@ -49,7 +50,7 @@ func runUserCreate(env *Env, opts createUserOptions) error { } if !opts.nonInteractive && opts.email == "" { - preEmail, err := env.backend.GetUserEmail() + preEmail, err := env.Backend.GetUserEmail() if err != nil { return err } @@ -68,7 +69,7 @@ func runUserCreate(env *Env, opts createUserOptions) error { } } - id, err := env.backend.NewIdentityRaw(opts.name, opts.email, "", opts.avatarURL, nil, nil) + id, err := env.Backend.NewIdentityRaw(opts.name, opts.email, "", opts.avatarURL, nil, nil) if err != nil { return err } @@ -78,20 +79,20 @@ func runUserCreate(env *Env, opts createUserOptions) error { return err } - set, err := env.backend.IsUserIdentitySet() + set, err := env.Backend.IsUserIdentitySet() if err != nil { return err } if !set { - err = env.backend.SetUserIdentity(id) + err = env.Backend.SetUserIdentity(id) if err != nil { return err } } - env.err.Println() - env.out.Println(id.Id()) + env.Err.Println() + env.Out.Println(id.Id()) return nil } diff --git a/commands/user/user_new_test.go b/commands/user/user_new_test.go new file mode 100644 index 0000000000000000000000000000000000000000..619e5de6390ace21ca552de39c5607c165d1603c --- /dev/null +++ b/commands/user/user_new_test.go @@ -0,0 +1,14 @@ +package usercmd + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/MichaelMure/git-bug/commands/bug/testenv" +) + +func TestUserNewCommand(t *testing.T) { + _, userID := testenv.NewTestEnvAndUser(t) + require.Regexp(t, "[0-9a-f]{64}", userID) +} diff --git a/commands/user/user_show.go b/commands/user/user_show.go new file mode 100644 index 0000000000000000000000000000000000000000..36c09e8e45a488bf814246cdcfc03e4c54cab829 --- /dev/null +++ b/commands/user/user_show.go @@ -0,0 +1,108 @@ +package usercmd + +import ( + "errors" + "fmt" + "strings" + + "github.com/spf13/cobra" + + "github.com/MichaelMure/git-bug/cache" + "github.com/MichaelMure/git-bug/commands/completion" + "github.com/MichaelMure/git-bug/commands/execenv" +) + +type userShowOptions struct { + fields string +} + +func newUserShowCommand() *cobra.Command { + env := execenv.NewEnv() + options := userShowOptions{} + + cmd := &cobra.Command{ + Use: "user show [USER_ID]", + Short: "Display a user identity", + PreRunE: execenv.LoadBackendEnsureUser(env), + RunE: execenv.CloseBackend(env, func(cmd *cobra.Command, args []string) error { + return runUserShow(env, options, args) + }), + ValidArgsFunction: completion.User(env), + } + + flags := cmd.Flags() + flags.SortFlags = false + + fields := []string{"email", "humanId", "id", "lastModification", "lastModificationLamports", "login", "metadata", "name"} + flags.StringVarP(&options.fields, "field", "f", "", + "Select field to display. Valid values are ["+strings.Join(fields, ",")+"]") + cmd.RegisterFlagCompletionFunc("field", completion.From(fields)) + + return cmd +} + +func runUserShow(env *execenv.Env, opts userShowOptions, args []string) error { + if len(args) > 1 { + return errors.New("only one identity can be displayed at a time") + } + + var id *cache.IdentityCache + var err error + if len(args) == 1 { + id, err = env.Backend.ResolveIdentityPrefix(args[0]) + } else { + id, err = env.Backend.GetUserIdentity() + } + + if err != nil { + return err + } + + if opts.fields != "" { + switch opts.fields { + case "email": + env.Out.Printf("%s\n", id.Email()) + case "login": + env.Out.Printf("%s\n", id.Login()) + case "humanId": + env.Out.Printf("%s\n", id.Id().Human()) + case "id": + env.Out.Printf("%s\n", id.Id()) + case "lastModification": + env.Out.Printf("%s\n", id.LastModification(). + Time().Format("Mon Jan 2 15:04:05 2006 +0200")) + case "lastModificationLamport": + for name, t := range id.LastModificationLamports() { + env.Out.Printf("%s\n%d\n", name, t) + } + case "metadata": + for key, value := range id.ImmutableMetadata() { + env.Out.Printf("%s\n%s\n", key, value) + } + case "name": + env.Out.Printf("%s\n", id.Name()) + + default: + return fmt.Errorf("\nUnsupported field: %s\n", opts.fields) + } + + return nil + } + + env.Out.Printf("Id: %s\n", id.Id()) + env.Out.Printf("Name: %s\n", id.Name()) + env.Out.Printf("Email: %s\n", id.Email()) + env.Out.Printf("Login: %s\n", id.Login()) + env.Out.Printf("Last modification: %s\n", id.LastModification().Time().Format("Mon Jan 2 15:04:05 2006 +0200")) + env.Out.Printf("Last moditication (lamport):\n") + for name, t := range id.LastModificationLamports() { + env.Out.Printf("\t%s: %d", name, t) + } + env.Out.Println("Metadata:") + for key, value := range id.ImmutableMetadata() { + env.Out.Printf(" %s --> %s\n", key, value) + } + // env.Out.Printf("Protected: %v\n", id.IsProtected()) + + return nil +} diff --git a/commands/user_adopt.go b/commands/user_adopt.go deleted file mode 100644 index afef94ea30e683ba7cf638c545dae16b54894937..0000000000000000000000000000000000000000 --- a/commands/user_adopt.go +++ /dev/null @@ -1,40 +0,0 @@ -package commands - -import ( - "github.com/spf13/cobra" -) - -func newUserAdoptCommand() *cobra.Command { - env := newEnv() - - cmd := &cobra.Command{ - Use: "adopt USER-ID", - Short: "Adopt an existing identity as your own.", - Args: cobra.ExactArgs(1), - PreRunE: loadBackend(env), - RunE: closeBackend(env, func(cmd *cobra.Command, args []string) error { - return runUserAdopt(env, args) - }), - ValidArgsFunction: completeUser(env), - } - - return cmd -} - -func runUserAdopt(env *Env, args []string) error { - prefix := args[0] - - i, err := env.backend.ResolveIdentityPrefix(prefix) - if err != nil { - return err - } - - err = env.backend.SetUserIdentity(i) - if err != nil { - return err - } - - env.out.Printf("Your identity is now: %s\n", i.DisplayName()) - - return nil -} diff --git a/commands/user_create_test.go b/commands/user_create_test.go deleted file mode 100644 index 08958344fad1ee520c9183b2c84e1bb8116f39ca..0000000000000000000000000000000000000000 --- a/commands/user_create_test.go +++ /dev/null @@ -1,38 +0,0 @@ -package commands - -import ( - "strings" - "testing" - - "github.com/stretchr/testify/require" -) - -const ( - testUserName = "John Doe" - testUserEmail = "jdoe@example.com" -) - -func newTestEnvAndUser(t *testing.T) (*testEnv, string) { - t.Helper() - - testEnv := newTestEnv(t) - - opts := createUserOptions{ - name: testUserName, - email: testUserEmail, - avatarURL: "", - nonInteractive: true, - } - - require.NoError(t, runUserCreate(testEnv.env, opts)) - - userID := strings.TrimSpace(testEnv.out.String()) - testEnv.out.Reset() - - return testEnv, userID -} - -func TestUserCreateCommand(t *testing.T) { - _, userID := newTestEnvAndUser(t) - require.Regexp(t, "[0-9a-f]{64}", userID) -} diff --git a/commands/user_ls.go b/commands/user_ls.go deleted file mode 100644 index 341f0dc1c0caf18e945d2b5410c88d3628cac83f..0000000000000000000000000000000000000000 --- a/commands/user_ls.go +++ /dev/null @@ -1,81 +0,0 @@ -package commands - -import ( - "encoding/json" - "fmt" - - "github.com/spf13/cobra" - - "github.com/MichaelMure/git-bug/cache" - "github.com/MichaelMure/git-bug/util/colors" -) - -type userLsOptions struct { - format string -} - -func newUserLsCommand() *cobra.Command { - env := newEnv() - options := userLsOptions{} - - cmd := &cobra.Command{ - Use: "ls", - Short: "List identities.", - PreRunE: loadBackend(env), - RunE: closeBackend(env, func(cmd *cobra.Command, args []string) error { - return runUserLs(env, options) - }), - } - - flags := cmd.Flags() - flags.SortFlags = false - - flags.StringVarP(&options.format, "format", "f", "default", - "Select the output formatting style. Valid values are [default,json]") - cmd.RegisterFlagCompletionFunc("format", completeFrom([]string{"default", "json"})) - - return cmd -} - -func runUserLs(env *Env, opts userLsOptions) error { - ids := env.backend.AllIdentityIds() - var users []*cache.IdentityExcerpt - for _, id := range ids { - user, err := env.backend.ResolveIdentityExcerpt(id) - if err != nil { - return err - } - users = append(users, user) - } - - switch opts.format { - case "json": - return userLsJsonFormatter(env, users) - case "default": - return userLsDefaultFormatter(env, users) - default: - return fmt.Errorf("unknown format %s", opts.format) - } -} - -func userLsDefaultFormatter(env *Env, users []*cache.IdentityExcerpt) error { - for _, user := range users { - env.out.Printf("%s %s\n", - colors.Cyan(user.Id.Human()), - user.DisplayName(), - ) - } - - return nil -} - -func userLsJsonFormatter(env *Env, users []*cache.IdentityExcerpt) error { - jsonUsers := make([]JSONIdentity, len(users)) - for i, user := range users { - jsonUsers[i] = NewJSONIdentityFromExcerpt(user) - } - - jsonObject, _ := json.MarshalIndent(jsonUsers, "", " ") - env.out.Printf("%s\n", jsonObject) - return nil -} diff --git a/commands/version.go b/commands/version.go index 71baba40a4894cdb2120962efb7307cb05bcf048..0e54bb92b244f708dc9f4d3476d65a164d2bb54d 100644 --- a/commands/version.go +++ b/commands/version.go @@ -4,6 +4,8 @@ import ( "runtime" "github.com/spf13/cobra" + + "github.com/MichaelMure/git-bug/commands/execenv" ) type versionOptions struct { @@ -13,12 +15,12 @@ type versionOptions struct { } func newVersionCommand() *cobra.Command { - env := newEnv() + env := execenv.NewEnv() options := versionOptions{} cmd := &cobra.Command{ Use: "version", - Short: "Show git-bug version information.", + Short: "Show git-bug version information", Run: func(cmd *cobra.Command, args []string) { runVersion(env, options, cmd.Root()) }, @@ -40,23 +42,23 @@ func newVersionCommand() *cobra.Command { return cmd } -func runVersion(env *Env, opts versionOptions, root *cobra.Command) { +func runVersion(env *execenv.Env, opts versionOptions, root *cobra.Command) { if opts.all { - env.out.Printf("%s version: %s\n", rootCommandName, root.Version) - env.out.Printf("System version: %s/%s\n", runtime.GOARCH, runtime.GOOS) - env.out.Printf("Golang version: %s\n", runtime.Version()) + env.Out.Printf("%s version: %s\n", execenv.RootCommandName, root.Version) + env.Out.Printf("System version: %s/%s\n", runtime.GOARCH, runtime.GOOS) + env.Out.Printf("Golang version: %s\n", runtime.Version()) return } if opts.number { - env.out.Println(root.Version) + env.Out.Println(root.Version) return } if opts.commit { - env.out.Println(GitCommit) + env.Out.Println(GitCommit) return } - env.out.Printf("%s version: %s\n", rootCommandName, root.Version) + env.Out.Printf("%s version: %s\n", execenv.RootCommandName, root.Version) } diff --git a/commands/webui.go b/commands/webui.go index 758a153bf34a95ffc47e19ac0d70f9d28678e86b..5fe66aa7c0ac6e154d1c2443d85d64c4319a5849 100644 --- a/commands/webui.go +++ b/commands/webui.go @@ -23,6 +23,7 @@ import ( "github.com/MichaelMure/git-bug/api/graphql" httpapi "github.com/MichaelMure/git-bug/api/http" "github.com/MichaelMure/git-bug/cache" + "github.com/MichaelMure/git-bug/commands/execenv" "github.com/MichaelMure/git-bug/entities/identity" "github.com/MichaelMure/git-bug/repository" "github.com/MichaelMure/git-bug/webui" @@ -41,18 +42,18 @@ type webUIOptions struct { } func newWebUICommand() *cobra.Command { - env := newEnv() + env := execenv.NewEnv() options := webUIOptions{} cmd := &cobra.Command{ Use: "webui", - Short: "Launch the web UI.", + Short: "Launch the web UI", Long: `Launch the web UI. Available git config: git-bug.webui.open [bool]: control the automatic opening of the web UI in the default browser `, - PreRunE: loadRepo(env), + PreRunE: execenv.LoadRepo(env), RunE: func(cmd *cobra.Command, args []string) error { return runWebUI(env, options) }, @@ -72,7 +73,7 @@ Available git config: return cmd } -func runWebUI(env *Env, opts webUIOptions) error { +func runWebUI(env *execenv.Env, opts webUIOptions) error { if opts.port == 0 { var err error opts.port, err = freeport.GetFreePort() @@ -96,7 +97,7 @@ func runWebUI(env *Env, opts webUIOptions) error { // fixed identity: the default user of the repo // TODO: support dynamic authentication with OAuth if !opts.readOnly { - author, err := identity.GetUserIdentity(env.repo) + author, err := identity.GetUserIdentity(env.Repo) if err != nil { return err } @@ -104,14 +105,14 @@ func runWebUI(env *Env, opts webUIOptions) error { } mrc := cache.NewMultiRepoCache() - _, err := mrc.RegisterDefaultRepository(env.repo) + _, err := mrc.RegisterDefaultRepository(env.Repo) if err != nil { return err } var errOut io.Writer if opts.logErrors { - errOut = env.err + errOut = env.Err } graphqlHandler := graphql.NewHandler(mrc, errOut) @@ -136,7 +137,7 @@ func runWebUI(env *Env, opts webUIOptions) error { go func() { <-quit - env.out.Println("WebUI is shutting down...") + env.Out.Println("WebUI is shutting down...") ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() @@ -149,18 +150,18 @@ func runWebUI(env *Env, opts webUIOptions) error { // Teardown err := graphqlHandler.Close() if err != nil { - env.out.Println(err) + env.Out.Println(err) } close(done) }() - env.out.Printf("Web UI: %s\n", webUiAddr) - env.out.Printf("Graphql API: http://%s/graphql\n", addr) - env.out.Printf("Graphql Playground: http://%s/playground\n", addr) - env.out.Println("Press Ctrl+c to quit") + env.Out.Printf("Web UI: %s\n", webUiAddr) + env.Out.Printf("Graphql API: http://%s/graphql\n", addr) + env.Out.Printf("Graphql Playground: http://%s/playground\n", addr) + env.Out.Println("Press Ctrl+c to quit") - configOpen, err := env.repo.AnyConfig().ReadBool(webUIOpenConfigKey) + configOpen, err := env.Repo.AnyConfig().ReadBool(webUIOpenConfigKey) if err == repository.ErrNoConfigEntry { // default to true configOpen = true @@ -173,7 +174,7 @@ func runWebUI(env *Env, opts webUIOptions) error { if shouldOpen { err = open.Run(toOpen) if err != nil { - env.out.Println(err) + env.Out.Println(err) } } @@ -184,6 +185,6 @@ func runWebUI(env *Env, opts webUIOptions) error { <-done - env.out.Println("WebUI stopped") + env.Out.Println("WebUI stopped") return nil } diff --git a/doc/cli-convention.md b/doc/cli-convention.md new file mode 100644 index 0000000000000000000000000000000000000000..47eccf694a181ba5922e7790ad69bb644030e489 --- /dev/null +++ b/doc/cli-convention.md @@ -0,0 +1,13 @@ +## Pattern + +CLI commands should consistently follow this pattern: + +``` +xxx --> list xxx things if list, otherwise show one +xxx new --> create thing +xxx rm --> delete thing +xxx show ID --> show one +xxx show --> show one with "select" implied ID +xxx yyy --> action commands for that thing, or subcommand +xxx select|deselect --> select/deselect implied ID +``` diff --git a/doc/man/git-bug-bridge-auth-rm.1 b/doc/man/git-bug-bridge-auth-rm.1 index a22da320a3fcfb48ec2058edc9099ab4b87f7eb7..4ce7672c551f6c5639e60b062ba8afa8b166bd20 100644 --- a/doc/man/git-bug-bridge-auth-rm.1 +++ b/doc/man/git-bug-bridge-auth-rm.1 @@ -3,17 +3,17 @@ .SH NAME .PP -git-bug-bridge-auth-rm - Remove a credential. +git-bug-bridge-auth-rm - Remove a credential .SH SYNOPSIS .PP -\fBgit-bug bridge auth rm ID [flags]\fP +\fBgit-bug bridge auth rm BRIDGE_ID [flags]\fP .SH DESCRIPTION .PP -Remove a credential. +Remove a credential .SH OPTIONS diff --git a/doc/man/git-bug-bridge-auth-show.1 b/doc/man/git-bug-bridge-auth-show.1 index 19ac9d37b8771f9a06bdff4121005ed4daee2a30..1c4385ffc2e425ffe5342cf4c223b38581472039 100644 --- a/doc/man/git-bug-bridge-auth-show.1 +++ b/doc/man/git-bug-bridge-auth-show.1 @@ -3,7 +3,7 @@ .SH NAME .PP -git-bug-bridge-auth-show - Display an authentication credential. +git-bug-bridge-auth-show - Display an authentication credential .SH SYNOPSIS @@ -13,7 +13,7 @@ git-bug-bridge-auth-show - Display an authentication credential. .SH DESCRIPTION .PP -Display an authentication credential. +Display an authentication credential .SH OPTIONS diff --git a/doc/man/git-bug-bridge-auth.1 b/doc/man/git-bug-bridge-auth.1 index 1fd94f5b383d1515e56431aed798c0c8bafc2261..f6f36623f8d70969b498d40e4cc50dd846faf785 100644 --- a/doc/man/git-bug-bridge-auth.1 +++ b/doc/man/git-bug-bridge-auth.1 @@ -3,7 +3,7 @@ .SH NAME .PP -git-bug-bridge-auth - List all known bridge authentication credentials. +git-bug-bridge-auth - List all known bridge authentication credentials .SH SYNOPSIS @@ -13,7 +13,7 @@ git-bug-bridge-auth - List all known bridge authentication credentials. .SH DESCRIPTION .PP -List all known bridge authentication credentials. +List all known bridge authentication credentials .SH OPTIONS diff --git a/doc/man/git-bug-bridge-configure.1 b/doc/man/git-bug-bridge-new.1 similarity index 92% rename from doc/man/git-bug-bridge-configure.1 rename to doc/man/git-bug-bridge-new.1 index 002d8202a9219fa2ec91e692b7bb2a0fea3a05fa..1b0b2f389075d227d7a9194f32d19bcf1b3bacd1 100644 --- a/doc/man/git-bug-bridge-configure.1 +++ b/doc/man/git-bug-bridge-new.1 @@ -3,12 +3,12 @@ .SH NAME .PP -git-bug-bridge-configure - Configure a new bridge. +git-bug-bridge-new - Configure a new bridge .SH SYNOPSIS .PP -\fBgit-bug bridge configure [flags]\fP +\fBgit-bug bridge new [flags]\fP .SH DESCRIPTION @@ -69,7 +69,7 @@ Configure a new bridge by passing flags or/and using interactive terminal prompt .PP \fB-h\fP, \fB--help\fP[=false] - help for configure + help for new .SH EXAMPLE @@ -111,7 +111,7 @@ Enter token: 87cf5c03b64029f18ea5f9ca5679daa08ccbd700 Successfully configured bridge: default # For GitHub -git bug bridge configure \\ +git bug bridge new \\ --name=default \\ --target=github \\ --owner=$(OWNER) \\ @@ -119,13 +119,13 @@ git bug bridge configure \\ --token=$(TOKEN) # For Launchpad -git bug bridge configure \\ +git bug bridge new \\ --name=default \\ --target=launchpad-preview \\ --url=https://bugs.launchpad.net/ubuntu/ # For Gitlab -git bug bridge configure \\ +git bug bridge new \\ --name=default \\ --target=github \\ --url=https://github.com/michaelmure/git-bug \\ diff --git a/doc/man/git-bug-bridge-pull.1 b/doc/man/git-bug-bridge-pull.1 index 0ff1f8605eec2539f3c94ed0e15760feefb1826b..4d39b0d7831249c8918bd62c45f80a1f1fb0d7d7 100644 --- a/doc/man/git-bug-bridge-pull.1 +++ b/doc/man/git-bug-bridge-pull.1 @@ -3,7 +3,7 @@ .SH NAME .PP -git-bug-bridge-pull - Pull updates. +git-bug-bridge-pull - Pull updates from a remote bug tracker .SH SYNOPSIS @@ -13,7 +13,7 @@ git-bug-bridge-pull - Pull updates. .SH DESCRIPTION .PP -Pull updates. +Pull updates from a remote bug tracker .SH OPTIONS diff --git a/doc/man/git-bug-bridge-push.1 b/doc/man/git-bug-bridge-push.1 index fa882cf0ce69ec1736dead6019b49412b60e6d86..9541c527f712948b7a2d3fa698393cc59518a6bb 100644 --- a/doc/man/git-bug-bridge-push.1 +++ b/doc/man/git-bug-bridge-push.1 @@ -3,7 +3,7 @@ .SH NAME .PP -git-bug-bridge-push - Push updates. +git-bug-bridge-push - Push updates to remote bug tracker .SH SYNOPSIS @@ -13,7 +13,7 @@ git-bug-bridge-push - Push updates. .SH DESCRIPTION .PP -Push updates. +Push updates to remote bug tracker .SH OPTIONS diff --git a/doc/man/git-bug-bridge-rm.1 b/doc/man/git-bug-bridge-rm.1 index 2445e6d8e2757d7bd4f213bdba7f9678838bd345..6010eca8e365e2eef0ac311948a376ee141ac390 100644 --- a/doc/man/git-bug-bridge-rm.1 +++ b/doc/man/git-bug-bridge-rm.1 @@ -3,7 +3,7 @@ .SH NAME .PP -git-bug-bridge-rm - Delete a configured bridge. +git-bug-bridge-rm - Delete a configured bridge .SH SYNOPSIS @@ -13,7 +13,7 @@ git-bug-bridge-rm - Delete a configured bridge. .SH DESCRIPTION .PP -Delete a configured bridge. +Delete a configured bridge .SH OPTIONS diff --git a/doc/man/git-bug-bridge.1 b/doc/man/git-bug-bridge.1 index 30e5f3ed43fe720b7bd0d8703925645889b1e7f4..7fbce2c194f34d167866e5f723a948f06fbe3b79 100644 --- a/doc/man/git-bug-bridge.1 +++ b/doc/man/git-bug-bridge.1 @@ -3,7 +3,7 @@ .SH NAME .PP -git-bug-bridge - Configure and use bridges to other bug trackers. +git-bug-bridge - List bridges to other bug trackers .SH SYNOPSIS @@ -13,7 +13,7 @@ git-bug-bridge - Configure and use bridges to other bug trackers. .SH DESCRIPTION .PP -Configure and use bridges to other bug trackers. +List bridges to other bug trackers .SH OPTIONS @@ -24,4 +24,4 @@ Configure and use bridges to other bug trackers. .SH SEE ALSO .PP -\fBgit-bug(1)\fP, \fBgit-bug-bridge-auth(1)\fP, \fBgit-bug-bridge-configure(1)\fP, \fBgit-bug-bridge-pull(1)\fP, \fBgit-bug-bridge-push(1)\fP, \fBgit-bug-bridge-rm(1)\fP +\fBgit-bug(1)\fP, \fBgit-bug-bridge-auth(1)\fP, \fBgit-bug-bridge-new(1)\fP, \fBgit-bug-bridge-pull(1)\fP, \fBgit-bug-bridge-push(1)\fP, \fBgit-bug-bridge-rm(1)\fP diff --git a/doc/man/git-bug-comment-edit.1 b/doc/man/git-bug-bug-comment-edit.1 similarity index 73% rename from doc/man/git-bug-comment-edit.1 rename to doc/man/git-bug-bug-comment-edit.1 index 03e6c1cd2bb56a8bc11237b1168fa0a6a3082dcb..5a3d3506ac8bac87d80d1d5b29865cdc01fe4e80 100644 --- a/doc/man/git-bug-comment-edit.1 +++ b/doc/man/git-bug-bug-comment-edit.1 @@ -3,17 +3,17 @@ .SH NAME .PP -git-bug-comment-edit - Edit an existing comment on a bug. +git-bug-bug-comment-edit - Edit an existing comment on a bug .SH SYNOPSIS .PP -\fBgit-bug comment edit [COMMENT_ID] [flags]\fP +\fBgit-bug bug comment edit [COMMENT_ID] [flags]\fP .SH DESCRIPTION .PP -Edit an existing comment on a bug. +Edit an existing comment on a bug .SH OPTIONS @@ -36,4 +36,4 @@ Edit an existing comment on a bug. .SH SEE ALSO .PP -\fBgit-bug-comment(1)\fP +\fBgit-bug-bug-comment(1)\fP diff --git a/doc/man/git-bug-comment-add.1 b/doc/man/git-bug-bug-comment-new.1 similarity index 73% rename from doc/man/git-bug-comment-add.1 rename to doc/man/git-bug-bug-comment-new.1 index 41aec9bb08e62d65c6315e181ca4456a314444a9..f2afc933807f2a0f1c96fb49c056ba0ca4e4ffcf 100644 --- a/doc/man/git-bug-comment-add.1 +++ b/doc/man/git-bug-bug-comment-new.1 @@ -3,17 +3,17 @@ .SH NAME .PP -git-bug-comment-add - Add a new comment to a bug. +git-bug-bug-comment-new - Add a new comment to a bug .SH SYNOPSIS .PP -\fBgit-bug comment add [ID] [flags]\fP +\fBgit-bug bug comment new [BUG_ID] [flags]\fP .SH DESCRIPTION .PP -Add a new comment to a bug. +Add a new comment to a bug .SH OPTIONS @@ -31,9 +31,9 @@ Add a new comment to a bug. .PP \fB-h\fP, \fB--help\fP[=false] - help for add + help for new .SH SEE ALSO .PP -\fBgit-bug-comment(1)\fP +\fBgit-bug-bug-comment(1)\fP diff --git a/doc/man/git-bug-comment.1 b/doc/man/git-bug-bug-comment.1 similarity index 52% rename from doc/man/git-bug-comment.1 rename to doc/man/git-bug-bug-comment.1 index 2939b641290f06209f8dc37ba89e3addeaf83ff5..162c41826b20998d939083f39f39d98282a7875c 100644 --- a/doc/man/git-bug-comment.1 +++ b/doc/man/git-bug-bug-comment.1 @@ -3,17 +3,17 @@ .SH NAME .PP -git-bug-comment - Display or add comments to a bug. +git-bug-bug-comment - List a bug's comments .SH SYNOPSIS .PP -\fBgit-bug comment [ID] [flags]\fP +\fBgit-bug bug comment [BUG_ID] [flags]\fP .SH DESCRIPTION .PP -Display or add comments to a bug. +List a bug's comments .SH OPTIONS @@ -24,4 +24,4 @@ Display or add comments to a bug. .SH SEE ALSO .PP -\fBgit-bug(1)\fP, \fBgit-bug-comment-add(1)\fP, \fBgit-bug-comment-edit(1)\fP +\fBgit-bug-bug(1)\fP, \fBgit-bug-bug-comment-edit(1)\fP, \fBgit-bug-bug-comment-new(1)\fP diff --git a/doc/man/git-bug-deselect.1 b/doc/man/git-bug-bug-deselect.1 similarity index 68% rename from doc/man/git-bug-deselect.1 rename to doc/man/git-bug-bug-deselect.1 index 9b6303577bc5e44bf43b377755ec5ebf2b9f8bda..fd3422456835fc62c0f7b398fddd3175f5a91154 100644 --- a/doc/man/git-bug-deselect.1 +++ b/doc/man/git-bug-bug-deselect.1 @@ -3,17 +3,17 @@ .SH NAME .PP -git-bug-deselect - Clear the implicitly selected bug. +git-bug-bug-deselect - Clear the implicitly selected bug .SH SYNOPSIS .PP -\fBgit-bug deselect [flags]\fP +\fBgit-bug bug deselect [flags]\fP .SH DESCRIPTION .PP -Clear the implicitly selected bug. +Clear the implicitly selected bug .SH OPTIONS @@ -39,4 +39,4 @@ git bug deselect .SH SEE ALSO .PP -\fBgit-bug(1)\fP +\fBgit-bug-bug(1)\fP diff --git a/doc/man/git-bug-label-add.1 b/doc/man/git-bug-bug-label-new.1 similarity index 55% rename from doc/man/git-bug-label-add.1 rename to doc/man/git-bug-bug-label-new.1 index a8ef3375d205515ffba505c4f4089148b4007b34..aa2b0c652250978714114f208f51ddec1ef38e9a 100644 --- a/doc/man/git-bug-label-add.1 +++ b/doc/man/git-bug-bug-label-new.1 @@ -3,25 +3,25 @@ .SH NAME .PP -git-bug-label-add - Add a label to a bug. +git-bug-bug-label-new - Add a label to a bug .SH SYNOPSIS .PP -\fBgit-bug label add [ID] LABEL... [flags]\fP +\fBgit-bug bug label new [BUG_ID] LABEL... [flags]\fP .SH DESCRIPTION .PP -Add a label to a bug. +Add a label to a bug .SH OPTIONS .PP \fB-h\fP, \fB--help\fP[=false] - help for add + help for new .SH SEE ALSO .PP -\fBgit-bug-label(1)\fP +\fBgit-bug-bug-label(1)\fP diff --git a/doc/man/git-bug-label-rm.1 b/doc/man/git-bug-bug-label-rm.1 similarity index 57% rename from doc/man/git-bug-label-rm.1 rename to doc/man/git-bug-bug-label-rm.1 index 6b98196ba2e5bcf977dc31ba920bea2d04766a9b..b204d61f32bf24d00b0318e53948e0e4a3b29b8a 100644 --- a/doc/man/git-bug-label-rm.1 +++ b/doc/man/git-bug-bug-label-rm.1 @@ -3,17 +3,17 @@ .SH NAME .PP -git-bug-label-rm - Remove a label from a bug. +git-bug-bug-label-rm - Remove a label from a bug .SH SYNOPSIS .PP -\fBgit-bug label rm [ID] LABEL... [flags]\fP +\fBgit-bug bug label rm [BUG_ID] LABEL... [flags]\fP .SH DESCRIPTION .PP -Remove a label from a bug. +Remove a label from a bug .SH OPTIONS @@ -24,4 +24,4 @@ Remove a label from a bug. .SH SEE ALSO .PP -\fBgit-bug-label(1)\fP +\fBgit-bug-bug-label(1)\fP diff --git a/doc/man/git-bug-bug-label.1 b/doc/man/git-bug-bug-label.1 new file mode 100644 index 0000000000000000000000000000000000000000..a98c228553c5cf2b4b56c69610b1fac987a34bf8 --- /dev/null +++ b/doc/man/git-bug-bug-label.1 @@ -0,0 +1,27 @@ +.nh +.TH "GIT-BUG" "1" "Apr 2019" "Generated from git-bug's source code" "" + +.SH NAME +.PP +git-bug-bug-label - Display labels of a bug + + +.SH SYNOPSIS +.PP +\fBgit-bug bug label [BUG_ID] [flags]\fP + + +.SH DESCRIPTION +.PP +Display labels of a bug + + +.SH OPTIONS +.PP +\fB-h\fP, \fB--help\fP[=false] + help for label + + +.SH SEE ALSO +.PP +\fBgit-bug-bug(1)\fP, \fBgit-bug-bug-label-new(1)\fP, \fBgit-bug-bug-label-rm(1)\fP diff --git a/doc/man/git-bug-add.1 b/doc/man/git-bug-bug-new.1 similarity index 81% rename from doc/man/git-bug-add.1 rename to doc/man/git-bug-bug-new.1 index 39c8fce454115d414fedb55755308540fa46d80a..91de2dcce038a52dd09bbe5a65f4bb3129aa70b5 100644 --- a/doc/man/git-bug-add.1 +++ b/doc/man/git-bug-bug-new.1 @@ -3,17 +3,17 @@ .SH NAME .PP -git-bug-add - Create a new bug. +git-bug-bug-new - Create a new bug .SH SYNOPSIS .PP -\fBgit-bug add [flags]\fP +\fBgit-bug bug new [flags]\fP .SH DESCRIPTION .PP -Create a new bug. +Create a new bug .SH OPTIONS @@ -35,9 +35,9 @@ Create a new bug. .PP \fB-h\fP, \fB--help\fP[=false] - help for add + help for new .SH SEE ALSO .PP -\fBgit-bug(1)\fP +\fBgit-bug-bug(1)\fP diff --git a/doc/man/git-bug-rm.1 b/doc/man/git-bug-bug-rm.1 similarity index 80% rename from doc/man/git-bug-rm.1 rename to doc/man/git-bug-bug-rm.1 index 78156f3702d31d896bf34c0d79c9ed3f30074922..ee7ea1851c5c010e0687911d94b68c60cdc6ebae 100644 --- a/doc/man/git-bug-rm.1 +++ b/doc/man/git-bug-bug-rm.1 @@ -3,12 +3,12 @@ .SH NAME .PP -git-bug-rm - Remove an existing bug. +git-bug-bug-rm - Remove an existing bug .SH SYNOPSIS .PP -\fBgit-bug rm ID [flags]\fP +\fBgit-bug bug rm BUG_ID [flags]\fP .SH DESCRIPTION @@ -24,4 +24,4 @@ Remove an existing bug in the local repository. Note removing bugs that were imp .SH SEE ALSO .PP -\fBgit-bug(1)\fP +\fBgit-bug-bug(1)\fP diff --git a/doc/man/git-bug-select.1 b/doc/man/git-bug-bug-select.1 similarity index 81% rename from doc/man/git-bug-select.1 rename to doc/man/git-bug-bug-select.1 index e94d80b15922085387df03e26b3deb318c9e0dbd..d73cd606982d35bbcf551e2c4454909eedf4b23b 100644 --- a/doc/man/git-bug-select.1 +++ b/doc/man/git-bug-bug-select.1 @@ -3,12 +3,12 @@ .SH NAME .PP -git-bug-select - Select a bug for implicit use in future commands. +git-bug-bug-select - Select a bug for implicit use in future commands .SH SYNOPSIS .PP -\fBgit-bug select ID [flags]\fP +\fBgit-bug bug select BUG_ID [flags]\fP .SH DESCRIPTION @@ -47,4 +47,4 @@ git bug status .SH SEE ALSO .PP -\fBgit-bug(1)\fP +\fBgit-bug-bug(1)\fP diff --git a/doc/man/git-bug-show.1 b/doc/man/git-bug-bug-show.1 similarity index 78% rename from doc/man/git-bug-show.1 rename to doc/man/git-bug-bug-show.1 index 6abf1b6f64886858e72aa3803bf8b82cc0c529a4..aec83fa3355710df66b1eb5193197279b81d31b7 100644 --- a/doc/man/git-bug-show.1 +++ b/doc/man/git-bug-bug-show.1 @@ -3,17 +3,17 @@ .SH NAME .PP -git-bug-show - Display the details of a bug. +git-bug-bug-show - Display the details of a bug .SH SYNOPSIS .PP -\fBgit-bug show [ID] [flags]\fP +\fBgit-bug bug show [BUG_ID] [flags]\fP .SH DESCRIPTION .PP -Display the details of a bug. +Display the details of a bug .SH OPTIONS @@ -32,4 +32,4 @@ Display the details of a bug. .SH SEE ALSO .PP -\fBgit-bug(1)\fP +\fBgit-bug-bug(1)\fP diff --git a/doc/man/git-bug-status-close.1 b/doc/man/git-bug-bug-status-close.1 similarity index 59% rename from doc/man/git-bug-status-close.1 rename to doc/man/git-bug-bug-status-close.1 index 3fcbdc3524b3a62dae210ed82cb21c15d767bd5a..edd9f6666506e1cf1b8c6554d0351d4858dafe7a 100644 --- a/doc/man/git-bug-status-close.1 +++ b/doc/man/git-bug-bug-status-close.1 @@ -3,17 +3,17 @@ .SH NAME .PP -git-bug-status-close - Mark a bug as closed. +git-bug-bug-status-close - Mark a bug as closed .SH SYNOPSIS .PP -\fBgit-bug status close [ID] [flags]\fP +\fBgit-bug bug status close [BUG_ID] [flags]\fP .SH DESCRIPTION .PP -Mark a bug as closed. +Mark a bug as closed .SH OPTIONS @@ -24,4 +24,4 @@ Mark a bug as closed. .SH SEE ALSO .PP -\fBgit-bug-status(1)\fP +\fBgit-bug-bug-status(1)\fP diff --git a/doc/man/git-bug-status-open.1 b/doc/man/git-bug-bug-status-open.1 similarity index 60% rename from doc/man/git-bug-status-open.1 rename to doc/man/git-bug-bug-status-open.1 index e13f11a3e13108401746462081f4667dc128e20e..4c001adae232530f90a230424bf214985c08727d 100644 --- a/doc/man/git-bug-status-open.1 +++ b/doc/man/git-bug-bug-status-open.1 @@ -3,17 +3,17 @@ .SH NAME .PP -git-bug-status-open - Mark a bug as open. +git-bug-bug-status-open - Mark a bug as open .SH SYNOPSIS .PP -\fBgit-bug status open [ID] [flags]\fP +\fBgit-bug bug status open [BUG_ID] [flags]\fP .SH DESCRIPTION .PP -Mark a bug as open. +Mark a bug as open .SH OPTIONS @@ -24,4 +24,4 @@ Mark a bug as open. .SH SEE ALSO .PP -\fBgit-bug-status(1)\fP +\fBgit-bug-bug-status(1)\fP diff --git a/doc/man/git-bug-status.1 b/doc/man/git-bug-bug-status.1 similarity index 50% rename from doc/man/git-bug-status.1 rename to doc/man/git-bug-bug-status.1 index e810b6733f89db7807ed7c1407f6322feef4693a..7abcd1a5408d3d0c177a01c7f1d6095826e0ff2d 100644 --- a/doc/man/git-bug-status.1 +++ b/doc/man/git-bug-bug-status.1 @@ -3,17 +3,17 @@ .SH NAME .PP -git-bug-status - Display or change a bug status. +git-bug-bug-status - Display the status of a bug .SH SYNOPSIS .PP -\fBgit-bug status [ID] [flags]\fP +\fBgit-bug bug status [BUG_ID] [flags]\fP .SH DESCRIPTION .PP -Display or change a bug status. +Display the status of a bug .SH OPTIONS @@ -24,4 +24,4 @@ Display or change a bug status. .SH SEE ALSO .PP -\fBgit-bug(1)\fP, \fBgit-bug-status-close(1)\fP, \fBgit-bug-status-open(1)\fP +\fBgit-bug-bug(1)\fP, \fBgit-bug-bug-status-close(1)\fP, \fBgit-bug-bug-status-open(1)\fP diff --git a/doc/man/git-bug-title-edit.1 b/doc/man/git-bug-bug-title-edit.1 similarity index 71% rename from doc/man/git-bug-title-edit.1 rename to doc/man/git-bug-bug-title-edit.1 index 5e0fa139a0ac430e40c5a51e228bc0c9a2c88aab..c5e33c7ae4a4f717cad46af291476d46df518813 100644 --- a/doc/man/git-bug-title-edit.1 +++ b/doc/man/git-bug-bug-title-edit.1 @@ -3,17 +3,17 @@ .SH NAME .PP -git-bug-title-edit - Edit a title of a bug. +git-bug-bug-title-edit - Edit a title of a bug .SH SYNOPSIS .PP -\fBgit-bug title edit [ID] [flags]\fP +\fBgit-bug bug title edit [BUG_ID] [flags]\fP .SH DESCRIPTION .PP -Edit a title of a bug. +Edit a title of a bug .SH OPTIONS @@ -32,4 +32,4 @@ Edit a title of a bug. .SH SEE ALSO .PP -\fBgit-bug-title(1)\fP +\fBgit-bug-bug-title(1)\fP diff --git a/doc/man/git-bug-title.1 b/doc/man/git-bug-bug-title.1 similarity index 55% rename from doc/man/git-bug-title.1 rename to doc/man/git-bug-bug-title.1 index 164c025733cf555a9405b3debb58b9b3a82d0940..945680356ab72c2ec07b526fad4b7d68d63e4723 100644 --- a/doc/man/git-bug-title.1 +++ b/doc/man/git-bug-bug-title.1 @@ -3,17 +3,17 @@ .SH NAME .PP -git-bug-title - Display or change a title of a bug. +git-bug-bug-title - Display the title of a bug .SH SYNOPSIS .PP -\fBgit-bug title [ID] [flags]\fP +\fBgit-bug bug title [BUG_ID] [flags]\fP .SH DESCRIPTION .PP -Display or change a title of a bug. +Display the title of a bug .SH OPTIONS @@ -24,4 +24,4 @@ Display or change a title of a bug. .SH SEE ALSO .PP -\fBgit-bug(1)\fP, \fBgit-bug-title-edit(1)\fP +\fBgit-bug-bug(1)\fP, \fBgit-bug-bug-title-edit(1)\fP diff --git a/doc/man/git-bug-ls.1 b/doc/man/git-bug-bug.1 similarity index 75% rename from doc/man/git-bug-ls.1 rename to doc/man/git-bug-bug.1 index e58177de29b5e2dc52ed29e082de2bbe20de9100..6ee62303ac2e97d80364b908428bb1eb744c8f28 100644 --- a/doc/man/git-bug-ls.1 +++ b/doc/man/git-bug-bug.1 @@ -3,12 +3,12 @@ .SH NAME .PP -git-bug-ls - List bugs. +git-bug-bug - List bugs .SH SYNOPSIS .PP -\fBgit-bug ls [QUERY] [flags]\fP +\fBgit-bug bug [QUERY] [flags]\fP .SH DESCRIPTION @@ -66,7 +66,7 @@ You can pass an additional query to filter and order the list. This query can be .PP \fB-h\fP, \fB--help\fP[=false] - help for ls + help for bug .SH EXAMPLE @@ -75,16 +75,16 @@ You can pass an additional query to filter and order the list. This query can be .nf List open bugs sorted by last edition with a query: -git bug ls status:open sort:edit-desc +git bug status:open sort:edit-desc List closed bugs sorted by creation with flags: -git bug ls --status closed --by creation +git bug --status closed --by creation Do a full text search of all bugs: -git bug ls "foo bar" baz +git bug "foo bar" baz Use queries, flags, and full text search: -git bug ls status:open --by creation "foo bar" baz +git bug status:open --by creation "foo bar" baz .fi @@ -93,4 +93,4 @@ git bug ls status:open --by creation "foo bar" baz .SH SEE ALSO .PP -\fBgit-bug(1)\fP +\fBgit-bug(1)\fP, \fBgit-bug-bug-comment(1)\fP, \fBgit-bug-bug-deselect(1)\fP, \fBgit-bug-bug-label(1)\fP, \fBgit-bug-bug-new(1)\fP, \fBgit-bug-bug-rm(1)\fP, \fBgit-bug-bug-select(1)\fP, \fBgit-bug-bug-show(1)\fP, \fBgit-bug-bug-status(1)\fP, \fBgit-bug-bug-title(1)\fP diff --git a/doc/man/git-bug-label-ls.1 b/doc/man/git-bug-label-ls.1 deleted file mode 100644 index 67ee7dd1f72bfa7de4e1b65ad76fdd867c761bdb..0000000000000000000000000000000000000000 --- a/doc/man/git-bug-label-ls.1 +++ /dev/null @@ -1,30 +0,0 @@ -.nh -.TH "GIT-BUG" "1" "Apr 2019" "Generated from git-bug's source code" "" - -.SH NAME -.PP -git-bug-label-ls - List valid labels. - - -.SH SYNOPSIS -.PP -\fBgit-bug label ls [flags]\fP - - -.SH DESCRIPTION -.PP -List valid labels. - -.PP -Note: in the future, a proper label policy could be implemented where valid labels are defined in a configuration file. Until that, the default behavior is to return the list of labels already used. - - -.SH OPTIONS -.PP -\fB-h\fP, \fB--help\fP[=false] - help for ls - - -.SH SEE ALSO -.PP -\fBgit-bug-label(1)\fP diff --git a/doc/man/git-bug-label.1 b/doc/man/git-bug-label.1 index 69a5c2178e9c3be5e5acd15ae0be5c043df2964a..9de7d2c0477640e294ebf83da5874db7c645d3b6 100644 --- a/doc/man/git-bug-label.1 +++ b/doc/man/git-bug-label.1 @@ -3,17 +3,20 @@ .SH NAME .PP -git-bug-label - Display, add or remove labels to/from a bug. +git-bug-label - List valid labels .SH SYNOPSIS .PP -\fBgit-bug label [ID] [flags]\fP +\fBgit-bug label [flags]\fP .SH DESCRIPTION .PP -Display, add or remove labels to/from a bug. +List valid labels. + +.PP +Note: in the future, a proper label policy could be implemented where valid labels are defined in a configuration file. Until that, the default behavior is to return the list of labels already used. .SH OPTIONS @@ -24,4 +27,4 @@ Display, add or remove labels to/from a bug. .SH SEE ALSO .PP -\fBgit-bug(1)\fP, \fBgit-bug-label-add(1)\fP, \fBgit-bug-label-ls(1)\fP, \fBgit-bug-label-rm(1)\fP +\fBgit-bug(1)\fP diff --git a/doc/man/git-bug-pull.1 b/doc/man/git-bug-pull.1 index f2536b3020a7886f81449f28b1bc29ba8067fc60..62485429c56746828445d23db2fc48ad84d1e817 100644 --- a/doc/man/git-bug-pull.1 +++ b/doc/man/git-bug-pull.1 @@ -3,7 +3,7 @@ .SH NAME .PP -git-bug-pull - Pull bugs update from a git remote. +git-bug-pull - Pull updates from a git remote .SH SYNOPSIS @@ -13,7 +13,7 @@ git-bug-pull - Pull bugs update from a git remote. .SH DESCRIPTION .PP -Pull bugs update from a git remote. +Pull updates from a git remote .SH OPTIONS diff --git a/doc/man/git-bug-push.1 b/doc/man/git-bug-push.1 index 2be8d6e2a1a87a56e248e79a8d6c9fe78a6f3271..2f15aacb8963e25b223f8039224743de3bf49be5 100644 --- a/doc/man/git-bug-push.1 +++ b/doc/man/git-bug-push.1 @@ -3,7 +3,7 @@ .SH NAME .PP -git-bug-push - Push bugs update to a git remote. +git-bug-push - Push updates to a git remote .SH SYNOPSIS @@ -13,7 +13,7 @@ git-bug-push - Push bugs update to a git remote. .SH DESCRIPTION .PP -Push bugs update to a git remote. +Push updates to a git remote .SH OPTIONS diff --git a/doc/man/git-bug-termui.1 b/doc/man/git-bug-termui.1 index ac09d2ed8d04d47024be468f86c9d42571428058..7409c9637bd3e74905cd634ead6e5ba75a336a6f 100644 --- a/doc/man/git-bug-termui.1 +++ b/doc/man/git-bug-termui.1 @@ -3,7 +3,7 @@ .SH NAME .PP -git-bug-termui - Launch the terminal UI. +git-bug-termui - Launch the terminal UI .SH SYNOPSIS @@ -13,7 +13,7 @@ git-bug-termui - Launch the terminal UI. .SH DESCRIPTION .PP -Launch the terminal UI. +Launch the terminal UI .SH OPTIONS diff --git a/doc/man/git-bug-user-adopt.1 b/doc/man/git-bug-user-adopt.1 index ce4e9fe132603c2131755ff4af9239afa4ace089..f7f2c8955529ab114c08bab26374b292b93a8cd9 100644 --- a/doc/man/git-bug-user-adopt.1 +++ b/doc/man/git-bug-user-adopt.1 @@ -3,17 +3,17 @@ .SH NAME .PP -git-bug-user-adopt - Adopt an existing identity as your own. +git-bug-user-adopt - Adopt an existing identity as your own .SH SYNOPSIS .PP -\fBgit-bug user adopt USER-ID [flags]\fP +\fBgit-bug user adopt USER_ID [flags]\fP .SH DESCRIPTION .PP -Adopt an existing identity as your own. +Adopt an existing identity as your own .SH OPTIONS diff --git a/doc/man/git-bug-user-ls.1 b/doc/man/git-bug-user-ls.1 deleted file mode 100644 index aff4633505a5894ca5661fd99128ef3bbe5d62f3..0000000000000000000000000000000000000000 --- a/doc/man/git-bug-user-ls.1 +++ /dev/null @@ -1,31 +0,0 @@ -.nh -.TH "GIT-BUG" "1" "Apr 2019" "Generated from git-bug's source code" "" - -.SH NAME -.PP -git-bug-user-ls - List identities. - - -.SH SYNOPSIS -.PP -\fBgit-bug user ls [flags]\fP - - -.SH DESCRIPTION -.PP -List identities. - - -.SH OPTIONS -.PP -\fB-f\fP, \fB--format\fP="default" - Select the output formatting style. Valid values are [default,json] - -.PP -\fB-h\fP, \fB--help\fP[=false] - help for ls - - -.SH SEE ALSO -.PP -\fBgit-bug-user(1)\fP diff --git a/doc/man/git-bug-user-create.1 b/doc/man/git-bug-user-new.1 similarity index 78% rename from doc/man/git-bug-user-create.1 rename to doc/man/git-bug-user-new.1 index 66c7db58d6541510ba67292145f516609c847a3d..cdb6f91f535cc0e9eb411410f3da3d5411acd00b 100644 --- a/doc/man/git-bug-user-create.1 +++ b/doc/man/git-bug-user-new.1 @@ -3,17 +3,17 @@ .SH NAME .PP -git-bug-user-create - Create a new identity. +git-bug-user-new - Create a new identity .SH SYNOPSIS .PP -\fBgit-bug user create [flags]\fP +\fBgit-bug user new [flags]\fP .SH DESCRIPTION .PP -Create a new identity. +Create a new identity .SH OPTIONS @@ -27,7 +27,7 @@ Create a new identity. .PP \fB-h\fP, \fB--help\fP[=false] - help for create + help for new .PP \fB-n\fP, \fB--name\fP="" diff --git a/doc/man/git-bug-user-user.1 b/doc/man/git-bug-user-user.1 new file mode 100644 index 0000000000000000000000000000000000000000..f4bf83d80d1092e4be2853c00ad5024182b92698 --- /dev/null +++ b/doc/man/git-bug-user-user.1 @@ -0,0 +1,31 @@ +.nh +.TH "GIT-BUG" "1" "Apr 2019" "Generated from git-bug's source code" "" + +.SH NAME +.PP +git-bug-user-user - Display a user identity + + +.SH SYNOPSIS +.PP +\fBgit-bug user user show [USER_ID] [flags]\fP + + +.SH DESCRIPTION +.PP +Display a user identity + + +.SH OPTIONS +.PP +\fB-f\fP, \fB--field\fP="" + Select field to display. Valid values are [email,humanId,id,lastModification,lastModificationLamports,login,metadata,name] + +.PP +\fB-h\fP, \fB--help\fP[=false] + help for user + + +.SH SEE ALSO +.PP +\fBgit-bug-user(1)\fP diff --git a/doc/man/git-bug-user.1 b/doc/man/git-bug-user.1 index 2a613ae238d8222f233ac02300d0bf6796bdd37b..276d0e43a1ececa4645b3e0dcc2c774a1e71692f 100644 --- a/doc/man/git-bug-user.1 +++ b/doc/man/git-bug-user.1 @@ -3,23 +3,23 @@ .SH NAME .PP -git-bug-user - Display or change the user identity. +git-bug-user - List identities .SH SYNOPSIS .PP -\fBgit-bug user [USER-ID] [flags]\fP +\fBgit-bug user [flags]\fP .SH DESCRIPTION .PP -Display or change the user identity. +List identities .SH OPTIONS .PP -\fB-f\fP, \fB--field\fP="" - Select field to display. Valid values are [email,humanId,id,lastModification,lastModificationLamports,login,metadata,name] +\fB-f\fP, \fB--format\fP="default" + Select the output formatting style. Valid values are [default,json] .PP \fB-h\fP, \fB--help\fP[=false] @@ -28,4 +28,4 @@ Display or change the user identity. .SH SEE ALSO .PP -\fBgit-bug(1)\fP, \fBgit-bug-user-adopt(1)\fP, \fBgit-bug-user-create(1)\fP, \fBgit-bug-user-ls(1)\fP +\fBgit-bug(1)\fP, \fBgit-bug-user-adopt(1)\fP, \fBgit-bug-user-new(1)\fP, \fBgit-bug-user-user(1)\fP diff --git a/doc/man/git-bug-version.1 b/doc/man/git-bug-version.1 index 311f1d2a7e2241bf89f1548b01cca94b89b344af..6b688221627f02e7720130732005b62234136f5b 100644 --- a/doc/man/git-bug-version.1 +++ b/doc/man/git-bug-version.1 @@ -3,7 +3,7 @@ .SH NAME .PP -git-bug-version - Show git-bug version information. +git-bug-version - Show git-bug version information .SH SYNOPSIS @@ -13,7 +13,7 @@ git-bug-version - Show git-bug version information. .SH DESCRIPTION .PP -Show git-bug version information. +Show git-bug version information .SH OPTIONS diff --git a/doc/man/git-bug-webui.1 b/doc/man/git-bug-webui.1 index 0fcfeac2f9e3479700dcc948827f5ae918ea6007..f6cdcfde020c62ab9ddec461ab79041d2e562cef 100644 --- a/doc/man/git-bug-webui.1 +++ b/doc/man/git-bug-webui.1 @@ -3,7 +3,7 @@ .SH NAME .PP -git-bug-webui - Launch the web UI. +git-bug-webui - Launch the web UI .SH SYNOPSIS diff --git a/doc/man/git-bug.1 b/doc/man/git-bug.1 index b982c7f47bbc167859b11397dc1de7436a21dcc8..8b66d31224c468025dfa841479886731f63a95de 100644 --- a/doc/man/git-bug.1 +++ b/doc/man/git-bug.1 @@ -3,7 +3,7 @@ .SH NAME .PP -git-bug - A bug tracker embedded in Git. +git-bug - A bug tracker embedded in Git .SH SYNOPSIS @@ -29,4 +29,4 @@ the same git remote you are already using to collaborate with other people. .SH SEE ALSO .PP -\fBgit-bug-add(1)\fP, \fBgit-bug-bridge(1)\fP, \fBgit-bug-commands(1)\fP, \fBgit-bug-comment(1)\fP, \fBgit-bug-deselect(1)\fP, \fBgit-bug-label(1)\fP, \fBgit-bug-ls(1)\fP, \fBgit-bug-pull(1)\fP, \fBgit-bug-push(1)\fP, \fBgit-bug-rm(1)\fP, \fBgit-bug-select(1)\fP, \fBgit-bug-show(1)\fP, \fBgit-bug-status(1)\fP, \fBgit-bug-termui(1)\fP, \fBgit-bug-title(1)\fP, \fBgit-bug-user(1)\fP, \fBgit-bug-version(1)\fP, \fBgit-bug-webui(1)\fP +\fBgit-bug-bridge(1)\fP, \fBgit-bug-bug(1)\fP, \fBgit-bug-commands(1)\fP, \fBgit-bug-label(1)\fP, \fBgit-bug-pull(1)\fP, \fBgit-bug-push(1)\fP, \fBgit-bug-termui(1)\fP, \fBgit-bug-user(1)\fP, \fBgit-bug-version(1)\fP, \fBgit-bug-webui(1)\fP diff --git a/doc/md/git-bug.md b/doc/md/git-bug.md index c47f848450896730cd8368ea4f530f0f48312733..b311aff59e6f8eeaba0bf98c65221c463757f703 100644 --- a/doc/md/git-bug.md +++ b/doc/md/git-bug.md @@ -1,6 +1,6 @@ ## git-bug -A bug tracker embedded in Git. +A bug tracker embedded in Git ### Synopsis @@ -24,22 +24,14 @@ git-bug [flags] ### SEE ALSO -* [git-bug add](git-bug_add.md) - Create a new bug. -* [git-bug bridge](git-bug_bridge.md) - Configure and use bridges to other bug trackers. +* [git-bug bridge](git-bug_bridge.md) - List bridges to other bug trackers +* [git-bug bug](git-bug_bug.md) - List bugs * [git-bug commands](git-bug_commands.md) - Display available commands. -* [git-bug comment](git-bug_comment.md) - Display or add comments to a bug. -* [git-bug deselect](git-bug_deselect.md) - Clear the implicitly selected bug. -* [git-bug label](git-bug_label.md) - Display, add or remove labels to/from a bug. -* [git-bug ls](git-bug_ls.md) - List bugs. -* [git-bug pull](git-bug_pull.md) - Pull bugs update from a git remote. -* [git-bug push](git-bug_push.md) - Push bugs update to a git remote. -* [git-bug rm](git-bug_rm.md) - Remove an existing bug. -* [git-bug select](git-bug_select.md) - Select a bug for implicit use in future commands. -* [git-bug show](git-bug_show.md) - Display the details of a bug. -* [git-bug status](git-bug_status.md) - Display or change a bug status. -* [git-bug termui](git-bug_termui.md) - Launch the terminal UI. -* [git-bug title](git-bug_title.md) - Display or change a title of a bug. -* [git-bug user](git-bug_user.md) - Display or change the user identity. -* [git-bug version](git-bug_version.md) - Show git-bug version information. -* [git-bug webui](git-bug_webui.md) - Launch the web UI. +* [git-bug label](git-bug_label.md) - List valid labels +* [git-bug pull](git-bug_pull.md) - Pull updates from a git remote +* [git-bug push](git-bug_push.md) - Push updates to a git remote +* [git-bug termui](git-bug_termui.md) - Launch the terminal UI +* [git-bug user](git-bug_user.md) - List identities +* [git-bug version](git-bug_version.md) - Show git-bug version information +* [git-bug webui](git-bug_webui.md) - Launch the web UI diff --git a/doc/md/git-bug_bridge.md b/doc/md/git-bug_bridge.md index e8b6e3823a483987073f645387e6e28947ad795f..c3e239e2e8c94f7504770d7480f1f39c91097ca5 100644 --- a/doc/md/git-bug_bridge.md +++ b/doc/md/git-bug_bridge.md @@ -1,6 +1,6 @@ ## git-bug bridge -Configure and use bridges to other bug trackers. +List bridges to other bug trackers ``` git-bug bridge [flags] @@ -14,10 +14,10 @@ git-bug bridge [flags] ### SEE ALSO -* [git-bug](git-bug.md) - A bug tracker embedded in Git. -* [git-bug bridge auth](git-bug_bridge_auth.md) - List all known bridge authentication credentials. -* [git-bug bridge configure](git-bug_bridge_configure.md) - Configure a new bridge. -* [git-bug bridge pull](git-bug_bridge_pull.md) - Pull updates. -* [git-bug bridge push](git-bug_bridge_push.md) - Push updates. -* [git-bug bridge rm](git-bug_bridge_rm.md) - Delete a configured bridge. +* [git-bug](git-bug.md) - A bug tracker embedded in Git +* [git-bug bridge auth](git-bug_bridge_auth.md) - List all known bridge authentication credentials +* [git-bug bridge new](git-bug_bridge_new.md) - Configure a new bridge +* [git-bug bridge pull](git-bug_bridge_pull.md) - Pull updates from a remote bug tracker +* [git-bug bridge push](git-bug_bridge_push.md) - Push updates to remote bug tracker +* [git-bug bridge rm](git-bug_bridge_rm.md) - Delete a configured bridge diff --git a/doc/md/git-bug_bridge_auth.md b/doc/md/git-bug_bridge_auth.md index bcaf60283ebee3958173a4c364261609b2e2bdae..e740482295c7dc4a5a00d30adb986eb938d24d1a 100644 --- a/doc/md/git-bug_bridge_auth.md +++ b/doc/md/git-bug_bridge_auth.md @@ -1,6 +1,6 @@ ## git-bug bridge auth -List all known bridge authentication credentials. +List all known bridge authentication credentials ``` git-bug bridge auth [flags] @@ -14,8 +14,8 @@ git-bug bridge auth [flags] ### SEE ALSO -* [git-bug bridge](git-bug_bridge.md) - Configure and use bridges to other bug trackers. +* [git-bug bridge](git-bug_bridge.md) - List bridges to other bug trackers * [git-bug bridge auth add-token](git-bug_bridge_auth_add-token.md) - Store a new token -* [git-bug bridge auth rm](git-bug_bridge_auth_rm.md) - Remove a credential. -* [git-bug bridge auth show](git-bug_bridge_auth_show.md) - Display an authentication credential. +* [git-bug bridge auth rm](git-bug_bridge_auth_rm.md) - Remove a credential +* [git-bug bridge auth show](git-bug_bridge_auth_show.md) - Display an authentication credential diff --git a/doc/md/git-bug_bridge_auth_add-token.md b/doc/md/git-bug_bridge_auth_add-token.md index faafaf61cdec4572deea09702594a2f5bcaab2d5..3fb33036c2995d8c8df2c37a191207c9f74b99ac 100644 --- a/doc/md/git-bug_bridge_auth_add-token.md +++ b/doc/md/git-bug_bridge_auth_add-token.md @@ -17,5 +17,5 @@ git-bug bridge auth add-token [TOKEN] [flags] ### SEE ALSO -* [git-bug bridge auth](git-bug_bridge_auth.md) - List all known bridge authentication credentials. +* [git-bug bridge auth](git-bug_bridge_auth.md) - List all known bridge authentication credentials diff --git a/doc/md/git-bug_bridge_auth_rm.md b/doc/md/git-bug_bridge_auth_rm.md index d890054eee5edecde2195ff9f623edc35f6129da..8ae3ad5266e545a4d796229329d4439b955ddec8 100644 --- a/doc/md/git-bug_bridge_auth_rm.md +++ b/doc/md/git-bug_bridge_auth_rm.md @@ -1,9 +1,9 @@ ## git-bug bridge auth rm -Remove a credential. +Remove a credential ``` -git-bug bridge auth rm ID [flags] +git-bug bridge auth rm BRIDGE_ID [flags] ``` ### Options @@ -14,5 +14,5 @@ git-bug bridge auth rm ID [flags] ### SEE ALSO -* [git-bug bridge auth](git-bug_bridge_auth.md) - List all known bridge authentication credentials. +* [git-bug bridge auth](git-bug_bridge_auth.md) - List all known bridge authentication credentials diff --git a/doc/md/git-bug_bridge_auth_show.md b/doc/md/git-bug_bridge_auth_show.md index 18d468db3b5849dbfadbcf67dfbf8fbec97da4ad..f73bb8768e6c3fe2acdfbb00f1788aa12ebe5273 100644 --- a/doc/md/git-bug_bridge_auth_show.md +++ b/doc/md/git-bug_bridge_auth_show.md @@ -1,6 +1,6 @@ ## git-bug bridge auth show -Display an authentication credential. +Display an authentication credential ``` git-bug bridge auth show [flags] @@ -14,5 +14,5 @@ git-bug bridge auth show [flags] ### SEE ALSO -* [git-bug bridge auth](git-bug_bridge_auth.md) - List all known bridge authentication credentials. +* [git-bug bridge auth](git-bug_bridge_auth.md) - List all known bridge authentication credentials diff --git a/doc/md/git-bug_bridge_configure.md b/doc/md/git-bug_bridge_new.md similarity index 88% rename from doc/md/git-bug_bridge_configure.md rename to doc/md/git-bug_bridge_new.md index e3b4c6bdcd4c28b3090c9d421d9fdd27d993bc66..81bffd49a06522e474c9eb25135a0c1208b9f2f0 100644 --- a/doc/md/git-bug_bridge_configure.md +++ b/doc/md/git-bug_bridge_new.md @@ -1,13 +1,13 @@ -## git-bug bridge configure +## git-bug bridge new -Configure a new bridge. +Configure a new bridge ### Synopsis 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. ``` -git-bug bridge configure [flags] +git-bug bridge new [flags] ``` ### Examples @@ -47,7 +47,7 @@ Enter token: 87cf5c03b64029f18ea5f9ca5679daa08ccbd700 Successfully configured bridge: default # For GitHub -git bug bridge configure \ +git bug bridge new \ --name=default \ --target=github \ --owner=$(OWNER) \ @@ -55,13 +55,13 @@ git bug bridge configure \ --token=$(TOKEN) # For Launchpad -git bug bridge configure \ +git bug bridge new \ --name=default \ --target=launchpad-preview \ --url=https://bugs.launchpad.net/ubuntu/ # For Gitlab -git bug bridge configure \ +git bug bridge new \ --name=default \ --target=github \ --url=https://github.com/michaelmure/git-bug \ @@ -82,10 +82,10 @@ git bug bridge configure \ -o, --owner string The owner of the remote repository -p, --project string The name of the remote repository --non-interactive Do not ask for user input - -h, --help help for configure + -h, --help help for new ``` ### SEE ALSO -* [git-bug bridge](git-bug_bridge.md) - Configure and use bridges to other bug trackers. +* [git-bug bridge](git-bug_bridge.md) - List bridges to other bug trackers diff --git a/doc/md/git-bug_bridge_pull.md b/doc/md/git-bug_bridge_pull.md index b1ce21692e6dc29df71da7787bb44162a1e06b57..0975010a4644c4f9ec370bcd7c139bf44af3ee4f 100644 --- a/doc/md/git-bug_bridge_pull.md +++ b/doc/md/git-bug_bridge_pull.md @@ -1,6 +1,6 @@ ## git-bug bridge pull -Pull updates. +Pull updates from a remote bug tracker ``` git-bug bridge pull [NAME] [flags] @@ -16,5 +16,5 @@ git-bug bridge pull [NAME] [flags] ### SEE ALSO -* [git-bug bridge](git-bug_bridge.md) - Configure and use bridges to other bug trackers. +* [git-bug bridge](git-bug_bridge.md) - List bridges to other bug trackers diff --git a/doc/md/git-bug_bridge_push.md b/doc/md/git-bug_bridge_push.md index a532a26d6aaaa4170f5633a1061eafc45c082efb..c7e1a86d089cd574b6569d14ec4c2b667e488c25 100644 --- a/doc/md/git-bug_bridge_push.md +++ b/doc/md/git-bug_bridge_push.md @@ -1,6 +1,6 @@ ## git-bug bridge push -Push updates. +Push updates to remote bug tracker ``` git-bug bridge push [NAME] [flags] @@ -14,5 +14,5 @@ git-bug bridge push [NAME] [flags] ### SEE ALSO -* [git-bug bridge](git-bug_bridge.md) - Configure and use bridges to other bug trackers. +* [git-bug bridge](git-bug_bridge.md) - List bridges to other bug trackers diff --git a/doc/md/git-bug_bridge_rm.md b/doc/md/git-bug_bridge_rm.md index 6d8ff67cb5374917282171102699d34cd3a20ce1..2b3baf710e0fcaeebf1c19683ba0fa4d2b8bcd89 100644 --- a/doc/md/git-bug_bridge_rm.md +++ b/doc/md/git-bug_bridge_rm.md @@ -1,6 +1,6 @@ ## git-bug bridge rm -Delete a configured bridge. +Delete a configured bridge ``` git-bug bridge rm NAME [flags] @@ -14,5 +14,5 @@ git-bug bridge rm NAME [flags] ### SEE ALSO -* [git-bug bridge](git-bug_bridge.md) - Configure and use bridges to other bug trackers. +* [git-bug bridge](git-bug_bridge.md) - List bridges to other bug trackers diff --git a/doc/md/git-bug_ls.md b/doc/md/git-bug_bug.md similarity index 58% rename from doc/md/git-bug_ls.md rename to doc/md/git-bug_bug.md index 5aec0fbc41da8f1a091eabd86087a9305e81c8a5..c040cd1646ac6a71817d57e9e88d8518e240ba8e 100644 --- a/doc/md/git-bug_ls.md +++ b/doc/md/git-bug_bug.md @@ -1,6 +1,6 @@ -## git-bug ls +## git-bug bug -List bugs. +List bugs ### Synopsis @@ -9,23 +9,23 @@ Display a summary of each bugs. You can pass an additional query to filter and order the list. This query can be expressed either with a simple query language, flags, a natural language full text search, or a combination of the aforementioned. ``` -git-bug ls [QUERY] [flags] +git-bug bug [QUERY] [flags] ``` ### Examples ``` List open bugs sorted by last edition with a query: -git bug ls status:open sort:edit-desc +git bug status:open sort:edit-desc List closed bugs sorted by creation with flags: -git bug ls --status closed --by creation +git bug --status closed --by creation Do a full text search of all bugs: -git bug ls "foo bar" baz +git bug "foo bar" baz Use queries, flags, and full text search: -git bug ls status:open --by creation "foo bar" baz +git bug status:open --by creation "foo bar" baz ``` @@ -43,10 +43,19 @@ git bug ls status:open --by creation "foo bar" baz -b, --by string Sort the results by a characteristic. Valid values are [id,creation,edit] (default "creation") -d, --direction string Select the sorting direction. Valid values are [asc,desc] (default "asc") -f, --format string Select the output formatting style. Valid values are [default,plain,compact,id,json,org-mode] (default "default") - -h, --help help for ls + -h, --help help for bug ``` ### SEE ALSO -* [git-bug](git-bug.md) - A bug tracker embedded in Git. +* [git-bug](git-bug.md) - A bug tracker embedded in Git +* [git-bug bug comment](git-bug_bug_comment.md) - List a bug's comments +* [git-bug bug deselect](git-bug_bug_deselect.md) - Clear the implicitly selected bug +* [git-bug bug label](git-bug_bug_label.md) - Display labels of a bug +* [git-bug bug new](git-bug_bug_new.md) - Create a new bug +* [git-bug bug rm](git-bug_bug_rm.md) - Remove an existing bug +* [git-bug bug select](git-bug_bug_select.md) - Select a bug for implicit use in future commands +* [git-bug bug show](git-bug_bug_show.md) - Display the details of a bug +* [git-bug bug status](git-bug_bug_status.md) - Display the status of a bug +* [git-bug bug title](git-bug_bug_title.md) - Display the title of a bug diff --git a/doc/md/git-bug_bug_comment.md b/doc/md/git-bug_bug_comment.md new file mode 100644 index 0000000000000000000000000000000000000000..6d56a87391f55b072d8b7a130ac9773f02087137 --- /dev/null +++ b/doc/md/git-bug_bug_comment.md @@ -0,0 +1,20 @@ +## git-bug bug comment + +List a bug's comments + +``` +git-bug bug comment [BUG_ID] [flags] +``` + +### Options + +``` + -h, --help help for comment +``` + +### SEE ALSO + +* [git-bug bug](git-bug_bug.md) - List bugs +* [git-bug bug comment edit](git-bug_bug_comment_edit.md) - Edit an existing comment on a bug +* [git-bug bug comment new](git-bug_bug_comment_new.md) - Add a new comment to a bug + diff --git a/doc/md/git-bug_comment_edit.md b/doc/md/git-bug_bug_comment_edit.md similarity index 64% rename from doc/md/git-bug_comment_edit.md rename to doc/md/git-bug_bug_comment_edit.md index 1a53b4394f5e10e9c94bc426d0d63e32b5a8c943..86a0a4e62b48b4a9f7ce4d48cc57fd370a4e6871 100644 --- a/doc/md/git-bug_comment_edit.md +++ b/doc/md/git-bug_bug_comment_edit.md @@ -1,9 +1,9 @@ -## git-bug comment edit +## git-bug bug comment edit -Edit an existing comment on a bug. +Edit an existing comment on a bug ``` -git-bug comment edit [COMMENT_ID] [flags] +git-bug bug comment edit [COMMENT_ID] [flags] ``` ### Options @@ -17,5 +17,5 @@ git-bug comment edit [COMMENT_ID] [flags] ### SEE ALSO -* [git-bug comment](git-bug_comment.md) - Display or add comments to a bug. +* [git-bug bug comment](git-bug_bug_comment.md) - List a bug's comments diff --git a/doc/md/git-bug_comment_add.md b/doc/md/git-bug_bug_comment_new.md similarity index 58% rename from doc/md/git-bug_comment_add.md rename to doc/md/git-bug_bug_comment_new.md index 5a199aba5e7c18aefe8a7639868bfd313cecfe61..b1ba697bbc53177be01ad7744216d889d6839f71 100644 --- a/doc/md/git-bug_comment_add.md +++ b/doc/md/git-bug_bug_comment_new.md @@ -1,9 +1,9 @@ -## git-bug comment add +## git-bug bug comment new -Add a new comment to a bug. +Add a new comment to a bug ``` -git-bug comment add [ID] [flags] +git-bug bug comment new [BUG_ID] [flags] ``` ### Options @@ -12,10 +12,10 @@ git-bug comment add [ID] [flags] -F, --file string Take the message from the given file. Use - to read the message from the standard input -m, --message string Provide the new message from the command line --non-interactive Do not ask for user input - -h, --help help for add + -h, --help help for new ``` ### SEE ALSO -* [git-bug comment](git-bug_comment.md) - Display or add comments to a bug. +* [git-bug bug comment](git-bug_bug_comment.md) - List a bug's comments diff --git a/doc/md/git-bug_deselect.md b/doc/md/git-bug_bug_deselect.md similarity index 55% rename from doc/md/git-bug_deselect.md rename to doc/md/git-bug_bug_deselect.md index 51ce5dda44b4b0bb56f27fbe3095add44705aaf3..d4136068389bb1d0879aef905351bd603371cbc5 100644 --- a/doc/md/git-bug_deselect.md +++ b/doc/md/git-bug_bug_deselect.md @@ -1,9 +1,9 @@ -## git-bug deselect +## git-bug bug deselect -Clear the implicitly selected bug. +Clear the implicitly selected bug ``` -git-bug deselect [flags] +git-bug bug deselect [flags] ``` ### Examples @@ -24,5 +24,5 @@ git bug deselect ### SEE ALSO -* [git-bug](git-bug.md) - A bug tracker embedded in Git. +* [git-bug bug](git-bug_bug.md) - List bugs diff --git a/doc/md/git-bug_bug_label.md b/doc/md/git-bug_bug_label.md new file mode 100644 index 0000000000000000000000000000000000000000..ac5fb985f8cb751722f7025355eb419960b283c4 --- /dev/null +++ b/doc/md/git-bug_bug_label.md @@ -0,0 +1,20 @@ +## git-bug bug label + +Display labels of a bug + +``` +git-bug bug label [BUG_ID] [flags] +``` + +### Options + +``` + -h, --help help for label +``` + +### SEE ALSO + +* [git-bug bug](git-bug_bug.md) - List bugs +* [git-bug bug label new](git-bug_bug_label_new.md) - Add a label to a bug +* [git-bug bug label rm](git-bug_bug_label_rm.md) - Remove a label from a bug + diff --git a/doc/md/git-bug_bug_label_new.md b/doc/md/git-bug_bug_label_new.md new file mode 100644 index 0000000000000000000000000000000000000000..ada978dc0bd8c2e60731e7b2129158f6964148a2 --- /dev/null +++ b/doc/md/git-bug_bug_label_new.md @@ -0,0 +1,18 @@ +## git-bug bug label new + +Add a label to a bug + +``` +git-bug bug label new [BUG_ID] LABEL... [flags] +``` + +### Options + +``` + -h, --help help for new +``` + +### SEE ALSO + +* [git-bug bug label](git-bug_bug_label.md) - Display labels of a bug + diff --git a/doc/md/git-bug_bug_label_rm.md b/doc/md/git-bug_bug_label_rm.md new file mode 100644 index 0000000000000000000000000000000000000000..7899b1be86a434cd05b95663579c63c93798915f --- /dev/null +++ b/doc/md/git-bug_bug_label_rm.md @@ -0,0 +1,18 @@ +## git-bug bug label rm + +Remove a label from a bug + +``` +git-bug bug label rm [BUG_ID] LABEL... [flags] +``` + +### Options + +``` + -h, --help help for rm +``` + +### SEE ALSO + +* [git-bug bug label](git-bug_bug_label.md) - Display labels of a bug + diff --git a/doc/md/git-bug_add.md b/doc/md/git-bug_bug_new.md similarity index 69% rename from doc/md/git-bug_add.md rename to doc/md/git-bug_bug_new.md index 5e7486105ac5957497fad9df58c41b58a7670cb5..4dacc5fd7ac560b44858fd73944ae21bdbbf0c1c 100644 --- a/doc/md/git-bug_add.md +++ b/doc/md/git-bug_bug_new.md @@ -1,9 +1,9 @@ -## git-bug add +## git-bug bug new -Create a new bug. +Create a new bug ``` -git-bug add [flags] +git-bug bug new [flags] ``` ### Options @@ -13,10 +13,10 @@ git-bug add [flags] -m, --message string Provide a message to describe the issue -F, --file string Take the message from the given file. Use - to read the message from the standard input --non-interactive Do not ask for user input - -h, --help help for add + -h, --help help for new ``` ### SEE ALSO -* [git-bug](git-bug.md) - A bug tracker embedded in Git. +* [git-bug bug](git-bug_bug.md) - List bugs diff --git a/doc/md/git-bug_rm.md b/doc/md/git-bug_bug_rm.md similarity index 70% rename from doc/md/git-bug_rm.md rename to doc/md/git-bug_bug_rm.md index 3b77ce1c1741c5b2d956d05366b0f5d24032c7e4..2e77c6ec3e33e572706a2b1e70307d68db492824 100644 --- a/doc/md/git-bug_rm.md +++ b/doc/md/git-bug_bug_rm.md @@ -1,13 +1,13 @@ -## git-bug rm +## git-bug bug rm -Remove an existing bug. +Remove an existing bug ### Synopsis Remove an existing bug in the local repository. Note removing bugs that were imported from bridges will not remove the bug on the remote, and will only remove the local copy of the bug. ``` -git-bug rm ID [flags] +git-bug bug rm BUG_ID [flags] ``` ### Options @@ -18,5 +18,5 @@ git-bug rm ID [flags] ### SEE ALSO -* [git-bug](git-bug.md) - A bug tracker embedded in Git. +* [git-bug bug](git-bug_bug.md) - List bugs diff --git a/doc/md/git-bug_select.md b/doc/md/git-bug_bug_select.md similarity index 73% rename from doc/md/git-bug_select.md rename to doc/md/git-bug_bug_select.md index b8a71120423cf17424314d345d3c222bd35ef430..d529c5448eb337ecf31bf6f9219692446c3bab79 100644 --- a/doc/md/git-bug_select.md +++ b/doc/md/git-bug_bug_select.md @@ -1,6 +1,6 @@ -## git-bug select +## git-bug bug select -Select a bug for implicit use in future commands. +Select a bug for implicit use in future commands ### Synopsis @@ -15,7 +15,7 @@ The complementary command is "git bug deselect" performing the opposite operatio ``` -git-bug select ID [flags] +git-bug bug select BUG_ID [flags] ``` ### Examples @@ -35,5 +35,5 @@ git bug status ### SEE ALSO -* [git-bug](git-bug.md) - A bug tracker embedded in Git. +* [git-bug bug](git-bug_bug.md) - List bugs diff --git a/doc/md/git-bug_show.md b/doc/md/git-bug_bug_show.md similarity index 74% rename from doc/md/git-bug_show.md rename to doc/md/git-bug_bug_show.md index c284fc710c33edf97fd9872a0f85c2b7cd40a02a..1c7b762fb45ff94dd7177ce9fde336d53690113d 100644 --- a/doc/md/git-bug_show.md +++ b/doc/md/git-bug_bug_show.md @@ -1,9 +1,9 @@ -## git-bug show +## git-bug bug show -Display the details of a bug. +Display the details of a bug ``` -git-bug show [ID] [flags] +git-bug bug show [BUG_ID] [flags] ``` ### Options @@ -16,5 +16,5 @@ git-bug show [ID] [flags] ### SEE ALSO -* [git-bug](git-bug.md) - A bug tracker embedded in Git. +* [git-bug bug](git-bug_bug.md) - List bugs diff --git a/doc/md/git-bug_bug_status.md b/doc/md/git-bug_bug_status.md new file mode 100644 index 0000000000000000000000000000000000000000..6e71e4584978e633192aee52c64d1c9e668127cb --- /dev/null +++ b/doc/md/git-bug_bug_status.md @@ -0,0 +1,20 @@ +## git-bug bug status + +Display the status of a bug + +``` +git-bug bug status [BUG_ID] [flags] +``` + +### Options + +``` + -h, --help help for status +``` + +### SEE ALSO + +* [git-bug bug](git-bug_bug.md) - List bugs +* [git-bug bug status close](git-bug_bug_status_close.md) - Mark a bug as closed +* [git-bug bug status open](git-bug_bug_status_open.md) - Mark a bug as open + diff --git a/doc/md/git-bug_bug_status_close.md b/doc/md/git-bug_bug_status_close.md new file mode 100644 index 0000000000000000000000000000000000000000..f63fabb54357a9a9c657303a052c789c0fb67b35 --- /dev/null +++ b/doc/md/git-bug_bug_status_close.md @@ -0,0 +1,18 @@ +## git-bug bug status close + +Mark a bug as closed + +``` +git-bug bug status close [BUG_ID] [flags] +``` + +### Options + +``` + -h, --help help for close +``` + +### SEE ALSO + +* [git-bug bug status](git-bug_bug_status.md) - Display the status of a bug + diff --git a/doc/md/git-bug_bug_status_open.md b/doc/md/git-bug_bug_status_open.md new file mode 100644 index 0000000000000000000000000000000000000000..0899de30c031fe11ee004b20462acf92d8dc084c --- /dev/null +++ b/doc/md/git-bug_bug_status_open.md @@ -0,0 +1,18 @@ +## git-bug bug status open + +Mark a bug as open + +``` +git-bug bug status open [BUG_ID] [flags] +``` + +### Options + +``` + -h, --help help for open +``` + +### SEE ALSO + +* [git-bug bug status](git-bug_bug_status.md) - Display the status of a bug + diff --git a/doc/md/git-bug_bug_title.md b/doc/md/git-bug_bug_title.md new file mode 100644 index 0000000000000000000000000000000000000000..2650487e07854f378e3f280c7154b1678362affe --- /dev/null +++ b/doc/md/git-bug_bug_title.md @@ -0,0 +1,19 @@ +## git-bug bug title + +Display the title of a bug + +``` +git-bug bug title [BUG_ID] [flags] +``` + +### Options + +``` + -h, --help help for title +``` + +### SEE ALSO + +* [git-bug bug](git-bug_bug.md) - List bugs +* [git-bug bug title edit](git-bug_bug_title_edit.md) - Edit a title of a bug + diff --git a/doc/md/git-bug_title_edit.md b/doc/md/git-bug_bug_title_edit.md similarity index 55% rename from doc/md/git-bug_title_edit.md rename to doc/md/git-bug_bug_title_edit.md index ae060680b9381b07f4d3d50e52faf95fac912b62..0a26609de9d9cf500ef186ce15cf96e520fd9695 100644 --- a/doc/md/git-bug_title_edit.md +++ b/doc/md/git-bug_bug_title_edit.md @@ -1,9 +1,9 @@ -## git-bug title edit +## git-bug bug title edit -Edit a title of a bug. +Edit a title of a bug ``` -git-bug title edit [ID] [flags] +git-bug bug title edit [BUG_ID] [flags] ``` ### Options @@ -16,5 +16,5 @@ git-bug title edit [ID] [flags] ### SEE ALSO -* [git-bug title](git-bug_title.md) - Display or change a title of a bug. +* [git-bug bug title](git-bug_bug_title.md) - Display the title of a bug diff --git a/doc/md/git-bug_commands.md b/doc/md/git-bug_commands.md index db1054f8bf79ed419def69d19d0b6d5039963eb9..bce599dd924a01d3608d213931293d289e2de2c9 100644 --- a/doc/md/git-bug_commands.md +++ b/doc/md/git-bug_commands.md @@ -15,5 +15,5 @@ git-bug commands [flags] ### SEE ALSO -* [git-bug](git-bug.md) - A bug tracker embedded in Git. +* [git-bug](git-bug.md) - A bug tracker embedded in Git diff --git a/doc/md/git-bug_comment.md b/doc/md/git-bug_comment.md deleted file mode 100644 index 48050a9795a30c740a62a408cd2a3401a43b1cf9..0000000000000000000000000000000000000000 --- a/doc/md/git-bug_comment.md +++ /dev/null @@ -1,20 +0,0 @@ -## git-bug comment - -Display or add comments to a bug. - -``` -git-bug comment [ID] [flags] -``` - -### Options - -``` - -h, --help help for comment -``` - -### SEE ALSO - -* [git-bug](git-bug.md) - A bug tracker embedded in Git. -* [git-bug comment add](git-bug_comment_add.md) - Add a new comment to a bug. -* [git-bug comment edit](git-bug_comment_edit.md) - Edit an existing comment on a bug. - diff --git a/doc/md/git-bug_label.md b/doc/md/git-bug_label.md index caeebe894ad1ea696858b0b098489f358550af38..969edc16f1f550add35ec67117ec6d380836604a 100644 --- a/doc/md/git-bug_label.md +++ b/doc/md/git-bug_label.md @@ -1,9 +1,15 @@ ## git-bug label -Display, add or remove labels to/from a bug. +List valid labels + +### Synopsis + +List valid labels. + +Note: in the future, a proper label policy could be implemented where valid labels are defined in a configuration file. Until that, the default behavior is to return the list of labels already used. ``` -git-bug label [ID] [flags] +git-bug label [flags] ``` ### Options @@ -14,8 +20,5 @@ git-bug label [ID] [flags] ### SEE ALSO -* [git-bug](git-bug.md) - A bug tracker embedded in Git. -* [git-bug label add](git-bug_label_add.md) - Add a label to a bug. -* [git-bug label ls](git-bug_label_ls.md) - List valid labels. -* [git-bug label rm](git-bug_label_rm.md) - Remove a label from a bug. +* [git-bug](git-bug.md) - A bug tracker embedded in Git diff --git a/doc/md/git-bug_label_add.md b/doc/md/git-bug_label_add.md deleted file mode 100644 index c90b45d31e97ed21e22d5a421d97c4e6ddead57e..0000000000000000000000000000000000000000 --- a/doc/md/git-bug_label_add.md +++ /dev/null @@ -1,18 +0,0 @@ -## git-bug label add - -Add a label to a bug. - -``` -git-bug label add [ID] LABEL... [flags] -``` - -### Options - -``` - -h, --help help for add -``` - -### SEE ALSO - -* [git-bug label](git-bug_label.md) - Display, add or remove labels to/from a bug. - diff --git a/doc/md/git-bug_label_ls.md b/doc/md/git-bug_label_ls.md deleted file mode 100644 index cda6ebba77a9ae650cd2d89418f8f71032385463..0000000000000000000000000000000000000000 --- a/doc/md/git-bug_label_ls.md +++ /dev/null @@ -1,24 +0,0 @@ -## git-bug label ls - -List valid labels. - -### Synopsis - -List valid labels. - -Note: in the future, a proper label policy could be implemented where valid labels are defined in a configuration file. Until that, the default behavior is to return the list of labels already used. - -``` -git-bug label ls [flags] -``` - -### Options - -``` - -h, --help help for ls -``` - -### SEE ALSO - -* [git-bug label](git-bug_label.md) - Display, add or remove labels to/from a bug. - diff --git a/doc/md/git-bug_label_rm.md b/doc/md/git-bug_label_rm.md deleted file mode 100644 index 15982d0ea905f444598903064e942366e053b79f..0000000000000000000000000000000000000000 --- a/doc/md/git-bug_label_rm.md +++ /dev/null @@ -1,18 +0,0 @@ -## git-bug label rm - -Remove a label from a bug. - -``` -git-bug label rm [ID] LABEL... [flags] -``` - -### Options - -``` - -h, --help help for rm -``` - -### SEE ALSO - -* [git-bug label](git-bug_label.md) - Display, add or remove labels to/from a bug. - diff --git a/doc/md/git-bug_pull.md b/doc/md/git-bug_pull.md index 5c1d51b6ae3ac690e6df5cfd2b7fe01fa50dcdf0..28f90959c9091753626d91bd5525c2c442e56ebd 100644 --- a/doc/md/git-bug_pull.md +++ b/doc/md/git-bug_pull.md @@ -1,6 +1,6 @@ ## git-bug pull -Pull bugs update from a git remote. +Pull updates from a git remote ``` git-bug pull [REMOTE] [flags] @@ -14,5 +14,5 @@ git-bug pull [REMOTE] [flags] ### SEE ALSO -* [git-bug](git-bug.md) - A bug tracker embedded in Git. +* [git-bug](git-bug.md) - A bug tracker embedded in Git diff --git a/doc/md/git-bug_push.md b/doc/md/git-bug_push.md index 1877970064cf9ab82f6c07fb3fbababe989c1c77..a0213a01e0898be50dcfc3cee1a2574ac34de083 100644 --- a/doc/md/git-bug_push.md +++ b/doc/md/git-bug_push.md @@ -1,6 +1,6 @@ ## git-bug push -Push bugs update to a git remote. +Push updates to a git remote ``` git-bug push [REMOTE] [flags] @@ -14,5 +14,5 @@ git-bug push [REMOTE] [flags] ### SEE ALSO -* [git-bug](git-bug.md) - A bug tracker embedded in Git. +* [git-bug](git-bug.md) - A bug tracker embedded in Git diff --git a/doc/md/git-bug_status.md b/doc/md/git-bug_status.md deleted file mode 100644 index 9eb56c6be78c880622df89a976fb4cf20d0982fc..0000000000000000000000000000000000000000 --- a/doc/md/git-bug_status.md +++ /dev/null @@ -1,20 +0,0 @@ -## git-bug status - -Display or change a bug status. - -``` -git-bug status [ID] [flags] -``` - -### Options - -``` - -h, --help help for status -``` - -### SEE ALSO - -* [git-bug](git-bug.md) - A bug tracker embedded in Git. -* [git-bug status close](git-bug_status_close.md) - Mark a bug as closed. -* [git-bug status open](git-bug_status_open.md) - Mark a bug as open. - diff --git a/doc/md/git-bug_status_close.md b/doc/md/git-bug_status_close.md deleted file mode 100644 index aee726491ec3ae9f469ce78d260c2459b54e467c..0000000000000000000000000000000000000000 --- a/doc/md/git-bug_status_close.md +++ /dev/null @@ -1,18 +0,0 @@ -## git-bug status close - -Mark a bug as closed. - -``` -git-bug status close [ID] [flags] -``` - -### Options - -``` - -h, --help help for close -``` - -### SEE ALSO - -* [git-bug status](git-bug_status.md) - Display or change a bug status. - diff --git a/doc/md/git-bug_status_open.md b/doc/md/git-bug_status_open.md deleted file mode 100644 index 332a1d96d5b374687846943a51f180e4f038fb8a..0000000000000000000000000000000000000000 --- a/doc/md/git-bug_status_open.md +++ /dev/null @@ -1,18 +0,0 @@ -## git-bug status open - -Mark a bug as open. - -``` -git-bug status open [ID] [flags] -``` - -### Options - -``` - -h, --help help for open -``` - -### SEE ALSO - -* [git-bug status](git-bug_status.md) - Display or change a bug status. - diff --git a/doc/md/git-bug_termui.md b/doc/md/git-bug_termui.md index 3da6869e70e100e47de225a6621ba772c6fcd762..ccab380c493d91a5ffaf2f6977571d3d6f3e124c 100644 --- a/doc/md/git-bug_termui.md +++ b/doc/md/git-bug_termui.md @@ -1,6 +1,6 @@ ## git-bug termui -Launch the terminal UI. +Launch the terminal UI ``` git-bug termui [flags] @@ -14,5 +14,5 @@ git-bug termui [flags] ### SEE ALSO -* [git-bug](git-bug.md) - A bug tracker embedded in Git. +* [git-bug](git-bug.md) - A bug tracker embedded in Git diff --git a/doc/md/git-bug_title.md b/doc/md/git-bug_title.md deleted file mode 100644 index 791fcb75b57966c2d8e0373b256e5c857f9ec814..0000000000000000000000000000000000000000 --- a/doc/md/git-bug_title.md +++ /dev/null @@ -1,19 +0,0 @@ -## git-bug title - -Display or change a title of a bug. - -``` -git-bug title [ID] [flags] -``` - -### Options - -``` - -h, --help help for title -``` - -### SEE ALSO - -* [git-bug](git-bug.md) - A bug tracker embedded in Git. -* [git-bug title edit](git-bug_title_edit.md) - Edit a title of a bug. - diff --git a/doc/md/git-bug_user.md b/doc/md/git-bug_user.md index 302a1eda8e0cfb6f0c3309fb14ac0a94d83bfa47..8693970508a6366a0be50aea3bcb7b23e33da0a7 100644 --- a/doc/md/git-bug_user.md +++ b/doc/md/git-bug_user.md @@ -1,22 +1,22 @@ ## git-bug user -Display or change the user identity. +List identities ``` -git-bug user [USER-ID] [flags] +git-bug user [flags] ``` ### Options ``` - -f, --field string Select field to display. Valid values are [email,humanId,id,lastModification,lastModificationLamports,login,metadata,name] - -h, --help help for user + -f, --format string Select the output formatting style. Valid values are [default,json] (default "default") + -h, --help help for user ``` ### SEE ALSO -* [git-bug](git-bug.md) - A bug tracker embedded in Git. -* [git-bug user adopt](git-bug_user_adopt.md) - Adopt an existing identity as your own. -* [git-bug user create](git-bug_user_create.md) - Create a new identity. -* [git-bug user ls](git-bug_user_ls.md) - List identities. +* [git-bug](git-bug.md) - A bug tracker embedded in Git +* [git-bug user adopt](git-bug_user_adopt.md) - Adopt an existing identity as your own +* [git-bug user new](git-bug_user_new.md) - Create a new identity +* [git-bug user user](git-bug_user_user.md) - Display a user identity diff --git a/doc/md/git-bug_user_adopt.md b/doc/md/git-bug_user_adopt.md index 87d1305f0844f4dcf53bf0172f8612576950ac1a..85a447ca4687be54e6fb66aa16bb9eafd134cd2c 100644 --- a/doc/md/git-bug_user_adopt.md +++ b/doc/md/git-bug_user_adopt.md @@ -1,9 +1,9 @@ ## git-bug user adopt -Adopt an existing identity as your own. +Adopt an existing identity as your own ``` -git-bug user adopt USER-ID [flags] +git-bug user adopt USER_ID [flags] ``` ### Options @@ -14,5 +14,5 @@ git-bug user adopt USER-ID [flags] ### SEE ALSO -* [git-bug user](git-bug_user.md) - Display or change the user identity. +* [git-bug user](git-bug_user.md) - List identities diff --git a/doc/md/git-bug_user_ls.md b/doc/md/git-bug_user_ls.md deleted file mode 100644 index d86263e32cf59cbe9fc43362cf51b931a049fc7b..0000000000000000000000000000000000000000 --- a/doc/md/git-bug_user_ls.md +++ /dev/null @@ -1,19 +0,0 @@ -## git-bug user ls - -List identities. - -``` -git-bug user ls [flags] -``` - -### Options - -``` - -f, --format string Select the output formatting style. Valid values are [default,json] (default "default") - -h, --help help for ls -``` - -### SEE ALSO - -* [git-bug user](git-bug_user.md) - Display or change the user identity. - diff --git a/doc/md/git-bug_user_create.md b/doc/md/git-bug_user_new.md similarity index 55% rename from doc/md/git-bug_user_create.md rename to doc/md/git-bug_user_new.md index 68fc2c0e094c9a19a8c0afdb26bac5f51cf18614..f9c5ed41088f30de00a7490de671aa6c91b3129b 100644 --- a/doc/md/git-bug_user_create.md +++ b/doc/md/git-bug_user_new.md @@ -1,9 +1,9 @@ -## git-bug user create +## git-bug user new -Create a new identity. +Create a new identity ``` -git-bug user create [flags] +git-bug user new [flags] ``` ### Options @@ -11,12 +11,12 @@ git-bug user create [flags] ``` -a, --avatar string Avatar URL -e, --email string Email of the user - -h, --help help for create + -h, --help help for new -n, --name string Name to identify the user --non-interactive Do not ask for user input ``` ### SEE ALSO -* [git-bug user](git-bug_user.md) - Display or change the user identity. +* [git-bug user](git-bug_user.md) - List identities diff --git a/doc/md/git-bug_user_user.md b/doc/md/git-bug_user_user.md new file mode 100644 index 0000000000000000000000000000000000000000..b3e6ac9bb6699e4fbd3aa3168c42339962ffc35d --- /dev/null +++ b/doc/md/git-bug_user_user.md @@ -0,0 +1,19 @@ +## git-bug user user + +Display a user identity + +``` +git-bug user user show [USER_ID] [flags] +``` + +### Options + +``` + -f, --field string Select field to display. Valid values are [email,humanId,id,lastModification,lastModificationLamports,login,metadata,name] + -h, --help help for user +``` + +### SEE ALSO + +* [git-bug user](git-bug_user.md) - List identities + diff --git a/doc/md/git-bug_version.md b/doc/md/git-bug_version.md index 2d1a16a956b4efe94f4ddaeb987b7e73ad10d8d0..ceba8790ff8d1dcbda7184f6803d98cba5f4f42e 100644 --- a/doc/md/git-bug_version.md +++ b/doc/md/git-bug_version.md @@ -1,6 +1,6 @@ ## git-bug version -Show git-bug version information. +Show git-bug version information ``` git-bug version [flags] @@ -17,5 +17,5 @@ git-bug version [flags] ### SEE ALSO -* [git-bug](git-bug.md) - A bug tracker embedded in Git. +* [git-bug](git-bug.md) - A bug tracker embedded in Git diff --git a/doc/md/git-bug_webui.md b/doc/md/git-bug_webui.md index b1c54d3b512a27fcb8ab41dca6a65c4e6bf0c99a..c342a9cf38e1bcbba7148aa6501819ac37e6327b 100644 --- a/doc/md/git-bug_webui.md +++ b/doc/md/git-bug_webui.md @@ -1,6 +1,6 @@ ## git-bug webui -Launch the web UI. +Launch the web UI ### Synopsis @@ -29,5 +29,5 @@ git-bug webui [flags] ### SEE ALSO -* [git-bug](git-bug.md) - A bug tracker embedded in Git. +* [git-bug](git-bug.md) - A bug tracker embedded in Git diff --git a/entities/identity/identity.go b/entities/identity/identity.go index fd9da0625979aa2beb3d2dc0dc88778bc61ecfad..d497dbcca8d4f1fbacc141e9238ebc46015bec47 100644 --- a/entities/identity/identity.go +++ b/entities/identity/identity.go @@ -22,7 +22,7 @@ const identityConfigKey = "git-bug.identity" var ErrNonFastForwardMerge = errors.New("non fast-forward identity merge") var ErrNoIdentitySet = errors.New("No identity is set.\n" + "To interact with bugs, an identity first needs to be created using " + - "\"git bug user create\" or adopted with \"git bug user adopt\"") + "\"git bug user new\" or adopted with \"git bug user adopt\"") var ErrMultipleIdentitiesSet = errors.New("multiple user identities set") func NewErrMultipleMatchIdentity(matching []entity.Id) *entity.ErrMultipleMatch { diff --git a/go.mod b/go.mod index 66c55162077eec1486008170d70014cbe7644e6a..0016de2fb2f1a8019a4d81c92890b0fa45fc657a 100644 --- a/go.mod +++ b/go.mod @@ -23,7 +23,7 @@ require ( github.com/pkg/errors v0.9.1 github.com/shurcooL/githubv4 v0.0.0-20190601194912-068505affed7 github.com/skratchdot/open-golang v0.0.0-20190402232053-79abb63cd66e - github.com/spf13/cobra v1.5.0 + github.com/spf13/cobra v1.6.1 github.com/stretchr/testify v1.8.1 github.com/vektah/gqlparser/v2 v2.5.1 github.com/xanzy/go-gitlab v0.74.0 @@ -79,7 +79,7 @@ require ( github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-retryablehttp v0.7.1 // indirect github.com/imdario/mergo v0.3.12 // indirect - github.com/inconshreveable/mousetrap v1.0.0 // indirect + github.com/inconshreveable/mousetrap v1.0.1 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351 // indirect github.com/lucasb-eyer/go-colorful v1.0.3 // indirect @@ -114,6 +114,5 @@ require ( google.golang.org/appengine v1.6.7 // indirect google.golang.org/protobuf v1.28.1 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 012320c58fc72e6a256823cb5f241c1cf3d681e3..683326c26e881f6fa64fcba9db9ffc989ac81519 100644 --- a/go.sum +++ b/go.sum @@ -167,8 +167,9 @@ github.com/icrowley/fake v0.0.0-20180203215853-4178557ae428/go.mod h1:uhpZMVGzny github.com/ikawaha/kagome.ipadic v1.1.2/go.mod h1:DPSBbU0czaJhAb/5uKQZHMc9MTVRpDugJfX+HddPHHg= github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= -github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc= +github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4= @@ -262,8 +263,8 @@ github.com/skratchdot/open-golang v0.0.0-20190402232053-79abb63cd66e/go.mod h1:s github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= -github.com/spf13/cobra v1.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU= -github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM= +github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA= +github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= diff --git a/misc/completion/bash/git-bug b/misc/completion/bash/git-bug index 2bca660b01b86d26d28135c8dbb7ed2288e03e1f..81eb0d3c9914ffa5b9db9a1d3c96891f4e097384 100644 --- a/misc/completion/bash/git-bug +++ b/misc/completion/bash/git-bug @@ -128,7 +128,7 @@ __git-bug_process_completion_results() { __git-bug_handle_special_char "$cur" = # Print the activeHelp statements before we finish - if [ ${#activeHelp} -ne 0 ]; then + if [ ${#activeHelp[*]} -ne 0 ]; then printf "\n"; printf "%s\n" "${activeHelp[@]}" printf "\n" diff --git a/misc/completion/powershell/git-bug b/misc/completion/powershell/git-bug index 1b3e99d0cf5ebc69fd05f1c499d2155984dc3750..424ad8acf8c9595593831fbfe97251d876a0cf57 100644 --- a/misc/completion/powershell/git-bug +++ b/misc/completion/powershell/git-bug @@ -10,7 +10,7 @@ filter __git-bug_escapeStringWithSpecialChars { $_ -replace '\s|#|@|\$|;|,|''|\{|\}|\(|\)|"|`|\||<|>|&','`$&' } -Register-ArgumentCompleter -CommandName 'git-bug' -ScriptBlock { +[scriptblock]$__git_bugCompleterBlock = { param( $WordToComplete, $CommandAst, @@ -226,3 +226,5 @@ Register-ArgumentCompleter -CommandName 'git-bug' -ScriptBlock { } } + +Register-ArgumentCompleter -CommandName 'git-bug' -ScriptBlock $__git_bugCompleterBlock diff --git a/repository/config.go b/repository/config.go index 4db8d4bea15a9c22d09e8109c8b73d71d1c3bf70..c6880b7d0bae5824c8d71a3393e521b5a9dde49a 100644 --- a/repository/config.go +++ b/repository/config.go @@ -26,7 +26,7 @@ type ConfigRead interface { // there is zero or more than one entry for this key ReadBool(key string) (bool, error) - // ReadBool read a single string value from the config + // ReadString read a single string value from the config // Return ErrNoConfigEntry or ErrMultipleConfigEntry if // there is zero or more than one entry for this key ReadString(key string) (string, error) @@ -38,13 +38,13 @@ type ConfigRead interface { } type ConfigWrite interface { - // Store writes a single key/value pair in the config + // StoreString writes a single string key/value pair in the config StoreString(key, value string) error - // Store writes a key and timestamp value to the config + // StoreTimestamp writes a key and timestamp value to the config StoreTimestamp(key string, value time.Time) error - // Store writes a key and boolean value to the config + // StoreBool writes a key and boolean value to the config StoreBool(key string, value bool) error // RemoveAll removes all key/value pair matching the key prefix