fix(routing): complete bd init --contributor routing (bd-6x6g) (#1088)
Implements the missing contributor routing logic so bd init --contributor actually works. Contributors' issues automatically route to ~/.beads-planning/ while maintainers' issues stay local.
This commit is contained in:
committed by
GitHub
parent
b9d2799d29
commit
31239495f1
107
cmd/bd/create.go
107
cmd/bd/create.go
@@ -1,6 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
@@ -14,6 +15,7 @@ import (
|
||||
"github.com/steveyegge/beads/internal/hooks"
|
||||
"github.com/steveyegge/beads/internal/routing"
|
||||
"github.com/steveyegge/beads/internal/rpc"
|
||||
"github.com/steveyegge/beads/internal/storage"
|
||||
"github.com/steveyegge/beads/internal/storage/sqlite"
|
||||
"github.com/steveyegge/beads/internal/timeparsing"
|
||||
"github.com/steveyegge/beads/internal/types"
|
||||
@@ -314,22 +316,64 @@ var createCmd = &cobra.Command{
|
||||
debug.Logf("Warning: failed to detect user role: %v\n", err)
|
||||
}
|
||||
|
||||
// Build routing config with backward compatibility for legacy contributor.* keys
|
||||
routingMode := config.GetString("routing.mode")
|
||||
contributorRepo := config.GetString("routing.contributor")
|
||||
|
||||
// NFR-001: Backward compatibility - fall back to legacy contributor.* keys
|
||||
if routingMode == "" {
|
||||
if config.GetString("contributor.auto_route") == "true" {
|
||||
routingMode = "auto"
|
||||
}
|
||||
}
|
||||
if contributorRepo == "" {
|
||||
contributorRepo = config.GetString("contributor.planning_repo")
|
||||
}
|
||||
|
||||
routingConfig := &routing.RoutingConfig{
|
||||
Mode: config.GetString("routing.mode"),
|
||||
Mode: routingMode,
|
||||
DefaultRepo: config.GetString("routing.default"),
|
||||
MaintainerRepo: config.GetString("routing.maintainer"),
|
||||
ContributorRepo: config.GetString("routing.contributor"),
|
||||
ContributorRepo: contributorRepo,
|
||||
ExplicitOverride: repoOverride,
|
||||
}
|
||||
|
||||
repoPath = routing.DetermineTargetRepo(routingConfig, userRole, ".")
|
||||
}
|
||||
|
||||
// TODO(bd-6x6g): Switch to target repo for multi-repo support
|
||||
// For now, we just log the target repo in debug mode
|
||||
// Switch to target repo for multi-repo support (bd-6x6g)
|
||||
// When routing to a different repo, we bypass daemon mode and use direct storage
|
||||
var targetStore storage.Storage
|
||||
var routedToTarget bool
|
||||
if repoPath != "." {
|
||||
debug.Logf("DEBUG: Target repo: %s\n", repoPath)
|
||||
targetBeadsDir := routing.ExpandPath(repoPath)
|
||||
debug.Logf("DEBUG: Routing to target repo: %s\n", targetBeadsDir)
|
||||
|
||||
// Ensure target beads directory exists with prefix inheritance
|
||||
if err := ensureBeadsDirForPath(rootCtx, targetBeadsDir, store); err != nil {
|
||||
FatalError("failed to initialize target repo: %v", err)
|
||||
}
|
||||
|
||||
// Open new store for target repo
|
||||
targetDBPath := filepath.Join(targetBeadsDir, ".beads", "beads.db")
|
||||
var err error
|
||||
targetStore, err = sqlite.New(rootCtx, targetDBPath)
|
||||
if err != nil {
|
||||
FatalError("failed to open target store: %v", err)
|
||||
}
|
||||
defer func() {
|
||||
if err := targetStore.Close(); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "warning: failed to close target store: %v\n", err)
|
||||
}
|
||||
}()
|
||||
|
||||
// Replace store for remainder of create operation
|
||||
// This also bypasses daemon mode since daemon owns the current repo's store
|
||||
store = targetStore
|
||||
daemonClient = nil // Bypass daemon for routed issues (T013)
|
||||
routedToTarget = true
|
||||
}
|
||||
_ = routedToTarget // Used for logging context
|
||||
|
||||
// Check for conflicting flags
|
||||
if explicitID != "" && parentID != "" {
|
||||
@@ -899,3 +943,56 @@ func formatTimeForRPC(t *time.Time) string {
|
||||
}
|
||||
return t.Format(time.RFC3339)
|
||||
}
|
||||
|
||||
// ensureBeadsDirForPath ensures a beads directory exists at the target path.
|
||||
// If the .beads directory doesn't exist, it creates it and initializes with
|
||||
// the same prefix as the source store (T010, T012: prefix inheritance).
|
||||
func ensureBeadsDirForPath(ctx context.Context, targetPath string, sourceStore storage.Storage) error {
|
||||
beadsDir := filepath.Join(targetPath, ".beads")
|
||||
dbPath := filepath.Join(beadsDir, "beads.db")
|
||||
|
||||
// Check if beads directory already exists
|
||||
if _, err := os.Stat(beadsDir); err == nil {
|
||||
// Directory exists, check if database exists
|
||||
if _, err := os.Stat(dbPath); err == nil {
|
||||
// Database exists, nothing to do
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Create .beads directory
|
||||
if err := os.MkdirAll(beadsDir, 0750); err != nil {
|
||||
return fmt.Errorf("cannot create .beads directory: %w", err)
|
||||
}
|
||||
|
||||
// Create issues.jsonl if it doesn't exist
|
||||
jsonlPath := filepath.Join(beadsDir, "issues.jsonl")
|
||||
if _, err := os.Stat(jsonlPath); os.IsNotExist(err) {
|
||||
// #nosec G306 -- planning repo JSONL must be shareable across collaborators
|
||||
if err := os.WriteFile(jsonlPath, []byte{}, 0644); err != nil {
|
||||
return fmt.Errorf("failed to create issues.jsonl: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize database - it will be created when sqlite.New is called
|
||||
// But we need to set the prefix if source store has one (T012: prefix inheritance)
|
||||
if sourceStore != nil {
|
||||
sourcePrefix, err := sourceStore.GetConfig(ctx, "issue_prefix")
|
||||
if err == nil && sourcePrefix != "" {
|
||||
// Open target store temporarily to set prefix
|
||||
tempStore, err := sqlite.New(ctx, dbPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to initialize target database: %w", err)
|
||||
}
|
||||
if err := tempStore.SetConfig(ctx, "issue_prefix", sourcePrefix); err != nil {
|
||||
_ = tempStore.Close()
|
||||
return fmt.Errorf("failed to set prefix in target store: %w", err)
|
||||
}
|
||||
if err := tempStore.Close(); err != nil {
|
||||
return fmt.Errorf("failed to close target store: %w", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user