aboutsummaryrefslogtreecommitdiffstats
path: root/commands
diff options
context:
space:
mode:
Diffstat (limited to 'commands')
-rw-r--r--commands/bridge/bridge.go14
-rw-r--r--commands/bridge/bridge_auth.go10
-rw-r--r--commands/bridge/bridge_auth_addtoken.go3
-rw-r--r--commands/bridge/bridge_auth_rm.go4
-rw-r--r--commands/bridge/bridge_auth_show.go4
-rw-r--r--commands/bridge/bridge_new.go3
-rw-r--r--commands/bridge/bridge_pull.go3
-rw-r--r--commands/bridge/bridge_push.go4
-rw-r--r--commands/bridge/bridge_rm.go4
-rw-r--r--commands/bug/bug.go151
-rw-r--r--commands/bug/bug_comment.go8
-rw-r--r--commands/bug/bug_comment_add.go3
-rw-r--r--commands/bug/bug_comment_edit.go3
-rw-r--r--commands/bug/bug_comment_test.go6
-rw-r--r--commands/bug/bug_deselect.go4
-rw-r--r--commands/bug/bug_label.go8
-rw-r--r--commands/bug/bug_label_new.go4
-rw-r--r--commands/bug/bug_label_rm.go4
-rw-r--r--commands/bug/bug_new.go3
-rw-r--r--commands/bug/bug_rm.go4
-rw-r--r--commands/bug/bug_select.go4
-rw-r--r--commands/bug/bug_show.go3
-rw-r--r--commands/bug/bug_status.go8
-rw-r--r--commands/bug/bug_status_close.go4
-rw-r--r--commands/bug/bug_status_open.go4
-rw-r--r--commands/bug/bug_test.go51
-rw-r--r--commands/bug/bug_title.go6
-rw-r--r--commands/bug/bug_title_edit.go3
-rw-r--r--commands/commands.go3
-rw-r--r--commands/execenv/env.go194
-rw-r--r--commands/execenv/env_test.go21
-rw-r--r--commands/execenv/env_testing.go50
-rw-r--r--commands/execenv/loading.go169
-rw-r--r--commands/label.go4
-rw-r--r--commands/pull.go4
-rw-r--r--commands/push.go4
-rw-r--r--commands/root.go23
-rw-r--r--commands/termui.go4
-rw-r--r--commands/user/user.go9
-rw-r--r--commands/user/user_adopt.go4
-rw-r--r--commands/user/user_new.go4
-rw-r--r--commands/user/user_show.go3
-rw-r--r--commands/version.go3
-rw-r--r--commands/webui.go20
-rw-r--r--commands/wipe.go56
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
+}