feat: add directory-scoped host defaults (match_dirs) and repo list --limit
Add match_dirs field to host config entries for directory-based host resolution. When no --hostname flag, FGJ_HOST env var, or git remote is detected, the longest matching directory prefix determines the host. Symlinks are resolved on both sides for macOS compatibility (/tmp → /private/tmp). Also adds --limit/-L flag to repo list.
This commit is contained in:
parent
113505de95
commit
c293e233d2
17 changed files with 252 additions and 79 deletions
|
|
@ -49,7 +49,7 @@ func TestConfig_GetHost(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
host, err := cfg.GetHost("codeberg.org", "")
|
||||
host, err := cfg.GetHost("codeberg.org", "", "")
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
}
|
||||
|
|
@ -58,7 +58,7 @@ func TestConfig_GetHost(t *testing.T) {
|
|||
t.Errorf("Expected hostname 'codeberg.org', got '%s'", host.Hostname)
|
||||
}
|
||||
|
||||
_, err = cfg.GetHost("nonexistent.org", "")
|
||||
_, err = cfg.GetHost("nonexistent.org", "", "")
|
||||
if err == nil {
|
||||
t.Error("Expected error for nonexistent host")
|
||||
}
|
||||
|
|
@ -275,7 +275,7 @@ func TestConfig_GetHost_EmptyString(t *testing.T) {
|
|||
}
|
||||
|
||||
// Empty hostname should default to codeberg.org
|
||||
host, err := cfg.GetHost("", "")
|
||||
host, err := cfg.GetHost("", "", "")
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
}
|
||||
|
|
@ -296,7 +296,7 @@ func TestConfig_GetHost_WhitespaceString(t *testing.T) {
|
|||
}
|
||||
|
||||
// Whitespace-only hostname should default to codeberg.org
|
||||
host, err := cfg.GetHost(" ", "")
|
||||
host, err := cfg.GetHost(" ", "", "")
|
||||
if err == nil {
|
||||
t.Logf("Got host: %+v (this may be expected behavior)", host)
|
||||
} else {
|
||||
|
|
@ -315,7 +315,7 @@ func TestConfig_SetHost_EmptyToken(t *testing.T) {
|
|||
|
||||
cfg.SetHost("codeberg.org", hostConfig)
|
||||
|
||||
host, err := cfg.GetHost("codeberg.org", "")
|
||||
host, err := cfg.GetHost("codeberg.org", "", "")
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
}
|
||||
|
|
@ -345,7 +345,7 @@ func TestConfig_SetHost_OverwriteExisting(t *testing.T) {
|
|||
|
||||
cfg.SetHost("codeberg.org", newConfig)
|
||||
|
||||
host, err := cfg.GetHost("codeberg.org", "")
|
||||
host, err := cfg.GetHost("codeberg.org", "", "")
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
}
|
||||
|
|
@ -388,7 +388,7 @@ func TestConfig_MultipleHosts(t *testing.T) {
|
|||
|
||||
// Verify each host can be retrieved correctly
|
||||
for _, h := range hosts {
|
||||
host, err := cfg.GetHost(h.hostname, "")
|
||||
host, err := cfg.GetHost(h.hostname, "", "")
|
||||
if err != nil {
|
||||
t.Errorf("Failed to get host %s: %v", h.hostname, err)
|
||||
continue
|
||||
|
|
@ -422,13 +422,120 @@ func TestConfig_GitProtocol(t *testing.T) {
|
|||
})
|
||||
|
||||
// Verify protocols are stored correctly
|
||||
sshHost, _ := cfg.GetHost("test-ssh.org", "")
|
||||
sshHost, _ := cfg.GetHost("test-ssh.org", "", "")
|
||||
if sshHost.GitProtocol != "ssh" {
|
||||
t.Errorf("Expected git_protocol 'ssh', got '%s'", sshHost.GitProtocol)
|
||||
}
|
||||
|
||||
httpsHost, _ := cfg.GetHost("test-https.org", "")
|
||||
httpsHost, _ := cfg.GetHost("test-https.org", "", "")
|
||||
if httpsHost.GitProtocol != "https" {
|
||||
t.Errorf("Expected git_protocol 'https', got '%s'", httpsHost.GitProtocol)
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolveHostByPath(t *testing.T) {
|
||||
cfg := &Config{
|
||||
Hosts: map[string]HostConfig{
|
||||
"forgejo.zerova.net": {
|
||||
Hostname: "forgejo.zerova.net",
|
||||
Token: "token1",
|
||||
MatchDirs: []string{"/Users/sid/repos/fgj", "/Users/sid/repos/zerova"},
|
||||
},
|
||||
"codeberg.org": {
|
||||
Hostname: "codeberg.org",
|
||||
Token: "token2",
|
||||
MatchDirs: []string{"/"},
|
||||
},
|
||||
"gitea.example.com": {
|
||||
Hostname: "gitea.example.com",
|
||||
Token: "token3",
|
||||
// no match_dirs — should never be selected by path
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
cwd string
|
||||
want string
|
||||
}{
|
||||
{"exact dir match", "/Users/sid/repos/fgj", "forgejo.zerova.net"},
|
||||
{"nested dir match", "/Users/sid/repos/fgj/cmd/root.go", "forgejo.zerova.net"},
|
||||
{"second match dir", "/Users/sid/repos/zerova/pkg", "forgejo.zerova.net"},
|
||||
{"longest prefix wins over /", "/Users/sid/repos/fgj/internal", "forgejo.zerova.net"},
|
||||
{"/ as global catch-all", "/tmp", "codeberg.org"},
|
||||
{"/ matches root itself", "/", "codeberg.org"},
|
||||
{"no match_dirs host not selected", "/some/random/path", "codeberg.org"},
|
||||
{"empty cwd returns empty", "", ""},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := cfg.ResolveHostByPath(tt.cwd)
|
||||
if got != tt.want {
|
||||
t.Errorf("ResolveHostByPath(%q) = %q, want %q", tt.cwd, got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolveHostByPath_LongestPrefixAcrossHosts(t *testing.T) {
|
||||
cfg := &Config{
|
||||
Hosts: map[string]HostConfig{
|
||||
"broad.org": {
|
||||
Hostname: "broad.org",
|
||||
Token: "t1",
|
||||
MatchDirs: []string{"/Users/sid"},
|
||||
},
|
||||
"specific.org": {
|
||||
Hostname: "specific.org",
|
||||
Token: "t2",
|
||||
MatchDirs: []string{"/Users/sid/repos/myproject"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
got := cfg.ResolveHostByPath("/Users/sid/repos/myproject/main.go")
|
||||
if got != "specific.org" {
|
||||
t.Errorf("expected specific.org, got %q", got)
|
||||
}
|
||||
|
||||
got = cfg.ResolveHostByPath("/Users/sid/other")
|
||||
if got != "broad.org" {
|
||||
t.Errorf("expected broad.org, got %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetHost_MatchDirsIntegration(t *testing.T) {
|
||||
cfg := &Config{
|
||||
Hosts: map[string]HostConfig{
|
||||
"forgejo.zerova.net": {
|
||||
Hostname: "forgejo.zerova.net",
|
||||
Token: "token1",
|
||||
MatchDirs: []string{"/Users/sid/repos/fgj"},
|
||||
},
|
||||
"codeberg.org": {
|
||||
Hostname: "codeberg.org",
|
||||
Token: "token2",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// cwd match should resolve to forgejo.zerova.net
|
||||
host, err := cfg.GetHost("", "", "/Users/sid/repos/fgj/cmd")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if host.Hostname != "forgejo.zerova.net" {
|
||||
t.Errorf("expected forgejo.zerova.net, got %s", host.Hostname)
|
||||
}
|
||||
|
||||
// no cwd match falls through to codeberg.org default
|
||||
host, err = cfg.GetHost("", "", "/tmp")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if host.Hostname != "codeberg.org" {
|
||||
t.Errorf("expected codeberg.org, got %s", host.Hostname)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue