search: accept entire:// mirror remotes · Entire
Search: Accept Entire Mirror Remotes
f72b778→main·
pfleidi·1mo ago·2 files·+24 added/-46 removed
entire search derived owner/repo via a GitHub-only parser that required the remote host to be github.com, so it rejected Entire mirror remotes (entire://host/gh/owner/repo) with "remote is not a GitHub repository".
Delegate ParseGitHubRemote to the canonical gitremote.ParseURL, which understands the entire:// scheme, and enforce the GitHub policy via CanonicalHost() (gh -> github.com). This also fixes the same limitation in the two dispatch callers that share the helper, and removes the duplicated URL parsing.
Sessions
13d8c9f7198fView transcript
[?
Fix Search to Accept Entire Mirror RemotesClaude Code·Opus 4.8[1m]·1 step](/content/gh/entireio/cli/session/5a037a28-15c4-4121-a391-4f1be5d90488#timeline-13d8c9f7198f/index.html)
Changes
2
cmd/entire/cli/search
Mgithub.go+13/-46
Msearch_test.go+11
1 unmodified line
2
3
4
5
5
7
8
6
7
8
9
11
12
13
14
10
11
12
13
14
15
16
17
18
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
20
21
22
47
48
49
50
51
52
53
54
55
56
23
24
1 unmodified line
package search
import (
"errors"
"fmt"
"net/url"
"strings"
"github.com/entireio/cli/cmd/entire/cli/gitremote"
)
// ParseGitHubRemote extracts owner and repo from a GitHub remote URL.
// Supports SCP-style SSH (git@github.com:owner/repo.git),
// ssh:// URLs (ssh://git@github.com/owner/repo.git),
// and HTTPS (https://github.com/owner/repo.git).
// ParseGitHubRemote extracts owner and repo from a git remote URL that resolves
// to GitHub. It accepts direct GitHub remotes (SCP-style SSH, ssh://, and
// https://) as well as Entire mirror remotes (entire://host/gh/owner/repo),
// whose forge prefix maps back to github.com. Remotes resolving to any other
// host are rejected.
func ParseGitHubRemote(remoteURL string) (owner, repo string, err error) {
remoteURL = strings.TrimSpace(remoteURL)
if remoteURL == "" {
return "", "", errors.New("empty remote URL")
}
info, err := gitremote.ParseURL(remoteURL)
if err != nil {
return "", "", fmt.Errorf("parsing remote URL: %w", err)
}
var path string
// SCP-style SSH: git@github.com:owner/repo.git
// Distinguished from ssh:// URLs by having no scheme and a colon before the path.
if strings.HasPrefix(remoteURL, "git@") && !strings.Contains(remoteURL, "://") {
idx := strings.Index(remoteURL, ":")
if idx < 0 {
return "", "", fmt.Errorf("invalid SSH remote URL: %s", remoteURL)
}
host := remoteURL[len("git@") : idx]
if host != "github.com" {
return "", "", fmt.Errorf("remote is not a GitHub repository (host: %s)", host)
}
path = remoteURL[idx+1:]
} else {
// URL format: https://, ssh://, git://
u, parseErr := url.Parse(remoteURL)
if parseErr != nil {
return "", "", fmt.Errorf("parsing remote URL: %w", parseErr)
}
host := u.Hostname()
if host != "github.com" {
return "", "", fmt.Errorf("remote is not a GitHub repository (host: %s)", host)
}
path = strings.TrimPrefix(u.Path, "/")
}
if host := info.CanonicalHost(); host != "github.com" {
return "", "", fmt.Errorf("remote is not a GitHub repository (host: %s)", host)
}
// Remove .git suffix
path = strings.TrimSuffix(path, ".git")
parts := strings.SplitN(path, "/", 3)
if len(parts) < 2 || parts[0] == "" || parts[1] == "" {
return "", "", fmt.Errorf("could not extract owner/repo from remote URL: %s", remoteURL)
}
return parts[0], parts[1], nil
return info.Owner, info.Repo, nil
}
Mcmd/entire/cli/search/github.go+13/-46
104 unmodified lines
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
104 unmodified lines
}
}
func TestParseGitHubRemote_EntireMirror(t *testing.T) {
t.Parallel()
owner, repo, err := ParseGitHubRemote("entire://aws-us-east-2.entire.io/gh/entirehq/entire.io")
if err != nil {
t.Fatal(err)
}
if owner != testOwner || repo != testRepo {
t.Errorf("got %s/%s, want %s/%s", owner, repo, testOwner, testRepo)
}
}
// -- Search() tests --
func TestSearch_URLConstruction(t *testing.T) {