diff options
Diffstat (limited to 'commands')
45 files changed, 531 insertions, 374 deletions
diff --git a/commands/bridge/bridge.go b/commands/bridge/bridge.go index 980a38e2..9f7c5e50 100644 --- a/commands/bridge/bridge.go +++ b/commands/bridge/bridge.go @@ -7,9 +7,7 @@ import ( "github.com/MichaelMure/git-bug/commands/execenv" ) -func NewBridgeCommand() *cobra.Command { - env := execenv.NewEnv() - +func NewBridgeCommand(env *execenv.Env) *cobra.Command { cmd := &cobra.Command{ Use: "bridge", Short: "List bridges to other bug trackers", @@ -20,11 +18,11 @@ func NewBridgeCommand() *cobra.Command { Args: cobra.NoArgs, } - cmd.AddCommand(newBridgeAuthCommand()) - cmd.AddCommand(newBridgeNewCommand()) - cmd.AddCommand(newBridgePullCommand()) - cmd.AddCommand(newBridgePushCommand()) - cmd.AddCommand(newBridgeRm()) + cmd.AddCommand(newBridgeAuthCommand(env)) + cmd.AddCommand(newBridgeNewCommand(env)) + cmd.AddCommand(newBridgePullCommand(env)) + cmd.AddCommand(newBridgePushCommand(env)) + cmd.AddCommand(newBridgeRm(env)) return cmd } diff --git a/commands/bridge/bridge_auth.go b/commands/bridge/bridge_auth.go index 52e063e6..f27004e0 100644 --- a/commands/bridge/bridge_auth.go +++ b/commands/bridge/bridge_auth.go @@ -12,9 +12,7 @@ import ( "github.com/MichaelMure/git-bug/util/colors" ) -func newBridgeAuthCommand() *cobra.Command { - env := execenv.NewEnv() - +func newBridgeAuthCommand(env *execenv.Env) *cobra.Command { cmd := &cobra.Command{ Use: "auth", Short: "List all known bridge authentication credentials", @@ -25,9 +23,9 @@ func newBridgeAuthCommand() *cobra.Command { Args: cobra.NoArgs, } - cmd.AddCommand(newBridgeAuthAddTokenCommand()) - cmd.AddCommand(newBridgeAuthRm()) - cmd.AddCommand(newBridgeAuthShow()) + cmd.AddCommand(newBridgeAuthAddTokenCommand(env)) + cmd.AddCommand(newBridgeAuthRm(env)) + cmd.AddCommand(newBridgeAuthShow(env)) return cmd } diff --git a/commands/bridge/bridge_auth_addtoken.go b/commands/bridge/bridge_auth_addtoken.go index 2992fa63..5af27728 100644 --- a/commands/bridge/bridge_auth_addtoken.go +++ b/commands/bridge/bridge_auth_addtoken.go @@ -24,8 +24,7 @@ type bridgeAuthAddTokenOptions struct { user string } -func newBridgeAuthAddTokenCommand() *cobra.Command { - env := execenv.NewEnv() +func newBridgeAuthAddTokenCommand(env *execenv.Env) *cobra.Command { options := bridgeAuthAddTokenOptions{} cmd := &cobra.Command{ diff --git a/commands/bridge/bridge_auth_rm.go b/commands/bridge/bridge_auth_rm.go index d58ca63e..33253e26 100644 --- a/commands/bridge/bridge_auth_rm.go +++ b/commands/bridge/bridge_auth_rm.go @@ -8,9 +8,7 @@ import ( "github.com/MichaelMure/git-bug/commands/execenv" ) -func newBridgeAuthRm() *cobra.Command { - env := execenv.NewEnv() - +func newBridgeAuthRm(env *execenv.Env) *cobra.Command { cmd := &cobra.Command{ Use: "rm BRIDGE_ID", Short: "Remove a credential", diff --git a/commands/bridge/bridge_auth_show.go b/commands/bridge/bridge_auth_show.go index d373273d..25c517d3 100644 --- a/commands/bridge/bridge_auth_show.go +++ b/commands/bridge/bridge_auth_show.go @@ -13,9 +13,7 @@ import ( "github.com/MichaelMure/git-bug/commands/execenv" ) -func newBridgeAuthShow() *cobra.Command { - env := execenv.NewEnv() - +func newBridgeAuthShow(env *execenv.Env) *cobra.Command { cmd := &cobra.Command{ Use: "show", Short: "Display an authentication credential", diff --git a/commands/bridge/bridge_new.go b/commands/bridge/bridge_new.go index 2c51d9ef..07a555da 100644 --- a/commands/bridge/bridge_new.go +++ b/commands/bridge/bridge_new.go @@ -26,8 +26,7 @@ type bridgeNewOptions struct { nonInteractive bool } -func newBridgeNewCommand() *cobra.Command { - env := execenv.NewEnv() +func newBridgeNewCommand(env *execenv.Env) *cobra.Command { options := bridgeNewOptions{} cmd := &cobra.Command{ diff --git a/commands/bridge/bridge_pull.go b/commands/bridge/bridge_pull.go index d1fc279a..03f4929a 100644 --- a/commands/bridge/bridge_pull.go +++ b/commands/bridge/bridge_pull.go @@ -23,8 +23,7 @@ type bridgePullOptions struct { noResume bool } -func newBridgePullCommand() *cobra.Command { - env := execenv.NewEnv() +func newBridgePullCommand(env *execenv.Env) *cobra.Command { options := bridgePullOptions{} cmd := &cobra.Command{ diff --git a/commands/bridge/bridge_push.go b/commands/bridge/bridge_push.go index 51baed4d..08f9b872 100644 --- a/commands/bridge/bridge_push.go +++ b/commands/bridge/bridge_push.go @@ -15,9 +15,7 @@ import ( "github.com/MichaelMure/git-bug/util/interrupt" ) -func newBridgePushCommand() *cobra.Command { - env := execenv.NewEnv() - +func newBridgePushCommand(env *execenv.Env) *cobra.Command { cmd := &cobra.Command{ Use: "push [NAME]", Short: "Push updates to remote bug tracker", diff --git a/commands/bridge/bridge_rm.go b/commands/bridge/bridge_rm.go index 5d8d23c5..f6279ade 100644 --- a/commands/bridge/bridge_rm.go +++ b/commands/bridge/bridge_rm.go @@ -8,9 +8,7 @@ import ( "github.com/MichaelMure/git-bug/commands/execenv" ) -func newBridgeRm() *cobra.Command { - env := execenv.NewEnv() - +func newBridgeRm(env *execenv.Env) *cobra.Command { cmd := &cobra.Command{ Use: "rm NAME", Short: "Delete a configured bridge", diff --git a/commands/bug/bug.go b/commands/bug/bug.go index a5ce11ed..55a1fa59 100644 --- a/commands/bug/bug.go +++ b/commands/bug/bug.go @@ -15,26 +15,27 @@ import ( "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/entity" "github.com/MichaelMure/git-bug/query" "github.com/MichaelMure/git-bug/util/colors" ) type bugOptions struct { - statusQuery []string - authorQuery []string - metadataQuery []string - participantQuery []string - actorQuery []string - labelQuery []string - titleQuery []string - noQuery []string - sortBy string - sortDirection string - outputFormat string + statusQuery []string + authorQuery []string + metadataQuery []string + participantQuery []string + actorQuery []string + labelQuery []string + titleQuery []string + noQuery []string + sortBy string + sortDirection string + outputFormat string + outputFormatChanged bool } -func NewBugCommand() *cobra.Command { - env := execenv.NewEnv() +func NewBugCommand(env *execenv.Env) *cobra.Command { options := bugOptions{} cmd := &cobra.Command{ @@ -57,6 +58,7 @@ git bug status:open --by creation "foo bar" baz `, PreRunE: execenv.LoadBackend(env), RunE: execenv.CloseBackend(env, func(cmd *cobra.Command, args []string) error { + options.outputFormatChanged = cmd.Flags().Changed("format") return runBug(env, options, args) }), ValidArgsFunction: completion.Ls(env), @@ -94,9 +96,9 @@ git bug status:open --by creation "foo bar" baz "Select the sorting direction. Valid values are [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]") + "Select the output formatting style. Valid values are [default,plain,id,json,org-mode]") cmd.RegisterFlagCompletionFunc("format", - completion.From([]string{"default", "plain", "compact", "id", "json", "org-mode"})) + completion.From([]string{"default", "plain", "id", "json", "org-mode"})) const selectGroup = "select" cmd.AddGroup(&cobra.Group{ID: selectGroup, Title: "Implicit selection"}) @@ -106,16 +108,16 @@ git bug status:open --by creation "foo bar" baz child.GroupID = groupID } - addCmdWithGroup(newBugDeselectCommand(), selectGroup) - addCmdWithGroup(newBugSelectCommand(), selectGroup) + addCmdWithGroup(newBugDeselectCommand(env), selectGroup) + addCmdWithGroup(newBugSelectCommand(env), selectGroup) - cmd.AddCommand(newBugCommentCommand()) - cmd.AddCommand(newBugLabelCommand()) - cmd.AddCommand(newBugNewCommand()) - cmd.AddCommand(newBugRmCommand()) - cmd.AddCommand(newBugShowCommand()) - cmd.AddCommand(newBugStatusCommand()) - cmd.AddCommand(newBugTitleCommand()) + cmd.AddCommand(newBugCommentCommand(env)) + cmd.AddCommand(newBugLabelCommand(env)) + cmd.AddCommand(newBugNewCommand(env)) + cmd.AddCommand(newBugRmCommand(env)) + cmd.AddCommand(newBugShowCommand(env)) + cmd.AddCommand(newBugStatusCommand(env)) + cmd.AddCommand(newBugTitleCommand(env)) return cmd } @@ -146,28 +148,33 @@ func runBug(env *execenv.Env, opts bugOptions, args []string) error { return err } - bugExcerpt := make([]*cache.BugExcerpt, len(allIds)) + excerpts := make([]*cache.BugExcerpt, len(allIds)) for i, id := range allIds { b, err := env.Backend.Bugs().ResolveExcerpt(id) if err != nil { return err } - bugExcerpt[i] = b + excerpts[i] = b } switch opts.outputFormat { - case "org-mode": - return bugsOrgmodeFormatter(env, bugExcerpt) + case "default": + if opts.outputFormatChanged { + return bugsDefaultFormatter(env, excerpts) + } + if env.Out.IsTerminal() { + return bugsDefaultFormatter(env, excerpts) + } else { + return bugsPlainFormatter(env, excerpts) + } + case "id": + return bugsIDFormatter(env, excerpts) case "plain": - return bugsPlainFormatter(env, bugExcerpt) + return bugsPlainFormatter(env, excerpts) case "json": - return bugsJsonFormatter(env, bugExcerpt) - case "compact": - return bugsCompactFormatter(env, bugExcerpt) - case "id": - return bugsIDFormatter(env, bugExcerpt) - case "default": - return bugsDefaultFormatter(env, bugExcerpt) + return bugsJsonFormatter(env, excerpts) + case "org-mode": + return bugsOrgmodeFormatter(env, excerpts) default: return fmt.Errorf("unknown format %s", opts.outputFormat) } @@ -186,9 +193,9 @@ func repairQuery(args []string) string { return strings.Join(args, " ") } -func bugsJsonFormatter(env *execenv.Env, bugExcerpts []*cache.BugExcerpt) error { - jsonBugs := make([]cmdjson.BugExcerpt, len(bugExcerpts)) - for i, b := range bugExcerpts { +func bugsJsonFormatter(env *execenv.Env, excerpts []*cache.BugExcerpt) error { + jsonBugs := make([]cmdjson.BugExcerpt, len(excerpts)) + for i, b := range excerpts { jsonBug, err := cmdjson.NewBugExcerpt(env.Backend, b) if err != nil { return err @@ -198,42 +205,34 @@ func bugsJsonFormatter(env *execenv.Env, bugExcerpts []*cache.BugExcerpt) error return env.Out.PrintJSON(jsonBugs) } -func bugsCompactFormatter(env *execenv.Env, bugExcerpts []*cache.BugExcerpt) error { - for _, b := range bugExcerpts { - author, err := env.Backend.Identities().ResolveExcerpt(b.AuthorId) - if err != nil { - return err - } - - var labelsTxt strings.Builder - for _, l := range b.Labels { - lc256 := l.Color().Term256() - labelsTxt.WriteString(lc256.Escape()) - labelsTxt.WriteString("◼") - labelsTxt.WriteString(lc256.Unescape()) - } - - 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), - text.LeftPadMaxLine(labelsTxt.String(), 5, 0), - colors.Magenta(text.TruncateMax(author.DisplayName(), 15)), - ) +func bugsIDFormatter(env *execenv.Env, excerpts []*cache.BugExcerpt) error { + for _, b := range excerpts { + env.Out.Println(b.Id().String()) } + return nil } -func bugsIDFormatter(env *execenv.Env, bugExcerpts []*cache.BugExcerpt) error { - for _, b := range bugExcerpts { - env.Out.Println(b.Id().String()) +func bugsDefaultFormatter(env *execenv.Env, excerpts []*cache.BugExcerpt) error { + width := env.Out.Width() + widthId := entity.HumanIdLength + widthStatus := len("closed") + widthComment := 6 + + widthRemaining := width - + widthId - 1 - + widthStatus - 1 - + widthComment - 1 + + widthTitle := int(float32(widthRemaining-3) * 0.7) + if widthTitle < 0 { + widthTitle = 0 } - return nil -} + widthRemaining = widthRemaining - widthTitle - 3 - 2 + widthAuthor := widthRemaining -func bugsDefaultFormatter(env *execenv.Env, bugExcerpts []*cache.BugExcerpt) error { - for _, b := range bugExcerpts { + for _, b := range excerpts { author, err := env.Backend.Identities().ResolveExcerpt(b.AuthorId) if err != nil { return err @@ -249,8 +248,8 @@ func bugsDefaultFormatter(env *execenv.Env, bugExcerpts []*cache.BugExcerpt) err // truncate + pad if needed labelsFmt := text.TruncateMax(labelsTxt.String(), 10) - titleFmt := text.LeftPadMaxLine(strings.TrimSpace(b.Title), 50-text.Len(labelsFmt), 0) - authorFmt := text.LeftPadMaxLine(author.DisplayName(), 15, 0) + titleFmt := text.LeftPadMaxLine(strings.TrimSpace(b.Title), widthTitle-text.Len(labelsFmt), 0) + authorFmt := text.LeftPadMaxLine(author.DisplayName(), widthAuthor, 0) comments := fmt.Sprintf("%3d 💬", b.LenComments-1) if b.LenComments-1 <= 0 { @@ -260,7 +259,7 @@ func bugsDefaultFormatter(env *execenv.Env, bugExcerpts []*cache.BugExcerpt) err comments = " ∞ 💬" } - env.Out.Printf("%s\t%s\t%s\t%s\t%s\n", + env.Out.Printf("%s\t%s\t%s %s %s\n", colors.Cyan(b.Id().Human()), colors.Yellow(b.Status), titleFmt+labelsFmt, @@ -271,14 +270,14 @@ func bugsDefaultFormatter(env *execenv.Env, bugExcerpts []*cache.BugExcerpt) err return nil } -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)) +func bugsPlainFormatter(env *execenv.Env, excerpts []*cache.BugExcerpt) error { + for _, b := range excerpts { + env.Out.Printf("%s\t%s\t%s\n", b.Id().Human(), b.Status, strings.TrimSpace(b.Title)) } return nil } -func bugsOrgmodeFormatter(env *execenv.Env, bugExcerpts []*cache.BugExcerpt) error { +func bugsOrgmodeFormatter(env *execenv.Env, excerpts []*cache.BugExcerpt) error { // see https://orgmode.org/manual/Tags.html orgTagRe := regexp.MustCompile("[^[:alpha:]_@]") formatTag := func(l bug.Label) string { @@ -291,7 +290,7 @@ func bugsOrgmodeFormatter(env *execenv.Env, bugExcerpts []*cache.BugExcerpt) err env.Out.Println("#+TODO: OPEN | CLOSED") - for _, b := range bugExcerpts { + for _, b := range excerpts { status := strings.ToUpper(b.Status.String()) var title string diff --git a/commands/bug/bug_comment.go b/commands/bug/bug_comment.go index 4dc8dc1f..5cb8ff17 100644 --- a/commands/bug/bug_comment.go +++ b/commands/bug/bug_comment.go @@ -8,9 +8,7 @@ import ( "github.com/MichaelMure/git-bug/util/colors" ) -func newBugCommentCommand() *cobra.Command { - env := execenv.NewEnv() - +func newBugCommentCommand(env *execenv.Env) *cobra.Command { cmd := &cobra.Command{ Use: "comment [BUG_ID]", Short: "List a bug's comments", @@ -21,8 +19,8 @@ func newBugCommentCommand() *cobra.Command { ValidArgsFunction: BugCompletion(env), } - cmd.AddCommand(newBugCommentNewCommand()) - cmd.AddCommand(newBugCommentEditCommand()) + cmd.AddCommand(newBugCommentNewCommand(env)) + cmd.AddCommand(newBugCommentEditCommand(env)) return cmd } diff --git a/commands/bug/bug_comment_add.go b/commands/bug/bug_comment_add.go index ff406b4f..152a1893 100644 --- a/commands/bug/bug_comment_add.go +++ b/commands/bug/bug_comment_add.go @@ -14,8 +14,7 @@ type bugCommentNewOptions struct { nonInteractive bool } -func newBugCommentNewCommand() *cobra.Command { - env := execenv.NewEnv() +func newBugCommentNewCommand(env *execenv.Env) *cobra.Command { options := bugCommentNewOptions{} cmd := &cobra.Command{ diff --git a/commands/bug/bug_comment_edit.go b/commands/bug/bug_comment_edit.go index ded3d82a..e6f8d667 100644 --- a/commands/bug/bug_comment_edit.go +++ b/commands/bug/bug_comment_edit.go @@ -13,8 +13,7 @@ type bugCommentEditOptions struct { nonInteractive bool } -func newBugCommentEditCommand() *cobra.Command { - env := execenv.NewEnv() +func newBugCommentEditCommand(env *execenv.Env) *cobra.Command { options := bugCommentEditOptions{} cmd := &cobra.Command{ diff --git a/commands/bug/bug_comment_test.go b/commands/bug/bug_comment_test.go index c1dc9952..ecc1c5f6 100644 --- a/commands/bug/bug_comment_test.go +++ b/commands/bug/bug_comment_test.go @@ -2,7 +2,7 @@ package bugcmd import ( "fmt" - "io/ioutil" + "os" "strings" "testing" "time" @@ -143,7 +143,7 @@ func requireCommentsEqual(t *testing.T, golden string, env *execenv.Env) { t.Log("Got here") for i, comment := range comments { fileName := fmt.Sprintf(goldenFilePattern, golden, i) - require.NoError(t, ioutil.WriteFile(fileName, []byte(comment.message), 0644)) + require.NoError(t, os.WriteFile(fileName, []byte(comment.message), 0644)) } } @@ -157,7 +157,7 @@ func requireCommentsEqual(t *testing.T, golden string, env *execenv.Env) { require.Equal(t, date.Add(time.Duration(i)*time.Minute), comment.date) fileName := fmt.Sprintf(goldenFilePattern, golden, i) - exp, err := ioutil.ReadFile(fileName) + exp, err := os.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 index 090a7bf2..5e9acc60 100644 --- a/commands/bug/bug_deselect.go +++ b/commands/bug/bug_deselect.go @@ -8,9 +8,7 @@ import ( "github.com/MichaelMure/git-bug/entities/bug" ) -func newBugDeselectCommand() *cobra.Command { - env := execenv.NewEnv() - +func newBugDeselectCommand(env *execenv.Env) *cobra.Command { cmd := &cobra.Command{ Use: "deselect", Short: "Clear the implicitly selected bug", diff --git a/commands/bug/bug_label.go b/commands/bug/bug_label.go index e6d0e603..554496e3 100644 --- a/commands/bug/bug_label.go +++ b/commands/bug/bug_label.go @@ -6,9 +6,7 @@ import ( "github.com/MichaelMure/git-bug/commands/execenv" ) -func newBugLabelCommand() *cobra.Command { - env := execenv.NewEnv() - +func newBugLabelCommand(env *execenv.Env) *cobra.Command { cmd := &cobra.Command{ Use: "label [BUG_ID]", Short: "Display labels of a bug", @@ -19,8 +17,8 @@ func newBugLabelCommand() *cobra.Command { ValidArgsFunction: BugCompletion(env), } - cmd.AddCommand(newBugLabelNewCommand()) - cmd.AddCommand(newBugLabelRmCommand()) + cmd.AddCommand(newBugLabelNewCommand(env)) + cmd.AddCommand(newBugLabelRmCommand(env)) return cmd } diff --git a/commands/bug/bug_label_new.go b/commands/bug/bug_label_new.go index aa4f9463..1e1f2d4f 100644 --- a/commands/bug/bug_label_new.go +++ b/commands/bug/bug_label_new.go @@ -7,9 +7,7 @@ import ( "github.com/MichaelMure/git-bug/util/text" ) -func newBugLabelNewCommand() *cobra.Command { - env := execenv.NewEnv() - +func newBugLabelNewCommand(env *execenv.Env) *cobra.Command { cmd := &cobra.Command{ Use: "new [BUG_ID] LABEL...", Short: "Add a label to a bug", diff --git a/commands/bug/bug_label_rm.go b/commands/bug/bug_label_rm.go index 18510bbd..6dda007c 100644 --- a/commands/bug/bug_label_rm.go +++ b/commands/bug/bug_label_rm.go @@ -7,9 +7,7 @@ import ( "github.com/MichaelMure/git-bug/util/text" ) -func newBugLabelRmCommand() *cobra.Command { - env := execenv.NewEnv() - +func newBugLabelRmCommand(env *execenv.Env) *cobra.Command { cmd := &cobra.Command{ Use: "rm [BUG_ID] LABEL...", Short: "Remove a label from a bug", diff --git a/commands/bug/bug_new.go b/commands/bug/bug_new.go index 9ef288e9..e66967f9 100644 --- a/commands/bug/bug_new.go +++ b/commands/bug/bug_new.go @@ -15,8 +15,7 @@ type bugNewOptions struct { nonInteractive bool } -func newBugNewCommand() *cobra.Command { - env := execenv.NewEnv() +func newBugNewCommand(env *execenv.Env) *cobra.Command { options := bugNewOptions{} cmd := &cobra.Command{ diff --git a/commands/bug/bug_rm.go b/commands/bug/bug_rm.go index 386c57ec..b9d3d525 100644 --- a/commands/bug/bug_rm.go +++ b/commands/bug/bug_rm.go @@ -8,9 +8,7 @@ import ( "github.com/MichaelMure/git-bug/commands/execenv" ) -func newBugRmCommand() *cobra.Command { - env := execenv.NewEnv() - +func newBugRmCommand(env *execenv.Env) *cobra.Command { cmd := &cobra.Command{ Use: "rm BUG_ID", Short: "Remove an existing bug", diff --git a/commands/bug/bug_select.go b/commands/bug/bug_select.go index bfad899d..652c61ea 100644 --- a/commands/bug/bug_select.go +++ b/commands/bug/bug_select.go @@ -15,9 +15,7 @@ func ResolveSelected(repo *cache.RepoCache, args []string) (*cache.BugCache, []s return _select.Resolve[*cache.BugCache](repo, bug.Typename, bug.Namespace, repo.Bugs(), args) } -func newBugSelectCommand() *cobra.Command { - env := execenv.NewEnv() - +func newBugSelectCommand(env *execenv.Env) *cobra.Command { cmd := &cobra.Command{ Use: "select BUG_ID", Short: "Select a bug for implicit use in future commands", diff --git a/commands/bug/bug_show.go b/commands/bug/bug_show.go index 9f80120c..9a03c9a3 100644 --- a/commands/bug/bug_show.go +++ b/commands/bug/bug_show.go @@ -19,8 +19,7 @@ type bugShowOptions struct { format string } -func newBugShowCommand() *cobra.Command { - env := execenv.NewEnv() +func newBugShowCommand(env *execenv.Env) *cobra.Command { options := bugShowOptions{} cmd := &cobra.Command{ diff --git a/commands/bug/bug_status.go b/commands/bug/bug_status.go index 807a9a60..59bef3fd 100644 --- a/commands/bug/bug_status.go +++ b/commands/bug/bug_status.go @@ -6,9 +6,7 @@ import ( "github.com/MichaelMure/git-bug/commands/execenv" ) -func newBugStatusCommand() *cobra.Command { - env := execenv.NewEnv() - +func newBugStatusCommand(env *execenv.Env) *cobra.Command { cmd := &cobra.Command{ Use: "status [BUG_ID]", Short: "Display the status of a bug", @@ -19,8 +17,8 @@ func newBugStatusCommand() *cobra.Command { ValidArgsFunction: BugCompletion(env), } - cmd.AddCommand(newBugStatusCloseCommand()) - cmd.AddCommand(newBugStatusOpenCommand()) + cmd.AddCommand(newBugStatusCloseCommand(env)) + cmd.AddCommand(newBugStatusOpenCommand(env)) return cmd } diff --git a/commands/bug/bug_status_close.go b/commands/bug/bug_status_close.go index e52959b2..1d06007b 100644 --- a/commands/bug/bug_status_close.go +++ b/commands/bug/bug_status_close.go @@ -6,9 +6,7 @@ import ( "github.com/MichaelMure/git-bug/commands/execenv" ) -func newBugStatusCloseCommand() *cobra.Command { - env := execenv.NewEnv() - +func newBugStatusCloseCommand(env *execenv.Env) *cobra.Command { cmd := &cobra.Command{ Use: "close [BUG_ID]", Short: "Mark a bug as closed", diff --git a/commands/bug/bug_status_open.go b/commands/bug/bug_status_open.go index 74177974..e99d2db0 100644 --- a/commands/bug/bug_status_open.go +++ b/commands/bug/bug_status_open.go @@ -6,9 +6,7 @@ import ( "github.com/MichaelMure/git-bug/commands/execenv" ) -func newBugStatusOpenCommand() *cobra.Command { - env := execenv.NewEnv() - +func newBugStatusOpenCommand(env *execenv.Env) *cobra.Command { cmd := &cobra.Command{ Use: "open [BUG_ID]", Short: "Mark a bug as open", diff --git a/commands/bug/bug_test.go b/commands/bug/bug_test.go index cb6a4373..6b0aa4e0 100644 --- a/commands/bug/bug_test.go +++ b/commands/bug/bug_test.go @@ -2,7 +2,6 @@ package bugcmd import ( "encoding/json" - "fmt" "testing" "github.com/stretchr/testify/require" @@ -61,44 +60,34 @@ $` format string exp string }{ - {"default", "^[0-9a-f]{7}\topen\tthis is a bug title \tJohn Doe \t\n$"}, - {"plain", "^[0-9a-f]{7} \\[open\\] this is a bug title\n$"}, - {"compact", "^[0-9a-f]{7} open this is a bug title John Doe\n$"}, + {"default", "^[0-9a-f]{7}\topen\tthis is a bug title John Doe \n$"}, + {"plain", "^[0-9a-f]{7}\topen\tthis is a bug title\n$"}, {"id", "^[0-9a-f]{64}\n$"}, {"org-mode", expOrgMode}, + {"json", ".*"}, } for _, testcase := range cases { - opts := bugOptions{ - sortDirection: "asc", - sortBy: "creation", - outputFormat: testcase.format, - } - - name := fmt.Sprintf("with %s format", testcase.format) - - t.Run(name, func(t *testing.T) { + t.Run(testcase.format, func(t *testing.T) { env, _ := testenv.NewTestEnvAndBug(t) + opts := bugOptions{ + sortDirection: "asc", + sortBy: "creation", + outputFormat: testcase.format, + outputFormatChanged: true, // disable auto-detect + } + require.NoError(t, runBug(env, opts, []string{})) - require.Regexp(t, testcase.exp, env.Out.String()) + + switch testcase.format { + case "json": + var bugs []cmdjson.BugExcerpt + require.NoError(t, json.Unmarshal(env.Out.Bytes(), &bugs)) + require.Len(t, bugs, 1) + default: + require.Regexp(t, testcase.exp, env.Out.String()) + } }) } - - t.Run("with JSON format", func(t *testing.T) { - opts := bugOptions{ - sortDirection: "asc", - sortBy: "creation", - outputFormat: "json", - } - - env, _ := testenv.NewTestEnvAndBug(t) - - require.NoError(t, runBug(env, opts, []string{})) - - var bugs []cmdjson.BugExcerpt - 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 index e59a1fdc..47603410 100644 --- a/commands/bug/bug_title.go +++ b/commands/bug/bug_title.go @@ -6,9 +6,7 @@ import ( "github.com/MichaelMure/git-bug/commands/execenv" ) -func newBugTitleCommand() *cobra.Command { - env := execenv.NewEnv() - +func newBugTitleCommand(env *execenv.Env) *cobra.Command { cmd := &cobra.Command{ Use: "title [BUG_ID]", Short: "Display the title of a bug", @@ -19,7 +17,7 @@ func newBugTitleCommand() *cobra.Command { ValidArgsFunction: BugCompletion(env), } - cmd.AddCommand(newBugTitleEditCommand()) + cmd.AddCommand(newBugTitleEditCommand(env)) return cmd } diff --git a/commands/bug/bug_title_edit.go b/commands/bug/bug_title_edit.go index 59898530..fc60824f 100644 --- a/commands/bug/bug_title_edit.go +++ b/commands/bug/bug_title_edit.go @@ -13,8 +13,7 @@ type bugTitleEditOptions struct { nonInteractive bool } -func newBugTitleEditCommand() *cobra.Command { - env := execenv.NewEnv() +func newBugTitleEditCommand(env *execenv.Env) *cobra.Command { options := bugTitleEditOptions{} cmd := &cobra.Command{ diff --git a/commands/commands.go b/commands/commands.go index 7d2fc37d..173a0904 100644 --- a/commands/commands.go +++ b/commands/commands.go @@ -12,8 +12,7 @@ type commandOptions struct { desc bool } -func newCommandsCommand() *cobra.Command { - env := execenv.NewEnv() +func newCommandsCommand(env *execenv.Env) *cobra.Command { options := commandOptions{} cmd := &cobra.Command{ diff --git a/commands/execenv/env.go b/commands/execenv/env.go index 4be7c247..e693807e 100644 --- a/commands/execenv/env.go +++ b/commands/execenv/env.go @@ -6,12 +6,11 @@ import ( "io" "os" - "github.com/spf13/cobra" + "github.com/mattn/go-isatty" + "golang.org/x/term" "github.com/MichaelMure/git-bug/cache" - "github.com/MichaelMure/git-bug/entities/identity" "github.com/MichaelMure/git-bug/repository" - "github.com/MichaelMure/git-bug/util/interrupt" ) const RootCommandName = "git-bug" @@ -22,6 +21,7 @@ const gitBugNamespace = "git-bug" type Env struct { Repo repository.ClockedRepo Backend *cache.RepoCache + In In Out Out Err Out } @@ -29,18 +29,42 @@ type Env struct { func NewEnv() *Env { return &Env{ Repo: nil, + In: in{Reader: os.Stdin}, Out: out{Writer: os.Stdout}, Err: out{Writer: os.Stderr}, } } +type In interface { + io.Reader + + // IsTerminal tells if the input is a user terminal (rather than a buffer, + // a pipe ...), which tells if we can use interactive features. + IsTerminal() bool + + // ForceIsTerminal allow to force the returned value of IsTerminal + // This only works in test scenario. + ForceIsTerminal(value bool) +} + type Out interface { io.Writer + Printf(format string, a ...interface{}) Print(a ...interface{}) Println(a ...interface{}) PrintJSON(v interface{}) error + // IsTerminal tells if the output is a user terminal (rather than a buffer, + // a pipe ...), which tells if we can use colors and other interactive features. + IsTerminal() bool + // Width return the width of the attached terminal, or a good enough value. + Width() int + + // Raw return the underlying io.Writer, or itself if not. + // This is useful if something need to access the raw file descriptor. + Raw() io.Writer + // String returns what have been written in the output before, as a string. // This only works in test scenario. String() string @@ -50,6 +74,24 @@ type Out interface { // Reset clear what has been recorded as written in the output before. // This only works in test scenario. Reset() + // ForceIsTerminal allow to force the returned value of IsTerminal + // This only works in test scenario. + ForceIsTerminal(value bool) +} + +type in struct { + io.Reader +} + +func (i in) IsTerminal() bool { + if f, ok := i.Reader.(*os.File); ok { + return isTerminal(f) + } + return false +} + +func (i in) ForceIsTerminal(_ bool) { + panic("only work with a test env") } type out struct { @@ -77,139 +119,43 @@ func (o out) PrintJSON(v interface{}) error { return nil } -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) - } - - // Note: we are not loading clocks here because we assume that LoadRepo is only used - // when we don't manipulate entities, or as a child call of LoadBackend which will - // read all clocks anyway. - env.Repo, err = repository.OpenGoGitRepo(cwd, gitBugNamespace, nil) - if err == repository.ErrNotARepo { - return fmt.Errorf("%s must be run from within a git Repo", RootCommandName) - } - if err != nil { - return err - } - - return nil +func (o out) IsTerminal() bool { + if f, ok := o.Writer.(*os.File); ok { + return isTerminal(f) } + return false } -// 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 { - return func(cmd *cobra.Command, args []string) error { - err := LoadRepo(env)(cmd, args) - if err != nil { - return err - } - - _, err = identity.GetUserIdentity(env.Repo) - if err != nil { - return err +func (o out) Width() int { + if f, ok := o.Raw().(*os.File); ok { + width, _, err := term.GetSize(int(f.Fd())) + if err == nil { + return width } - - return nil } + return 80 } -// 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) - if err != nil { - return err - } - - var events chan cache.BuildEvent - env.Backend, events = cache.NewRepoCache(env.Repo) - - for event := range events { - if event.Err != nil { - return event.Err - } - switch event.Event { - case cache.BuildEventCacheIsBuilt: - env.Err.Println("Building cache... ") - case cache.BuildEventStarted: - env.Err.Printf("[%s] started\n", event.Typename) - case cache.BuildEventFinished: - env.Err.Printf("[%s] done\n", event.Typename) - } - } - - cleaner := func(env *Env) interrupt.CleanerFunc { - return func() error { - if env.Backend != nil { - err := env.Backend.Close() - env.Backend = nil - return err - } - return nil - } - } - - // Cleanup properly on interrupt - interrupt.RegisterCleaner(cleaner(env)) - return nil - } +func (o out) Raw() io.Writer { + return o.Writer } -// 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 { - return func(cmd *cobra.Command, args []string) error { - err := LoadBackend(env)(cmd, args) - if err != nil { - return err - } - - _, err = identity.GetUserIdentity(env.Repo) - if err != nil { - return err - } +func (o out) String() string { + panic("only work with a test env") +} - return nil - } +func (o out) Bytes() []byte { + panic("only work with a test env") } -// 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 { - return func(cmd *cobra.Command, args []string) error { - errRun := runE(cmd, args) +func (o out) Reset() { + panic("only work with a test env") +} - if env.Backend == nil { - return nil - } - err := env.Backend.Close() - env.Backend = nil +func (o out) ForceIsTerminal(_ bool) { + panic("only work with a test env") +} - // prioritize the RunE error - if errRun != nil { - return errRun - } - return err - } +func isTerminal(file *os.File) bool { + return isatty.IsTerminal(file.Fd()) || isatty.IsCygwinTerminal(file.Fd()) } diff --git a/commands/execenv/env_test.go b/commands/execenv/env_test.go new file mode 100644 index 00000000..3fc6e581 --- /dev/null +++ b/commands/execenv/env_test.go @@ -0,0 +1,21 @@ +package execenv + +import ( + "os" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestIsTerminal(t *testing.T) { + // easy way to get a reader and a writer + r, w, err := os.Pipe() + require.NoError(t, err) + + require.False(t, isTerminal(r)) + require.False(t, isTerminal(w)) + + // golang's testing framework replaces os.Stdin and os.Stdout, so the following doesn't work here + // require.True(t, isTerminal(os.Stdin)) + // require.True(t, isTerminal(os.Stdout)) +} diff --git a/commands/execenv/env_testing.go b/commands/execenv/env_testing.go index 34eafc9c..15d7b646 100644 --- a/commands/execenv/env_testing.go +++ b/commands/execenv/env_testing.go @@ -4,6 +4,7 @@ import ( "bytes" "encoding/json" "fmt" + "io" "testing" "github.com/stretchr/testify/require" @@ -12,8 +13,26 @@ import ( "github.com/MichaelMure/git-bug/repository" ) +var _ In = &TestIn{} + +type TestIn struct { + *bytes.Buffer + forceIsTerminal bool +} + +func (t *TestIn) IsTerminal() bool { + return t.forceIsTerminal +} + +func (t *TestIn) ForceIsTerminal(value bool) { + t.forceIsTerminal = value +} + +var _ Out = &TestOut{} + type TestOut struct { *bytes.Buffer + forceIsTerminal bool } func (te *TestOut) Printf(format string, a ...interface{}) { @@ -37,12 +56,34 @@ func (te *TestOut) PrintJSON(v interface{}) error { return nil } +func (te *TestOut) IsTerminal() bool { + return te.forceIsTerminal +} + +func (te *TestOut) Width() int { + return 80 +} + +func (te *TestOut) Raw() io.Writer { + return te.Buffer +} + +func (te *TestOut) ForceIsTerminal(value bool) { + te.forceIsTerminal = value +} + func NewTestEnv(t *testing.T) *Env { t.Helper() + return newTestEnv(t, false) +} - repo := repository.CreateGoGitTestRepo(t, false) +func NewTestEnvTerminal(t *testing.T) *Env { + t.Helper() + return newTestEnv(t, true) +} - buf := new(bytes.Buffer) +func newTestEnv(t *testing.T, isTerminal bool) *Env { + repo := repository.CreateGoGitTestRepo(t, false) backend, err := cache.NewRepoCacheNoEvents(repo) require.NoError(t, err) @@ -54,7 +95,8 @@ func NewTestEnv(t *testing.T) *Env { return &Env{ Repo: repo, Backend: backend, - Out: &TestOut{buf}, - Err: &TestOut{buf}, + In: &TestIn{Buffer: &bytes.Buffer{}, forceIsTerminal: isTerminal}, + Out: &TestOut{Buffer: &bytes.Buffer{}, forceIsTerminal: isTerminal}, + Err: &TestOut{Buffer: &bytes.Buffer{}, forceIsTerminal: isTerminal}, } } diff --git a/commands/execenv/loading.go b/commands/execenv/loading.go new file mode 100644 index 00000000..2263f700 --- /dev/null +++ b/commands/execenv/loading.go @@ -0,0 +1,169 @@ +package execenv + +import ( + "fmt" + "os" + + "github.com/spf13/cobra" + "github.com/vbauerster/mpb/v8" + "github.com/vbauerster/mpb/v8/decor" + + "github.com/MichaelMure/git-bug/cache" + "github.com/MichaelMure/git-bug/entities/identity" + "github.com/MichaelMure/git-bug/repository" + "github.com/MichaelMure/git-bug/util/interrupt" +) + +// 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) + } + + // Note: we are not loading clocks here because we assume that LoadRepo is only used + // when we don't manipulate entities, or as a child call of LoadBackend which will + // read all clocks anyway. + env.Repo, err = repository.OpenGoGitRepo(cwd, gitBugNamespace, nil) + if err == repository.ErrNotARepo { + return fmt.Errorf("%s must be run from within a git Repo", RootCommandName) + } + if err != nil { + return err + } + + return nil + } +} + +// 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 { + return func(cmd *cobra.Command, args []string) error { + err := LoadRepo(env)(cmd, args) + if err != nil { + return err + } + + _, err = identity.GetUserIdentity(env.Repo) + if err != nil { + return err + } + + return nil + } +} + +// 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) + if err != nil { + return err + } + + var events chan cache.BuildEvent + env.Backend, events = cache.NewRepoCache(env.Repo) + + err = CacheBuildProgressBar(env, events) + 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 + return err + } + return nil + } + } + + // Cleanup properly on interrupt + interrupt.RegisterCleaner(cleaner(env)) + return nil + } +} + +// 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 { + return func(cmd *cobra.Command, args []string) error { + err := LoadBackend(env)(cmd, args) + if err != nil { + return err + } + + _, err = identity.GetUserIdentity(env.Repo) + if err != nil { + return err + } + + return nil + } +} + +// 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 { + return func(cmd *cobra.Command, args []string) error { + errRun := runE(cmd, args) + + if env.Backend == nil { + return nil + } + err := env.Backend.Close() + env.Backend = nil + + // prioritize the RunE error + if errRun != nil { + return errRun + } + return err + } +} + +func CacheBuildProgressBar(env *Env, events chan cache.BuildEvent) error { + var progress *mpb.Progress + var bars = make(map[string]*mpb.Bar) + + for event := range events { + if event.Err != nil { + return event.Err + } + + if progress == nil { + progress = mpb.New(mpb.WithOutput(env.Err.Raw())) + } + + switch event.Event { + case cache.BuildEventCacheIsBuilt: + env.Err.Println("Building cache... ") + case cache.BuildEventStarted: + bars[event.Typename] = progress.AddBar(-1, + mpb.BarRemoveOnComplete(), + mpb.PrependDecorators( + decor.Name(event.Typename, decor.WCSyncSpace), + decor.CountersNoUnit("%d / %d", decor.WCSyncSpace), + ), + mpb.AppendDecorators(decor.Percentage(decor.WCSyncSpace)), + ) + case cache.BuildEventProgress: + bars[event.Typename].SetTotal(event.Total, false) + bars[event.Typename].SetCurrent(event.Progress) + } + } + + if progress != nil { + progress.Shutdown() + } + + return nil +} diff --git a/commands/label.go b/commands/label.go index 08b9e31f..d59479b4 100644 --- a/commands/label.go +++ b/commands/label.go @@ -6,9 +6,7 @@ import ( "github.com/MichaelMure/git-bug/commands/execenv" ) -func newLabelCommand() *cobra.Command { - env := execenv.NewEnv() - +func newLabelCommand(env *execenv.Env) *cobra.Command { cmd := &cobra.Command{ Use: "label", Short: "List valid labels", diff --git a/commands/pull.go b/commands/pull.go index 2e2639e1..91eadcf8 100644 --- a/commands/pull.go +++ b/commands/pull.go @@ -10,9 +10,7 @@ import ( "github.com/MichaelMure/git-bug/entity" ) -func newPullCommand() *cobra.Command { - env := execenv.NewEnv() - +func newPullCommand(env *execenv.Env) *cobra.Command { cmd := &cobra.Command{ Use: "pull [REMOTE]", Short: "Pull updates from a git remote", diff --git a/commands/push.go b/commands/push.go index d45e301a..254bbb27 100644 --- a/commands/push.go +++ b/commands/push.go @@ -9,9 +9,7 @@ import ( "github.com/MichaelMure/git-bug/commands/execenv" ) -func newPushCommand() *cobra.Command { - env := execenv.NewEnv() - +func newPushCommand(env *execenv.Env) *cobra.Command { cmd := &cobra.Command{ Use: "push [REMOTE]", Short: "Push updates to a git remote", diff --git a/commands/root.go b/commands/root.go index 1ccc3e56..01c817ee 100644 --- a/commands/root.go +++ b/commands/root.go @@ -62,19 +62,22 @@ the same git remote you are already using to collaborate with other people. child.GroupID = groupID } - addCmdWithGroup(bugcmd.NewBugCommand(), entityGroup) - addCmdWithGroup(usercmd.NewUserCommand(), entityGroup) - addCmdWithGroup(newLabelCommand(), entityGroup) + env := execenv.NewEnv() - addCmdWithGroup(newTermUICommand(), uiGroup) - addCmdWithGroup(newWebUICommand(), uiGroup) + addCmdWithGroup(bugcmd.NewBugCommand(env), entityGroup) + addCmdWithGroup(usercmd.NewUserCommand(env), entityGroup) + addCmdWithGroup(newLabelCommand(env), entityGroup) - addCmdWithGroup(newPullCommand(), remoteGroup) - addCmdWithGroup(newPushCommand(), remoteGroup) - addCmdWithGroup(bridgecmd.NewBridgeCommand(), remoteGroup) + addCmdWithGroup(newTermUICommand(env), uiGroup) + addCmdWithGroup(newWebUICommand(env), uiGroup) - cmd.AddCommand(newCommandsCommand()) - cmd.AddCommand(newVersionCommand()) + addCmdWithGroup(newPullCommand(env), remoteGroup) + addCmdWithGroup(newPushCommand(env), remoteGroup) + addCmdWithGroup(bridgecmd.NewBridgeCommand(env), remoteGroup) + + cmd.AddCommand(newCommandsCommand(env)) + cmd.AddCommand(newVersionCommand(env)) + cmd.AddCommand(newWipeCommand(env)) return cmd } diff --git a/commands/termui.go b/commands/termui.go index 1cfdd8f3..08eba1d6 100644 --- a/commands/termui.go +++ b/commands/termui.go @@ -7,9 +7,7 @@ import ( "github.com/MichaelMure/git-bug/termui" ) -func newTermUICommand() *cobra.Command { - env := execenv.NewEnv() - +func newTermUICommand(env *execenv.Env) *cobra.Command { cmd := &cobra.Command{ Use: "termui", Aliases: []string{"tui"}, diff --git a/commands/user/user.go b/commands/user/user.go index a9a45726..de5c1ccd 100644 --- a/commands/user/user.go +++ b/commands/user/user.go @@ -16,8 +16,7 @@ type userOptions struct { format string } -func NewUserCommand() *cobra.Command { - env := execenv.NewEnv() +func NewUserCommand(env *execenv.Env) *cobra.Command { options := userOptions{} cmd := &cobra.Command{ @@ -29,9 +28,9 @@ func NewUserCommand() *cobra.Command { }), } - cmd.AddCommand(newUserNewCommand()) - cmd.AddCommand(newUserShowCommand()) - cmd.AddCommand(newUserAdoptCommand()) + cmd.AddCommand(newUserNewCommand(env)) + cmd.AddCommand(newUserShowCommand(env)) + cmd.AddCommand(newUserAdoptCommand(env)) flags := cmd.Flags() flags.SortFlags = false diff --git a/commands/user/user_adopt.go b/commands/user/user_adopt.go index 30fdb442..47f0f04e 100644 --- a/commands/user/user_adopt.go +++ b/commands/user/user_adopt.go @@ -7,9 +7,7 @@ import ( "github.com/MichaelMure/git-bug/commands/execenv" ) -func newUserAdoptCommand() *cobra.Command { - env := execenv.NewEnv() - +func newUserAdoptCommand(env *execenv.Env) *cobra.Command { cmd := &cobra.Command{ Use: "adopt USER_ID", Short: "Adopt an existing identity as your own", diff --git a/commands/user/user_new.go b/commands/user/user_new.go index 7b287492..ba4198f8 100644 --- a/commands/user/user_new.go +++ b/commands/user/user_new.go @@ -14,9 +14,7 @@ type userNewOptions struct { nonInteractive bool } -func newUserNewCommand() *cobra.Command { - env := execenv.NewEnv() - +func newUserNewCommand(env *execenv.Env) *cobra.Command { options := userNewOptions{} cmd := &cobra.Command{ Use: "new", diff --git a/commands/user/user_show.go b/commands/user/user_show.go index 225d0ef4..049eee93 100644 --- a/commands/user/user_show.go +++ b/commands/user/user_show.go @@ -16,8 +16,7 @@ type userShowOptions struct { fields string } -func newUserShowCommand() *cobra.Command { - env := execenv.NewEnv() +func newUserShowCommand(env *execenv.Env) *cobra.Command { options := userShowOptions{} cmd := &cobra.Command{ diff --git a/commands/version.go b/commands/version.go index 0e54bb92..957cc653 100644 --- a/commands/version.go +++ b/commands/version.go @@ -14,8 +14,7 @@ type versionOptions struct { all bool } -func newVersionCommand() *cobra.Command { - env := execenv.NewEnv() +func newVersionCommand(env *execenv.Env) *cobra.Command { options := versionOptions{} cmd := &cobra.Command{ diff --git a/commands/webui.go b/commands/webui.go index 0b3b24a6..3129a222 100644 --- a/commands/webui.go +++ b/commands/webui.go @@ -42,8 +42,7 @@ type webUIOptions struct { query string } -func newWebUICommand() *cobra.Command { - env := execenv.NewEnv() +func newWebUICommand(env *execenv.Env) *cobra.Command { options := webUIOptions{} cmd := &cobra.Command{ @@ -108,19 +107,10 @@ func runWebUI(env *execenv.Env, opts webUIOptions) error { mrc := cache.NewMultiRepoCache() _, events := mrc.RegisterDefaultRepository(env.Repo) - for event := range events { - if event.Err != nil { - env.Err.Printf("Cache building error [%s]: %v\n", event.Typename, event.Err) - continue - } - switch event.Event { - case cache.BuildEventCacheIsBuilt: - env.Err.Println("Building cache... ") - case cache.BuildEventStarted: - env.Err.Printf("[%s] started\n", event.Typename) - case cache.BuildEventFinished: - env.Err.Printf("[%s] done\n", event.Typename) - } + + err := execenv.CacheBuildProgressBar(env, events) + if err != nil { + return err } var errOut io.Writer diff --git a/commands/wipe.go b/commands/wipe.go new file mode 100644 index 00000000..ce9da032 --- /dev/null +++ b/commands/wipe.go @@ -0,0 +1,56 @@ +package commands + +import ( + "github.com/spf13/cobra" + + "github.com/MichaelMure/git-bug/commands/execenv" +) + +func newWipeCommand(env *execenv.Env) *cobra.Command { + cmd := &cobra.Command{ + Use: "wipe", + Short: "Wipe git-bug from the git repository", + PreRunE: execenv.LoadBackend(env), + RunE: func(cmd *cobra.Command, args []string) error { + return runWipe(env) + }, + } + + return cmd +} + +func runWipe(env *execenv.Env) error { + env.Out.Println("cleaning entities...") + err := env.Backend.RemoveAll() + if err != nil { + _ = env.Backend.Close() + return err + } + + env.Out.Println("cleaning git config ...") + err = env.Backend.ClearUserIdentity() + if err != nil { + _ = env.Backend.Close() + return err + } + err = env.Backend.LocalConfig().RemoveAll("git-bug") + if err != nil { + _ = env.Backend.Close() + return err + } + + storage := env.Backend.LocalStorage() + + err = env.Backend.Close() + if err != nil { + return err + } + + env.Out.Println("cleaning caches ...") + err = storage.RemoveAll(".") + if err != nil { + return err + } + + return nil +} |