Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 47 additions & 1 deletion commands/bug/bug_show.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (

"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"
Expand All @@ -23,7 +24,7 @@ func newBugShowCommand(env *execenv.Env) *cobra.Command {
options := bugShowOptions{}

cmd := &cobra.Command{
Use: "show [BUG_ID]",
Use: "show [BUG_ID...]",
Short: "Display the details of a bug",
PreRunE: execenv.LoadBackend(env),
RunE: execenv.CloseBackend(env, func(cmd *cobra.Command, args []string) error {
Expand All @@ -47,6 +48,31 @@ func newBugShowCommand(env *execenv.Env) *cobra.Command {
}

func runBugShow(env *execenv.Env, opts bugShowOptions, args []string) error {
if len(args) > 1 {
if opts.fields != "" {
return errors.New("multiple bug ids are not supported with --field")
}
if opts.format != "json" {
return errors.New("multiple bug ids are only supported with --format=json")
}

bugs, err := resolveExplicitBugs(env, args)
if err != nil {
return err
}

snaps := make([]*bug.Snapshot, len(bugs))
for i, b := range bugs {
snap := b.Snapshot()
if len(snap.Comments) == 0 {
return errors.New("invalid bug: no comment")
}
snaps[i] = snap
}

return showJsonMultiFormatter(env, snaps)
}

b, _, err := ResolveSelected(env.Backend, args)
if err != nil {
return err
Expand Down Expand Up @@ -109,6 +135,18 @@ func runBugShow(env *execenv.Env, opts bugShowOptions, args []string) error {
}
}

func resolveExplicitBugs(env *execenv.Env, args []string) ([]*cache.BugCache, error) {
bugs := make([]*cache.BugCache, len(args))
for i, arg := range args {
b, err := env.Backend.Bugs().ResolvePrefix(arg)
if err != nil {
return nil, err
}
bugs[i] = b
}
return bugs, nil
}

func showDefaultFormatter(env *execenv.Env, snapshot *bug.Snapshot) error {
// Header
env.Out.Printf("%s [%s] %s\n\n",
Expand Down Expand Up @@ -189,6 +227,14 @@ func showJsonFormatter(env *execenv.Env, snap *bug.Snapshot) error {
return env.Out.PrintJSON(jsonBug)
}

func showJsonMultiFormatter(env *execenv.Env, snaps []*bug.Snapshot) error {
jsonBugs := make([]cmdjson.BugSnapshot, len(snaps))
for i, snap := range snaps {
jsonBugs[i] = cmdjson.NewBugSnapshot(snap)
}
return env.Out.PrintJSON(jsonBugs)
}

func showOrgModeFormatter(env *execenv.Env, snapshot *bug.Snapshot) error {
// Header
env.Out.Printf("%s [%s] %s\n",
Expand Down
160 changes: 160 additions & 0 deletions commands/bug/bug_show_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
package bugcmd

import (
"encoding/json"
"fmt"
"strings"
"testing"

"github.com/stretchr/testify/require"

"github.com/git-bug/git-bug/commands/bug/testenv"
)

func TestBugShowJsonSingleIDRemainsObject(t *testing.T) {
env, bugID := testenv.NewTestEnvAndBug(t)

opts := bugShowOptions{format: "json"}
require.NoError(t, runBugShow(env, opts, []string{bugID.Human()}))

require.True(t, strings.HasPrefix(env.Out.String(), "{"))

var got map[string]any
require.NoError(t, json.Unmarshal(env.Out.Bytes(), &got))
require.Equal(t, bugID.String(), got["id"])
}

func TestBugShowJsonMultipleIDsIsArrayInArgumentOrder(t *testing.T) {
env, b1ID := testenv.NewTestEnvAndBug(t)
b2, _, err := env.Backend.Bugs().New("second bug title", "second bug body")
require.NoError(t, err)

opts := bugShowOptions{format: "json"}
require.NoError(t, runBugShow(env, opts, []string{b2.Id().Human(), b1ID.Human()}))

var got []map[string]any
require.NoError(t, json.Unmarshal(env.Out.Bytes(), &got))
require.Len(t, got, 2)
require.Equal(t, b2.Id().Human(), got[0]["human_id"])
require.Equal(t, b1ID.Human(), got[1]["human_id"])
}

func TestBugShowMultipleIDsRejectDefaultFormat(t *testing.T) {
env, b1ID := testenv.NewTestEnvAndBug(t)
b2, _, err := env.Backend.Bugs().New("second bug title", "second bug body")
require.NoError(t, err)

opts := bugShowOptions{format: "default"}
err = runBugShow(env, opts, []string{b1ID.Human(), b2.Id().Human()})
require.EqualError(t, err, "multiple bug ids are only supported with --format=json")
require.Empty(t, env.Out.String())
}

func TestBugShowMultipleIDsRejectFieldOutput(t *testing.T) {
env, b1ID := testenv.NewTestEnvAndBug(t)
b2, _, err := env.Backend.Bugs().New("second bug title", "second bug body")
require.NoError(t, err)

opts := bugShowOptions{format: "json", fields: "labels"}
err = runBugShow(env, opts, []string{b1ID.Human(), b2.Id().Human()})
require.EqualError(t, err, "multiple bug ids are not supported with --field")
require.Empty(t, env.Out.String())
}

func TestBugShowJsonUnknownSecondIDPrintsNoPartialJSON(t *testing.T) {
env, b1ID := testenv.NewTestEnvAndBug(t)

opts := bugShowOptions{format: "json"}
err := runBugShow(env, opts, []string{b1ID.Human(), "deadbee"})
require.Error(t, err)
require.Empty(t, env.Out.String())
}

func TestBugShowJsonMultipleIDsMatchSingleIDRepresentations(t *testing.T) {
env, b1ID := testenv.NewTestEnvAndBug(t)
b2, _, err := env.Backend.Bugs().New("second bug title", "second bug body")
require.NoError(t, err)

opts := bugShowOptions{format: "json"}
require.NoError(t, runBugShow(env, opts, []string{b1ID.Human()}))
var single map[string]any
require.NoError(t, json.Unmarshal(env.Out.Bytes(), &single))
env.Out.Reset()

require.NoError(t, runBugShow(env, opts, []string{b1ID.Human(), b2.Id().Human()}))
var batch []map[string]any
require.NoError(t, json.Unmarshal(env.Out.Bytes(), &batch))
require.Equal(t, single, batch[0])
}

func TestBugShowJsonThreeIDsPreserveArgumentOrder(t *testing.T) {
env, b1ID := testenv.NewTestEnvAndBug(t)
b2, _, err := env.Backend.Bugs().New("second bug title", "second bug body")
require.NoError(t, err)
b3, _, err := env.Backend.Bugs().New("third bug title", "third bug body")
require.NoError(t, err)

opts := bugShowOptions{format: "json"}
require.NoError(t, runBugShow(env, opts, []string{
b3.Id().Human(), b1ID.Human(), b2.Id().Human(),
}))

var got []map[string]any
require.NoError(t, json.Unmarshal(env.Out.Bytes(), &got))
require.Equal(t, []any{
b3.Id().Human(), b1ID.Human(), b2.Id().Human(),
}, []any{got[0]["human_id"], got[1]["human_id"], got[2]["human_id"]})
}

func TestBugShowJsonAmbiguousIDPrintsNoPartialJSON(t *testing.T) {
env, b1ID := testenv.NewTestEnvAndBug(t)
prefixes := map[string]struct{}{b1ID.Human()[:1]: {}}
ambiguousPrefix := ""
for i := 0; i < 256 && ambiguousPrefix == ""; i++ {
b, _, err := env.Backend.Bugs().New(
fmt.Sprintf("collision candidate %d", i),
"collision candidate body",
)
require.NoError(t, err)

prefix := b.Id().Human()[:1]
if _, exists := prefixes[prefix]; exists {
ambiguousPrefix = prefix
} else {
prefixes[prefix] = struct{}{}
}
}
require.NotEmpty(t, ambiguousPrefix)

opts := bugShowOptions{format: "json"}
err := runBugShow(env, opts, []string{b1ID.Human(), ambiguousPrefix})
require.Error(t, err)
require.Empty(t, env.Out.String())
}

func TestBugShowJsonDuplicateIDsRemainDuplicated(t *testing.T) {
env, bugID := testenv.NewTestEnvAndBug(t)

opts := bugShowOptions{format: "json"}
require.NoError(t, runBugShow(env, opts, []string{bugID.Human(), bugID.Human()}))

var got []map[string]any
require.NoError(t, json.Unmarshal(env.Out.Bytes(), &got))
require.Len(t, got, 2)
require.Equal(t, got[0], got[1])
}

func TestBugShowCommandAcceptsMultipleIDs(t *testing.T) {
env, b1ID := testenv.NewTestEnvAndBug(t)
b2, _, err := env.Backend.Bugs().New("second bug title", "second bug body")
require.NoError(t, err)

cmd := newBugShowCommand(env)
cmd.PreRunE = nil
cmd.SetArgs([]string{"--format=json", b1ID.Human(), b2.Id().Human()})
require.NoError(t, cmd.Execute())

var got []map[string]any
require.NoError(t, json.Unmarshal(env.Out.Bytes(), &got))
require.Len(t, got, 2)
}
2 changes: 1 addition & 1 deletion doc/man/git-bug-bug-show.1
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ git-bug-bug-show - Display the details of a bug


.SH SYNOPSIS
\fBgit-bug bug show [BUG_ID] [flags]\fP
\fBgit-bug bug show [BUG_ID...] [flags]\fP


.SH DESCRIPTION
Expand Down
2 changes: 1 addition & 1 deletion doc/md/git-bug_bug_show.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
Display the details of a bug

```
git-bug bug show [BUG_ID] [flags]
git-bug bug show [BUG_ID...] [flags]
```

### Options
Expand Down