git-remote-entire: actionable hint when the cluster host is missing · Entire

git-remote-entire: actionable hint when the cluster host is missing

115fccb→main·

Soph·1w ago·2 files·+100 added/-2 removed

`git clone entire://gh/entire.io/cli` parses `gh` into the host slot, so the helper previously failed at cluster discovery with an opaque `https://gh/.well-known/...\` error. The empty-host form (`entire:///gh/...`) hit only a bare "missing host" message.

Detect both shapes in the URL-validation switch: when the host is empty or is a known forge id (`gitremote.IsSupportedForge`), print a message that names the problem and points at the interactive mirror picker, e.g.

entire repo clone /gh/entire.io/cli

This reuses the existing actionable-error idiom (`fatalMessage`) and keeps the helper a dumb transport — no TTY/stdin/stdout involvement, and safe in CI (stderr line + exit 128, never a hang). The picker lives in `entire repo clone`, the right layer, because it clones a fully-qualified URL so later fetches don't re-prompt.

Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com

Sessions

acbb7200e503View transcript

Changes

2

33 unmodified lines

34
35
36
37
38
39
40
49 unmodified lines

90
91
92
92
93
93
94
95
96
97
98
99
113 unmodified lines

213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250

33 unmodified lines

"time"

"github.com/entireio/cli/cmd/entire/cli/auth"
    "github.com/entireio/cli/cmd/entire/cli/gitremote"
    "github.com/entireio/cli/cmd/entire/cli/versioninfo"
    "github.com/entireio/cli/internal/entireclient/clusterdiscovery"
    "github.com/entireio/cli/internal/entireclient/httpclient"
49 unmodified lines

case parsedURL.Scheme != "entire":
        fmt.Fprintf(os.Stderr, "fatal: unsupported URL scheme %q (expected 'entire')\n", parsedURL.Scheme)
        return 128
    case parsedURL.Host == "":
        fmt.Fprintf(os.Stderr, "fatal: missing host in URL %q\n", rawURL)
    case parsedURL.Host == "" || gitremote.IsSupportedForge(parsedURL.Host):
        // Cluster host absent (empty, or a forge id in its slot);
        // missingClusterHostMessage renders the actionable hint.
        fmt.Fprint(os.Stderr, missingClusterHostMessage(parsedURL, rawURL))
        return 128
    }

113 unmodified lines

return fmt.Sprintf("fatal: %v\n", err)
}

// missingClusterHostMessage renders the stderr "fatal: …" line for an entire://
// URL that omits its cluster host. Two shapes reach here: a forge id typed
// where the host belongs (entire://gh/owner/repo, Host="gh") and an empty host
// (entire:///gh/owner/repo, Host=""). When the intended repo shorthand is
// recoverable — the host slot, or the path's leading segment, is a known forge
// id — it points at the interactive picker (`entire repo clone <shorthand>`),
// which resolves the mirror and clones a fully-qualified URL. Anything else
// (bare entire://, a non-forge leading segment) falls back to the plain
// missing-host error. Kept pure so it's unit-testable.
func missingClusterHostMessage(parsedURL *url.URL, rawURL string) string {
    // Forge id in the host slot: the shorthand is /<host><path>.
    if gitremote.IsSupportedForge(parsedURL.Host) {
        return clusterHostHint(parsedURL.Host, "/"+parsedURL.Host+parsedURL.Path)
    }
    // Empty host with a forge-led path: the path is already the shorthand.
    if forge, _, ok := strings.Cut(strings.TrimPrefix(parsedURL.Path, "/"), "/"); ok && gitremote.IsSupportedForge(forge) {
        return clusterHostHint(forge, parsedURL.Path)
    }
    return fmt.Sprintf("fatal: missing host in URL %q\n", rawURL)
}

// clusterHostHint is the actionable message pointing a host-less entire:// URL
// at the `entire repo clone` picker, which resolves the mirror for the given
// forge shorthand.
func clusterHostHint(forge, shorthand string) string {
    return fmt.Sprintf(
        "fatal: entire:// URL is missing its cluster host (%q is a forge id, not a host).\n"+
        "The full form is entire://<cluster-host>/%s/<owner>/<repo>.\n"+
        "To pick a mirror interactively, run:\n\n    entire repo clone %s\n",
        forge, forge, shorthand)
}

// loadedVersion populates the build info and returns the resolved version.
func loadedVersion() string {
    versioninfo.Load()

Mcmd/git-remote-entire/main.go+37/-2

172 unmodified lines

173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241

172 unmodified lines

}
}

func TestMissingClusterHostMessage(t *testing.T) {
    t.Parallel()
    tests := []struct {
        name        string
        rawURL      string
        contains    []string
        notContains []string
    }{
        {
            // The motivating case: forge id typed where the cluster host belongs.
            name:     "forge id in host slot points at repo clone",
            rawURL:   "entire://gh/entire.io/cli",
            contains: []string{"missing its cluster host", `"gh" is a forge id`, "entire repo clone /gh/entire.io/cli"},
            },
        {
            // Empty host but the path already reads as a forge shorthand.
            name:     "empty host with forge path points at repo clone",
            rawURL:   "entire:///gh/entire.io/cli",
            contains: []string{"missing its cluster host", "entire repo clone /gh/entire.io/cli"},
            },
        {
            // Empty host, leading segment is not a known forge → generic error.
            name:        "empty host with non-forge path falls back",
            rawURL:      "entire:///not-a-forge/owner/repo",
            contains:    []string{`fatal: missing host in URL "entire:///not-a-forge/owner/repo"`},
            notContains: []string{"entire repo clone"},
            },
        {
            name:        "bare scheme falls back",
            rawURL:      "entire://",
            contains:    []string{`fatal: missing host in URL "entire://"`},
            notContains: []string{"entire repo clone"},
            },
        {
            // Not enough path to form owner/repo → not worth pointing at clone.
            name:        "empty host single-segment path falls back",
            rawURL:      "entire:///gh",
            contains:    []string{`fatal: missing host in URL "entire:///gh"`},
            notContains: []string{"entire repo clone"},
            },
    }
    for _, tc := range tests {
        t.Run(tc.name, func(t *testing.T) {
            t.Parallel()
            parsed, err := url.Parse(tc.rawURL)
            if err != nil {
                t.Fatalf("parse %q: %v", tc.rawURL, err)
            }
            got := missingClusterHostMessage(parsed, tc.rawURL)
            for _, sub := range tc.contains {
                if !strings.Contains(got, sub) {
                    t.Errorf("missingClusterHostMessage(%q) = %q, missing %q", tc.rawURL, got, sub)
                }
            }
            for _, sub := range tc.notContains {
                if strings.Contains(got, sub) {
                    t.Errorf("missingClusterHostMessage(%q) = %q, should not contain %q", tc.rawURL, got, sub)
                }
            }
        }
    }
}

func TestCoreTrusted(t *testing.T) {
    t.Parallel()
    trusted := []string{"https://core.us.entire.io", "https://core.eu.entire.io/"}