feat(show): add --id flag for IDs that look like flags
When an issue ID starts with dashes (e.g., gt--kzx), it can be misinterpreted by Cobra's argument parser. The new --id flag allows these IDs to be passed safely: bd show --id=gt--xyz Multiple --id flags can be used, and they can be combined with positional arguments. Closes: bd-ix0ak Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
committed by
Steve Yegge
parent
03400fbdbc
commit
a9c8c952f6
@@ -16,19 +16,29 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var showCmd = &cobra.Command{
|
var showCmd = &cobra.Command{
|
||||||
Use: "show [id...]",
|
Use: "show [id...] [--id=<id>...]",
|
||||||
Aliases: []string{"view"},
|
Aliases: []string{"view"},
|
||||||
GroupID: "issues",
|
GroupID: "issues",
|
||||||
Short: "Show issue details",
|
Short: "Show issue details",
|
||||||
Args: cobra.MinimumNArgs(1),
|
Args: cobra.ArbitraryArgs, // Allow zero positional args when --id is used
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
showThread, _ := cmd.Flags().GetBool("thread")
|
showThread, _ := cmd.Flags().GetBool("thread")
|
||||||
shortMode, _ := cmd.Flags().GetBool("short")
|
shortMode, _ := cmd.Flags().GetBool("short")
|
||||||
showRefs, _ := cmd.Flags().GetBool("refs")
|
showRefs, _ := cmd.Flags().GetBool("refs")
|
||||||
showChildren, _ := cmd.Flags().GetBool("children")
|
showChildren, _ := cmd.Flags().GetBool("children")
|
||||||
asOfRef, _ := cmd.Flags().GetString("as-of")
|
asOfRef, _ := cmd.Flags().GetString("as-of")
|
||||||
|
idFlags, _ := cmd.Flags().GetStringArray("id")
|
||||||
ctx := rootCtx
|
ctx := rootCtx
|
||||||
|
|
||||||
|
// Merge --id flag values with positional args
|
||||||
|
// This allows IDs that look like flags (e.g., --xyz or gt--abc) to be passed safely
|
||||||
|
args = append(args, idFlags...)
|
||||||
|
|
||||||
|
// Validate that at least one ID is provided
|
||||||
|
if len(args) == 0 {
|
||||||
|
FatalErrorRespectJSON("at least one issue ID is required (use positional args or --id flag)")
|
||||||
|
}
|
||||||
|
|
||||||
// Handle --as-of flag: show issue at a specific point in history
|
// Handle --as-of flag: show issue at a specific point in history
|
||||||
if asOfRef != "" {
|
if asOfRef != "" {
|
||||||
showIssueAsOf(ctx, args, asOfRef, shortMode)
|
showIssueAsOf(ctx, args, asOfRef, shortMode)
|
||||||
@@ -1103,6 +1113,7 @@ func init() {
|
|||||||
showCmd.Flags().Bool("refs", false, "Show issues that reference this issue (reverse lookup)")
|
showCmd.Flags().Bool("refs", false, "Show issues that reference this issue (reverse lookup)")
|
||||||
showCmd.Flags().Bool("children", false, "Show only the children of this issue")
|
showCmd.Flags().Bool("children", false, "Show only the children of this issue")
|
||||||
showCmd.Flags().String("as-of", "", "Show issue as it existed at a specific commit hash or branch (requires Dolt)")
|
showCmd.Flags().String("as-of", "", "Show issue as it existed at a specific commit hash or branch (requires Dolt)")
|
||||||
|
showCmd.Flags().StringArray("id", nil, "Issue ID (use for IDs that look like flags, e.g., --id=gt--xyz)")
|
||||||
showCmd.ValidArgsFunction = issueIDCompletion
|
showCmd.ValidArgsFunction = issueIDCompletion
|
||||||
rootCmd.AddCommand(showCmd)
|
rootCmd.AddCommand(showCmd)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -114,3 +114,83 @@ func TestShow_NoExternalRef(t *testing.T) {
|
|||||||
t.Errorf("expected no 'External:' line for issue without external ref, got: %s", out)
|
t.Errorf("expected no 'External:' line for issue without external ref, got: %s", out)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestShow_IDFlag(t *testing.T) {
|
||||||
|
if testing.Short() {
|
||||||
|
t.Skip("skipping CLI test in short mode")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build bd binary
|
||||||
|
tmpBin := filepath.Join(t.TempDir(), "bd")
|
||||||
|
buildCmd := exec.Command("go", "build", "-o", tmpBin, "./")
|
||||||
|
buildCmd.Dir = "."
|
||||||
|
if out, err := buildCmd.CombinedOutput(); err != nil {
|
||||||
|
t.Fatalf("failed to build bd: %v\n%s", err, out)
|
||||||
|
}
|
||||||
|
|
||||||
|
tmpDir := t.TempDir()
|
||||||
|
|
||||||
|
// Initialize beads
|
||||||
|
initCmd := exec.Command(tmpBin, "init", "--prefix", "test", "--quiet")
|
||||||
|
initCmd.Dir = tmpDir
|
||||||
|
if out, err := initCmd.CombinedOutput(); err != nil {
|
||||||
|
t.Fatalf("init failed: %v\n%s", err, out)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create an issue
|
||||||
|
createCmd := exec.Command(tmpBin, "--no-daemon", "create", "ID flag test", "-p", "1", "--json", "--repo", ".")
|
||||||
|
createCmd.Dir = tmpDir
|
||||||
|
createOut, err := createCmd.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("create failed: %v\n%s", err, createOut)
|
||||||
|
}
|
||||||
|
|
||||||
|
var issue map[string]interface{}
|
||||||
|
if err := json.Unmarshal(createOut, &issue); err != nil {
|
||||||
|
t.Fatalf("failed to parse create output: %v, output: %s", err, createOut)
|
||||||
|
}
|
||||||
|
id := issue["id"].(string)
|
||||||
|
|
||||||
|
// Test 1: Using --id flag works
|
||||||
|
showCmd := exec.Command(tmpBin, "--no-daemon", "show", "--id="+id, "--short")
|
||||||
|
showCmd.Dir = tmpDir
|
||||||
|
showOut, err := showCmd.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("show with --id flag failed: %v\n%s", err, showOut)
|
||||||
|
}
|
||||||
|
if !strings.Contains(string(showOut), id) {
|
||||||
|
t.Errorf("expected issue ID in output, got: %s", showOut)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test 2: Multiple --id flags work
|
||||||
|
showCmd2 := exec.Command(tmpBin, "--no-daemon", "show", "--id="+id, "--id="+id, "--short")
|
||||||
|
showCmd2.Dir = tmpDir
|
||||||
|
showOut2, err := showCmd2.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("show with multiple --id flags failed: %v\n%s", err, showOut2)
|
||||||
|
}
|
||||||
|
// Should see the ID twice (one for each --id flag)
|
||||||
|
if strings.Count(string(showOut2), id) != 2 {
|
||||||
|
t.Errorf("expected issue ID twice in output, got: %s", showOut2)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test 3: Combining positional and --id flag
|
||||||
|
showCmd3 := exec.Command(tmpBin, "--no-daemon", "show", id, "--id="+id, "--short")
|
||||||
|
showCmd3.Dir = tmpDir
|
||||||
|
showOut3, err := showCmd3.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("show with positional + --id failed: %v\n%s", err, showOut3)
|
||||||
|
}
|
||||||
|
// Should see the ID twice
|
||||||
|
if strings.Count(string(showOut3), id) != 2 {
|
||||||
|
t.Errorf("expected issue ID twice in output, got: %s", showOut3)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test 4: No args at all should fail
|
||||||
|
showCmd4 := exec.Command(tmpBin, "--no-daemon", "show")
|
||||||
|
showCmd4.Dir = tmpDir
|
||||||
|
_, err = showCmd4.CombinedOutput()
|
||||||
|
if err == nil {
|
||||||
|
t.Error("expected error when no ID provided, but command succeeded")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user