diff --git a/cache/board_cache.go b/cache/board_cache.go index fbae731756b3446f375ad8b2afbe2762a03e183e..ecf7e735d94aead4f0bdd6c0958e8cde6cae61d3 100644 --- a/cache/board_cache.go +++ b/cache/board_cache.go @@ -3,10 +3,10 @@ package cache import ( "time" - "github.com/MichaelMure/git-bug/entities/board" - "github.com/MichaelMure/git-bug/entities/identity" - "github.com/MichaelMure/git-bug/entity" - "github.com/MichaelMure/git-bug/repository" + "github.com/git-bug/git-bug/entities/board" + "github.com/git-bug/git-bug/entities/identity" + "github.com/git-bug/git-bug/entity" + "github.com/git-bug/git-bug/repository" ) // BoardCache is a wrapper around a Board. It provides multiple functions: diff --git a/cache/board_excerpt.go b/cache/board_excerpt.go index bb7981d989cc11ab53f31f4ad1ae7f7df74a5a3c..6c50b52180bc8155e54e9f6158df15011f647ed0 100644 --- a/cache/board_excerpt.go +++ b/cache/board_excerpt.go @@ -4,8 +4,8 @@ import ( "encoding/gob" "time" - "github.com/MichaelMure/git-bug/entity" - "github.com/MichaelMure/git-bug/util/lamport" + "github.com/git-bug/git-bug/entity" + "github.com/git-bug/git-bug/util/lamport" ) // Package initialisation used to register the type for (de)serialization @@ -25,10 +25,10 @@ type BoardExcerpt struct { CreateUnixTime int64 EditUnixTime int64 - Title string - Description string - ItemCount int - Actors []entity.Id + Title string + Description string + ItemCount int + Participants []entity.Id CreateMetadata map[string]string } @@ -36,9 +36,9 @@ type BoardExcerpt struct { func NewBoardExcerpt(b *BoardCache) *BoardExcerpt { snap := b.Snapshot() - actorsIds := make([]entity.Id, 0, len(snap.Actors)) - for _, actor := range snap.Actors { - actorsIds = append(actorsIds, actor.Id()) + participantsIds := make([]entity.Id, 0, len(snap.Participants)) + for _, participant := range snap.Participants { + participantsIds = append(participantsIds, participant.Id()) } return &BoardExcerpt{ @@ -50,7 +50,7 @@ func NewBoardExcerpt(b *BoardCache) *BoardExcerpt { Title: snap.Title, Description: snap.Description, ItemCount: snap.ItemCount(), - Actors: actorsIds, + Participants: participantsIds, CreateMetadata: b.FirstOp().AllMetadata(), } } diff --git a/cache/board_subcache.go b/cache/board_subcache.go index db1aff261c0a8019d6c91d48125c9ee8940c4713..bc33fb3b1448b040414a2d79b11495af21e9151a 100644 --- a/cache/board_subcache.go +++ b/cache/board_subcache.go @@ -3,10 +3,10 @@ package cache import ( "time" - "github.com/MichaelMure/git-bug/entities/board" - "github.com/MichaelMure/git-bug/entities/identity" - "github.com/MichaelMure/git-bug/entity" - "github.com/MichaelMure/git-bug/repository" + "github.com/git-bug/git-bug/entities/board" + "github.com/git-bug/git-bug/entities/identity" + "github.com/git-bug/git-bug/entity" + "github.com/git-bug/git-bug/repository" ) type RepoCacheBoard struct { diff --git a/commands/board/board.go b/commands/board/board.go new file mode 100644 index 0000000000000000000000000000000000000000..52cf9d39b8dae3a404ace3a22fb22565c5face01 --- /dev/null +++ b/commands/board/board.go @@ -0,0 +1,145 @@ +package boardcmd + +import ( + "fmt" + "strings" + + text "github.com/MichaelMure/go-term-text" + "github.com/spf13/cobra" + + "github.com/git-bug/git-bug/cache" + "github.com/git-bug/git-bug/commands/cmdjson" + "github.com/git-bug/git-bug/commands/completion" + "github.com/git-bug/git-bug/commands/execenv" + "github.com/git-bug/git-bug/util/colors" +) + +type boardOptions struct { + metadataQuery []string + actorQuery []string + titleQuery []string + outputFormat string +} + +func NewBoardCommand() *cobra.Command { + env := execenv.NewEnv() + options := boardOptions{} + + cmd := &cobra.Command{ + Use: "board", + Short: "List boards", + PreRunE: execenv.LoadBackend(env), + RunE: execenv.CloseBackend(env, func(cmd *cobra.Command, args []string) error { + return runBoard(env, options, args) + }), + } + + flags := cmd.Flags() + flags.SortFlags = false + + flags.StringSliceVarP(&options.metadataQuery, "metadata", "m", nil, + "Filter by metadata. Example: github-url=URL") + cmd.RegisterFlagCompletionFunc("author", completion.UserForQuery(env)) + flags.StringSliceVarP(&options.actorQuery, "actor", "A", nil, + "Filter by actor") + cmd.RegisterFlagCompletionFunc("actor", completion.UserForQuery(env)) + flags.StringSliceVarP(&options.titleQuery, "title", "t", nil, + "Filter by title") + flags.StringVarP(&options.outputFormat, "format", "f", "default", + "Select the output formatting style. Valid values are [default,plain,compact,id,json,org-mode]") + cmd.RegisterFlagCompletionFunc("format", + completion.From([]string{"default", "id", "json"})) + + const selectGroup = "select" + cmd.AddGroup(&cobra.Group{ID: selectGroup, Title: "Implicit selection"}) + + addCmdWithGroup := func(child *cobra.Command, groupID string) { + cmd.AddCommand(child) + child.GroupID = groupID + } + + addCmdWithGroup(newBoardDeselectCommand(), selectGroup) + addCmdWithGroup(newBoardSelectCommand(), selectGroup) + + cmd.AddCommand(newBoardNewCommand()) + cmd.AddCommand(newBoardRmCommand()) + cmd.AddCommand(newBoardShowCommand()) + cmd.AddCommand(newBoardDescriptionCommand()) + cmd.AddCommand(newBoardTitleCommand()) + cmd.AddCommand(newBoardAddDraftCommand()) + + return cmd +} + +func runBoard(env *execenv.Env, opts boardOptions, args []string) error { + // TODO: query + + allIds := env.Backend.Boards().AllIds() + + excerpts := make([]*cache.BoardExcerpt, len(allIds)) + for i, id := range allIds { + b, err := env.Backend.Boards().ResolveExcerpt(id) + if err != nil { + return err + } + excerpts[i] = b + } + + switch opts.outputFormat { + case "json": + return boardJsonFormatter(env, excerpts) + case "id": + return boardIDFormatter(env, excerpts) + case "default": + return boardDefaultFormatter(env, excerpts) + default: + return fmt.Errorf("unknown format %s", opts.outputFormat) + } +} + +func boardIDFormatter(env *execenv.Env, excerpts []*cache.BoardExcerpt) error { + for _, b := range excerpts { + env.Out.Println(b.Id().String()) + } + + return nil +} + +func boardDefaultFormatter(env *execenv.Env, excerpts []*cache.BoardExcerpt) error { + for _, b := range excerpts { + // truncate + pad if needed + titleFmt := text.LeftPadMaxLine(strings.TrimSpace(b.Title), 50, 0) + descFmt := text.LeftPadMaxLine(strings.TrimSpace(b.Description), 50, 0) + + var itemFmt string + switch { + case b.ItemCount < 1: + itemFmt = "empty" + case b.ItemCount < 1000: + itemFmt = fmt.Sprintf("%3d 📝", b.ItemCount) + default: + itemFmt = " ∞ 📝" + + } + + env.Out.Printf("%s\t%s\t%s\t%s\n", + colors.Cyan(b.Id().Human()), + titleFmt, + descFmt, + itemFmt, + ) + } + return nil +} + +func boardJsonFormatter(env *execenv.Env, excerpts []*cache.BoardExcerpt) error { + res := make([]cmdjson.BoardExcerpt, len(excerpts)) + for i, b := range excerpts { + jsonBoard, err := cmdjson.NewBoardExcerpt(env.Backend, b) + if err != nil { + return err + } + res[i] = jsonBoard + } + return env.Out.PrintJSON(res) +} diff --git a/commands/board/board_adddraft.go b/commands/board/board_adddraft.go new file mode 100644 index 0000000000000000000000000000000000000000..0c67e75700b4589b7b3516c3ee420e0ed3413ac7 --- /dev/null +++ b/commands/board/board_adddraft.go @@ -0,0 +1,96 @@ +package boardcmd + +import ( + "strconv" + + "github.com/spf13/cobra" + + buginput "github.com/git-bug/git-bug/commands/bug/input" + "github.com/git-bug/git-bug/commands/execenv" + "github.com/git-bug/git-bug/entity" +) + +type boardAddDraftOptions struct { + title string + messageFile string + message string + column string + nonInteractive bool +} + +func newBoardAddDraftCommand() *cobra.Command { + env := execenv.NewEnv() + options := boardAddDraftOptions{} + + cmd := &cobra.Command{ + Use: "add-draft [BOARD_ID]", + Short: "Add a draft item to a board", + PreRunE: execenv.LoadBackend(env), + RunE: execenv.CloseBackend(env, func(cmd *cobra.Command, args []string) error { + return runBoardAddDraft(env, options, args) + }), + ValidArgsFunction: BoardCompletion(env), + } + + flags := cmd.Flags() + flags.SortFlags = false + + flags.StringVarP(&options.title, "title", "t", "", + "Provide the title to describe the draft item") + flags.StringVarP(&options.message, "message", "m", "", + "Provide the message of the draft item") + flags.StringVarP(&options.messageFile, "file", "F", "", + "Take the message from the given file. Use - to read the message from the standard input") + flags.StringVarP(&options.column, "column", "c", "1", + "The column to add to. Either a column Id or prefix, or the column number starting from 1.") + // _ = cmd.MarkFlagRequired("column") + _ = cmd.RegisterFlagCompletionFunc("column", ColumnCompletion(env)) + flags.BoolVar(&options.nonInteractive, "non-interactive", false, "Do not ask for user input") + + return cmd +} + +func runBoardAddDraft(env *execenv.Env, opts boardAddDraftOptions, args []string) error { + b, args, err := ResolveSelected(env.Backend, args) + if err != nil { + return err + } + + var columnId entity.Id + + index, err := strconv.Atoi(opts.column) + if err == nil && index-1 >= 0 && index-1 < len(b.Snapshot().Columns) { + columnId = b.Snapshot().Columns[index-1].Id + } else { + // TODO: ID or combined ID? + // TODO: resolve + } + + if opts.messageFile != "" && opts.message == "" { + // Note: reuse the bug inputs + opts.title, opts.message, err = buginput.BugCreateFileInput(opts.messageFile) + if err != nil { + return err + } + } + + if !opts.nonInteractive && opts.messageFile == "" && (opts.message == "" || opts.title == "") { + opts.title, opts.message, err = buginput.BugCreateEditorInput(env.Backend, opts.title, opts.message) + if err == buginput.ErrEmptyTitle { + env.Out.Println("Empty title, aborting.") + return nil + } + if err != nil { + return err + } + } + + id, _, err := b.AddItemDraft(columnId, opts.title, opts.message, nil) + if err != nil { + return err + } + + env.Out.Printf("%s created\n", id.Human()) + + return b.Commit() +} diff --git a/commands/board/board_description.go b/commands/board/board_description.go new file mode 100644 index 0000000000000000000000000000000000000000..17a8ec2952b1c779c180b2d1aa9dbfb6e4ce0d34 --- /dev/null +++ b/commands/board/board_description.go @@ -0,0 +1,38 @@ +package boardcmd + +import ( + "github.com/spf13/cobra" + + "github.com/git-bug/git-bug/commands/execenv" +) + +func newBoardDescriptionCommand() *cobra.Command { + env := execenv.NewEnv() + + cmd := &cobra.Command{ + Use: "description [BOARD_ID]", + Short: "Display the description of a board", + PreRunE: execenv.LoadBackend(env), + RunE: execenv.CloseBackend(env, func(cmd *cobra.Command, args []string) error { + return runBoardDescription(env, args) + }), + ValidArgsFunction: BoardCompletion(env), + } + + cmd.AddCommand(newBoardDescriptionEditCommand()) + + return cmd +} + +func runBoardDescription(env *execenv.Env, args []string) error { + b, args, err := ResolveSelected(env.Backend, args) + if err != nil { + return err + } + + snap := b.Snapshot() + + env.Out.Println(snap.Description) + + return nil +} diff --git a/commands/board/board_description_edit.go b/commands/board/board_description_edit.go new file mode 100644 index 0000000000000000000000000000000000000000..503c907564d91baf466938246749ff99396a5fe7 --- /dev/null +++ b/commands/board/board_description_edit.go @@ -0,0 +1,70 @@ +package boardcmd + +import ( + "github.com/spf13/cobra" + + "github.com/git-bug/git-bug/commands/execenv" + "github.com/git-bug/git-bug/commands/input" + "github.com/git-bug/git-bug/util/text" +) + +type boardDescriptionEditOptions struct { + description string + nonInteractive bool +} + +func newBoardDescriptionEditCommand() *cobra.Command { + env := execenv.NewEnv() + options := boardDescriptionEditOptions{} + + cmd := &cobra.Command{ + Use: "edit [BUG_ID]", + Short: "Edit a description of a board", + PreRunE: execenv.LoadBackendEnsureUser(env), + RunE: execenv.CloseBackend(env, func(cmd *cobra.Command, args []string) error { + return runBugDescriptionEdit(env, options, args) + }), + ValidArgsFunction: BoardCompletion(env), + } + + flags := cmd.Flags() + flags.SortFlags = false + + flags.StringVarP(&options.description, "description", "t", "", + "Provide a description for the board", + ) + flags.BoolVar(&options.nonInteractive, "non-interactive", false, "Do not ask for user input") + + return cmd +} + +func runBugDescriptionEdit(env *execenv.Env, opts boardDescriptionEditOptions, args []string) error { + b, args, err := ResolveSelected(env.Backend, args) + if err != nil { + return err + } + + snap := b.Snapshot() + + if opts.description == "" { + if opts.nonInteractive { + env.Err.Println("No description given. Aborting.") + return nil + } + opts.description, err = input.PromptDefault("Board description", "description", snap.Description, input.Required) + if err != nil { + return err + } + } + + if opts.description == snap.Description { + env.Err.Println("No change, aborting.") + } + + _, err = b.SetDescription(text.CleanupOneLine(opts.description)) + if err != nil { + return err + } + + return b.Commit() +} diff --git a/commands/board/board_deselect.go b/commands/board/board_deselect.go new file mode 100644 index 0000000000000000000000000000000000000000..f66b6e6a0ae773a1367cf53c44882991f4ea8776 --- /dev/null +++ b/commands/board/board_deselect.go @@ -0,0 +1,34 @@ +package boardcmd + +import ( + "github.com/spf13/cobra" + + "github.com/git-bug/git-bug/commands/execenv" + _select "github.com/git-bug/git-bug/commands/select" + "github.com/git-bug/git-bug/entities/board" +) + +func newBoardDeselectCommand() *cobra.Command { + env := execenv.NewEnv() + + cmd := &cobra.Command{ + Use: "deselect", + Short: "Clear the implicitly selected board", + + PreRunE: execenv.LoadBackend(env), + RunE: execenv.CloseBackend(env, func(cmd *cobra.Command, args []string) error { + return runBoardDeselect(env) + }), + } + + return cmd +} + +func runBoardDeselect(env *execenv.Env) error { + err := _select.Clear(env.Backend, board.Namespace) + if err != nil { + return err + } + + return nil +} diff --git a/commands/board/board_new.go b/commands/board/board_new.go new file mode 100644 index 0000000000000000000000000000000000000000..447763e150631825794cb2afeeb557b4c3e78c61 --- /dev/null +++ b/commands/board/board_new.go @@ -0,0 +1,83 @@ +package boardcmd + +import ( + "fmt" + "strings" + + "github.com/spf13/cobra" + + "github.com/git-bug/git-bug/commands/execenv" + "github.com/git-bug/git-bug/commands/input" + "github.com/git-bug/git-bug/entities/board" + "github.com/git-bug/git-bug/util/text" +) + +type boardNewOptions struct { + title string + description string + columns []string + nonInteractive bool +} + +func newBoardNewCommand() *cobra.Command { + env := execenv.NewEnv() + options := boardNewOptions{} + + cmd := &cobra.Command{ + Use: "new", + Short: "Create a new board", + PreRunE: execenv.LoadBackendEnsureUser(env), + RunE: execenv.CloseBackend(env, func(cmd *cobra.Command, args []string) error { + return runBugNew(env, options) + }), + } + + flags := cmd.Flags() + flags.SortFlags = false + + flags.StringVarP(&options.title, "title", "t", "", + "Provide a title to describe the issue") + flags.StringVarP(&options.description, "description", "d", "", + "Provide a message to describe the board") + flags.StringArrayVarP(&options.columns, "columns", "c", board.DefaultColumns, + fmt.Sprintf("Define the columns of the board (default to %s)", + strings.Join(board.DefaultColumns, ","))) + flags.BoolVar(&options.nonInteractive, "non-interactive", false, "Do not ask for user input") + + return cmd +} + +func runBugNew(env *execenv.Env, opts boardNewOptions) error { + var err error + + if !opts.nonInteractive && opts.title == "" { + opts.title, err = input.Prompt("Board title", "title", input.Required) + if err != nil { + return err + } + } + + if !opts.nonInteractive && opts.description == "" { + opts.description, err = input.Prompt("Board description", "description") + if err != nil { + return err + } + } + + for i, column := range opts.columns { + opts.columns[i] = text.Cleanup(column) + } + + b, _, err := env.Backend.Boards().New( + text.CleanupOneLine(opts.title), + text.CleanupOneLine(opts.description), + opts.columns, + ) + if err != nil { + return err + } + + env.Out.Printf("%s created\n", b.Id().Human()) + + return nil +} diff --git a/commands/board/board_rm.go b/commands/board/board_rm.go new file mode 100644 index 0000000000000000000000000000000000000000..e90e86132200132d7ddabd68142de1622615ced4 --- /dev/null +++ b/commands/board/board_rm.go @@ -0,0 +1,45 @@ +package boardcmd + +import ( + "errors" + + "github.com/spf13/cobra" + + "github.com/git-bug/git-bug/commands/execenv" +) + +func newBoardRmCommand() *cobra.Command { + env := execenv.NewEnv() + + cmd := &cobra.Command{ + Use: "rm BOARD_ID", + Short: "Remove an existing board", + Long: "Remove an existing board in the local repository.", + PreRunE: execenv.LoadBackendEnsureUser(env), + RunE: execenv.CloseBackend(env, func(cmd *cobra.Command, args []string) error { + return runBoardRm(env, args) + }), + ValidArgsFunction: BoardCompletion(env), + } + + flags := cmd.Flags() + flags.SortFlags = false + + return cmd +} + +func runBoardRm(env *execenv.Env, args []string) (err error) { + if len(args) == 0 { + return errors.New("you must provide a board prefix to remove") + } + + err = env.Backend.Boards().Remove(args[0]) + + if err != nil { + return + } + + env.Out.Printf("board %s removed\n", args[0]) + + return +} diff --git a/commands/board/board_select.go b/commands/board/board_select.go new file mode 100644 index 0000000000000000000000000000000000000000..f62acab564ae9e821034ed70eca59724760e2f75 --- /dev/null +++ b/commands/board/board_select.go @@ -0,0 +1,58 @@ +package boardcmd + +import ( + "errors" + + "github.com/spf13/cobra" + + "github.com/git-bug/git-bug/cache" + "github.com/git-bug/git-bug/commands/execenv" + _select "github.com/git-bug/git-bug/commands/select" + "github.com/git-bug/git-bug/entities/board" +) + +func ResolveSelected(repo *cache.RepoCache, args []string) (*cache.BoardCache, []string, error) { + return _select.Resolve[*cache.BoardCache](repo, board.Typename, board.Namespace, repo.Boards(), args) +} + +func newBoardSelectCommand() *cobra.Command { + env := execenv.NewEnv() + + cmd := &cobra.Command{ + Use: "select BOARD_ID", + Short: "Select a board for implicit use in future commands", + Long: `Select a board for implicit use in future commands. + +The complementary command is "git board deselect" performing the opposite operation. +`, + PreRunE: execenv.LoadBackend(env), + RunE: execenv.CloseBackend(env, func(cmd *cobra.Command, args []string) error { + return runBoardSelect(env, args) + }), + ValidArgsFunction: BoardCompletion(env), + } + + return cmd +} + +func runBoardSelect(env *execenv.Env, args []string) error { + if len(args) == 0 { + return errors.New("You must provide a board id") + } + + prefix := args[0] + + b, err := env.Backend.Boards().ResolvePrefix(prefix) + if err != nil { + return err + } + + err = _select.Select(env.Backend, board.Namespace, b.Id()) + if err != nil { + return err + } + + env.Out.Printf("selected board %s: %s\n", b.Id().Human(), b.Snapshot().Title) + + return nil +} diff --git a/commands/board/board_show.go b/commands/board/board_show.go new file mode 100644 index 0000000000000000000000000000000000000000..dcc71306d8fe6ea04fb0e853649f81b6ceb1c0e5 --- /dev/null +++ b/commands/board/board_show.go @@ -0,0 +1,136 @@ +package boardcmd + +import ( + "fmt" + + "github.com/spf13/cobra" + + "github.com/git-bug/git-bug/commands/cmdjson" + "github.com/git-bug/git-bug/commands/execenv" + "github.com/git-bug/git-bug/entities/board" +) + +type boardShowOptions struct { + format string +} + +func newBoardShowCommand() *cobra.Command { + env := execenv.NewEnv() + options := boardShowOptions{} + + cmd := &cobra.Command{ + Use: "show [BOARD_ID]", + Short: "Display a board", + PreRunE: execenv.LoadBackend(env), + RunE: execenv.CloseBackend(env, func(cmd *cobra.Command, args []string) error { + return runBoardShow(env, options, args) + }), + ValidArgsFunction: BoardCompletion(env), + } + + flags := cmd.Flags() + flags.SortFlags = false + + flags.StringVarP(&options.format, "format", "f", "default", + "Select the output formatting style. Valid values are [default,json,org-mode]") + + return cmd +} + +func runBoardShow(env *execenv.Env, opts boardShowOptions, args []string) error { + b, args, err := ResolveSelected(env.Backend, args) + if err != nil { + return err + } + + snap := b.Snapshot() + + switch opts.format { + case "json": + return showJsonFormatter(env, snap) + case "default": + return showDefaultFormatter(env, snap) + default: + return fmt.Errorf("unknown format %s", opts.format) + } +} + +func showDefaultFormatter(env *execenv.Env, snapshot *board.Snapshot) error { + // // Header + // env.Out.Printf("%s [%s] %s\n\n", + // colors.Cyan(snapshot.Id().Human()), + // colors.Yellow(snapshot.Status), + // snapshot.Title, + // ) + // + // env.Out.Printf("%s opened this issue %s\n", + // colors.Magenta(snapshot.Author.DisplayName()), + // snapshot.CreateTime.String(), + // ) + // + // env.Out.Printf("This was last edited at %s\n\n", + // snapshot.EditTime().String(), + // ) + // + // // Labels + // var labels = make([]string, len(snapshot.Labels)) + // for i := range snapshot.Labels { + // labels[i] = string(snapshot.Labels[i]) + // } + // + // env.Out.Printf("labels: %s\n", + // strings.Join(labels, ", "), + // ) + // + // // Actors + // var actors = make([]string, len(snapshot.Actors)) + // for i := range snapshot.Actors { + // actors[i] = snapshot.Actors[i].DisplayName() + // } + // + // env.Out.Printf("actors: %s\n", + // strings.Join(actors, ", "), + // ) + // + // // Participants + // var participants = make([]string, len(snapshot.Participants)) + // for i := range snapshot.Participants { + // participants[i] = snapshot.Participants[i].DisplayName() + // } + // + // env.Out.Printf("participants: %s\n\n", + // strings.Join(participants, ", "), + // ) + // + // // Comments + // indent := " " + // + // for i, comment := range snapshot.Comments { + // var message string + // env.Out.Printf("%s%s #%d %s <%s>\n\n", + // indent, + // comment.CombinedId().Human(), + // i, + // comment.Author.DisplayName(), + // comment.Author.Email(), + // ) + // + // if comment.Message == "" { + // message = colors.BlackBold(colors.WhiteBg("No description provided.")) + // } else { + // message = comment.Message + // } + // + // env.Out.Printf("%s%s\n\n\n", + // indent, + // message, + // ) + // } + + return nil +} + +func showJsonFormatter(env *execenv.Env, snap *board.Snapshot) error { + jsonBoard := cmdjson.NewBoardSnapshot(snap) + return env.Out.PrintJSON(jsonBoard) +} diff --git a/commands/board/board_title.go b/commands/board/board_title.go new file mode 100644 index 0000000000000000000000000000000000000000..d0cc73729c0ad709a8258c151e139293e3b2deae --- /dev/null +++ b/commands/board/board_title.go @@ -0,0 +1,38 @@ +package boardcmd + +import ( + "github.com/spf13/cobra" + + "github.com/git-bug/git-bug/commands/execenv" +) + +func newBoardTitleCommand() *cobra.Command { + env := execenv.NewEnv() + + cmd := &cobra.Command{ + Use: "title [BOARD_ID]", + Short: "Display the title of a board", + PreRunE: execenv.LoadBackend(env), + RunE: execenv.CloseBackend(env, func(cmd *cobra.Command, args []string) error { + return runBoardTitle(env, args) + }), + ValidArgsFunction: BoardCompletion(env), + } + + cmd.AddCommand(newBoardTitleEditCommand()) + + return cmd +} + +func runBoardTitle(env *execenv.Env, args []string) error { + b, args, err := ResolveSelected(env.Backend, args) + if err != nil { + return err + } + + snap := b.Snapshot() + + env.Out.Println(snap.Title) + + return nil +} diff --git a/commands/board/board_title_edit.go b/commands/board/board_title_edit.go new file mode 100644 index 0000000000000000000000000000000000000000..c307f7483143ac80ac277d5f0214c26ed61ec0a6 --- /dev/null +++ b/commands/board/board_title_edit.go @@ -0,0 +1,70 @@ +package boardcmd + +import ( + "github.com/spf13/cobra" + + "github.com/git-bug/git-bug/commands/execenv" + "github.com/git-bug/git-bug/commands/input" + "github.com/git-bug/git-bug/util/text" +) + +type boardTitleEditOptions struct { + title string + nonInteractive bool +} + +func newBoardTitleEditCommand() *cobra.Command { + env := execenv.NewEnv() + options := boardTitleEditOptions{} + + cmd := &cobra.Command{ + Use: "edit [BUG_ID]", + Short: "Edit a title of a board", + PreRunE: execenv.LoadBackendEnsureUser(env), + RunE: execenv.CloseBackend(env, func(cmd *cobra.Command, args []string) error { + return runBugTitleEdit(env, options, args) + }), + ValidArgsFunction: BoardCompletion(env), + } + + flags := cmd.Flags() + flags.SortFlags = false + + flags.StringVarP(&options.title, "title", "t", "", + "Provide a title to describe the board", + ) + flags.BoolVar(&options.nonInteractive, "non-interactive", false, "Do not ask for user input") + + return cmd +} + +func runBugTitleEdit(env *execenv.Env, opts boardTitleEditOptions, args []string) error { + b, args, err := ResolveSelected(env.Backend, args) + if err != nil { + return err + } + + snap := b.Snapshot() + + if opts.title == "" { + if opts.nonInteractive { + env.Err.Println("No title given. Aborting.") + return nil + } + opts.title, err = input.PromptDefault("Board title", "title", snap.Title, input.Required) + if err != nil { + return err + } + } + + if opts.title == snap.Title { + env.Err.Println("No change, aborting.") + } + + _, err = b.SetTitle(text.CleanupOneLine(opts.title)) + if err != nil { + return err + } + + return b.Commit() +} diff --git a/commands/board/completion.go b/commands/board/completion.go new file mode 100644 index 0000000000000000000000000000000000000000..fe802f570160caf96880183b713374f599e818bd --- /dev/null +++ b/commands/board/completion.go @@ -0,0 +1,56 @@ +package boardcmd + +import ( + "strings" + + "github.com/spf13/cobra" + + "github.com/git-bug/git-bug/commands/completion" + "github.com/git-bug/git-bug/commands/execenv" +) + +// BoardCompletion complete a board id +func BoardCompletion(env *execenv.Env) completion.ValidArgsFunction { + return func(cmd *cobra.Command, args []string, toComplete string) (completions []string, directives cobra.ShellCompDirective) { + if err := execenv.LoadBackend(env)(cmd, args); err != nil { + return completion.HandleError(err) + } + defer func() { + _ = env.Backend.Close() + }() + + for _, id := range env.Backend.Boards().AllIds() { + if strings.Contains(id.String(), strings.TrimSpace(toComplete)) { + excerpt, err := env.Backend.Boards().ResolveExcerpt(id) + if err != nil { + return completion.HandleError(err) + } + completions = append(completions, id.Human()+"\t"+excerpt.Title) + } + } + + return completions, cobra.ShellCompDirectiveNoFileComp + } +} + +func ColumnCompletion(env *execenv.Env) completion.ValidArgsFunction { + return func(cmd *cobra.Command, args []string, toComplete string) (completions []string, directives cobra.ShellCompDirective) { + if err := execenv.LoadBackend(env)(cmd, args); err != nil { + return completion.HandleError(err) + } + defer func() { + _ = env.Backend.Close() + }() + + b, _, err := ResolveSelected(env.Backend, args) + if err != nil { + return completion.HandleError(err) + } + + for _, column := range b.Snapshot().Columns { + completions = append(completions, column.Id.Human()+"\t"+column.Name) + } + + return completions, cobra.ShellCompDirectiveNoFileComp + } +} diff --git a/commands/cmdjson/board.go b/commands/cmdjson/board.go new file mode 100644 index 0000000000000000000000000000000000000000..01826cd037f5162dfcba3f0d5dbf15bdd5a27f5a --- /dev/null +++ b/commands/cmdjson/board.go @@ -0,0 +1,123 @@ +package cmdjson + +import ( + "github.com/git-bug/git-bug/cache" + "github.com/git-bug/git-bug/entities/board" +) + +type BoardSnapshot struct { + Id string `json:"id"` + HumanId string `json:"human_id"` + CreateTime Time `json:"create_time"` + EditTime Time `json:"edit_time"` + + Title string `json:"title"` + Description string `json:"description"` + Participants []Identity `json:"participants"` + Columns []BoardColumn `json:"columns"` +} + +func NewBoardSnapshot(snapshot *board.Snapshot) BoardSnapshot { + jsonBoard := BoardSnapshot{ + Id: snapshot.Id().String(), + HumanId: snapshot.Id().Human(), + CreateTime: NewTime(snapshot.CreateTime, 0), + EditTime: NewTime(snapshot.EditTime(), 0), + Title: snapshot.Title, + Description: snapshot.Description, + } + + jsonBoard.Participants = make([]Identity, len(snapshot.Participants)) + for i, element := range snapshot.Participants { + jsonBoard.Participants[i] = NewIdentity(element) + } + + jsonBoard.Columns = make([]BoardColumn, len(snapshot.Columns)) + for i, column := range snapshot.Columns { + jsonBoard.Columns[i] = NewBoardColumn(column) + } + + return jsonBoard +} + +type BoardColumn struct { + Id string `json:"id"` + HumanId string `json:"human_id"` + Name string `json:"name"` + Items []any `json:"items"` +} + +func NewBoardColumn(column *board.Column) BoardColumn { + jsonColumn := BoardColumn{ + Id: column.Id.String(), + HumanId: column.Id.Human(), + Name: column.Name, + } + jsonColumn.Items = make([]any, len(column.Items)) + for j, item := range column.Items { + switch item := item.(type) { + case *board.Draft: + jsonColumn.Items[j] = NewBoardDraftItem(item) + case *board.BugItem: + jsonColumn.Items[j] = NewBugSnapshot(item.Bug.Compile()) + default: + panic("unknown item type") + } + } + return jsonColumn +} + +type BoardDraftItem struct { + Id string `json:"id"` + HumanId string `json:"human_id"` + Author Identity `json:"author"` + Title string `json:"title"` + Message string `json:"message"` +} + +func NewBoardDraftItem(item *board.Draft) BoardDraftItem { + return BoardDraftItem{ + Id: item.CombinedId().String(), + HumanId: item.CombinedId().Human(), + Author: NewIdentity(item.Author), + Title: item.Title, + Message: item.Message, + } +} + +type BoardExcerpt struct { + Id string `json:"id"` + HumanId string `json:"human_id"` + CreateTime Time `json:"create_time"` + EditTime Time `json:"edit_time"` + + Title string `json:"title"` + Description string `json:"description"` + Participants []Identity `json:"participants"` + + Items int `json:"items"` + Metadata map[string]string `json:"metadata"` +} + +func NewBoardExcerpt(backend *cache.RepoCache, b *cache.BoardExcerpt) (BoardExcerpt, error) { + jsonBoard := BoardExcerpt{ + Id: b.Id().String(), + HumanId: b.Id().Human(), + CreateTime: NewTime(b.CreateTime(), b.CreateLamportTime), + EditTime: NewTime(b.EditTime(), b.EditLamportTime), + Title: b.Title, + Description: b.Description, + Items: b.ItemCount, + Metadata: b.CreateMetadata, + } + + jsonBoard.Participants = make([]Identity, len(b.Participants)) + for i, element := range b.Participants { + participant, err := backend.Identities().ResolveExcerpt(element) + if err != nil { + return BoardExcerpt{}, err + } + jsonBoard.Participants[i] = NewIdentityFromExcerpt(participant) + } + return jsonBoard, nil +} diff --git a/commands/root.go b/commands/root.go index 1b64b5090b624f29c923285667ebe7b2d8924174..b94a659b65dff1c77852d59da7fde952c1c22ac6 100644 --- a/commands/root.go +++ b/commands/root.go @@ -5,8 +5,9 @@ import ( "github.com/spf13/cobra" - "github.com/git-bug/git-bug/commands/bridge" - "github.com/git-bug/git-bug/commands/bug" + boardcmd "github.com/git-bug/git-bug/commands/board" + bridgecmd "github.com/git-bug/git-bug/commands/bridge" + bugcmd "github.com/git-bug/git-bug/commands/bug" "github.com/git-bug/git-bug/commands/execenv" "github.com/git-bug/git-bug/commands/user" ) @@ -56,6 +57,7 @@ the same git remote you are already using to collaborate with other people. env := execenv.NewEnv() + addCmdWithGroup(boardcmd.NewBoardCommand(), entityGroup) addCmdWithGroup(bugcmd.NewBugCommand(env), entityGroup) addCmdWithGroup(usercmd.NewUserCommand(env), entityGroup) addCmdWithGroup(newLabelCommand(env), entityGroup) diff --git a/doc/man/git-bug-board-add-draft.1 b/doc/man/git-bug-board-add-draft.1 new file mode 100644 index 0000000000000000000000000000000000000000..ae3214c1103424b14aef64b29395709aaae1e017 --- /dev/null +++ b/doc/man/git-bug-board-add-draft.1 @@ -0,0 +1,42 @@ +.nh +.TH "GIT-BUG" "1" "Apr 2019" "Generated from git-bug's source code" "" + +.SH NAME +git-bug-board-add-draft - Add a draft item to a board + + +.SH SYNOPSIS +\fBgit-bug board add-draft [BOARD_ID] [flags]\fP + + +.SH DESCRIPTION +Add a draft item to a board + + +.SH OPTIONS +\fB-t\fP, \fB--title\fP="" + Provide the title to describe the draft item + +.PP +\fB-m\fP, \fB--message\fP="" + Provide the message of the draft item + +.PP +\fB-F\fP, \fB--file\fP="" + Take the message from the given file. Use - to read the message from the standard input + +.PP +\fB-c\fP, \fB--column\fP="1" + The column to add to. Either a column Id or prefix, or the column number starting from 1. + +.PP +\fB--non-interactive\fP[=false] + Do not ask for user input + +.PP +\fB-h\fP, \fB--help\fP[=false] + help for add-draft + + +.SH SEE ALSO +\fBgit-bug-board(1)\fP diff --git a/doc/man/git-bug-board-description-edit.1 b/doc/man/git-bug-board-description-edit.1 new file mode 100644 index 0000000000000000000000000000000000000000..92340cb06a31d6a5cac7329ee4afbebb750ab63d --- /dev/null +++ b/doc/man/git-bug-board-description-edit.1 @@ -0,0 +1,30 @@ +.nh +.TH "GIT-BUG" "1" "Apr 2019" "Generated from git-bug's source code" "" + +.SH NAME +git-bug-board-description-edit - Edit a description of a board + + +.SH SYNOPSIS +\fBgit-bug board description edit [BUG_ID] [flags]\fP + + +.SH DESCRIPTION +Edit a description of a board + + +.SH OPTIONS +\fB-t\fP, \fB--description\fP="" + Provide a description for the board + +.PP +\fB--non-interactive\fP[=false] + Do not ask for user input + +.PP +\fB-h\fP, \fB--help\fP[=false] + help for edit + + +.SH SEE ALSO +\fBgit-bug-board-description(1)\fP diff --git a/doc/man/git-bug-board-description.1 b/doc/man/git-bug-board-description.1 new file mode 100644 index 0000000000000000000000000000000000000000..2b2a438c2635919fff63dd2d83b22ab73d7729ea --- /dev/null +++ b/doc/man/git-bug-board-description.1 @@ -0,0 +1,22 @@ +.nh +.TH "GIT-BUG" "1" "Apr 2019" "Generated from git-bug's source code" "" + +.SH NAME +git-bug-board-description - Display the description of a board + + +.SH SYNOPSIS +\fBgit-bug board description [BOARD_ID] [flags]\fP + + +.SH DESCRIPTION +Display the description of a board + + +.SH OPTIONS +\fB-h\fP, \fB--help\fP[=false] + help for description + + +.SH SEE ALSO +\fBgit-bug-board(1)\fP, \fBgit-bug-board-description-edit(1)\fP diff --git a/doc/man/git-bug-board-deselect.1 b/doc/man/git-bug-board-deselect.1 new file mode 100644 index 0000000000000000000000000000000000000000..679a6a3b625cc7e216d817ab26e755dfa1ad37cb --- /dev/null +++ b/doc/man/git-bug-board-deselect.1 @@ -0,0 +1,22 @@ +.nh +.TH "GIT-BUG" "1" "Apr 2019" "Generated from git-bug's source code" "" + +.SH NAME +git-bug-board-deselect - Clear the implicitly selected board + + +.SH SYNOPSIS +\fBgit-bug board deselect [flags]\fP + + +.SH DESCRIPTION +Clear the implicitly selected board + + +.SH OPTIONS +\fB-h\fP, \fB--help\fP[=false] + help for deselect + + +.SH SEE ALSO +\fBgit-bug-board(1)\fP diff --git a/doc/man/git-bug-board-new.1 b/doc/man/git-bug-board-new.1 new file mode 100644 index 0000000000000000000000000000000000000000..c9fa1242fabc59a2e965c208820e9781c3e835f1 --- /dev/null +++ b/doc/man/git-bug-board-new.1 @@ -0,0 +1,38 @@ +.nh +.TH "GIT-BUG" "1" "Apr 2019" "Generated from git-bug's source code" "" + +.SH NAME +git-bug-board-new - Create a new board + + +.SH SYNOPSIS +\fBgit-bug board new [flags]\fP + + +.SH DESCRIPTION +Create a new board + + +.SH OPTIONS +\fB-t\fP, \fB--title\fP="" + Provide a title to describe the issue + +.PP +\fB-d\fP, \fB--description\fP="" + Provide a message to describe the board + +.PP +\fB-c\fP, \fB--columns\fP=[To Do,In Progress,Done] + Define the columns of the board (default to To Do,In Progress,Done) + +.PP +\fB--non-interactive\fP[=false] + Do not ask for user input + +.PP +\fB-h\fP, \fB--help\fP[=false] + help for new + + +.SH SEE ALSO +\fBgit-bug-board(1)\fP diff --git a/doc/man/git-bug-board-rm.1 b/doc/man/git-bug-board-rm.1 new file mode 100644 index 0000000000000000000000000000000000000000..f8e6ebe5a1c2880c24552578636af2cbbc539669 --- /dev/null +++ b/doc/man/git-bug-board-rm.1 @@ -0,0 +1,22 @@ +.nh +.TH "GIT-BUG" "1" "Apr 2019" "Generated from git-bug's source code" "" + +.SH NAME +git-bug-board-rm - Remove an existing board + + +.SH SYNOPSIS +\fBgit-bug board rm BOARD_ID [flags]\fP + + +.SH DESCRIPTION +Remove an existing board in the local repository. + + +.SH OPTIONS +\fB-h\fP, \fB--help\fP[=false] + help for rm + + +.SH SEE ALSO +\fBgit-bug-board(1)\fP diff --git a/doc/man/git-bug-board-select.1 b/doc/man/git-bug-board-select.1 new file mode 100644 index 0000000000000000000000000000000000000000..ada3c9eb706550589f604c4790fe00659b00274a --- /dev/null +++ b/doc/man/git-bug-board-select.1 @@ -0,0 +1,25 @@ +.nh +.TH "GIT-BUG" "1" "Apr 2019" "Generated from git-bug's source code" "" + +.SH NAME +git-bug-board-select - Select a board for implicit use in future commands + + +.SH SYNOPSIS +\fBgit-bug board select BOARD_ID [flags]\fP + + +.SH DESCRIPTION +Select a board for implicit use in future commands. + +.PP +The complementary command is "git board deselect" performing the opposite operation. + + +.SH OPTIONS +\fB-h\fP, \fB--help\fP[=false] + help for select + + +.SH SEE ALSO +\fBgit-bug-board(1)\fP diff --git a/doc/man/git-bug-board-show.1 b/doc/man/git-bug-board-show.1 new file mode 100644 index 0000000000000000000000000000000000000000..2e069c822bfc8f30f7e91dbbb23cb673d7023b70 --- /dev/null +++ b/doc/man/git-bug-board-show.1 @@ -0,0 +1,26 @@ +.nh +.TH "GIT-BUG" "1" "Apr 2019" "Generated from git-bug's source code" "" + +.SH NAME +git-bug-board-show - Display a board + + +.SH SYNOPSIS +\fBgit-bug board show [BOARD_ID] [flags]\fP + + +.SH DESCRIPTION +Display a board + + +.SH OPTIONS +\fB-f\fP, \fB--format\fP="default" + Select the output formatting style. Valid values are [default,json,org-mode] + +.PP +\fB-h\fP, \fB--help\fP[=false] + help for show + + +.SH SEE ALSO +\fBgit-bug-board(1)\fP diff --git a/doc/man/git-bug-board-title-edit.1 b/doc/man/git-bug-board-title-edit.1 new file mode 100644 index 0000000000000000000000000000000000000000..46b4c13d9139d9bd17742129033be1a047ac54b1 --- /dev/null +++ b/doc/man/git-bug-board-title-edit.1 @@ -0,0 +1,30 @@ +.nh +.TH "GIT-BUG" "1" "Apr 2019" "Generated from git-bug's source code" "" + +.SH NAME +git-bug-board-title-edit - Edit a title of a board + + +.SH SYNOPSIS +\fBgit-bug board title edit [BUG_ID] [flags]\fP + + +.SH DESCRIPTION +Edit a title of a board + + +.SH OPTIONS +\fB-t\fP, \fB--title\fP="" + Provide a title to describe the board + +.PP +\fB--non-interactive\fP[=false] + Do not ask for user input + +.PP +\fB-h\fP, \fB--help\fP[=false] + help for edit + + +.SH SEE ALSO +\fBgit-bug-board-title(1)\fP diff --git a/doc/man/git-bug-board-title.1 b/doc/man/git-bug-board-title.1 new file mode 100644 index 0000000000000000000000000000000000000000..be26bce7cab3feb14bed9dbf90c392bbbf01e19a --- /dev/null +++ b/doc/man/git-bug-board-title.1 @@ -0,0 +1,22 @@ +.nh +.TH "GIT-BUG" "1" "Apr 2019" "Generated from git-bug's source code" "" + +.SH NAME +git-bug-board-title - Display the title of a board + + +.SH SYNOPSIS +\fBgit-bug board title [BOARD_ID] [flags]\fP + + +.SH DESCRIPTION +Display the title of a board + + +.SH OPTIONS +\fB-h\fP, \fB--help\fP[=false] + help for title + + +.SH SEE ALSO +\fBgit-bug-board(1)\fP, \fBgit-bug-board-title-edit(1)\fP diff --git a/doc/man/git-bug-board.1 b/doc/man/git-bug-board.1 new file mode 100644 index 0000000000000000000000000000000000000000..8d68804fd3e568a89ae646e5ecd5be7828fbf439 --- /dev/null +++ b/doc/man/git-bug-board.1 @@ -0,0 +1,38 @@ +.nh +.TH "GIT-BUG" "1" "Apr 2019" "Generated from git-bug's source code" "" + +.SH NAME +git-bug-board - List boards + + +.SH SYNOPSIS +\fBgit-bug board [flags]\fP + + +.SH DESCRIPTION +List boards + + +.SH OPTIONS +\fB-m\fP, \fB--metadata\fP=[] + Filter by metadata. Example: github-url=URL + +.PP +\fB-A\fP, \fB--actor\fP=[] + Filter by actor + +.PP +\fB-t\fP, \fB--title\fP=[] + Filter by title + +.PP +\fB-f\fP, \fB--format\fP="default" + Select the output formatting style. Valid values are [default,plain,compact,id,json,org-mode] + +.PP +\fB-h\fP, \fB--help\fP[=false] + help for board + + +.SH SEE ALSO +\fBgit-bug(1)\fP, \fBgit-bug-board-add-draft(1)\fP, \fBgit-bug-board-description(1)\fP, \fBgit-bug-board-deselect(1)\fP, \fBgit-bug-board-new(1)\fP, \fBgit-bug-board-rm(1)\fP, \fBgit-bug-board-select(1)\fP, \fBgit-bug-board-show(1)\fP, \fBgit-bug-board-title(1)\fP diff --git a/doc/man/git-bug.1 b/doc/man/git-bug.1 index 7320689b472d189d1922d74f984cedc9f4669c50..50af42339b9a5f15e6c56d89071b4ba2803fbf29 100644 --- a/doc/man/git-bug.1 +++ b/doc/man/git-bug.1 @@ -24,4 +24,4 @@ the same git remote you are already using to collaborate with other people. .SH SEE ALSO -\fBgit-bug-bridge(1)\fP, \fBgit-bug-bug(1)\fP, \fBgit-bug-label(1)\fP, \fBgit-bug-pull(1)\fP, \fBgit-bug-push(1)\fP, \fBgit-bug-termui(1)\fP, \fBgit-bug-user(1)\fP, \fBgit-bug-version(1)\fP, \fBgit-bug-webui(1)\fP, \fBgit-bug-wipe(1)\fP +\fBgit-bug-board(1)\fP, \fBgit-bug-bridge(1)\fP, \fBgit-bug-bug(1)\fP, \fBgit-bug-label(1)\fP, \fBgit-bug-pull(1)\fP, \fBgit-bug-push(1)\fP, \fBgit-bug-termui(1)\fP, \fBgit-bug-user(1)\fP, \fBgit-bug-version(1)\fP, \fBgit-bug-webui(1)\fP, \fBgit-bug-wipe(1)\fP diff --git a/doc/md/git-bug.md b/doc/md/git-bug.md index 2ef0b77252bd5f7bc7ca4835a002688a51d871a6..d11ed58961e427ee615a421a6d24ec692cb05fdd 100644 --- a/doc/md/git-bug.md +++ b/doc/md/git-bug.md @@ -24,6 +24,7 @@ git-bug [flags] ### SEE ALSO +* [git-bug board](git-bug_board.md) - List boards * [git-bug bridge](git-bug_bridge.md) - List bridges to other bug trackers * [git-bug bug](git-bug_bug.md) - List bugs * [git-bug label](git-bug_label.md) - List valid labels diff --git a/doc/md/git-bug_board.md b/doc/md/git-bug_board.md new file mode 100644 index 0000000000000000000000000000000000000000..d7d836e8b045d011e7c41a928e37345484cce63d --- /dev/null +++ b/doc/md/git-bug_board.md @@ -0,0 +1,30 @@ +## git-bug board + +List boards + +``` +git-bug board [flags] +``` + +### Options + +``` + -m, --metadata strings Filter by metadata. Example: github-url=URL + -A, --actor strings Filter by actor + -t, --title strings Filter by title + -f, --format string Select the output formatting style. Valid values are [default,plain,compact,id,json,org-mode] (default "default") + -h, --help help for board +``` + +### SEE ALSO + +* [git-bug](git-bug.md) - A bug tracker embedded in Git +* [git-bug board add-draft](git-bug_board_add-draft.md) - Add a draft item to a board +* [git-bug board description](git-bug_board_description.md) - Display the description of a board +* [git-bug board deselect](git-bug_board_deselect.md) - Clear the implicitly selected board +* [git-bug board new](git-bug_board_new.md) - Create a new board +* [git-bug board rm](git-bug_board_rm.md) - Remove an existing board +* [git-bug board select](git-bug_board_select.md) - Select a board for implicit use in future commands +* [git-bug board show](git-bug_board_show.md) - Display a board +* [git-bug board title](git-bug_board_title.md) - Display the title of a board + diff --git a/doc/md/git-bug_board_add-draft.md b/doc/md/git-bug_board_add-draft.md new file mode 100644 index 0000000000000000000000000000000000000000..865fc1c9d2b51bef6e622181736332a8bf1906c3 --- /dev/null +++ b/doc/md/git-bug_board_add-draft.md @@ -0,0 +1,23 @@ +## git-bug board add-draft + +Add a draft item to a board + +``` +git-bug board add-draft [BOARD_ID] [flags] +``` + +### Options + +``` + -t, --title string Provide the title to describe the draft item + -m, --message string Provide the message of the draft item + -F, --file string Take the message from the given file. Use - to read the message from the standard input + -c, --column string The column to add to. Either a column Id or prefix, or the column number starting from 1. (default "1") + --non-interactive Do not ask for user input + -h, --help help for add-draft +``` + +### SEE ALSO + +* [git-bug board](git-bug_board.md) - List boards + diff --git a/doc/md/git-bug_board_description.md b/doc/md/git-bug_board_description.md new file mode 100644 index 0000000000000000000000000000000000000000..b59fcbb0b5252c00a62d8c203511574d551416ac --- /dev/null +++ b/doc/md/git-bug_board_description.md @@ -0,0 +1,19 @@ +## git-bug board description + +Display the description of a board + +``` +git-bug board description [BOARD_ID] [flags] +``` + +### Options + +``` + -h, --help help for description +``` + +### SEE ALSO + +* [git-bug board](git-bug_board.md) - List boards +* [git-bug board description edit](git-bug_board_description_edit.md) - Edit a description of a board + diff --git a/doc/md/git-bug_board_description_edit.md b/doc/md/git-bug_board_description_edit.md new file mode 100644 index 0000000000000000000000000000000000000000..35e8bac7ed44307293161ce6697bd42650624ef6 --- /dev/null +++ b/doc/md/git-bug_board_description_edit.md @@ -0,0 +1,20 @@ +## git-bug board description edit + +Edit a description of a board + +``` +git-bug board description edit [BUG_ID] [flags] +``` + +### Options + +``` + -t, --description string Provide a description for the board + --non-interactive Do not ask for user input + -h, --help help for edit +``` + +### SEE ALSO + +* [git-bug board description](git-bug_board_description.md) - Display the description of a board + diff --git a/doc/md/git-bug_board_deselect.md b/doc/md/git-bug_board_deselect.md new file mode 100644 index 0000000000000000000000000000000000000000..52c54fe473409ef26527ddf4d7c4de9513324a86 --- /dev/null +++ b/doc/md/git-bug_board_deselect.md @@ -0,0 +1,18 @@ +## git-bug board deselect + +Clear the implicitly selected board + +``` +git-bug board deselect [flags] +``` + +### Options + +``` + -h, --help help for deselect +``` + +### SEE ALSO + +* [git-bug board](git-bug_board.md) - List boards + diff --git a/doc/md/git-bug_board_new.md b/doc/md/git-bug_board_new.md new file mode 100644 index 0000000000000000000000000000000000000000..d4511127bfb32a5273e571aa8f82145007f86536 --- /dev/null +++ b/doc/md/git-bug_board_new.md @@ -0,0 +1,22 @@ +## git-bug board new + +Create a new board + +``` +git-bug board new [flags] +``` + +### Options + +``` + -t, --title string Provide a title to describe the issue + -d, --description string Provide a message to describe the board + -c, --columns stringArray Define the columns of the board (default to To Do,In Progress,Done) (default [To Do,In Progress,Done]) + --non-interactive Do not ask for user input + -h, --help help for new +``` + +### SEE ALSO + +* [git-bug board](git-bug_board.md) - List boards + diff --git a/doc/md/git-bug_board_rm.md b/doc/md/git-bug_board_rm.md new file mode 100644 index 0000000000000000000000000000000000000000..3055757d1d453f41a599b6ed11f1f71e51c0451c --- /dev/null +++ b/doc/md/git-bug_board_rm.md @@ -0,0 +1,22 @@ +## git-bug board rm + +Remove an existing board + +### Synopsis + +Remove an existing board in the local repository. + +``` +git-bug board rm BOARD_ID [flags] +``` + +### Options + +``` + -h, --help help for rm +``` + +### SEE ALSO + +* [git-bug board](git-bug_board.md) - List boards + diff --git a/doc/md/git-bug_board_select.md b/doc/md/git-bug_board_select.md new file mode 100644 index 0000000000000000000000000000000000000000..cfc64999b15751dcd9b158f47101734760444edc --- /dev/null +++ b/doc/md/git-bug_board_select.md @@ -0,0 +1,25 @@ +## git-bug board select + +Select a board for implicit use in future commands + +### Synopsis + +Select a board for implicit use in future commands. + +The complementary command is "git board deselect" performing the opposite operation. + + +``` +git-bug board select BOARD_ID [flags] +``` + +### Options + +``` + -h, --help help for select +``` + +### SEE ALSO + +* [git-bug board](git-bug_board.md) - List boards + diff --git a/doc/md/git-bug_board_show.md b/doc/md/git-bug_board_show.md new file mode 100644 index 0000000000000000000000000000000000000000..a5420bd797c3185eddd25bcdfd7a93eacb802446 --- /dev/null +++ b/doc/md/git-bug_board_show.md @@ -0,0 +1,19 @@ +## git-bug board show + +Display a board + +``` +git-bug board show [BOARD_ID] [flags] +``` + +### Options + +``` + -f, --format string Select the output formatting style. Valid values are [default,json,org-mode] (default "default") + -h, --help help for show +``` + +### SEE ALSO + +* [git-bug board](git-bug_board.md) - List boards + diff --git a/doc/md/git-bug_board_title.md b/doc/md/git-bug_board_title.md new file mode 100644 index 0000000000000000000000000000000000000000..afeedb1e4320a6c1d9d21fd5306f8eb71438f278 --- /dev/null +++ b/doc/md/git-bug_board_title.md @@ -0,0 +1,19 @@ +## git-bug board title + +Display the title of a board + +``` +git-bug board title [BOARD_ID] [flags] +``` + +### Options + +``` + -h, --help help for title +``` + +### SEE ALSO + +* [git-bug board](git-bug_board.md) - List boards +* [git-bug board title edit](git-bug_board_title_edit.md) - Edit a title of a board + diff --git a/doc/md/git-bug_board_title_edit.md b/doc/md/git-bug_board_title_edit.md new file mode 100644 index 0000000000000000000000000000000000000000..d5a346a6018750a6c923d99fee246c77d5dbf6ec --- /dev/null +++ b/doc/md/git-bug_board_title_edit.md @@ -0,0 +1,20 @@ +## git-bug board title edit + +Edit a title of a board + +``` +git-bug board title edit [BUG_ID] [flags] +``` + +### Options + +``` + -t, --title string Provide a title to describe the board + --non-interactive Do not ask for user input + -h, --help help for edit +``` + +### SEE ALSO + +* [git-bug board title](git-bug_board_title.md) - Display the title of a board + diff --git a/entities/board/board.go b/entities/board/board.go index 2b523fb7f629bcb0ede9ac1074d86d08d0e9131e..ecc7ff20ad7a0a29d4e9358459559e1b4ded00dc 100644 --- a/entities/board/board.go +++ b/entities/board/board.go @@ -3,12 +3,12 @@ package board import ( "fmt" - "github.com/MichaelMure/git-bug/entities/bug" - "github.com/MichaelMure/git-bug/entities/identity" + "github.com/git-bug/git-bug/entities/bug" + "github.com/git-bug/git-bug/entities/identity" - "github.com/MichaelMure/git-bug/entity" - "github.com/MichaelMure/git-bug/entity/dag" - "github.com/MichaelMure/git-bug/repository" + "github.com/git-bug/git-bug/entity" + "github.com/git-bug/git-bug/entity/dag" + "github.com/git-bug/git-bug/repository" ) var _ Interface = &Board{} diff --git a/entities/board/board_actions.go b/entities/board/board_actions.go index e1d9d37485f2fc805031ad2587c7daeacf5137bd..65feda5a9b2c75e6b08952a0c5f571202b877a5f 100644 --- a/entities/board/board_actions.go +++ b/entities/board/board_actions.go @@ -1,10 +1,10 @@ package board import ( - "github.com/MichaelMure/git-bug/entities/identity" - "github.com/MichaelMure/git-bug/entity" - "github.com/MichaelMure/git-bug/entity/dag" - "github.com/MichaelMure/git-bug/repository" + "github.com/git-bug/git-bug/entities/identity" + "github.com/git-bug/git-bug/entity" + "github.com/git-bug/git-bug/entity/dag" + "github.com/git-bug/git-bug/repository" ) // Fetch retrieve updates from a remote diff --git a/entities/board/item_draft.go b/entities/board/item_draft.go index 1ebd521e26b6af72364b32d27fd6c83890bb82ec..30171afd8d74caacb217e9d89284ea419c9daff8 100644 --- a/entities/board/item_draft.go +++ b/entities/board/item_draft.go @@ -1,12 +1,12 @@ package board import ( - "github.com/MichaelMure/git-bug/entities/identity" - "github.com/MichaelMure/git-bug/entity" "github.com/dustin/go-humanize" - "github.com/MichaelMure/git-bug/entities/common" - "github.com/MichaelMure/git-bug/util/timestamp" + "github.com/git-bug/git-bug/entities/identity" + "github.com/git-bug/git-bug/entity" + + "github.com/git-bug/git-bug/util/timestamp" ) var _ Item = &Draft{} @@ -16,10 +16,9 @@ type Draft struct { // of the Operation that created the Draft combinedId entity.CombinedId - author identity.Interface - status common.Status - title string - message string + Author identity.Interface + Title string + Message string // Creation time of the comment. // Should be used only for human display, never for ordering as we can't rely on it in a distributed system. @@ -34,11 +33,6 @@ func (d *Draft) CombinedId() entity.CombinedId { return d.combinedId } -func (d *Draft) Status() common.Status { - // TODO implement me - panic("implement me") -} - // FormatTimeRel format the UnixTime of the comment for human consumption func (d *Draft) FormatTimeRel() string { return humanize.Time(d.unixTime.Time()) diff --git a/entities/board/item_entity.go b/entities/board/item_entity.go index d7457027e0f1defb2239259594191c0e7d8ffee9..497f6869c0ff39e03560e03e74bb5395ead59e1c 100644 --- a/entities/board/item_entity.go +++ b/entities/board/item_entity.go @@ -1,15 +1,15 @@ package board import ( - "github.com/MichaelMure/git-bug/entities/bug" - "github.com/MichaelMure/git-bug/entity" + "github.com/git-bug/git-bug/entities/bug" + "github.com/git-bug/git-bug/entity" ) var _ Item = &BugItem{} type BugItem struct { combinedId entity.CombinedId - bug bug.Interface + Bug bug.Interface } func (e *BugItem) CombinedId() entity.CombinedId { diff --git a/entities/board/op_add_item_draft.go b/entities/board/op_add_item_draft.go index 051144084de3d731bfd3f0fb22685339e327ef73..fe51c689e797d7e74be6532ac13a5c3a092ab05b 100644 --- a/entities/board/op_add_item_draft.go +++ b/entities/board/op_add_item_draft.go @@ -3,13 +3,12 @@ package board import ( "fmt" - "github.com/MichaelMure/git-bug/entities/common" - "github.com/MichaelMure/git-bug/entities/identity" - "github.com/MichaelMure/git-bug/entity" - "github.com/MichaelMure/git-bug/entity/dag" - "github.com/MichaelMure/git-bug/repository" - "github.com/MichaelMure/git-bug/util/text" - "github.com/MichaelMure/git-bug/util/timestamp" + "github.com/git-bug/git-bug/entities/identity" + "github.com/git-bug/git-bug/entity" + "github.com/git-bug/git-bug/entity/dag" + "github.com/git-bug/git-bug/repository" + "github.com/git-bug/git-bug/util/text" + "github.com/git-bug/git-bug/util/timestamp" ) var _ Operation = &AddItemDraftOperation{} @@ -60,16 +59,15 @@ func (op *AddItemDraftOperation) Validate() error { } func (op *AddItemDraftOperation) Apply(snapshot *Snapshot) { - snapshot.addActor(op.Author()) + snapshot.addParticipant(op.Author()) for _, column := range snapshot.Columns { if column.Id == op.ColumnId { column.Items = append(column.Items, &Draft{ combinedId: entity.CombineIds(snapshot.id, op.Id()), - author: op.Author(), - status: common.OpenStatus, - title: op.Title, - message: op.Message, + Author: op.Author(), + Title: op.Title, + Message: op.Message, unixTime: timestamp.Timestamp(op.UnixTime), }) return diff --git a/entities/board/op_add_item_draft_test.go b/entities/board/op_add_item_draft_test.go index 3faaea86ef7d51105a65f1abd9b02debf6a076a0..7721e2131f61233feac1b240716fee13949c29ce 100644 --- a/entities/board/op_add_item_draft_test.go +++ b/entities/board/op_add_item_draft_test.go @@ -3,10 +3,10 @@ package board import ( "testing" - "github.com/MichaelMure/git-bug/entities/identity" - "github.com/MichaelMure/git-bug/entity" - "github.com/MichaelMure/git-bug/entity/dag" - "github.com/MichaelMure/git-bug/repository" + "github.com/git-bug/git-bug/entities/identity" + "github.com/git-bug/git-bug/entity" + "github.com/git-bug/git-bug/entity/dag" + "github.com/git-bug/git-bug/repository" ) func TestAddItemDraftOpSerialize(t *testing.T) { diff --git a/entities/board/op_add_item_entity.go b/entities/board/op_add_item_entity.go index 5161483d8540a6b9589a99476857882268427b09..fa4f54595c1e21f1ef7cccc634e61fd827afa69f 100644 --- a/entities/board/op_add_item_entity.go +++ b/entities/board/op_add_item_entity.go @@ -3,10 +3,10 @@ package board import ( "fmt" - "github.com/MichaelMure/git-bug/entities/bug" - "github.com/MichaelMure/git-bug/entities/identity" - "github.com/MichaelMure/git-bug/entity" - "github.com/MichaelMure/git-bug/entity/dag" + "github.com/git-bug/git-bug/entities/bug" + "github.com/git-bug/git-bug/entities/identity" + "github.com/git-bug/git-bug/entity" + "github.com/git-bug/git-bug/entity/dag" ) // itemEntityType indicate the type of entity board item @@ -57,7 +57,7 @@ func (op *AddItemEntityOperation) Apply(snapshot *Snapshot) { return } - snapshot.addActor(op.Author()) + snapshot.addParticipant(op.Author()) for _, column := range snapshot.Columns { if column.Id == op.ColumnId { @@ -65,7 +65,7 @@ func (op *AddItemEntityOperation) Apply(snapshot *Snapshot) { case bug.Interface: column.Items = append(column.Items, &BugItem{ combinedId: entity.CombineIds(snapshot.Id(), e.Id()), - bug: e, + Bug: e, }) } return diff --git a/entities/board/op_add_item_entity_test.go b/entities/board/op_add_item_entity_test.go index 307a7b2fe9e6c983996301d2b2331fa0b8ef9e57..248ee7836f57873daa456c4a785175431579c76d 100644 --- a/entities/board/op_add_item_entity_test.go +++ b/entities/board/op_add_item_entity_test.go @@ -5,10 +5,10 @@ import ( "github.com/stretchr/testify/require" - "github.com/MichaelMure/git-bug/entities/bug" - "github.com/MichaelMure/git-bug/entities/identity" - "github.com/MichaelMure/git-bug/entity" - "github.com/MichaelMure/git-bug/entity/dag" + "github.com/git-bug/git-bug/entities/bug" + "github.com/git-bug/git-bug/entities/identity" + "github.com/git-bug/git-bug/entity" + "github.com/git-bug/git-bug/entity/dag" ) func TestAddItemEntityOpSerialize(t *testing.T) { diff --git a/entities/board/op_create.go b/entities/board/op_create.go index fe7a11541d6745060cf0c660c15ba9dfe61d6936..e401ed0bb20a3a5ce8bf4c9e0bcbd9ad67731a6a 100644 --- a/entities/board/op_create.go +++ b/entities/board/op_create.go @@ -3,11 +3,11 @@ package board import ( "fmt" - "github.com/MichaelMure/git-bug/entities/identity" + "github.com/git-bug/git-bug/entities/identity" - "github.com/MichaelMure/git-bug/entity" - "github.com/MichaelMure/git-bug/entity/dag" - "github.com/MichaelMure/git-bug/util/text" + "github.com/git-bug/git-bug/entity" + "github.com/git-bug/git-bug/entity/dag" + "github.com/git-bug/git-bug/util/text" ) var DefaultColumns = []string{"To Do", "In Progress", "Done"} @@ -96,7 +96,7 @@ func (op *CreateOperation) Apply(snap *Snapshot) { }) } - snap.addActor(op.Author()) + snap.addParticipant(op.Author()) } // CreateDefaultColumns is a convenience function to create a board with the default columns diff --git a/entities/board/op_create_test.go b/entities/board/op_create_test.go index 943fd1fbb491044a1ed36b93d403eaad37b7f6b9..3a385fcbf0c9947ae1a934bc18a5d949499970c7 100644 --- a/entities/board/op_create_test.go +++ b/entities/board/op_create_test.go @@ -6,10 +6,10 @@ import ( "github.com/stretchr/testify/require" - "github.com/MichaelMure/git-bug/entities/identity" - "github.com/MichaelMure/git-bug/entity" - "github.com/MichaelMure/git-bug/entity/dag" - "github.com/MichaelMure/git-bug/repository" + "github.com/git-bug/git-bug/entities/identity" + "github.com/git-bug/git-bug/entity" + "github.com/git-bug/git-bug/entity/dag" + "github.com/git-bug/git-bug/repository" ) func TestCreate(t *testing.T) { diff --git a/entities/board/op_set_description.go b/entities/board/op_set_description.go index fa48e3c929586a5076900dd21aa0a79b07848798..7483de811e6102401fe48cdc962cb857c5234f84 100644 --- a/entities/board/op_set_description.go +++ b/entities/board/op_set_description.go @@ -3,10 +3,10 @@ package board import ( "fmt" - "github.com/MichaelMure/git-bug/entities/identity" - "github.com/MichaelMure/git-bug/entity" - "github.com/MichaelMure/git-bug/entity/dag" - "github.com/MichaelMure/git-bug/util/text" + "github.com/git-bug/git-bug/entities/identity" + "github.com/git-bug/git-bug/entity" + "github.com/git-bug/git-bug/entity/dag" + "github.com/git-bug/git-bug/util/text" ) var _ Operation = &SetDescriptionOperation{} @@ -44,7 +44,7 @@ func (op *SetDescriptionOperation) Validate() error { func (op *SetDescriptionOperation) Apply(snapshot *Snapshot) { snapshot.Description = op.Description - snapshot.addActor(op.Author()) + snapshot.addParticipant(op.Author()) } func NewSetDescriptionOp(author identity.Interface, unixTime int64, description string, was string) *SetDescriptionOperation { diff --git a/entities/board/op_set_description_test.go b/entities/board/op_set_description_test.go index 271b1f2962adf1bd392a58e0ddc3b1f9185b212d..be118aa03f73454883e60c7ec401913776f4efef 100644 --- a/entities/board/op_set_description_test.go +++ b/entities/board/op_set_description_test.go @@ -3,9 +3,9 @@ package board import ( "testing" - "github.com/MichaelMure/git-bug/entities/identity" - "github.com/MichaelMure/git-bug/entity" - "github.com/MichaelMure/git-bug/entity/dag" + "github.com/git-bug/git-bug/entities/identity" + "github.com/git-bug/git-bug/entity" + "github.com/git-bug/git-bug/entity/dag" ) func TestSetDescriptionSerialize(t *testing.T) { diff --git a/entities/board/op_set_title.go b/entities/board/op_set_title.go index c5fb5f4d8a73a23b83ad2492de2b9a4133c096ff..1e03f8c2000c960237060ea4fdeeb8bb57017009 100644 --- a/entities/board/op_set_title.go +++ b/entities/board/op_set_title.go @@ -3,10 +3,10 @@ package board import ( "fmt" - "github.com/MichaelMure/git-bug/entities/identity" - "github.com/MichaelMure/git-bug/entity" - "github.com/MichaelMure/git-bug/entity/dag" - "github.com/MichaelMure/git-bug/util/text" + "github.com/git-bug/git-bug/entities/identity" + "github.com/git-bug/git-bug/entity" + "github.com/git-bug/git-bug/entity/dag" + "github.com/git-bug/git-bug/util/text" ) var _ Operation = &SetTitleOperation{} @@ -44,7 +44,7 @@ func (op *SetTitleOperation) Validate() error { func (op *SetTitleOperation) Apply(snapshot *Snapshot) { snapshot.Title = op.Title - snapshot.addActor(op.Author()) + snapshot.addParticipant(op.Author()) } func NewSetTitleOp(author identity.Interface, unixTime int64, title string, was string) *SetTitleOperation { diff --git a/entities/board/op_set_title_test.go b/entities/board/op_set_title_test.go index 4cce61d1d9f463602097361261fa743619a0ad02..e56722a42276b9907bab8abc872db6b446e283d3 100644 --- a/entities/board/op_set_title_test.go +++ b/entities/board/op_set_title_test.go @@ -3,9 +3,9 @@ package board import ( "testing" - "github.com/MichaelMure/git-bug/entities/identity" - "github.com/MichaelMure/git-bug/entity" - "github.com/MichaelMure/git-bug/entity/dag" + "github.com/git-bug/git-bug/entities/identity" + "github.com/git-bug/git-bug/entity" + "github.com/git-bug/git-bug/entity/dag" ) func TestSetTitleSerialize(t *testing.T) { diff --git a/entities/board/operation.go b/entities/board/operation.go index 66cdc3789deb0cb3ba7373a511a338aeb55d57bd..aa7ffc4629a338e5bc96384753dcc109615bbad1 100644 --- a/entities/board/operation.go +++ b/entities/board/operation.go @@ -4,9 +4,9 @@ import ( "encoding/json" "fmt" - "github.com/MichaelMure/git-bug/entities/bug" - "github.com/MichaelMure/git-bug/entity" - "github.com/MichaelMure/git-bug/entity/dag" + "github.com/git-bug/git-bug/entities/bug" + "github.com/git-bug/git-bug/entity" + "github.com/git-bug/git-bug/entity/dag" ) // OperationType is an operation type identifier diff --git a/entities/board/snapshot.go b/entities/board/snapshot.go index 99c61887d09ecd965ff4ba9872ecc4955ab85161..d895fca36c77036b6de32dc56c206aff2880915a 100644 --- a/entities/board/snapshot.go +++ b/entities/board/snapshot.go @@ -3,9 +3,9 @@ package board import ( "time" - "github.com/MichaelMure/git-bug/entities/identity" - "github.com/MichaelMure/git-bug/entity" - "github.com/MichaelMure/git-bug/entity/dag" + "github.com/git-bug/git-bug/entities/identity" + "github.com/git-bug/git-bug/entity" + "github.com/git-bug/git-bug/entity/dag" ) type Column struct { @@ -16,6 +16,7 @@ type Column struct { type Item interface { CombinedId() entity.CombinedId + // TODO: all items have status? // Status() common.Status } @@ -24,10 +25,10 @@ var _ dag.Snapshot = &Snapshot{} type Snapshot struct { id entity.Id - Title string - Description string - Columns []*Column - Actors []identity.Interface + Title string + Description string + Columns []*Column + Participants []identity.Interface CreateTime time.Time Operations []dag.Operation @@ -59,20 +60,20 @@ func (snap *Snapshot) EditTime() time.Time { return snap.Operations[len(snap.Operations)-1].Time() } -// append the operation author to the actors list -func (snap *Snapshot) addActor(actor identity.Interface) { - for _, a := range snap.Actors { - if actor.Id() == a.Id() { +// append the operation author to the participants list +func (snap *Snapshot) addParticipant(participant identity.Interface) { + for _, p := range snap.Participants { + if participant.Id() == p.Id() { return } } - snap.Actors = append(snap.Actors, actor) + snap.Participants = append(snap.Participants, participant) } -// HasActor return true if the id is a actor -func (snap *Snapshot) HasActor(id entity.Id) bool { - for _, p := range snap.Actors { +// HasParticipant return true if the id is a participant +func (snap *Snapshot) HasParticipant(id entity.Id) bool { + for _, p := range snap.Participants { if p.Id() == id { return true } @@ -80,10 +81,10 @@ func (snap *Snapshot) HasActor(id entity.Id) bool { return false } -// HasAnyActor return true if one of the ids is a actor -func (snap *Snapshot) HasAnyActor(ids ...entity.Id) bool { +// HasAnyParticipant return true if one of the ids is a participant +func (snap *Snapshot) HasAnyParticipant(ids ...entity.Id) bool { for _, id := range ids { - if snap.HasActor(id) { + if snap.HasParticipant(id) { return true } }