feat(repo): add `entire repo clone /gh//` · Entire

feat(repo): add entire repo clone /gh/<owner>/<repo>

69c0328·

toothbrush·3w ago·3 files·+388 added/-0 removed

Looks up where a repo is mirrored: clones directly when on one cluster, prompts (or --cluster) when on more than one. Optional [target-dir] is passed through to git clone.

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

Sessions

245ec47a154fView transcript

Changes

3

26 unmodified lines

27
28
29
30
31
32
33

26 unmodified lines

cmd.AddCommand(newRepoListCmd())
    cmd.AddCommand(newRepoGetCmd())
    cmd.AddCommand(newRepoDeleteCmd())
    cmd.AddCommand(newRepoCloneCmd())
    cmd.AddCommand(newRepoMirrorCmd())
    return cmd
}

Mcmd/entire/cli/repo.go+1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
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

package cli

import (
    "context"
    "errors"
    "fmt"
    "os/exec"
    "regexp"
    "sort"
    "strings"

"charm.land/huh/v2"
    "github.com/spf13/cobra"

"github.com/entireio/cli/cmd/entire/cli/interactive"
    "github.com/entireio/cli/internal/coreapi"
)

// mirrorCloneRefRe parses the clone-ref shape `entire repo clone` accepts:
// the `/gh/<owner>/<repo>` path of a mirror's clone URL, with or without the
// leading slash. owner/repo reuse the GitHub identifier charsets from
// parseGitHubURL so the same metacharacter vectors are closed at the boundary
// (owner/repo flow unescaped into the synthesised entire:// clone URL).
var mirrorCloneRefRe = regexp.MustCompile(`^/?gh/` + gitHubOwnerPat + `/` + gitHubRepoPat + `$`)

// parseMirrorCloneRef turns a clone ref like `/gh/entirehq/entire-api` into the
// API provider ("github") and the lowercased owner/repo. The `gh` token is the
// path provider used in entire:// clone URLs; it maps to the "github" upstream
// provider the control plane records.
func parseMirrorCloneRef(ref string) (provider, owner, repo string, err error) {
    m := mirrorCloneRefRe.FindStringSubmatch(strings.TrimSpace(ref))
    if m == nil {
        return "", "", "", fmt.Errorf("expected /gh/<owner>/<repo>, got %q", ref)
    }
    owner, repo = strings.ToLower(m[1]), strings.ToLower(m[2])
    if gitHubDotOnlyRe.MatchString(repo) {
        return "", "", "", fmt.Errorf("repo cannot be dot-only: %s", ref)
    }
    return checkpointProviderGitHub, owner, repo, nil
}

func newRepoCloneCmd() *cobra.Command {
    var cluster string
    cmd := &cobra.Command{
        Use:   "clone <repo> [target-dir]",
        Short: "Clone a mirrored repository",
        Long: "Clone a GitHub mirror by its `/gh/<owner>/<repo>` ref. Looks up where " +
            "the repo is mirrored: if it's on a single cluster, clones it directly; " +
            "if it's mirrored on more than one, prompts you to pick which to clone " +
            "from (or pass --cluster to choose non-interactively). The optional " +
            "[target-dir] is passed straight through to `git clone`.",
        Example: "  entire repo clone /gh/entirehq/entire-api\n" +
            "  entire repo clone /gh/entirehq/entire-api ./entire-api\n" +
            "  entirely repo clone /gh/entirehq/entire-api --cluster aws-us-east-2.entire.io",
        Args: cobra.RangeArgs(1, 2),
        RunE: func(cmd *cobra.Command, args []string) error {
            cmd.SilenceUsage = true
            provider, owner, repo, err := parseMirrorCloneRef(args[0])
            if err != nil {
                return fmt.Errorf("invalid <repo>: %w", err)
            }
            var targetDir string
            if len(args) > 1 {
                targetDir = args[1]
            }

var mirrors []coreapi.Mirror
            if err := runCore(cmd, func(ctx context.Context, c *coreapi.Client) error {
                ms, err := listMirrorsForRepo(ctx, c, provider, owner, repo)
                if err != nil {
                    return err
                }
                mirrors = ms
                return nil
            }); err != nil {
                return err
            }

if len(mirrors) == 0 {
                return fmt.Errorf("no mirror found for gh/%s/%s; run 'entire repo mirror create github.com/%s/%s' to onboard it", owner, repo, owner, repo)
            }

chosen, err := selectCloneTarget(cmd, mirrors, cluster)
            if err != nil {
                return err
            }

cloneURL := fmt.Sprintf("entire://%s/gh/%s/%s", chosen.ClusterHost, owner, repo)
            return runGitClone(cmd.Context(), cmd, cloneURL, targetDir)
        },
    }
    cmd.Flags().StringVar(&cluster, "cluster", "", "cluster host to clone from when the repo is mirrored on more than one")
    return cmd
}

// listMirrorsForRepo returns every mirror placement of one upstream repo across
// clusters. The list API filters by provider+owner server-side but has no repo
// filter, so the repo match is applied client-side (owner is already lowercased
// to match what the server persists).
func listMirrorsForRepo(ctx context.Context, c *coreapi.Client, provider, owner, repo string) ([]coreapi.Mirror, error) {
    all, err := fetchAllPages(ctx, func(ctx context.Context, cursor string) ([]coreapi.Mirror, string, error) {
        params := coreapi.ListMirrorsParams{
            Provider: coreapi.NewOptString(provider),
            Owner:    coreapi.NewOptString(owner),
        }
        if cursor != "" {
            params.PageToken = coreapi.NewOptString(cursor)
        }
        out, err := c.ListMirrors(ctx, params)
        if err != nil {
            return nil, "", err
        }
        return out.Mirrors, out.NextPageToken.Or(""), nil
    })
    if err != nil {
        return nil, err
    }
    matched := make([]coreapi.Mirror, 0, len(all))
    for _, m := range all {
        if strings.EqualFold(m.Repo, repo) {
            matched = append(matched, m)
        }
    }
    return matched, nil
}

// selectCloneTarget resolves which mirror placement to clone from. With one
// placement it returns it directly. With --cluster it picks the matching one (or
// errors listing the available hosts). With more than one and no flag it prompts
// interactively, failing fast with a --cluster pointer when there's no terminal.
func selectCloneTarget(cmd *cobra.Command, mirrors []coreapi.Mirror, clusterFlag string) (coreapi.Mirror, error) {
    // Dedupe by cluster host: one placement per cluster is what a clone targets,
    // and the same host appearing twice would only confuse the picker.
    byHost := make(map[string]coreapi.Mirror, len(mirrors))
    hosts := make([]string, 0, len(mirrors))
    for _, m := range mirrors {
        if _, seen := byHost[m.ClusterHost]; seen {
            continue
        }
        byHost[m.ClusterHost] = m
        hosts = append(hosts, m.ClusterHost)
    }
    sort.Strings(hosts)

if clusterFlag != "" {
        m, ok := byHost[clusterFlag]
        if !ok {
            return coreapi.Mirror{}, fmt.Errorf("repo is not mirrored on %q; available: %s", clusterFlag, strings.Join(hosts, ", "))
        }
        return m, nil
    }

if len(hosts) == 1 {
        return byHost[hosts[0]], nil
    }

if !interactive.CanPromptInteractively() {
        return coreapi.Mirror{}, fmt.Errorf("repo is mirrored on %d clusters; pass --cluster to choose one of: %s", len(hosts), strings.Join(hosts, ", "))
    }

options := make([]huh.Option[string], len(hosts))
    for i, h := range hosts {
        options[i] = huh.NewOption(mirrorCellLabel(byHost[h]), h)
    }
    var selected string
    form := NewAccessibleForm(
        huh.NewGroup(
            huh.NewSelect[string]().
                Title("This repo is mirrored on more than one cluster — pick one to clone from").
                Options(options...).
                Value(&selected),
            ),
    )
    if err := form.RunWithContext(cmd.Context()); err != nil {
        return coreapi.Mirror{}, handleFormCancellation(cmd.ErrOrStderr(), "Clone", err)
    }
    m, ok := byHost[selected]
    if !ok {
        return coreapi.Mirror{}, errors.New("no cluster selected")
    }
    return m, nil
}

// runGitClone shells out to `git clone <cloneURL> [target-dir]`, wiring the
// child's stdio through so git-remote-entire's auth prompts and clone progress
// reach the user. A clone failure is wrapped as a SilentError: git already
// printed its own diagnostics, so main.go shouldn't reprint the wrapper.
func runGitClone(ctx context.Context, cmd *cobra.Command, cloneURL, targetDir string) error {
    args := []string{"clone", cloneURL}
    if targetDir != "" {
        args = append(args, targetDir)
    }
    fmt.Fprintf(cmd.ErrOrStderr(), "Cloning %s\n", cloneURL)
    gitCmd := exec.CommandContext(ctx, "git", args...)
    gitCmd.Stdin = cmd.InOrStdin()
    gitCmd.Stdout = cmd.OutOrStdout()
    gitCmd.Stderr = cmd.ErrOrStderr()
    if err := gitCmd.Run(); err != nil {
        return NewSilentError(fmt.Errorf("git clone failed: %w", err))
    }
    return nil
}

Acmd/entire/cli/repo_clone.go+218


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169

package cli

import (
    "net/http"
    "net/http/httptest"
    "testing"

"github.com/spf13/cobra"
    "github.com/stretchr/testify/require"

"github.com/entireio/cli/internal/coreapi"
)

func TestParseMirrorCloneRef(t *testing.T) {
    t.Parallel()
    tests := []struct {
        name      string
        ref       string
        wantOwner string
        wantRepo  string
        wantErr   bool
    }{
        {name: "leading slash", ref: "/gh/entirehq/entire-api", wantOwner: "entirehq", wantRepo: "entire-api"},
        {name: "no leading slash", ref: "gh/entirehq/entire-api", wantOwner: "entirehq", wantRepo: "entire-api"},
        {name: "lowercased", ref: "/gh/EntireHQ/Entire-API", wantOwner: "entirehq", wantRepo: "entire-api"},
        {name: "wrong provider", ref: "/gl/entirehq/entire-api", wantErr: true},
        {name: "missing repo", ref: "/gh/entirehq", wantErr: true},
        {name: "extra segment", ref: "/gh/entirehq/entire-api/extra", wantErr: true},
        {name: "dot-only repo", ref: "/gh/entirehq/..", wantErr: true},
        {name: "metachar in repo", ref: "/gh/entirehq/repo?x=1", wantErr: true},
        {name: "empty", ref: "", wantErr: true},
    }
    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            t.Parallel()
            provider, owner, repo, err := parseMirrorCloneRef(tt.ref)
            if tt.wantErr {
                require.Error(t, err)
                return
            }
            require.NoError(t, err)
            require.Equal(t, "github", provider)
            require.Equal(t, tt.wantOwner, owner)
            require.Equal(t, tt.wantRepo, repo)
        })
    }
}

func TestMirrorCellLabel(t *testing.T) {
    t.Parallel()
    tests := []struct {
        name   string
        mirror coreapi.Mirror
        want   string
    }{
        {
            name:   "host only",
            mirror: coreapi.Mirror{ClusterHost: "aws-us-east-2.entire.io"},
            want:   "aws-us-east-2.entire.io",
        },
        {
            name: "cell and jurisdiction",
            mirror: coreapi.Mirror{
                ClusterHost:  "aws-us-east-2.entire.io",
                Cell:         coreapi.NewOptString("aws-us-east-2"),
                Jurisdiction: coreapi.NewOptString("us"),
            },
            want: "aws-us-east-2 (us) — aws-us-east-2.entire.io",
        },
        {
            name: "cell without jurisdiction",
            mirror: coreapi.Mirror{
                ClusterHost: "aws-us-east-2.entire.io",
                Cell:        coreapi.NewOptString("aws-us-east-2"),
            },
            want: "aws-us-east-2 — aws-us-east-2.entire.io",
        },
    }
    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            t.Parallel()
            require.Equal(t, tt.want, mirrorCellLabel(tt.mirror))
        })
    }
}

func newCloneTestCmd() *cobra.Command {
    cmd := newRepoCloneCmd()
    cmd.SetOut(&nopWriter{})
    cmd.SetErr(&nopWriter{})
    return cmd
}

type nopWriter struct {}

func (*nopWriter) Write(p []byte) (int, error) { return len(p), nil }

func TestSelectCloneTarget(t *testing.T) {
    t.Parallel()

usEast := coreapi.Mirror{Repo: "entire-api", ClusterHost: "aws-us-east-2.entire.io"}
    euWest := coreapi.Mirror{Repo: "entire-api", ClusterHost: "aws-eu-west-1.entire.io"}

t.Run("single placement returns directly", func(t *testing.T) {
        t.Parallel()
        got, err := selectCloneTarget(newCloneTestCmd(), []coreapi.Mirror{usEast}, "")
        require.NoError(t, err)
        require.Equal(t, "aws-us-east-2.entire.io", got.ClusterHost)
    })

t.Run("dedupes repeated host to a single placement", func(t *testing.T) {
        t.Parallel()
        got, err := selectCloneTarget(newCloneTestCmd(), []coreapi.Mirror{usEast, usEast}, "")
        require.NoError(t, err)
        require.Equal(t, "aws-us-east-2.entire.io", got.ClusterHost)
    })

t.Run("--cluster picks the matching placement", func(t *testing.T) {
        t.Parallel()
        got, err := selectCloneTarget(newCloneTestCmd(), []coreapi.Mirror{usEast, euWest}, "aws-eu-west-1.entire.io")
        require.NoError(t, err)
        require.Equal(t, "aws-eu-west-1.entire.io", got.ClusterHost)
    })

t.Run("--cluster with no match errors and lists hosts", func(t *testing.T) {
        t.Parallel()
        _, err := selectCloneTarget(newCloneTestCmd(), []coreapi.Mirror{usEast, euWest}, "aws-ap-south-1.entire.io")
        require.Error(t, err)
        require.Contains(t, err.Error(), "aws-us-east-2.entire.io")
        require.Contains(t, err.Error(), "aws-eu-west-1.entire.io")
    })

t.Run("multiple placements with no terminal errors with a --cluster pointer", func(t *testing.T) {
        t.Parallel()
        // go test is non-interactive, so the picker path is unreachable here.
        _, err := selectCloneTarget(newCloneTestCmd(), []coreapi.Mirror{usEast, euWest}, "")
        require.Error(t, err)
        require.Contains(t, err.Error(), "--cluster")
    })
}

// TestListMirrorsForRepo_FiltersByRepo verifies the client-side repo filter:
// the list API filters provider+owner server-side, but the repo match (which
// the API has no param for) is applied locally.
func TestListMirrorsForRepo_FiltersByRepo(t *testing.T) {
    t.Parallel()
    srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
        w.Header().Set("Content-Type", "application/json")
        body := &coreapi.ListMirrorsOutputBody{Mirrors: []coreapi.Mirror{
            {Owner: "entirehq", Repo: "entire-api", ClusterHost: "aws-us-east-2.entire.io"},
            {Owner: "entirehq", Repo: "entire-api", ClusterHost: "aws-eu-west-1.entire.io"},
            {Owner: "entirehq", Repo: "entire-cli", ClusterHost: "aws-us-east-2.entire.io"},
        }}
        if err := writeJSON(w, body); err != nil {
            t.Errorf("encode response: %v", err)
        }
    }))
    t.Cleanup(srv.Close)

c, err := coreapi.NewWithBearer(srv.URL, "tok")
    require.NoError(t, err)

got, err := listMirrorsForRepo(t.Context(), c, "github", "entirehq", "entire-api")
    require.NoError(t, err)
    require.Len(t, got, 2)
    for _, m := range got {
        require.Equal(t, "entire-api", m.Repo)
    }
}