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

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) {