* fix(orphans): honor --db flag for cross-repo orphan detection Problem: - `bd orphans --db /path` ignored the --db flag entirely - FindOrphanedIssues() hardcoded local .beads/ directory Solution: - Introduce IssueProvider interface for abstract issue lookup - Add StorageProvider adapter wrapping Storage instances - Update FindOrphanedIssues to accept provider instead of path - Wire orphans command to create provider from --db flag Closes: steveyegge/beads#1196 * test(orphans): add cross-repo and provider tests for --db flag fix - Add TestFindOrphanedIssues_WithMockProvider (table-driven, UT-01 through UT-09) - Add TestFindOrphanedIssues_CrossRepo (validates --db flag honored) - Add TestFindOrphanedIssues_LocalProvider (backward compat RT-01) - Add TestFindOrphanedIssues_ProviderError (error handling UT-07) - Add TestFindOrphanedIssues_IntegrationCrossRepo (IT-02 full) - Add TestLocalProvider_* unit tests Coverage for IssueProvider interface and cross-repo orphan detection. * docs: add bd orphans command to CLI reference Document the orphan detection command including the cross-repo workflow enabled by the --db flag fix in this PR.
60 lines
1.8 KiB
Go
60 lines
1.8 KiB
Go
// Package storage defines the interface for issue storage backends.
|
|
package storage
|
|
|
|
import (
|
|
"context"
|
|
|
|
"github.com/steveyegge/beads/internal/types"
|
|
)
|
|
|
|
// StorageProvider wraps a Storage interface to provide IssueProvider functionality.
|
|
// This adapts the full Storage interface to the minimal IssueProvider interface
|
|
// needed for orphan detection.
|
|
type StorageProvider struct {
|
|
storage Storage
|
|
prefix string // Cached prefix (empty = not cached yet)
|
|
}
|
|
|
|
// NewStorageProvider creates an IssueProvider backed by a Storage instance.
|
|
func NewStorageProvider(s Storage) *StorageProvider {
|
|
return &StorageProvider{storage: s}
|
|
}
|
|
|
|
// GetOpenIssues returns issues that are open or in_progress.
|
|
func (p *StorageProvider) GetOpenIssues(ctx context.Context) ([]*types.Issue, error) {
|
|
// Use SearchIssues with empty query and status filter
|
|
// We need to search for both "open" and "in_progress" issues
|
|
openStatus := types.StatusOpen
|
|
openIssues, err := p.storage.SearchIssues(ctx, "", types.IssueFilter{Status: &openStatus})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
inProgressStatus := types.StatusInProgress
|
|
inProgressIssues, err := p.storage.SearchIssues(ctx, "", types.IssueFilter{Status: &inProgressStatus})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Combine results
|
|
return append(openIssues, inProgressIssues...), nil
|
|
}
|
|
|
|
// GetIssuePrefix returns the configured issue prefix.
|
|
func (p *StorageProvider) GetIssuePrefix() string {
|
|
// Cache the prefix on first access
|
|
if p.prefix == "" {
|
|
ctx := context.Background()
|
|
prefix, err := p.storage.GetConfig(ctx, "issue_prefix")
|
|
if err != nil || prefix == "" {
|
|
p.prefix = "bd" // default
|
|
} else {
|
|
p.prefix = prefix
|
|
}
|
|
}
|
|
return p.prefix
|
|
}
|
|
|
|
// Ensure StorageProvider implements types.IssueProvider
|
|
var _ types.IssueProvider = (*StorageProvider)(nil)
|