feat(bd): add --ephemeral and --persistent flags to bd update (#1263)
Adds --ephemeral and --persistent flags to bd update command. Author: aleiby
This commit is contained in:
@@ -312,6 +312,94 @@ func TestCLI_UpdateLabels(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCLI_UpdateEphemeral(t *testing.T) {
|
||||||
|
if testing.Short() {
|
||||||
|
t.Skip("skipping slow CLI test in short mode")
|
||||||
|
}
|
||||||
|
// Note: Not using t.Parallel() because inProcessMutex serializes execution anyway
|
||||||
|
tmpDir := setupCLITestDB(t)
|
||||||
|
out := runBDInProcess(t, tmpDir, "create", "Issue for ephemeral testing", "-p", "2", "--json")
|
||||||
|
|
||||||
|
var issue map[string]interface{}
|
||||||
|
if err := json.Unmarshal([]byte(out), &issue); err != nil {
|
||||||
|
t.Fatalf("Failed to parse create output: %v", err)
|
||||||
|
}
|
||||||
|
id := issue["id"].(string)
|
||||||
|
|
||||||
|
// Mark as ephemeral
|
||||||
|
runBDInProcess(t, tmpDir, "update", id, "--ephemeral")
|
||||||
|
|
||||||
|
out = runBDInProcess(t, tmpDir, "show", id, "--json")
|
||||||
|
var updated []map[string]interface{}
|
||||||
|
if err := json.Unmarshal([]byte(out), &updated); err != nil {
|
||||||
|
t.Fatalf("Failed to parse show output: %v", err)
|
||||||
|
}
|
||||||
|
if updated[0]["ephemeral"] != true {
|
||||||
|
t.Errorf("Expected ephemeral to be true after --ephemeral, got: %v", updated[0]["ephemeral"])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCLI_UpdatePersistent(t *testing.T) {
|
||||||
|
if testing.Short() {
|
||||||
|
t.Skip("skipping slow CLI test in short mode")
|
||||||
|
}
|
||||||
|
// Note: Not using t.Parallel() because inProcessMutex serializes execution anyway
|
||||||
|
tmpDir := setupCLITestDB(t)
|
||||||
|
|
||||||
|
// Create ephemeral issue directly
|
||||||
|
out := runBDInProcess(t, tmpDir, "create", "Ephemeral issue", "-p", "2", "--ephemeral", "--json")
|
||||||
|
|
||||||
|
var issue map[string]interface{}
|
||||||
|
if err := json.Unmarshal([]byte(out), &issue); err != nil {
|
||||||
|
t.Fatalf("Failed to parse create output: %v", err)
|
||||||
|
}
|
||||||
|
id := issue["id"].(string)
|
||||||
|
|
||||||
|
// Verify it's ephemeral
|
||||||
|
out = runBDInProcess(t, tmpDir, "show", id, "--json")
|
||||||
|
var initial []map[string]interface{}
|
||||||
|
if err := json.Unmarshal([]byte(out), &initial); err != nil {
|
||||||
|
t.Fatalf("Failed to parse show output: %v", err)
|
||||||
|
}
|
||||||
|
if initial[0]["ephemeral"] != true {
|
||||||
|
t.Fatalf("Expected issue to be ephemeral initially, got: %v", initial[0]["ephemeral"])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Promote to persistent
|
||||||
|
runBDInProcess(t, tmpDir, "update", id, "--persistent")
|
||||||
|
|
||||||
|
out = runBDInProcess(t, tmpDir, "show", id, "--json")
|
||||||
|
var updated []map[string]interface{}
|
||||||
|
if err := json.Unmarshal([]byte(out), &updated); err != nil {
|
||||||
|
t.Fatalf("Failed to parse show output after persistent: %v", err)
|
||||||
|
}
|
||||||
|
if updated[0]["ephemeral"] == true {
|
||||||
|
t.Errorf("Expected ephemeral to be false after --persistent, got: %v", updated[0]["ephemeral"])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCLI_UpdateEphemeralMutualExclusion(t *testing.T) {
|
||||||
|
if testing.Short() {
|
||||||
|
t.Skip("skipping slow CLI test in short mode")
|
||||||
|
}
|
||||||
|
// Note: Not using t.Parallel() because inProcessMutex serializes execution anyway
|
||||||
|
tmpDir := setupCLITestDB(t)
|
||||||
|
out := runBDInProcess(t, tmpDir, "create", "Issue for mutual exclusion test", "-p", "2", "--json")
|
||||||
|
|
||||||
|
var issue map[string]interface{}
|
||||||
|
json.Unmarshal([]byte(out), &issue)
|
||||||
|
id := issue["id"].(string)
|
||||||
|
|
||||||
|
// Both flags should error
|
||||||
|
_, stderr, err := runBDInProcessAllowError(t, tmpDir, "update", id, "--ephemeral", "--persistent")
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("Expected error when both flags specified, got none")
|
||||||
|
}
|
||||||
|
if !strings.Contains(stderr, "cannot specify both") {
|
||||||
|
t.Errorf("Expected mutual exclusion error message, got: %v", stderr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestCLI_Close(t *testing.T) {
|
func TestCLI_Close(t *testing.T) {
|
||||||
if testing.Short() {
|
if testing.Short() {
|
||||||
t.Skip("skipping slow CLI test in short mode")
|
t.Skip("skipping slow CLI test in short mode")
|
||||||
|
|||||||
@@ -165,6 +165,19 @@ create, update, show, or close operation).`,
|
|||||||
updates["defer_until"] = t
|
updates["defer_until"] = t
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Ephemeral/persistent flags
|
||||||
|
// Note: storage layer uses "wisp" field name, maps to "ephemeral" column
|
||||||
|
ephemeralChanged := cmd.Flags().Changed("ephemeral")
|
||||||
|
persistentChanged := cmd.Flags().Changed("persistent")
|
||||||
|
if ephemeralChanged && persistentChanged {
|
||||||
|
FatalErrorRespectJSON("cannot specify both --ephemeral and --persistent flags")
|
||||||
|
}
|
||||||
|
if ephemeralChanged {
|
||||||
|
updates["wisp"] = true
|
||||||
|
}
|
||||||
|
if persistentChanged {
|
||||||
|
updates["wisp"] = false
|
||||||
|
}
|
||||||
|
|
||||||
// Get claim flag
|
// Get claim flag
|
||||||
claimFlag, _ := cmd.Flags().GetBool("claim")
|
claimFlag, _ := cmd.Flags().GetBool("claim")
|
||||||
@@ -278,6 +291,10 @@ create, update, show, or close operation).`,
|
|||||||
empty := ""
|
empty := ""
|
||||||
updateArgs.DeferUntil = &empty
|
updateArgs.DeferUntil = &empty
|
||||||
}
|
}
|
||||||
|
// Ephemeral/persistent
|
||||||
|
if wisp, ok := updates["wisp"].(bool); ok {
|
||||||
|
updateArgs.Ephemeral = &wisp
|
||||||
|
}
|
||||||
|
|
||||||
// Set claim flag for atomic claim operation
|
// Set claim flag for atomic claim operation
|
||||||
updateArgs.Claim = claimFlag
|
updateArgs.Claim = claimFlag
|
||||||
@@ -613,6 +630,9 @@ func init() {
|
|||||||
updateCmd.Flags().String("defer", "", "Defer until date (empty to clear). Issue hidden from bd ready until then")
|
updateCmd.Flags().String("defer", "", "Defer until date (empty to clear). Issue hidden from bd ready until then")
|
||||||
// Gate fields (bd-z6kw)
|
// Gate fields (bd-z6kw)
|
||||||
updateCmd.Flags().String("await-id", "", "Set gate await_id (e.g., GitHub run ID for gh:run gates)")
|
updateCmd.Flags().String("await-id", "", "Set gate await_id (e.g., GitHub run ID for gh:run gates)")
|
||||||
|
// Ephemeral/persistent flags
|
||||||
|
updateCmd.Flags().Bool("ephemeral", false, "Mark issue as ephemeral (wisp) - not exported to JSONL")
|
||||||
|
updateCmd.Flags().Bool("persistent", false, "Mark issue as persistent (promote wisp to regular issue)")
|
||||||
updateCmd.ValidArgsFunction = issueIDCompletion
|
updateCmd.ValidArgsFunction = issueIDCompletion
|
||||||
rootCmd.AddCommand(updateCmd)
|
rootCmd.AddCommand(updateCmd)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ func updatesFromArgs(a UpdateArgs) (map[string]interface{}, error) {
|
|||||||
u["sender"] = *a.Sender
|
u["sender"] = *a.Sender
|
||||||
}
|
}
|
||||||
if a.Ephemeral != nil {
|
if a.Ephemeral != nil {
|
||||||
u["ephemeral"] = *a.Ephemeral
|
u["wisp"] = *a.Ephemeral // Storage API uses "wisp", maps to "ephemeral" column
|
||||||
}
|
}
|
||||||
if a.RepliesTo != nil {
|
if a.RepliesTo != nil {
|
||||||
u["replies_to"] = *a.RepliesTo
|
u["replies_to"] = *a.RepliesTo
|
||||||
|
|||||||
Reference in New Issue
Block a user