fix(dashboard): use registered rigs for merge queue instead of hardcoded repos (#863)

The FetchMergeQueue function was hardcoded to query PRs from
michaellady/roxas and michaellady/gastown, causing the dashboard
to show unrelated PRs regardless of which rigs are actually registered.

This fix:
- Adds townRoot to LiveConvoyFetcher to access workspace config
- Loads registered rigs from mayor/rigs.json dynamically
- Adds gitURLToRepoPath helper to convert git URLs (HTTPS/SSH) to
  owner/repo format for the gh CLI
- Updates comments to reflect the new behavior

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
The App Agency
2026-01-21 22:11:29 -05:00
committed by GitHub
parent d67aa0212c
commit 3931d10af3

View File

@@ -10,11 +10,13 @@ import (
"time" "time"
"github.com/steveyegge/gastown/internal/activity" "github.com/steveyegge/gastown/internal/activity"
"github.com/steveyegge/gastown/internal/config"
"github.com/steveyegge/gastown/internal/workspace" "github.com/steveyegge/gastown/internal/workspace"
) )
// LiveConvoyFetcher fetches convoy data from beads. // LiveConvoyFetcher fetches convoy data from beads.
type LiveConvoyFetcher struct { type LiveConvoyFetcher struct {
townRoot string
townBeads string townBeads string
} }
@@ -26,6 +28,7 @@ func NewLiveConvoyFetcher() (*LiveConvoyFetcher, error) {
} }
return &LiveConvoyFetcher{ return &LiveConvoyFetcher{
townRoot: townRoot,
townBeads: filepath.Join(townRoot, ".beads"), townBeads: filepath.Join(townRoot, ".beads"),
}, nil }, nil
} }
@@ -439,21 +442,25 @@ func calculateWorkStatus(completed, total int, activityColor string) string {
} }
} }
// FetchMergeQueue fetches open PRs from configured repos. // FetchMergeQueue fetches open PRs from registered rigs.
func (f *LiveConvoyFetcher) FetchMergeQueue() ([]MergeQueueRow, error) { func (f *LiveConvoyFetcher) FetchMergeQueue() ([]MergeQueueRow, error) {
// Repos to query for PRs // Load registered rigs from config
repos := []struct { rigsConfigPath := filepath.Join(f.townRoot, "mayor", "rigs.json")
Full string // Full repo path for gh CLI rigsConfig, err := config.LoadRigsConfig(rigsConfigPath)
Short string // Short name for display if err != nil {
}{ return nil, fmt.Errorf("loading rigs config: %w", err)
{"michaellady/roxas", "roxas"},
{"michaellady/gastown", "gastown"},
} }
var result []MergeQueueRow var result []MergeQueueRow
for _, repo := range repos { for rigName, entry := range rigsConfig.Rigs {
prs, err := f.fetchPRsForRepo(repo.Full, repo.Short) // Convert git URL to owner/repo format for gh CLI
repoPath := gitURLToRepoPath(entry.GitURL)
if repoPath == "" {
continue
}
prs, err := f.fetchPRsForRepo(repoPath, rigName)
if err != nil { if err != nil {
// Non-fatal: continue with other repos // Non-fatal: continue with other repos
continue continue
@@ -464,6 +471,28 @@ func (f *LiveConvoyFetcher) FetchMergeQueue() ([]MergeQueueRow, error) {
return result, nil return result, nil
} }
// gitURLToRepoPath converts a git URL to owner/repo format.
// Supports HTTPS (https://github.com/owner/repo.git) and
// SSH (git@github.com:owner/repo.git) formats.
func gitURLToRepoPath(gitURL string) string {
// Handle HTTPS format: https://github.com/owner/repo.git
if strings.HasPrefix(gitURL, "https://github.com/") {
path := strings.TrimPrefix(gitURL, "https://github.com/")
path = strings.TrimSuffix(path, ".git")
return path
}
// Handle SSH format: git@github.com:owner/repo.git
if strings.HasPrefix(gitURL, "git@github.com:") {
path := strings.TrimPrefix(gitURL, "git@github.com:")
path = strings.TrimSuffix(path, ".git")
return path
}
// Unsupported format
return ""
}
// prResponse represents the JSON response from gh pr list. // prResponse represents the JSON response from gh pr list.
type prResponse struct { type prResponse struct {
Number int `json:"number"` Number int `json:"number"`
@@ -479,7 +508,7 @@ type prResponse struct {
// fetchPRsForRepo fetches open PRs for a single repo. // fetchPRsForRepo fetches open PRs for a single repo.
func (f *LiveConvoyFetcher) fetchPRsForRepo(repoFull, repoShort string) ([]MergeQueueRow, error) { func (f *LiveConvoyFetcher) fetchPRsForRepo(repoFull, repoShort string) ([]MergeQueueRow, error) {
// #nosec G204 -- gh is a trusted CLI, repo is from hardcoded list // #nosec G204 -- gh is a trusted CLI, repo is from registered rigs config
cmd := exec.Command("gh", "pr", "list", cmd := exec.Command("gh", "pr", "list",
"--repo", repoFull, "--repo", repoFull,
"--state", "open", "--state", "open",