From 5a370f22ba16cf2213860599892e483e91a7b235 Mon Sep 17 00:00:00 2001 From: Erik Seliger Date: Thu, 4 Apr 2024 15:56:51 +0200 Subject: [PATCH] gerrit: Add support for SSH cloning (#61537) To bring Gerrit support more in line with other code hosts and because a customer recently ran into this limitation, this PR adds support for the SSH flag. The diff is mostly straightforward, with two things to watch out for: - The clone URL construction in makeRepo was wrong previously, it missed the `/a/`. This is only used for visuals, but still annoying. So I moved the whole construction into here from the gitserver cloneURL package. - The SSH hostname and port are configurable in Gerrit, so to construct the right URL we need to fetch some instance metadata. This would be costly to do in the gitserver method, so we persist all the info needed to construct clone URLs "offline" during the cloning process by storing all the data for HTTP and SSH clones on the repo metadata column. This is mostly in line with other code hosts as well, see GitLab right above in the gitserver/cloneurl package. Closes https://github.com/sourcegraph/sourcegraph/issues/60597 ## Test plan Added tests for the various stages, recreated recorded responses, and tried both HTTP and SSH cloning locally, both worked with out Gerrit instance. --- CHANGELOG.md | 1 + cmd/gitserver/internal/cloneurl/clone_url.go | 24 +- .../internal/cloneurl/clone_url_test.go | 38 +- internal/authz/providers/gerrit/mocks_test.go | 124 ++++++ internal/batches/sources/mocks_test.go | 124 ++++++ internal/extsvc/gerrit/client.go | 77 +++- internal/extsvc/gerrit/client_test.go | 15 + .../gerrit/testdata/golden/ListProjects.json | 22 +- .../gerrit/testdata/vcr/GetSSHInfo.yaml | 37 ++ internal/extsvc/gerrit/types.go | 5 + internal/repos/gerrit.go | 43 +- internal/repos/gerrit_test.go | 21 + .../TestGerritSource_ListRepos/no_filtering | 208 +++++++++- .../TestGerritSource_ListRepos/ssh_enabled | 376 ++++++++++++++++++ .../exclusion_overrides_inclusion.yaml | 73 ++-- .../no_filtering.yaml | 73 ++-- .../ssh_enabled.yaml | 155 ++++++++ .../with_exclusion.yaml | 73 ++-- .../with_filtering.yaml | 73 ++-- schema/gerrit.schema.json | 8 +- schema/schema.go | 8 +- 21 files changed, 1391 insertions(+), 187 deletions(-) create mode 100644 internal/extsvc/gerrit/testdata/vcr/GetSSHInfo.yaml create mode 100644 internal/repos/testdata/sources/GERRIT/TestGerritSource_ListRepos/ssh_enabled create mode 100644 internal/repos/testdata/sources/TestGerritSource_ListRepos/ssh_enabled.yaml diff --git a/CHANGELOG.md b/CHANGELOG.md index 7c1603266d2..25353676b28 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ All notable changes to Sourcegraph are documented in this file. - Auth providers now support a `noSignIn` option that, when set to true, will hide the auth provider from the sign in page, but still allow users to connect the external account from their Account Security page for permissions syncing. [#60722](https://github.com/sourcegraph/sourcegraph/pull/60722) - Added a "Commits" button to the folders in repos that shows commits for the items in that folder. [#60909](https://github.com/sourcegraph/sourcegraph/pull/60909) - The frontend Grafana dashboard has a new Prometheus metric that tracks the rate of requests that Sourcegraph issues to external services. [#61348](https://github.com/sourcegraph/sourcegraph/pull/61348) +- Added support for the `gitURLType` setting for Gerrit, Sourcegraph now supports cloning from Gerrit via SSH. Note: Not on Cloud yet, like for all code hosts. [#61537](https://github.com/sourcegraph/sourcegraph/pull/61537) - Support for OpenAI chat models for enterprise customers. [#61539](https://github.com/sourcegraph/sourcegraph/pull/61539) ### Changed diff --git a/cmd/gitserver/internal/cloneurl/clone_url.go b/cmd/gitserver/internal/cloneurl/clone_url.go index 4989bc4b569..fd8ba4feb82 100644 --- a/cmd/gitserver/internal/cloneurl/clone_url.go +++ b/cmd/gitserver/internal/cloneurl/clone_url.go @@ -268,16 +268,32 @@ func gitlabCloneURL(logger log.Logger, repo *gitlab.Project, cfg *schema.GitLabC } func gerritCloneURL(logger log.Logger, project *gerrit.Project, cfg *schema.GerritConnection) string { - u, err := url.Parse(cfg.Url) + if cfg.GitURLType == "ssh" { + return project.SSHURLToRepo // SSH authentication must be provided out-of-band + } + + // TODO: Backcompat. Remove this branch in Sourcegraph 5.5. + if project.HTTPURLToRepo == "" { + u, err := url.Parse(cfg.Url) + if err != nil { + logger.Warn("Error adding authentication to Gerrit project remote URL.", log.String("url", cfg.Url), log.Error(err)) + return cfg.Url + } + u.User = url.UserPassword(cfg.Username, cfg.Password) + + // Gerrit encodes slashes in IDs, so need to decode them. The 'a' is for cloning with auth. + u.Path = path.Join("a", strings.ReplaceAll(project.ID, "%2F", "/")) + + return u.String() + } + + u, err := url.Parse(project.HTTPURLToRepo) if err != nil { logger.Warn("Error adding authentication to Gerrit project remote URL.", log.String("url", cfg.Url), log.Error(err)) return cfg.Url } u.User = url.UserPassword(cfg.Username, cfg.Password) - // Gerrit encodes slashes in IDs, so need to decode them. The 'a' is for cloning with auth. - u.Path = path.Join("a", strings.ReplaceAll(project.ID, "%2F", "/")) - return u.String() } diff --git a/cmd/gitserver/internal/cloneurl/clone_url_test.go b/cmd/gitserver/internal/cloneurl/clone_url_test.go index d1e0d5c3f68..d29b416406d 100644 --- a/cmd/gitserver/internal/cloneurl/clone_url_test.go +++ b/cmd/gitserver/internal/cloneurl/clone_url_test.go @@ -262,14 +262,40 @@ func TestGerritCloneURL(t *testing.T) { } project := &gerrit.Project{ - ID: "test-project", + ID: "test-project", + HTTPURLToRepo: "https://gerrit.com/a/test-project", + SSHURLToRepo: "ssh://gerrit-ssh.com:29418/a/test-project", } - got := gerritCloneURL(logtest.Scoped(t), project, &cfg) - want := "https://admin:pa$$word@gerrit.com/a/test-project" - if got != want { - t.Fatalf("wrong cloneURL, got: %q, want: %q", got, want) - } + t.Run("HTTP", func(t *testing.T) { + got := gerritCloneURL(logtest.Scoped(t), project, &cfg) + want := "https://admin:pa$$word@gerrit.com/a/test-project" + if got != want { + t.Fatalf("wrong cloneURL, got: %q, want: %q", got, want) + } + }) + t.Run("Legacy HTTP", func(t *testing.T) { + project := &gerrit.Project{ + ID: "test-project", + // Those values are not yet populated. This will only happen on the + // next sync after we introduced them. This function should still work. + HTTPURLToRepo: "", + SSHURLToRepo: "", + } + got := gerritCloneURL(logtest.Scoped(t), project, &cfg) + want := "https://admin:pa$$word@gerrit.com/a/test-project" + if got != want { + t.Fatalf("wrong cloneURL, got: %q, want: %q", got, want) + } + }) + t.Run("SSH", func(t *testing.T) { + cfg.GitURLType = "ssh" + got := gerritCloneURL(logtest.Scoped(t), project, &cfg) + want := "ssh://gerrit-ssh.com:29418/a/test-project" + if got != want { + t.Fatalf("wrong cloneURL, got: %q, want: %q", got, want) + } + }) } func TestPerforceCloneURL(t *testing.T) { diff --git a/internal/authz/providers/gerrit/mocks_test.go b/internal/authz/providers/gerrit/mocks_test.go index e6543af2da6..654eea8b8b7 100644 --- a/internal/authz/providers/gerrit/mocks_test.go +++ b/internal/authz/providers/gerrit/mocks_test.go @@ -41,6 +41,9 @@ type MockGerritClient struct { // GetGroupFunc is an instance of a mock function object controlling the // behavior of the method GetGroup. GetGroupFunc *GerritClientGetGroupFunc + // GetSSHInfoFunc is an instance of a mock function object controlling + // the behavior of the method GetSSHInfo. + GetSSHInfoFunc *GerritClientGetSSHInfoFunc // GetURLFunc is an instance of a mock function object controlling the // behavior of the method GetURL. GetURLFunc *GerritClientGetURLFunc @@ -112,6 +115,11 @@ func NewMockGerritClient() *MockGerritClient { return }, }, + GetSSHInfoFunc: &GerritClientGetSSHInfoFunc{ + defaultHook: func(context.Context) (r0 string, r1 int, r2 error) { + return + }, + }, GetURLFunc: &GerritClientGetURLFunc{ defaultHook: func() (r0 *url.URL) { return @@ -204,6 +212,11 @@ func NewStrictMockGerritClient() *MockGerritClient { panic("unexpected invocation of MockGerritClient.GetGroup") }, }, + GetSSHInfoFunc: &GerritClientGetSSHInfoFunc{ + defaultHook: func(context.Context) (string, int, error) { + panic("unexpected invocation of MockGerritClient.GetSSHInfo") + }, + }, GetURLFunc: &GerritClientGetURLFunc{ defaultHook: func() *url.URL { panic("unexpected invocation of MockGerritClient.GetURL") @@ -283,6 +296,9 @@ func NewMockGerritClientFrom(i gerrit.Client) *MockGerritClient { GetGroupFunc: &GerritClientGetGroupFunc{ defaultHook: i.GetGroup, }, + GetSSHInfoFunc: &GerritClientGetSSHInfoFunc{ + defaultHook: i.GetSSHInfo, + }, GetURLFunc: &GerritClientGetURLFunc{ defaultHook: i.GetURL, }, @@ -1062,6 +1078,114 @@ func (c GerritClientGetGroupFuncCall) Results() []interface{} { return []interface{}{c.Result0, c.Result1} } +// GerritClientGetSSHInfoFunc describes the behavior when the GetSSHInfo +// method of the parent MockGerritClient instance is invoked. +type GerritClientGetSSHInfoFunc struct { + defaultHook func(context.Context) (string, int, error) + hooks []func(context.Context) (string, int, error) + history []GerritClientGetSSHInfoFuncCall + mutex sync.Mutex +} + +// GetSSHInfo delegates to the next hook function in the queue and stores +// the parameter and result values of this invocation. +func (m *MockGerritClient) GetSSHInfo(v0 context.Context) (string, int, error) { + r0, r1, r2 := m.GetSSHInfoFunc.nextHook()(v0) + m.GetSSHInfoFunc.appendCall(GerritClientGetSSHInfoFuncCall{v0, r0, r1, r2}) + return r0, r1, r2 +} + +// SetDefaultHook sets function that is called when the GetSSHInfo method of +// the parent MockGerritClient instance is invoked and the hook queue is +// empty. +func (f *GerritClientGetSSHInfoFunc) SetDefaultHook(hook func(context.Context) (string, int, error)) { + f.defaultHook = hook +} + +// PushHook adds a function to the end of hook queue. Each invocation of the +// GetSSHInfo method of the parent MockGerritClient instance invokes the +// hook at the front of the queue and discards it. After the queue is empty, +// the default hook function is invoked for any future action. +func (f *GerritClientGetSSHInfoFunc) PushHook(hook func(context.Context) (string, int, error)) { + f.mutex.Lock() + f.hooks = append(f.hooks, hook) + f.mutex.Unlock() +} + +// SetDefaultReturn calls SetDefaultHook with a function that returns the +// given values. +func (f *GerritClientGetSSHInfoFunc) SetDefaultReturn(r0 string, r1 int, r2 error) { + f.SetDefaultHook(func(context.Context) (string, int, error) { + return r0, r1, r2 + }) +} + +// PushReturn calls PushHook with a function that returns the given values. +func (f *GerritClientGetSSHInfoFunc) PushReturn(r0 string, r1 int, r2 error) { + f.PushHook(func(context.Context) (string, int, error) { + return r0, r1, r2 + }) +} + +func (f *GerritClientGetSSHInfoFunc) nextHook() func(context.Context) (string, int, error) { + f.mutex.Lock() + defer f.mutex.Unlock() + + if len(f.hooks) == 0 { + return f.defaultHook + } + + hook := f.hooks[0] + f.hooks = f.hooks[1:] + return hook +} + +func (f *GerritClientGetSSHInfoFunc) appendCall(r0 GerritClientGetSSHInfoFuncCall) { + f.mutex.Lock() + f.history = append(f.history, r0) + f.mutex.Unlock() +} + +// History returns a sequence of GerritClientGetSSHInfoFuncCall objects +// describing the invocations of this function. +func (f *GerritClientGetSSHInfoFunc) History() []GerritClientGetSSHInfoFuncCall { + f.mutex.Lock() + history := make([]GerritClientGetSSHInfoFuncCall, len(f.history)) + copy(history, f.history) + f.mutex.Unlock() + + return history +} + +// GerritClientGetSSHInfoFuncCall is an object that describes an invocation +// of method GetSSHInfo on an instance of MockGerritClient. +type GerritClientGetSSHInfoFuncCall struct { + // Arg0 is the value of the 1st argument passed to this method + // invocation. + Arg0 context.Context + // Result0 is the value of the 1st result returned from this method + // invocation. + Result0 string + // Result1 is the value of the 2nd result returned from this method + // invocation. + Result1 int + // Result2 is the value of the 3rd result returned from this method + // invocation. + Result2 error +} + +// Args returns an interface slice containing the arguments of this +// invocation. +func (c GerritClientGetSSHInfoFuncCall) Args() []interface{} { + return []interface{}{c.Arg0} +} + +// Results returns an interface slice containing the results of this +// invocation. +func (c GerritClientGetSSHInfoFuncCall) Results() []interface{} { + return []interface{}{c.Result0, c.Result1, c.Result2} +} + // GerritClientGetURLFunc describes the behavior when the GetURL method of // the parent MockGerritClient instance is invoked. type GerritClientGetURLFunc struct { diff --git a/internal/batches/sources/mocks_test.go b/internal/batches/sources/mocks_test.go index d83f8578064..bdde03249a5 100644 --- a/internal/batches/sources/mocks_test.go +++ b/internal/batches/sources/mocks_test.go @@ -8759,6 +8759,9 @@ type MockGerritClient struct { // GetGroupFunc is an instance of a mock function object controlling the // behavior of the method GetGroup. GetGroupFunc *GerritClientGetGroupFunc + // GetSSHInfoFunc is an instance of a mock function object controlling + // the behavior of the method GetSSHInfo. + GetSSHInfoFunc *GerritClientGetSSHInfoFunc // GetURLFunc is an instance of a mock function object controlling the // behavior of the method GetURL. GetURLFunc *GerritClientGetURLFunc @@ -8830,6 +8833,11 @@ func NewMockGerritClient() *MockGerritClient { return }, }, + GetSSHInfoFunc: &GerritClientGetSSHInfoFunc{ + defaultHook: func(context.Context) (r0 string, r1 int, r2 error) { + return + }, + }, GetURLFunc: &GerritClientGetURLFunc{ defaultHook: func() (r0 *url.URL) { return @@ -8922,6 +8930,11 @@ func NewStrictMockGerritClient() *MockGerritClient { panic("unexpected invocation of MockGerritClient.GetGroup") }, }, + GetSSHInfoFunc: &GerritClientGetSSHInfoFunc{ + defaultHook: func(context.Context) (string, int, error) { + panic("unexpected invocation of MockGerritClient.GetSSHInfo") + }, + }, GetURLFunc: &GerritClientGetURLFunc{ defaultHook: func() *url.URL { panic("unexpected invocation of MockGerritClient.GetURL") @@ -9001,6 +9014,9 @@ func NewMockGerritClientFrom(i gerrit.Client) *MockGerritClient { GetGroupFunc: &GerritClientGetGroupFunc{ defaultHook: i.GetGroup, }, + GetSSHInfoFunc: &GerritClientGetSSHInfoFunc{ + defaultHook: i.GetSSHInfo, + }, GetURLFunc: &GerritClientGetURLFunc{ defaultHook: i.GetURL, }, @@ -9780,6 +9796,114 @@ func (c GerritClientGetGroupFuncCall) Results() []interface{} { return []interface{}{c.Result0, c.Result1} } +// GerritClientGetSSHInfoFunc describes the behavior when the GetSSHInfo +// method of the parent MockGerritClient instance is invoked. +type GerritClientGetSSHInfoFunc struct { + defaultHook func(context.Context) (string, int, error) + hooks []func(context.Context) (string, int, error) + history []GerritClientGetSSHInfoFuncCall + mutex sync.Mutex +} + +// GetSSHInfo delegates to the next hook function in the queue and stores +// the parameter and result values of this invocation. +func (m *MockGerritClient) GetSSHInfo(v0 context.Context) (string, int, error) { + r0, r1, r2 := m.GetSSHInfoFunc.nextHook()(v0) + m.GetSSHInfoFunc.appendCall(GerritClientGetSSHInfoFuncCall{v0, r0, r1, r2}) + return r0, r1, r2 +} + +// SetDefaultHook sets function that is called when the GetSSHInfo method of +// the parent MockGerritClient instance is invoked and the hook queue is +// empty. +func (f *GerritClientGetSSHInfoFunc) SetDefaultHook(hook func(context.Context) (string, int, error)) { + f.defaultHook = hook +} + +// PushHook adds a function to the end of hook queue. Each invocation of the +// GetSSHInfo method of the parent MockGerritClient instance invokes the +// hook at the front of the queue and discards it. After the queue is empty, +// the default hook function is invoked for any future action. +func (f *GerritClientGetSSHInfoFunc) PushHook(hook func(context.Context) (string, int, error)) { + f.mutex.Lock() + f.hooks = append(f.hooks, hook) + f.mutex.Unlock() +} + +// SetDefaultReturn calls SetDefaultHook with a function that returns the +// given values. +func (f *GerritClientGetSSHInfoFunc) SetDefaultReturn(r0 string, r1 int, r2 error) { + f.SetDefaultHook(func(context.Context) (string, int, error) { + return r0, r1, r2 + }) +} + +// PushReturn calls PushHook with a function that returns the given values. +func (f *GerritClientGetSSHInfoFunc) PushReturn(r0 string, r1 int, r2 error) { + f.PushHook(func(context.Context) (string, int, error) { + return r0, r1, r2 + }) +} + +func (f *GerritClientGetSSHInfoFunc) nextHook() func(context.Context) (string, int, error) { + f.mutex.Lock() + defer f.mutex.Unlock() + + if len(f.hooks) == 0 { + return f.defaultHook + } + + hook := f.hooks[0] + f.hooks = f.hooks[1:] + return hook +} + +func (f *GerritClientGetSSHInfoFunc) appendCall(r0 GerritClientGetSSHInfoFuncCall) { + f.mutex.Lock() + f.history = append(f.history, r0) + f.mutex.Unlock() +} + +// History returns a sequence of GerritClientGetSSHInfoFuncCall objects +// describing the invocations of this function. +func (f *GerritClientGetSSHInfoFunc) History() []GerritClientGetSSHInfoFuncCall { + f.mutex.Lock() + history := make([]GerritClientGetSSHInfoFuncCall, len(f.history)) + copy(history, f.history) + f.mutex.Unlock() + + return history +} + +// GerritClientGetSSHInfoFuncCall is an object that describes an invocation +// of method GetSSHInfo on an instance of MockGerritClient. +type GerritClientGetSSHInfoFuncCall struct { + // Arg0 is the value of the 1st argument passed to this method + // invocation. + Arg0 context.Context + // Result0 is the value of the 1st result returned from this method + // invocation. + Result0 string + // Result1 is the value of the 2nd result returned from this method + // invocation. + Result1 int + // Result2 is the value of the 3rd result returned from this method + // invocation. + Result2 error +} + +// Args returns an interface slice containing the arguments of this +// invocation. +func (c GerritClientGetSSHInfoFuncCall) Args() []interface{} { + return []interface{}{c.Arg0} +} + +// Results returns an interface slice containing the results of this +// invocation. +func (c GerritClientGetSSHInfoFuncCall) Results() []interface{} { + return []interface{}{c.Result0, c.Result1, c.Result2} +} + // GerritClientGetURLFunc describes the behavior when the GetURL method of // the parent MockGerritClient instance is invoked. type GerritClientGetURLFunc struct { diff --git a/internal/extsvc/gerrit/client.go b/internal/extsvc/gerrit/client.go index c232e1cc469..db6c2358ce6 100644 --- a/internal/extsvc/gerrit/client.go +++ b/internal/extsvc/gerrit/client.go @@ -9,6 +9,7 @@ import ( "io" "net/http" "net/url" + "strconv" "github.com/sourcegraph/log" @@ -52,6 +53,7 @@ type Client interface { SetReadyForReview(ctx context.Context, changeID string) error MoveChange(ctx context.Context, changeID string, input MoveChangePayload) (*Change, error) SetCommitMessage(ctx context.Context, changeID string, input SetCommitMessagePayload) error + GetSSHInfo(ctx context.Context) (hostname string, port int, _ error) } // NewClient returns an authenticated Gerrit API client with @@ -115,6 +117,40 @@ func (c *client) GetAuthenticatedUserAccount(ctx context.Context) (*Account, err return &account, nil } +func (c *client) GetSSHInfo(ctx context.Context) (hostname string, port int, _ error) { + req, err := http.NewRequest("GET", "ssh_info", nil) + if err != nil { + return "", 0, err + } + + resp, err := c.doHTTP(ctx, req) + if err != nil { + return "", 0, err + } + defer resp.Body.Close() + + var bs []byte + bs, err = io.ReadAll(resp.Body) + if err != nil { + return "", 0, err + } + + fs := bytes.Fields(bs) + + if len(fs) != 2 { + return "", 0, errors.Wrapf(err, "got invalid reponse from Gerrit for ssh_info %q", string(bs)) + } + + hostname = string(fs[0]) + + port, err = strconv.Atoi(string(fs[1])) + if err != nil { + return "", 0, errors.Wrapf(err, "got invalid port from Gerrit for ssh_info %q", string(bs)) + } + + return hostname, port, nil +} + func (c *client) GetGroup(ctx context.Context, groupName string) (Group, error) { urlGroup := url.URL{Path: fmt.Sprintf("a/groups/%s", groupName)} @@ -131,6 +167,28 @@ func (c *client) GetGroup(ctx context.Context, groupName string) (Group, error) } func (c *client) do(ctx context.Context, req *http.Request, result any) (*http.Response, error) { //nolint:unparam // http.Response is never used, but it makes sense API wise. + resp, err := c.doHTTP(ctx, req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + var bs []byte + bs, err = io.ReadAll(resp.Body) + if err != nil { + return nil, err + } + + // Gerrit attaches this prefix to most of its responses, so if it exists, we cut it, so we can parse it as a json properly. + bs, _ = bytes.CutPrefix(bs, []byte(")]}'")) + + if result == nil { + return resp, nil + } + return resp, json.Unmarshal(bs, result) +} + +func (c *client) doHTTP(ctx context.Context, req *http.Request) (*http.Response, error) { //nolint:unparam // http.Response is never used, but it makes sense API wise. req.URL = c.URL.ResolveReference(req.URL) // Authenticate request with auther @@ -148,28 +206,19 @@ func (c *client) do(ctx context.Context, req *http.Request, result any) (*http.R if err != nil { return nil, err } - defer resp.Body.Close() - - var bs []byte - bs, err = io.ReadAll(resp.Body) - if err != nil { - return nil, err - } - - // Gerrit attaches this prefix to most of its responses, so if it exists, we cut it, so we can parse it as a json properly. - bs, _ = bytes.CutPrefix(bs, []byte(")]}'")) if resp.StatusCode < 200 || resp.StatusCode >= 400 { + defer resp.Body.Close() + // Read the body best effort. + bs, _ := io.ReadAll(resp.Body) return nil, &httpError{ URL: req.URL, StatusCode: resp.StatusCode, Body: bs, } } - if result == nil { - return resp, nil - } - return resp, json.Unmarshal(bs, result) + + return resp, nil } func (c *client) GetURL() *url.URL { diff --git a/internal/extsvc/gerrit/client_test.go b/internal/extsvc/gerrit/client_test.go index fc62a4169a1..a7e0afaf7f8 100644 --- a/internal/extsvc/gerrit/client_test.go +++ b/internal/extsvc/gerrit/client_test.go @@ -58,3 +58,18 @@ func TestClient_do(t *testing.T) { require.Equal(t, "value", respStruct.Key) }) } + +func TestClient_GetSSHInfo(t *testing.T) { + cli, save := NewTestClient(t, "GetSSHInfo", *update) + defer save() + + ctx := context.Background() + + hostname, port, err := cli.GetSSHInfo(ctx) + if err != nil { + t.Fatal(err) + } + + require.Equal(t, "gerrit-ssh.sgdev.org", hostname) + require.Equal(t, 29418, port) +} diff --git a/internal/extsvc/gerrit/testdata/golden/ListProjects.json b/internal/extsvc/gerrit/testdata/golden/ListProjects.json index bef7faab640..a89439e2450 100644 --- a/internal/extsvc/gerrit/testdata/golden/ListProjects.json +++ b/internal/extsvc/gerrit/testdata/golden/ListProjects.json @@ -6,7 +6,9 @@ "parent": "", "state": "ACTIVE", "branches": null, - "labels": null + "labels": null, + "http_url_to_repo": "", + "ssh_url_to_repo": "" }, "All-Users": { "description": "", @@ -15,7 +17,9 @@ "parent": "", "state": "ACTIVE", "branches": null, - "labels": null + "labels": null, + "http_url_to_repo": "", + "ssh_url_to_repo": "" }, "beatrix": { "description": "", @@ -24,7 +28,9 @@ "parent": "", "state": "ACTIVE", "branches": null, - "labels": null + "labels": null, + "http_url_to_repo": "", + "ssh_url_to_repo": "" }, "idan-temp": { "description": "", @@ -33,7 +39,9 @@ "parent": "", "state": "ACTIVE", "branches": null, - "labels": null + "labels": null, + "http_url_to_repo": "", + "ssh_url_to_repo": "" }, "k8s-gerrit": { "description": "", @@ -42,6 +50,8 @@ "parent": "", "state": "ACTIVE", "branches": null, - "labels": null + "labels": null, + "http_url_to_repo": "", + "ssh_url_to_repo": "" } - } \ No newline at end of file + } diff --git a/internal/extsvc/gerrit/testdata/vcr/GetSSHInfo.yaml b/internal/extsvc/gerrit/testdata/vcr/GetSSHInfo.yaml new file mode 100644 index 00000000000..2c365fc40ea --- /dev/null +++ b/internal/extsvc/gerrit/testdata/vcr/GetSSHInfo.yaml @@ -0,0 +1,37 @@ +--- +version: 1 +interactions: +- request: + body: "" + form: {} + headers: {} + url: https://gerrit.sgdev.org/ssh_info + method: GET + response: + body: gerrit-ssh.sgdev.org 29418 + headers: + Cache-Control: + - no-cache, no-store, max-age=0, must-revalidate + Cf-Cache-Status: + - DYNAMIC + Cf-Ray: + - 86e55e9c3cf74504-TXL + Content-Length: + - "26" + Content-Type: + - text/plain;charset=utf-8 + Date: + - Wed, 03 Apr 2024 01:43:45 GMT + Expires: + - Mon, 01 Jan 1990 00:00:00 GMT + Pragma: + - no-cache + Server: + - cloudflare + Strict-Transport-Security: + - max-age=15724800; includeSubDomains + X-Frame-Options: + - DENY + status: 200 OK + code: 200 + duration: "" diff --git a/internal/extsvc/gerrit/types.go b/internal/extsvc/gerrit/types.go index 3f58bd45e9c..75b18f0eb17 100644 --- a/internal/extsvc/gerrit/types.go +++ b/internal/extsvc/gerrit/types.go @@ -149,6 +149,11 @@ type Project struct { State string `json:"state"` Branches map[string]string `json:"branches"` Labels map[string]Label `json:"labels"` + + // This field is not part of the API, and will be computed in repos.GerritSource.ListRepos. + HTTPURLToRepo string `json:"http_url_to_repo"` + // This field is not part of the API, and will be computed in repos.GerritSource.ListRepos. + SSHURLToRepo string `json:"ssh_url_to_repo"` } type Label struct { diff --git a/internal/repos/gerrit.go b/internal/repos/gerrit.go index 7873f9df408..af2c51e4ce5 100644 --- a/internal/repos/gerrit.go +++ b/internal/repos/gerrit.go @@ -5,8 +5,8 @@ import ( "net/url" "path" "sort" - - "github.com/goware/urlx" + "strconv" + "strings" "github.com/sourcegraph/sourcegraph/internal/api" "github.com/sourcegraph/sourcegraph/internal/extsvc" @@ -31,6 +31,8 @@ type GerritSource struct { // disallowedProjects is a set of project names that will never be added // by this source. This takes precedence over allowedProjects. disallowedProjects map[string]struct{} + // If true, the connection is configured to use SSH instead of HTTPS. + ssh bool } // NewGerritSource returns a new GerritSource from the given external service. @@ -84,6 +86,7 @@ func NewGerritSource(ctx context.Context, svc *types.ExternalService, cf *httpcl serviceID: extsvc.NormalizeBaseURL(cli.GetURL()).String(), perPage: 100, private: c.Authorization != nil, + ssh: c.GitURLType == "ssh", }, nil } @@ -101,6 +104,12 @@ func (s *GerritSource) ListRepos(ctx context.Context, results chan SourceResult) OnlyCodeProjects: true, } + sshHostname, sshPort, err := s.cli.GetSSHInfo(ctx) + if err != nil { + results <- SourceResult{Source: s, Err: errors.Wrap(err, "failed to get ssh info for Gerrit instance")} + return + } + for { page, nextPage, err := s.cli.ListProjects(ctx, args) if err != nil { @@ -129,11 +138,7 @@ func (s *GerritSource) ListRepos(ctx context.Context, results chan SourceResult) } } - repo, err := s.makeRepo(p, page[p]) - if err != nil { - results <- SourceResult{Source: s, Err: err} - return - } + repo := s.makeRepo(page[p], s.cli.GetURL(), sshHostname, sshPort) results <- SourceResult{Source: s, Repo: repo} } @@ -150,15 +155,25 @@ func (s *GerritSource) ExternalServices() types.ExternalServices { return types.ExternalServices{s.svc} } -func (s *GerritSource) makeRepo(projectName string, p *gerrit.Project) (*types.Repo, error) { +func (s *GerritSource) makeRepo(p *gerrit.Project, instanceHTTPURL *url.URL, sshHostname string, sshPort int) *types.Repo { + u := *instanceHTTPURL + u.User = nil + // Gerrit encodes slashes in IDs, so need to decode them. + decodedName := strings.ReplaceAll(p.ID, "%2F", "/") + // The 'a' is for cloning with auth. + u.Path = path.Join("a", decodedName) + urn := s.svc.URN() - fullURL, err := urlx.Parse(s.cli.GetURL().JoinPath(projectName).String()) - if err != nil { - return nil, err + p.HTTPURLToRepo = u.String() + p.SSHURLToRepo = "ssh://" + sshHostname + ":" + strconv.Itoa(sshPort) + "/" + decodedName + + cloneURL := p.HTTPURLToRepo + if s.ssh { + cloneURL = p.SSHURLToRepo } - name := path.Join(fullURL.Host, fullURL.Path) + name := path.Join(u.Host, decodedName) return &types.Repo{ Name: api.RepoName(name), URI: name, @@ -172,12 +187,12 @@ func (s *GerritSource) makeRepo(projectName string, p *gerrit.Project) (*types.R Sources: map[string]*types.SourceInfo{ urn: { ID: urn, - CloneURL: fullURL.String(), + CloneURL: cloneURL, }, }, Metadata: p, Private: s.private, - }, nil + } } // WithAuthenticator returns a copy of the original Source configured to use the diff --git a/internal/repos/gerrit_test.go b/internal/repos/gerrit_test.go index 7a628e27f75..1b6a72eedbf 100644 --- a/internal/repos/gerrit_test.go +++ b/internal/repos/gerrit_test.go @@ -40,6 +40,27 @@ func TestGerritSource_ListRepos(t *testing.T) { testutil.AssertGolden(t, "testdata/sources/GERRIT/"+t.Name(), Update(t.Name()), repos) }) + t.Run("ssh enabled", func(t *testing.T) { + cf, save := NewClientFactory(t, t.Name()) + defer save(t) + + svc := typestest.MakeExternalService(t, extsvc.VariantGerrit, &schema.GerritConnection{ + Url: "https://gerrit.sgdev.org", + Username: os.Getenv("GERRIT_USERNAME"), + Password: os.Getenv("GERRIT_PASSWORD"), + GitURLType: "ssh", + }) + + ctx := context.Background() + src, err := NewGerritSource(ctx, svc, cf) + require.NoError(t, err) + + repos, err := ListAll(ctx, src) + require.NoError(t, err) + + testutil.AssertGolden(t, "testdata/sources/GERRIT/"+t.Name(), Update(t.Name()), repos) + }) + t.Run("with filtering", func(t *testing.T) { cf, save := NewClientFactory(t, t.Name()) defer save(t) diff --git a/internal/repos/testdata/sources/GERRIT/TestGerritSource_ListRepos/no_filtering b/internal/repos/testdata/sources/GERRIT/TestGerritSource_ListRepos/no_filtering index e7f1417272c..a1f05cfbeda 100644 --- a/internal/repos/testdata/sources/GERRIT/TestGerritSource_ListRepos/no_filtering +++ b/internal/repos/testdata/sources/GERRIT/TestGerritSource_ListRepos/no_filtering @@ -1,4 +1,72 @@ [ + { + "ID": 0, + "Name": "gerrit.sgdev.org/a/gabe/repo", + "URI": "gerrit.sgdev.org/a/gabe/repo", + "Description": "", + "Fork": false, + "Archived": false, + "Private": false, + "CreatedAt": "0001-01-01T00:00:00Z", + "UpdatedAt": "0001-01-01T00:00:00Z", + "DeletedAt": "0001-01-01T00:00:00Z", + "ExternalRepo": { + "ID": "a%2Fgabe%2Frepo", + "ServiceType": "gerrit", + "ServiceID": "https://gerrit.sgdev.org/" + }, + "Sources": { + "extsvc:gerrit:0": { + "ID": "extsvc:gerrit:0", + "CloneURL": "https://gerrit.sgdev.org/a/a/gabe/repo" + } + }, + "Metadata": { + "description": "", + "id": "a%2Fgabe%2Frepo", + "name": "", + "parent": "", + "state": "ACTIVE", + "branches": null, + "labels": null, + "http_url_to_repo": "https://gerrit.sgdev.org/a/a/gabe/repo", + "ssh_url_to_repo": "ssh://gerrit-ssh.sgdev.org:29418/a/gabe/repo" + } + }, + { + "ID": 0, + "Name": "gerrit.sgdev.org/batch-changes-test-repo", + "URI": "gerrit.sgdev.org/batch-changes-test-repo", + "Description": "", + "Fork": false, + "Archived": false, + "Private": false, + "CreatedAt": "0001-01-01T00:00:00Z", + "UpdatedAt": "0001-01-01T00:00:00Z", + "DeletedAt": "0001-01-01T00:00:00Z", + "ExternalRepo": { + "ID": "batch-changes-test-repo", + "ServiceType": "gerrit", + "ServiceID": "https://gerrit.sgdev.org/" + }, + "Sources": { + "extsvc:gerrit:0": { + "ID": "extsvc:gerrit:0", + "CloneURL": "https://gerrit.sgdev.org/a/batch-changes-test-repo" + } + }, + "Metadata": { + "description": "", + "id": "batch-changes-test-repo", + "name": "", + "parent": "", + "state": "ACTIVE", + "branches": null, + "labels": null, + "http_url_to_repo": "https://gerrit.sgdev.org/a/batch-changes-test-repo", + "ssh_url_to_repo": "ssh://gerrit-ssh.sgdev.org:29418/batch-changes-test-repo" + } + }, { "ID": 0, "Name": "gerrit.sgdev.org/beatrix", @@ -18,7 +86,7 @@ "Sources": { "extsvc:gerrit:0": { "ID": "extsvc:gerrit:0", - "CloneURL": "https://gerrit.sgdev.org/beatrix" + "CloneURL": "https://gerrit.sgdev.org/a/beatrix" } }, "Metadata": { @@ -28,7 +96,111 @@ "parent": "", "state": "ACTIVE", "branches": null, - "labels": null + "labels": null, + "http_url_to_repo": "https://gerrit.sgdev.org/a/beatrix", + "ssh_url_to_repo": "ssh://gerrit-ssh.sgdev.org:29418/beatrix" + } + }, + { + "ID": 0, + "Name": "gerrit.sgdev.org/cbart-test", + "URI": "gerrit.sgdev.org/cbart-test", + "Description": "", + "Fork": false, + "Archived": false, + "Private": false, + "CreatedAt": "0001-01-01T00:00:00Z", + "UpdatedAt": "0001-01-01T00:00:00Z", + "DeletedAt": "0001-01-01T00:00:00Z", + "ExternalRepo": { + "ID": "cbart-test", + "ServiceType": "gerrit", + "ServiceID": "https://gerrit.sgdev.org/" + }, + "Sources": { + "extsvc:gerrit:0": { + "ID": "extsvc:gerrit:0", + "CloneURL": "https://gerrit.sgdev.org/a/cbart-test" + } + }, + "Metadata": { + "description": "", + "id": "cbart-test", + "name": "", + "parent": "", + "state": "ACTIVE", + "branches": null, + "labels": null, + "http_url_to_repo": "https://gerrit.sgdev.org/a/cbart-test", + "ssh_url_to_repo": "ssh://gerrit-ssh.sgdev.org:29418/cbart-test" + } + }, + { + "ID": 0, + "Name": "gerrit.sgdev.org/gabe", + "URI": "gerrit.sgdev.org/gabe", + "Description": "", + "Fork": false, + "Archived": false, + "Private": false, + "CreatedAt": "0001-01-01T00:00:00Z", + "UpdatedAt": "0001-01-01T00:00:00Z", + "DeletedAt": "0001-01-01T00:00:00Z", + "ExternalRepo": { + "ID": "gabe", + "ServiceType": "gerrit", + "ServiceID": "https://gerrit.sgdev.org/" + }, + "Sources": { + "extsvc:gerrit:0": { + "ID": "extsvc:gerrit:0", + "CloneURL": "https://gerrit.sgdev.org/a/gabe" + } + }, + "Metadata": { + "description": "", + "id": "gabe", + "name": "", + "parent": "", + "state": "ACTIVE", + "branches": null, + "labels": null, + "http_url_to_repo": "https://gerrit.sgdev.org/a/gabe", + "ssh_url_to_repo": "ssh://gerrit-ssh.sgdev.org:29418/gabe" + } + }, + { + "ID": 0, + "Name": "gerrit.sgdev.org/gabe/test", + "URI": "gerrit.sgdev.org/gabe/test", + "Description": "", + "Fork": false, + "Archived": false, + "Private": false, + "CreatedAt": "0001-01-01T00:00:00Z", + "UpdatedAt": "0001-01-01T00:00:00Z", + "DeletedAt": "0001-01-01T00:00:00Z", + "ExternalRepo": { + "ID": "gabe%2Ftest", + "ServiceType": "gerrit", + "ServiceID": "https://gerrit.sgdev.org/" + }, + "Sources": { + "extsvc:gerrit:0": { + "ID": "extsvc:gerrit:0", + "CloneURL": "https://gerrit.sgdev.org/a/gabe/test" + } + }, + "Metadata": { + "description": "", + "id": "gabe%2Ftest", + "name": "", + "parent": "", + "state": "ACTIVE", + "branches": null, + "labels": null, + "http_url_to_repo": "https://gerrit.sgdev.org/a/gabe/test", + "ssh_url_to_repo": "ssh://gerrit-ssh.sgdev.org:29418/gabe/test" } }, { @@ -50,7 +222,7 @@ "Sources": { "extsvc:gerrit:0": { "ID": "extsvc:gerrit:0", - "CloneURL": "https://gerrit.sgdev.org/idan-temp" + "CloneURL": "https://gerrit.sgdev.org/a/idan-temp" } }, "Metadata": { @@ -60,7 +232,9 @@ "parent": "", "state": "ACTIVE", "branches": null, - "labels": null + "labels": null, + "http_url_to_repo": "https://gerrit.sgdev.org/a/idan-temp", + "ssh_url_to_repo": "ssh://gerrit-ssh.sgdev.org:29418/idan-temp" } }, { @@ -82,7 +256,7 @@ "Sources": { "extsvc:gerrit:0": { "ID": "extsvc:gerrit:0", - "CloneURL": "https://gerrit.sgdev.org/k8s-gerrit" + "CloneURL": "https://gerrit.sgdev.org/a/k8s-gerrit" } }, "Metadata": { @@ -92,7 +266,9 @@ "parent": "", "state": "ACTIVE", "branches": null, - "labels": null + "labels": null, + "http_url_to_repo": "https://gerrit.sgdev.org/a/k8s-gerrit", + "ssh_url_to_repo": "ssh://gerrit-ssh.sgdev.org:29418/k8s-gerrit" } }, { @@ -114,7 +290,7 @@ "Sources": { "extsvc:gerrit:0": { "ID": "extsvc:gerrit:0", - "CloneURL": "https://gerrit.sgdev.org/sourcegraph" + "CloneURL": "https://gerrit.sgdev.org/a/sourcegraph" } }, "Metadata": { @@ -124,7 +300,9 @@ "parent": "", "state": "ACTIVE", "branches": null, - "labels": null + "labels": null, + "http_url_to_repo": "https://gerrit.sgdev.org/a/sourcegraph", + "ssh_url_to_repo": "ssh://gerrit-ssh.sgdev.org:29418/sourcegraph" } }, { @@ -146,7 +324,7 @@ "Sources": { "extsvc:gerrit:0": { "ID": "extsvc:gerrit:0", - "CloneURL": "https://gerrit.sgdev.org/sourcegraph/create" + "CloneURL": "https://gerrit.sgdev.org/a/sourcegraph/create" } }, "Metadata": { @@ -156,7 +334,9 @@ "parent": "", "state": "ACTIVE", "branches": null, - "labels": null + "labels": null, + "http_url_to_repo": "https://gerrit.sgdev.org/a/sourcegraph/create", + "ssh_url_to_repo": "ssh://gerrit-ssh.sgdev.org:29418/sourcegraph/create" } }, { @@ -178,7 +358,7 @@ "Sources": { "extsvc:gerrit:0": { "ID": "extsvc:gerrit:0", - "CloneURL": "https://gerrit.sgdev.org/src-cli" + "CloneURL": "https://gerrit.sgdev.org/a/src-cli" } }, "Metadata": { @@ -188,7 +368,9 @@ "parent": "", "state": "ACTIVE", "branches": null, - "labels": null + "labels": null, + "http_url_to_repo": "https://gerrit.sgdev.org/a/src-cli", + "ssh_url_to_repo": "ssh://gerrit-ssh.sgdev.org:29418/src-cli" } } - ] \ No newline at end of file + ] diff --git a/internal/repos/testdata/sources/GERRIT/TestGerritSource_ListRepos/ssh_enabled b/internal/repos/testdata/sources/GERRIT/TestGerritSource_ListRepos/ssh_enabled new file mode 100644 index 00000000000..21d986c213a --- /dev/null +++ b/internal/repos/testdata/sources/GERRIT/TestGerritSource_ListRepos/ssh_enabled @@ -0,0 +1,376 @@ +[ + { + "ID": 0, + "Name": "gerrit.sgdev.org/a/gabe/repo", + "URI": "gerrit.sgdev.org/a/gabe/repo", + "Description": "", + "Fork": false, + "Archived": false, + "Private": false, + "CreatedAt": "0001-01-01T00:00:00Z", + "UpdatedAt": "0001-01-01T00:00:00Z", + "DeletedAt": "0001-01-01T00:00:00Z", + "ExternalRepo": { + "ID": "a%2Fgabe%2Frepo", + "ServiceType": "gerrit", + "ServiceID": "https://gerrit.sgdev.org/" + }, + "Sources": { + "extsvc:gerrit:0": { + "ID": "extsvc:gerrit:0", + "CloneURL": "ssh://gerrit-ssh.sgdev.org:29418/a/gabe/repo" + } + }, + "Metadata": { + "description": "", + "id": "a%2Fgabe%2Frepo", + "name": "", + "parent": "", + "state": "ACTIVE", + "branches": null, + "labels": null, + "http_url_to_repo": "https://gerrit.sgdev.org/a/a/gabe/repo", + "ssh_url_to_repo": "ssh://gerrit-ssh.sgdev.org:29418/a/gabe/repo" + } + }, + { + "ID": 0, + "Name": "gerrit.sgdev.org/batch-changes-test-repo", + "URI": "gerrit.sgdev.org/batch-changes-test-repo", + "Description": "", + "Fork": false, + "Archived": false, + "Private": false, + "CreatedAt": "0001-01-01T00:00:00Z", + "UpdatedAt": "0001-01-01T00:00:00Z", + "DeletedAt": "0001-01-01T00:00:00Z", + "ExternalRepo": { + "ID": "batch-changes-test-repo", + "ServiceType": "gerrit", + "ServiceID": "https://gerrit.sgdev.org/" + }, + "Sources": { + "extsvc:gerrit:0": { + "ID": "extsvc:gerrit:0", + "CloneURL": "ssh://gerrit-ssh.sgdev.org:29418/batch-changes-test-repo" + } + }, + "Metadata": { + "description": "", + "id": "batch-changes-test-repo", + "name": "", + "parent": "", + "state": "ACTIVE", + "branches": null, + "labels": null, + "http_url_to_repo": "https://gerrit.sgdev.org/a/batch-changes-test-repo", + "ssh_url_to_repo": "ssh://gerrit-ssh.sgdev.org:29418/batch-changes-test-repo" + } + }, + { + "ID": 0, + "Name": "gerrit.sgdev.org/beatrix", + "URI": "gerrit.sgdev.org/beatrix", + "Description": "", + "Fork": false, + "Archived": false, + "Private": false, + "CreatedAt": "0001-01-01T00:00:00Z", + "UpdatedAt": "0001-01-01T00:00:00Z", + "DeletedAt": "0001-01-01T00:00:00Z", + "ExternalRepo": { + "ID": "beatrix", + "ServiceType": "gerrit", + "ServiceID": "https://gerrit.sgdev.org/" + }, + "Sources": { + "extsvc:gerrit:0": { + "ID": "extsvc:gerrit:0", + "CloneURL": "ssh://gerrit-ssh.sgdev.org:29418/beatrix" + } + }, + "Metadata": { + "description": "", + "id": "beatrix", + "name": "", + "parent": "", + "state": "ACTIVE", + "branches": null, + "labels": null, + "http_url_to_repo": "https://gerrit.sgdev.org/a/beatrix", + "ssh_url_to_repo": "ssh://gerrit-ssh.sgdev.org:29418/beatrix" + } + }, + { + "ID": 0, + "Name": "gerrit.sgdev.org/cbart-test", + "URI": "gerrit.sgdev.org/cbart-test", + "Description": "", + "Fork": false, + "Archived": false, + "Private": false, + "CreatedAt": "0001-01-01T00:00:00Z", + "UpdatedAt": "0001-01-01T00:00:00Z", + "DeletedAt": "0001-01-01T00:00:00Z", + "ExternalRepo": { + "ID": "cbart-test", + "ServiceType": "gerrit", + "ServiceID": "https://gerrit.sgdev.org/" + }, + "Sources": { + "extsvc:gerrit:0": { + "ID": "extsvc:gerrit:0", + "CloneURL": "ssh://gerrit-ssh.sgdev.org:29418/cbart-test" + } + }, + "Metadata": { + "description": "", + "id": "cbart-test", + "name": "", + "parent": "", + "state": "ACTIVE", + "branches": null, + "labels": null, + "http_url_to_repo": "https://gerrit.sgdev.org/a/cbart-test", + "ssh_url_to_repo": "ssh://gerrit-ssh.sgdev.org:29418/cbart-test" + } + }, + { + "ID": 0, + "Name": "gerrit.sgdev.org/gabe", + "URI": "gerrit.sgdev.org/gabe", + "Description": "", + "Fork": false, + "Archived": false, + "Private": false, + "CreatedAt": "0001-01-01T00:00:00Z", + "UpdatedAt": "0001-01-01T00:00:00Z", + "DeletedAt": "0001-01-01T00:00:00Z", + "ExternalRepo": { + "ID": "gabe", + "ServiceType": "gerrit", + "ServiceID": "https://gerrit.sgdev.org/" + }, + "Sources": { + "extsvc:gerrit:0": { + "ID": "extsvc:gerrit:0", + "CloneURL": "ssh://gerrit-ssh.sgdev.org:29418/gabe" + } + }, + "Metadata": { + "description": "", + "id": "gabe", + "name": "", + "parent": "", + "state": "ACTIVE", + "branches": null, + "labels": null, + "http_url_to_repo": "https://gerrit.sgdev.org/a/gabe", + "ssh_url_to_repo": "ssh://gerrit-ssh.sgdev.org:29418/gabe" + } + }, + { + "ID": 0, + "Name": "gerrit.sgdev.org/gabe/test", + "URI": "gerrit.sgdev.org/gabe/test", + "Description": "", + "Fork": false, + "Archived": false, + "Private": false, + "CreatedAt": "0001-01-01T00:00:00Z", + "UpdatedAt": "0001-01-01T00:00:00Z", + "DeletedAt": "0001-01-01T00:00:00Z", + "ExternalRepo": { + "ID": "gabe%2Ftest", + "ServiceType": "gerrit", + "ServiceID": "https://gerrit.sgdev.org/" + }, + "Sources": { + "extsvc:gerrit:0": { + "ID": "extsvc:gerrit:0", + "CloneURL": "ssh://gerrit-ssh.sgdev.org:29418/gabe/test" + } + }, + "Metadata": { + "description": "", + "id": "gabe%2Ftest", + "name": "", + "parent": "", + "state": "ACTIVE", + "branches": null, + "labels": null, + "http_url_to_repo": "https://gerrit.sgdev.org/a/gabe/test", + "ssh_url_to_repo": "ssh://gerrit-ssh.sgdev.org:29418/gabe/test" + } + }, + { + "ID": 0, + "Name": "gerrit.sgdev.org/idan-temp", + "URI": "gerrit.sgdev.org/idan-temp", + "Description": "", + "Fork": false, + "Archived": false, + "Private": false, + "CreatedAt": "0001-01-01T00:00:00Z", + "UpdatedAt": "0001-01-01T00:00:00Z", + "DeletedAt": "0001-01-01T00:00:00Z", + "ExternalRepo": { + "ID": "idan-temp", + "ServiceType": "gerrit", + "ServiceID": "https://gerrit.sgdev.org/" + }, + "Sources": { + "extsvc:gerrit:0": { + "ID": "extsvc:gerrit:0", + "CloneURL": "ssh://gerrit-ssh.sgdev.org:29418/idan-temp" + } + }, + "Metadata": { + "description": "", + "id": "idan-temp", + "name": "", + "parent": "", + "state": "ACTIVE", + "branches": null, + "labels": null, + "http_url_to_repo": "https://gerrit.sgdev.org/a/idan-temp", + "ssh_url_to_repo": "ssh://gerrit-ssh.sgdev.org:29418/idan-temp" + } + }, + { + "ID": 0, + "Name": "gerrit.sgdev.org/k8s-gerrit", + "URI": "gerrit.sgdev.org/k8s-gerrit", + "Description": "", + "Fork": false, + "Archived": false, + "Private": false, + "CreatedAt": "0001-01-01T00:00:00Z", + "UpdatedAt": "0001-01-01T00:00:00Z", + "DeletedAt": "0001-01-01T00:00:00Z", + "ExternalRepo": { + "ID": "k8s-gerrit", + "ServiceType": "gerrit", + "ServiceID": "https://gerrit.sgdev.org/" + }, + "Sources": { + "extsvc:gerrit:0": { + "ID": "extsvc:gerrit:0", + "CloneURL": "ssh://gerrit-ssh.sgdev.org:29418/k8s-gerrit" + } + }, + "Metadata": { + "description": "", + "id": "k8s-gerrit", + "name": "", + "parent": "", + "state": "ACTIVE", + "branches": null, + "labels": null, + "http_url_to_repo": "https://gerrit.sgdev.org/a/k8s-gerrit", + "ssh_url_to_repo": "ssh://gerrit-ssh.sgdev.org:29418/k8s-gerrit" + } + }, + { + "ID": 0, + "Name": "gerrit.sgdev.org/sourcegraph", + "URI": "gerrit.sgdev.org/sourcegraph", + "Description": "", + "Fork": false, + "Archived": false, + "Private": false, + "CreatedAt": "0001-01-01T00:00:00Z", + "UpdatedAt": "0001-01-01T00:00:00Z", + "DeletedAt": "0001-01-01T00:00:00Z", + "ExternalRepo": { + "ID": "sourcegraph", + "ServiceType": "gerrit", + "ServiceID": "https://gerrit.sgdev.org/" + }, + "Sources": { + "extsvc:gerrit:0": { + "ID": "extsvc:gerrit:0", + "CloneURL": "ssh://gerrit-ssh.sgdev.org:29418/sourcegraph" + } + }, + "Metadata": { + "description": "", + "id": "sourcegraph", + "name": "", + "parent": "", + "state": "ACTIVE", + "branches": null, + "labels": null, + "http_url_to_repo": "https://gerrit.sgdev.org/a/sourcegraph", + "ssh_url_to_repo": "ssh://gerrit-ssh.sgdev.org:29418/sourcegraph" + } + }, + { + "ID": 0, + "Name": "gerrit.sgdev.org/sourcegraph/create", + "URI": "gerrit.sgdev.org/sourcegraph/create", + "Description": "", + "Fork": false, + "Archived": false, + "Private": false, + "CreatedAt": "0001-01-01T00:00:00Z", + "UpdatedAt": "0001-01-01T00:00:00Z", + "DeletedAt": "0001-01-01T00:00:00Z", + "ExternalRepo": { + "ID": "sourcegraph%2Fcreate", + "ServiceType": "gerrit", + "ServiceID": "https://gerrit.sgdev.org/" + }, + "Sources": { + "extsvc:gerrit:0": { + "ID": "extsvc:gerrit:0", + "CloneURL": "ssh://gerrit-ssh.sgdev.org:29418/sourcegraph/create" + } + }, + "Metadata": { + "description": "", + "id": "sourcegraph%2Fcreate", + "name": "", + "parent": "", + "state": "ACTIVE", + "branches": null, + "labels": null, + "http_url_to_repo": "https://gerrit.sgdev.org/a/sourcegraph/create", + "ssh_url_to_repo": "ssh://gerrit-ssh.sgdev.org:29418/sourcegraph/create" + } + }, + { + "ID": 0, + "Name": "gerrit.sgdev.org/src-cli", + "URI": "gerrit.sgdev.org/src-cli", + "Description": "", + "Fork": false, + "Archived": false, + "Private": false, + "CreatedAt": "0001-01-01T00:00:00Z", + "UpdatedAt": "0001-01-01T00:00:00Z", + "DeletedAt": "0001-01-01T00:00:00Z", + "ExternalRepo": { + "ID": "src-cli", + "ServiceType": "gerrit", + "ServiceID": "https://gerrit.sgdev.org/" + }, + "Sources": { + "extsvc:gerrit:0": { + "ID": "extsvc:gerrit:0", + "CloneURL": "ssh://gerrit-ssh.sgdev.org:29418/src-cli" + } + }, + "Metadata": { + "description": "", + "id": "src-cli", + "name": "", + "parent": "", + "state": "ACTIVE", + "branches": null, + "labels": null, + "http_url_to_repo": "https://gerrit.sgdev.org/a/src-cli", + "ssh_url_to_repo": "ssh://gerrit-ssh.sgdev.org:29418/src-cli" + } + } + ] diff --git a/internal/repos/testdata/sources/TestGerritSource_ListRepos/exclusion_overrides_inclusion.yaml b/internal/repos/testdata/sources/TestGerritSource_ListRepos/exclusion_overrides_inclusion.yaml index 627a76c950c..0888c10b398 100644 --- a/internal/repos/testdata/sources/TestGerritSource_ListRepos/exclusion_overrides_inclusion.yaml +++ b/internal/repos/testdata/sources/TestGerritSource_ListRepos/exclusion_overrides_inclusion.yaml @@ -1,6 +1,40 @@ --- version: 1 interactions: +- request: + body: "" + form: {} + headers: {} + url: https://gerrit.sgdev.org/ssh_info + method: GET + response: + body: gerrit-ssh.sgdev.org 29418 + headers: + Cache-Control: + - no-cache, no-store, max-age=0, must-revalidate + Cf-Cache-Status: + - DYNAMIC + Cf-Ray: + - 86e56693cae5266d-TXL + Content-Length: + - "26" + Content-Type: + - text/plain;charset=utf-8 + Date: + - Wed, 03 Apr 2024 01:49:11 GMT + Expires: + - Mon, 01 Jan 1990 00:00:00 GMT + Pragma: + - no-cache + Server: + - cloudflare + Strict-Transport-Security: + - max-age=15724800; includeSubDomains + X-Frame-Options: + - DENY + status: 200 OK + code: 200 + duration: "" - request: body: "" form: {} @@ -10,45 +44,20 @@ interactions: response: body: | )]}' - { - "beatrix": { - "id": "beatrix", - "state": "ACTIVE" - }, - "idan-temp": { - "id": "idan-temp", - "state": "ACTIVE" - }, - "k8s-gerrit": { - "id": "k8s-gerrit", - "state": "ACTIVE" - }, - "sourcegraph": { - "id": "sourcegraph", - "state": "ACTIVE" - }, - "sourcegraph/create": { - "id": "sourcegraph%2Fcreate", - "state": "ACTIVE" - }, - "src-cli": { - "id": "src-cli", - "state": "ACTIVE" - } - } + {"a/gabe/repo":{"id":"a%2Fgabe%2Frepo","state":"ACTIVE"},"batch-changes-test-repo":{"id":"batch-changes-test-repo","state":"ACTIVE"},"beatrix":{"id":"beatrix","state":"ACTIVE"},"cbart-test":{"id":"cbart-test","state":"ACTIVE"},"gabe":{"id":"gabe","state":"ACTIVE"},"gabe/test":{"id":"gabe%2Ftest","state":"ACTIVE"},"idan-temp":{"id":"idan-temp","state":"ACTIVE"},"k8s-gerrit":{"id":"k8s-gerrit","state":"ACTIVE"},"sourcegraph":{"id":"sourcegraph","state":"ACTIVE"},"sourcegraph/create":{"id":"sourcegraph%2Fcreate","state":"ACTIVE"},"src-cli":{"id":"src-cli","state":"ACTIVE"}} headers: Cache-Control: - no-cache, no-store, max-age=0, must-revalidate Cf-Cache-Status: - DYNAMIC Cf-Ray: - - 7c872c876c9ca214-YYZ + - 86e56694dbc2266d-TXL Content-Disposition: - attachment Content-Type: - application/json;charset=utf-8 Date: - - Tue, 16 May 2023 22:49:27 GMT + - Wed, 03 Apr 2024 01:49:11 GMT Expires: - Mon, 01 Jan 1990 00:00:00 GMT Pragma: @@ -80,7 +89,7 @@ interactions: Cf-Cache-Status: - DYNAMIC Cf-Ray: - - 7c872c88aedba214-YYZ + - 86e56695ec85266d-TXL Content-Disposition: - attachment Content-Length: @@ -88,7 +97,7 @@ interactions: Content-Type: - application/json;charset=utf-8 Date: - - Tue, 16 May 2023 22:49:27 GMT + - Wed, 03 Apr 2024 01:49:12 GMT Expires: - Mon, 01 Jan 1990 00:00:00 GMT Pragma: @@ -120,7 +129,7 @@ interactions: Cf-Cache-Status: - DYNAMIC Cf-Ray: - - 7c872c891ff8a214-YYZ + - 86e56696fd47266d-TXL Content-Disposition: - attachment Content-Length: @@ -128,7 +137,7 @@ interactions: Content-Type: - application/json;charset=utf-8 Date: - - Tue, 16 May 2023 22:49:27 GMT + - Wed, 03 Apr 2024 01:49:12 GMT Expires: - Mon, 01 Jan 1990 00:00:00 GMT Pragma: diff --git a/internal/repos/testdata/sources/TestGerritSource_ListRepos/no_filtering.yaml b/internal/repos/testdata/sources/TestGerritSource_ListRepos/no_filtering.yaml index 627a76c950c..c80fca0b716 100644 --- a/internal/repos/testdata/sources/TestGerritSource_ListRepos/no_filtering.yaml +++ b/internal/repos/testdata/sources/TestGerritSource_ListRepos/no_filtering.yaml @@ -1,6 +1,40 @@ --- version: 1 interactions: +- request: + body: "" + form: {} + headers: {} + url: https://gerrit.sgdev.org/ssh_info + method: GET + response: + body: gerrit-ssh.sgdev.org 29418 + headers: + Cache-Control: + - no-cache, no-store, max-age=0, must-revalidate + Cf-Cache-Status: + - DYNAMIC + Cf-Ray: + - 86e56680dd1a266d-TXL + Content-Length: + - "26" + Content-Type: + - text/plain;charset=utf-8 + Date: + - Wed, 03 Apr 2024 01:49:09 GMT + Expires: + - Mon, 01 Jan 1990 00:00:00 GMT + Pragma: + - no-cache + Server: + - cloudflare + Strict-Transport-Security: + - max-age=15724800; includeSubDomains + X-Frame-Options: + - DENY + status: 200 OK + code: 200 + duration: "" - request: body: "" form: {} @@ -10,45 +44,20 @@ interactions: response: body: | )]}' - { - "beatrix": { - "id": "beatrix", - "state": "ACTIVE" - }, - "idan-temp": { - "id": "idan-temp", - "state": "ACTIVE" - }, - "k8s-gerrit": { - "id": "k8s-gerrit", - "state": "ACTIVE" - }, - "sourcegraph": { - "id": "sourcegraph", - "state": "ACTIVE" - }, - "sourcegraph/create": { - "id": "sourcegraph%2Fcreate", - "state": "ACTIVE" - }, - "src-cli": { - "id": "src-cli", - "state": "ACTIVE" - } - } + {"a/gabe/repo":{"id":"a%2Fgabe%2Frepo","state":"ACTIVE"},"batch-changes-test-repo":{"id":"batch-changes-test-repo","state":"ACTIVE"},"beatrix":{"id":"beatrix","state":"ACTIVE"},"cbart-test":{"id":"cbart-test","state":"ACTIVE"},"gabe":{"id":"gabe","state":"ACTIVE"},"gabe/test":{"id":"gabe%2Ftest","state":"ACTIVE"},"idan-temp":{"id":"idan-temp","state":"ACTIVE"},"k8s-gerrit":{"id":"k8s-gerrit","state":"ACTIVE"},"sourcegraph":{"id":"sourcegraph","state":"ACTIVE"},"sourcegraph/create":{"id":"sourcegraph%2Fcreate","state":"ACTIVE"},"src-cli":{"id":"src-cli","state":"ACTIVE"}} headers: Cache-Control: - no-cache, no-store, max-age=0, must-revalidate Cf-Cache-Status: - DYNAMIC Cf-Ray: - - 7c872c876c9ca214-YYZ + - 86e56683ef4c266d-TXL Content-Disposition: - attachment Content-Type: - application/json;charset=utf-8 Date: - - Tue, 16 May 2023 22:49:27 GMT + - Wed, 03 Apr 2024 01:49:09 GMT Expires: - Mon, 01 Jan 1990 00:00:00 GMT Pragma: @@ -80,7 +89,7 @@ interactions: Cf-Cache-Status: - DYNAMIC Cf-Ray: - - 7c872c88aedba214-YYZ + - 86e56685188c266d-TXL Content-Disposition: - attachment Content-Length: @@ -88,7 +97,7 @@ interactions: Content-Type: - application/json;charset=utf-8 Date: - - Tue, 16 May 2023 22:49:27 GMT + - Wed, 03 Apr 2024 01:49:09 GMT Expires: - Mon, 01 Jan 1990 00:00:00 GMT Pragma: @@ -120,7 +129,7 @@ interactions: Cf-Cache-Status: - DYNAMIC Cf-Ray: - - 7c872c891ff8a214-YYZ + - 86e566861949266d-TXL Content-Disposition: - attachment Content-Length: @@ -128,7 +137,7 @@ interactions: Content-Type: - application/json;charset=utf-8 Date: - - Tue, 16 May 2023 22:49:27 GMT + - Wed, 03 Apr 2024 01:49:09 GMT Expires: - Mon, 01 Jan 1990 00:00:00 GMT Pragma: diff --git a/internal/repos/testdata/sources/TestGerritSource_ListRepos/ssh_enabled.yaml b/internal/repos/testdata/sources/TestGerritSource_ListRepos/ssh_enabled.yaml new file mode 100644 index 00000000000..c65361f3f17 --- /dev/null +++ b/internal/repos/testdata/sources/TestGerritSource_ListRepos/ssh_enabled.yaml @@ -0,0 +1,155 @@ +--- +version: 1 +interactions: +- request: + body: "" + form: {} + headers: {} + url: https://gerrit.sgdev.org/ssh_info + method: GET + response: + body: gerrit-ssh.sgdev.org 29418 + headers: + Cache-Control: + - no-cache, no-store, max-age=0, must-revalidate + Cf-Cache-Status: + - DYNAMIC + Cf-Ray: + - 86e566874a1b266d-TXL + Content-Length: + - "26" + Content-Type: + - text/plain;charset=utf-8 + Date: + - Wed, 03 Apr 2024 01:49:09 GMT + Expires: + - Mon, 01 Jan 1990 00:00:00 GMT + Pragma: + - no-cache + Server: + - cloudflare + Strict-Transport-Security: + - max-age=15724800; includeSubDomains + X-Frame-Options: + - DENY + status: 200 OK + code: 200 + duration: "" +- request: + body: "" + form: {} + headers: {} + url: https://gerrit.sgdev.org/a/projects/?S=0&n=100&type=CODE + method: GET + response: + body: | + )]}' + {"a/gabe/repo":{"id":"a%2Fgabe%2Frepo","state":"ACTIVE"},"batch-changes-test-repo":{"id":"batch-changes-test-repo","state":"ACTIVE"},"beatrix":{"id":"beatrix","state":"ACTIVE"},"cbart-test":{"id":"cbart-test","state":"ACTIVE"},"gabe":{"id":"gabe","state":"ACTIVE"},"gabe/test":{"id":"gabe%2Ftest","state":"ACTIVE"},"idan-temp":{"id":"idan-temp","state":"ACTIVE"},"k8s-gerrit":{"id":"k8s-gerrit","state":"ACTIVE"},"sourcegraph":{"id":"sourcegraph","state":"ACTIVE"},"sourcegraph/create":{"id":"sourcegraph%2Fcreate","state":"ACTIVE"},"src-cli":{"id":"src-cli","state":"ACTIVE"}} + headers: + Cache-Control: + - no-cache, no-store, max-age=0, must-revalidate + Cf-Cache-Status: + - DYNAMIC + Cf-Ray: + - 86e566885ae3266d-TXL + Content-Disposition: + - attachment + Content-Type: + - application/json;charset=utf-8 + Date: + - Wed, 03 Apr 2024 01:49:09 GMT + Expires: + - Mon, 01 Jan 1990 00:00:00 GMT + Pragma: + - no-cache + Server: + - cloudflare + Strict-Transport-Security: + - max-age=15724800; includeSubDomains + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + status: 200 OK + code: 200 + duration: "" +- request: + body: "" + form: {} + headers: {} + url: https://gerrit.sgdev.org/a/projects/?S=100&n=100&type=CODE + method: GET + response: + body: | + )]}' + {} + headers: + Cache-Control: + - no-cache, no-store, max-age=0, must-revalidate + Cf-Cache-Status: + - DYNAMIC + Cf-Ray: + - 86e566896b95266d-TXL + Content-Disposition: + - attachment + Content-Length: + - "8" + Content-Type: + - application/json;charset=utf-8 + Date: + - Wed, 03 Apr 2024 01:49:10 GMT + Expires: + - Mon, 01 Jan 1990 00:00:00 GMT + Pragma: + - no-cache + Server: + - cloudflare + Strict-Transport-Security: + - max-age=15724800; includeSubDomains + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + status: 200 OK + code: 200 + duration: "" +- request: + body: "" + form: {} + headers: {} + url: https://gerrit.sgdev.org/a/projects/?S=200&n=1 + method: GET + response: + body: | + )]}' + {} + headers: + Cache-Control: + - no-cache, no-store, max-age=0, must-revalidate + Cf-Cache-Status: + - DYNAMIC + Cf-Ray: + - 86e5668a7c4e266d-TXL + Content-Disposition: + - attachment + Content-Length: + - "8" + Content-Type: + - application/json;charset=utf-8 + Date: + - Wed, 03 Apr 2024 01:49:10 GMT + Expires: + - Mon, 01 Jan 1990 00:00:00 GMT + Pragma: + - no-cache + Server: + - cloudflare + Strict-Transport-Security: + - max-age=15724800; includeSubDomains + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + status: 200 OK + code: 200 + duration: "" diff --git a/internal/repos/testdata/sources/TestGerritSource_ListRepos/with_exclusion.yaml b/internal/repos/testdata/sources/TestGerritSource_ListRepos/with_exclusion.yaml index 627a76c950c..d0f20c97614 100644 --- a/internal/repos/testdata/sources/TestGerritSource_ListRepos/with_exclusion.yaml +++ b/internal/repos/testdata/sources/TestGerritSource_ListRepos/with_exclusion.yaml @@ -1,6 +1,40 @@ --- version: 1 interactions: +- request: + body: "" + form: {} + headers: {} + url: https://gerrit.sgdev.org/ssh_info + method: GET + response: + body: gerrit-ssh.sgdev.org 29418 + headers: + Cache-Control: + - no-cache, no-store, max-age=0, must-revalidate + Cf-Cache-Status: + - DYNAMIC + Cf-Ray: + - 86e5668fbff2266d-TXL + Content-Length: + - "26" + Content-Type: + - text/plain;charset=utf-8 + Date: + - Wed, 03 Apr 2024 01:49:11 GMT + Expires: + - Mon, 01 Jan 1990 00:00:00 GMT + Pragma: + - no-cache + Server: + - cloudflare + Strict-Transport-Security: + - max-age=15724800; includeSubDomains + X-Frame-Options: + - DENY + status: 200 OK + code: 200 + duration: "" - request: body: "" form: {} @@ -10,45 +44,20 @@ interactions: response: body: | )]}' - { - "beatrix": { - "id": "beatrix", - "state": "ACTIVE" - }, - "idan-temp": { - "id": "idan-temp", - "state": "ACTIVE" - }, - "k8s-gerrit": { - "id": "k8s-gerrit", - "state": "ACTIVE" - }, - "sourcegraph": { - "id": "sourcegraph", - "state": "ACTIVE" - }, - "sourcegraph/create": { - "id": "sourcegraph%2Fcreate", - "state": "ACTIVE" - }, - "src-cli": { - "id": "src-cli", - "state": "ACTIVE" - } - } + {"a/gabe/repo":{"id":"a%2Fgabe%2Frepo","state":"ACTIVE"},"batch-changes-test-repo":{"id":"batch-changes-test-repo","state":"ACTIVE"},"beatrix":{"id":"beatrix","state":"ACTIVE"},"cbart-test":{"id":"cbart-test","state":"ACTIVE"},"gabe":{"id":"gabe","state":"ACTIVE"},"gabe/test":{"id":"gabe%2Ftest","state":"ACTIVE"},"idan-temp":{"id":"idan-temp","state":"ACTIVE"},"k8s-gerrit":{"id":"k8s-gerrit","state":"ACTIVE"},"sourcegraph":{"id":"sourcegraph","state":"ACTIVE"},"sourcegraph/create":{"id":"sourcegraph%2Fcreate","state":"ACTIVE"},"src-cli":{"id":"src-cli","state":"ACTIVE"}} headers: Cache-Control: - no-cache, no-store, max-age=0, must-revalidate Cf-Cache-Status: - DYNAMIC Cf-Ray: - - 7c872c876c9ca214-YYZ + - 86e56690a8a1266d-TXL Content-Disposition: - attachment Content-Type: - application/json;charset=utf-8 Date: - - Tue, 16 May 2023 22:49:27 GMT + - Wed, 03 Apr 2024 01:49:11 GMT Expires: - Mon, 01 Jan 1990 00:00:00 GMT Pragma: @@ -80,7 +89,7 @@ interactions: Cf-Cache-Status: - DYNAMIC Cf-Ray: - - 7c872c88aedba214-YYZ + - 86e56691c97b266d-TXL Content-Disposition: - attachment Content-Length: @@ -88,7 +97,7 @@ interactions: Content-Type: - application/json;charset=utf-8 Date: - - Tue, 16 May 2023 22:49:27 GMT + - Wed, 03 Apr 2024 01:49:11 GMT Expires: - Mon, 01 Jan 1990 00:00:00 GMT Pragma: @@ -120,7 +129,7 @@ interactions: Cf-Cache-Status: - DYNAMIC Cf-Ray: - - 7c872c891ff8a214-YYZ + - 86e56692ca39266d-TXL Content-Disposition: - attachment Content-Length: @@ -128,7 +137,7 @@ interactions: Content-Type: - application/json;charset=utf-8 Date: - - Tue, 16 May 2023 22:49:27 GMT + - Wed, 03 Apr 2024 01:49:11 GMT Expires: - Mon, 01 Jan 1990 00:00:00 GMT Pragma: diff --git a/internal/repos/testdata/sources/TestGerritSource_ListRepos/with_filtering.yaml b/internal/repos/testdata/sources/TestGerritSource_ListRepos/with_filtering.yaml index 627a76c950c..639f6085526 100644 --- a/internal/repos/testdata/sources/TestGerritSource_ListRepos/with_filtering.yaml +++ b/internal/repos/testdata/sources/TestGerritSource_ListRepos/with_filtering.yaml @@ -1,6 +1,40 @@ --- version: 1 interactions: +- request: + body: "" + form: {} + headers: {} + url: https://gerrit.sgdev.org/ssh_info + method: GET + response: + body: gerrit-ssh.sgdev.org 29418 + headers: + Cache-Control: + - no-cache, no-store, max-age=0, must-revalidate + Cf-Cache-Status: + - DYNAMIC + Cf-Ray: + - 86e5668b8cee266d-TXL + Content-Length: + - "26" + Content-Type: + - text/plain;charset=utf-8 + Date: + - Wed, 03 Apr 2024 01:49:10 GMT + Expires: + - Mon, 01 Jan 1990 00:00:00 GMT + Pragma: + - no-cache + Server: + - cloudflare + Strict-Transport-Security: + - max-age=15724800; includeSubDomains + X-Frame-Options: + - DENY + status: 200 OK + code: 200 + duration: "" - request: body: "" form: {} @@ -10,45 +44,20 @@ interactions: response: body: | )]}' - { - "beatrix": { - "id": "beatrix", - "state": "ACTIVE" - }, - "idan-temp": { - "id": "idan-temp", - "state": "ACTIVE" - }, - "k8s-gerrit": { - "id": "k8s-gerrit", - "state": "ACTIVE" - }, - "sourcegraph": { - "id": "sourcegraph", - "state": "ACTIVE" - }, - "sourcegraph/create": { - "id": "sourcegraph%2Fcreate", - "state": "ACTIVE" - }, - "src-cli": { - "id": "src-cli", - "state": "ACTIVE" - } - } + {"a/gabe/repo":{"id":"a%2Fgabe%2Frepo","state":"ACTIVE"},"batch-changes-test-repo":{"id":"batch-changes-test-repo","state":"ACTIVE"},"beatrix":{"id":"beatrix","state":"ACTIVE"},"cbart-test":{"id":"cbart-test","state":"ACTIVE"},"gabe":{"id":"gabe","state":"ACTIVE"},"gabe/test":{"id":"gabe%2Ftest","state":"ACTIVE"},"idan-temp":{"id":"idan-temp","state":"ACTIVE"},"k8s-gerrit":{"id":"k8s-gerrit","state":"ACTIVE"},"sourcegraph":{"id":"sourcegraph","state":"ACTIVE"},"sourcegraph/create":{"id":"sourcegraph%2Fcreate","state":"ACTIVE"},"src-cli":{"id":"src-cli","state":"ACTIVE"}} headers: Cache-Control: - no-cache, no-store, max-age=0, must-revalidate Cf-Cache-Status: - DYNAMIC Cf-Ray: - - 7c872c876c9ca214-YYZ + - 86e5668c8d9d266d-TXL Content-Disposition: - attachment Content-Type: - application/json;charset=utf-8 Date: - - Tue, 16 May 2023 22:49:27 GMT + - Wed, 03 Apr 2024 01:49:10 GMT Expires: - Mon, 01 Jan 1990 00:00:00 GMT Pragma: @@ -80,7 +89,7 @@ interactions: Cf-Cache-Status: - DYNAMIC Cf-Ray: - - 7c872c88aedba214-YYZ + - 86e5668d9e6f266d-TXL Content-Disposition: - attachment Content-Length: @@ -88,7 +97,7 @@ interactions: Content-Type: - application/json;charset=utf-8 Date: - - Tue, 16 May 2023 22:49:27 GMT + - Wed, 03 Apr 2024 01:49:10 GMT Expires: - Mon, 01 Jan 1990 00:00:00 GMT Pragma: @@ -120,7 +129,7 @@ interactions: Cf-Cache-Status: - DYNAMIC Cf-Ray: - - 7c872c891ff8a214-YYZ + - 86e5668eaf43266d-TXL Content-Disposition: - attachment Content-Length: @@ -128,7 +137,7 @@ interactions: Content-Type: - application/json;charset=utf-8 Date: - - Tue, 16 May 2023 22:49:27 GMT + - Wed, 03 Apr 2024 01:49:10 GMT Expires: - Mon, 01 Jan 1990 00:00:00 GMT Pragma: diff --git a/schema/gerrit.schema.json b/schema/gerrit.schema.json index 5306c5143d2..28518a420b0 100644 --- a/schema/gerrit.schema.json +++ b/schema/gerrit.schema.json @@ -20,7 +20,7 @@ "examples": ["https://gerrit.example.com"] }, "username": { - "description": "A username for authentication withe the Gerrit code host.", + "description": "A username for authentication with the Gerrit code host.", "type": "string", "minLength": 1 }, @@ -38,6 +38,12 @@ ["docs", "kubernetes/kubernetes", "golang/go", "facebook/react"] ] }, + "gitURLType": { + "description": "The type of Git URLs to use for cloning and fetching Git repositories on this Gerrit instance.\n\nIf \"http\", Sourcegraph will access Gerrit repositories using Git URLs of the form http(s)://gerrit.example.com/a/myteam/myproject.git (using https: if the Gerrit instance uses HTTPS).\n\nIf \"ssh\", Sourcegraph will access Gerrit repositories using Git URLs of the form git@gerrit.example.com:myteam/myproject.git. The exact hostname and port will be fetched from /ssh_info. See the documentation for how to provide SSH private keys and known_hosts: https://sourcegraph.com/docs/admin/repo/auth.", + "type": "string", + "enum": ["http", "ssh"], + "default": "http" + }, "exclude": { "description": "A list of repositories to never mirror from this Gerrit instance. Takes precedence over \"projects\" configuration.\n\nSupports excluding by name ({\"name\": \"owner/name\"})", "type": "array", diff --git a/schema/schema.go b/schema/schema.go index b0219a755f6..bd59b9c2887 100644 --- a/schema/schema.go +++ b/schema/schema.go @@ -1159,13 +1159,19 @@ type GerritConnection struct { // // Supports excluding by name ({"name": "owner/name"}) Exclude []*ExcludedGerritProject `json:"exclude,omitempty"` + // GitURLType description: The type of Git URLs to use for cloning and fetching Git repositories on this Gerrit instance. + // + // If "http", Sourcegraph will access Gerrit repositories using Git URLs of the form http(s)://gerrit.example.com/a/myteam/myproject.git (using https: if the Gerrit instance uses HTTPS). + // + // If "ssh", Sourcegraph will access Gerrit repositories using Git URLs of the form git@gerrit.example.com:myteam/myproject.git. The exact hostname and port will be fetched from /ssh_info. See the documentation for how to provide SSH private keys and known_hosts: https://sourcegraph.com/docs/admin/repo/auth. + GitURLType string `json:"gitURLType,omitempty"` // Password description: The password associated with the Gerrit username used for authentication. Password string `json:"password"` // Projects description: An array of project strings specifying which Gerrit projects to mirror on Sourcegraph. If empty, all projects will be mirrored. Projects []string `json:"projects,omitempty"` // Url description: URL of a Gerrit instance, such as https://gerrit.example.com. Url string `json:"url"` - // Username description: A username for authentication withe the Gerrit code host. + // Username description: A username for authentication with the Gerrit code host. Username string `json:"username"` }