Add label filtering to bd list with AND/OR semantics
- Add --label flag for AND filtering (must have ALL labels) - Add --label-any flag for OR filtering (must have AT LEAST ONE label) - Add normalizeLabels() helper to trim, dedupe, and clean inputs - Fix RPC title filtering parity bug (forward via Query field) - Add comprehensive tests for label filtering including combined AND+OR - Update documentation in README and CHANGELOG - Improve flag help text to clarify combined semantics Closes bd-161
This commit is contained in:
@@ -81,13 +81,15 @@ type CloseArgs struct {
|
||||
|
||||
// ListArgs represents arguments for the list operation
|
||||
type ListArgs struct {
|
||||
Query string `json:"query,omitempty"`
|
||||
Status string `json:"status,omitempty"`
|
||||
Priority *int `json:"priority,omitempty"`
|
||||
IssueType string `json:"issue_type,omitempty"`
|
||||
Assignee string `json:"assignee,omitempty"`
|
||||
Label string `json:"label,omitempty"`
|
||||
Limit int `json:"limit,omitempty"`
|
||||
Query string `json:"query,omitempty"`
|
||||
Status string `json:"status,omitempty"`
|
||||
Priority *int `json:"priority,omitempty"`
|
||||
IssueType string `json:"issue_type,omitempty"`
|
||||
Assignee string `json:"assignee,omitempty"`
|
||||
Label string `json:"label,omitempty"` // Deprecated: use Labels
|
||||
Labels []string `json:"labels,omitempty"` // AND semantics
|
||||
LabelsAny []string `json:"labels_any,omitempty"` // OR semantics
|
||||
Limit int `json:"limit,omitempty"`
|
||||
}
|
||||
|
||||
// ShowArgs represents arguments for the show operation
|
||||
|
||||
@@ -28,6 +28,24 @@ import (
|
||||
// It's set as a var so it can be initialized from main
|
||||
var ServerVersion = "0.9.10"
|
||||
|
||||
// normalizeLabels trims whitespace, removes empty strings, and deduplicates labels
|
||||
func normalizeLabels(ss []string) []string {
|
||||
seen := make(map[string]struct{})
|
||||
out := make([]string, 0, len(ss))
|
||||
for _, s := range ss {
|
||||
s = strings.TrimSpace(s)
|
||||
if s == "" {
|
||||
continue
|
||||
}
|
||||
if _, ok := seen[s]; ok {
|
||||
continue
|
||||
}
|
||||
seen[s] = struct{}{}
|
||||
out = append(out, s)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// StorageCacheEntry holds a cached storage with metadata for eviction
|
||||
type StorageCacheEntry struct {
|
||||
store storage.Storage
|
||||
@@ -899,6 +917,18 @@ func (s *Server) handleList(req *Request) Response {
|
||||
if listArgs.Priority != nil {
|
||||
filter.Priority = listArgs.Priority
|
||||
}
|
||||
// Normalize and apply label filters
|
||||
labels := normalizeLabels(listArgs.Labels)
|
||||
labelsAny := normalizeLabels(listArgs.LabelsAny)
|
||||
// Support both old single Label and new Labels array
|
||||
if len(labels) > 0 {
|
||||
filter.Labels = labels
|
||||
} else if listArgs.Label != "" {
|
||||
filter.Labels = []string{strings.TrimSpace(listArgs.Label)}
|
||||
}
|
||||
if len(labelsAny) > 0 {
|
||||
filter.LabelsAny = labelsAny
|
||||
}
|
||||
|
||||
ctx := s.reqCtx(req)
|
||||
issues, err := store.SearchIssues(ctx, listArgs.Query, filter)
|
||||
|
||||
Reference in New Issue
Block a user