diff options
author | Will Chandler <wchandler@gitlab.com> | 2023-03-15 19:54:00 +0300 |
---|---|---|
committer | Will Chandler <wchandler@gitlab.com> | 2023-03-15 19:54:00 +0300 |
commit | 5c42c9509ff0fab716f7b903b9dc4be77f1c15d9 (patch) | |
tree | 3a67cfbc25df6d5d9df30fa056195ba245333177 /internal | |
parent | 705e00092a936f0233fe16339aba3a1c65db36b0 (diff) | |
parent | 9faffc5215dc5803cd7746b1adba7c50b4ace79f (diff) |
Merge branch 'toon-license-in-go' into 'master'
repository: Remove use of Ruby FindLicense
See merge request https://gitlab.com/gitlab-org/gitaly/-/merge_requests/5495
Merged-by: Will Chandler <wchandler@gitlab.com>
Approved-by: karthik nayak <knayak@gitlab.com>
Approved-by: Will Chandler <wchandler@gitlab.com>
Co-authored-by: Toon Claes <toon@gitlab.com>
Diffstat (limited to 'internal')
-rw-r--r-- | internal/gitaly/service/repository/license.go | 32 | ||||
-rw-r--r-- | internal/gitaly/service/repository/license_test.go | 433 | ||||
-rw-r--r-- | internal/gitaly/service/repository/ruby_sidecar_test.go | 41 | ||||
-rw-r--r-- | internal/metadata/featureflag/ff_go_find_license.go | 9 |
4 files changed, 202 insertions, 313 deletions
diff --git a/internal/gitaly/service/repository/license.go b/internal/gitaly/service/repository/license.go index df90cebd7..e91355dfa 100644 --- a/internal/gitaly/service/repository/license.go +++ b/internal/gitaly/service/repository/license.go @@ -16,9 +16,7 @@ import ( "gitlab.com/gitlab-org/gitaly/v15/internal/git" "gitlab.com/gitlab-org/gitaly/v15/internal/git/localrepo" "gitlab.com/gitlab-org/gitaly/v15/internal/git/lstree" - "gitlab.com/gitlab-org/gitaly/v15/internal/gitaly/rubyserver" "gitlab.com/gitlab-org/gitaly/v15/internal/gitaly/service" - "gitlab.com/gitlab-org/gitaly/v15/internal/metadata/featureflag" "gitlab.com/gitlab-org/gitaly/v15/internal/structerr" "gitlab.com/gitlab-org/gitaly/v15/internal/tracing" "gitlab.com/gitlab-org/gitaly/v15/internal/unarycache" @@ -55,34 +53,22 @@ func (s *server) FindLicense(ctx context.Context, req *gitalypb.FindLicenseReque if err := service.ValidateRepository(repository); err != nil { return nil, structerr.NewInvalidArgument("%w", err) } - if featureflag.GoFindLicense.IsEnabled(ctx) { - repo := localrepo.New(s.locator, s.gitCmdFactory, s.catfileCache, repository) + repo := localrepo.New(s.locator, s.gitCmdFactory, s.catfileCache, repository) - headOID, err := repo.ResolveRevision(ctx, "HEAD") - if err != nil { - if errors.Is(err, git.ErrReferenceNotFound) { - return &gitalypb.FindLicenseResponse{}, nil - } - return nil, structerr.NewInternal("cannot find HEAD revision: %v", err) - } - - response, err := s.licenseCache.GetOrCompute(ctx, repo, headOID) - if err != nil { - return nil, err + headOID, err := repo.ResolveRevision(ctx, "HEAD") + if err != nil { + if errors.Is(err, git.ErrReferenceNotFound) { + return &gitalypb.FindLicenseResponse{}, nil } - - return response, nil + return nil, structerr.NewInternal("cannot find HEAD revision: %v", err) } - client, err := s.ruby.RepositoryServiceClient(ctx) - if err != nil { - return nil, err - } - clientCtx, err := rubyserver.SetHeaders(ctx, s.locator, repository) + response, err := s.licenseCache.GetOrCompute(ctx, repo, headOID) if err != nil { return nil, err } - return client.FindLicense(clientCtx, req) + + return response, nil } func findLicense(ctx context.Context, repo *localrepo.Repo, commitID git.ObjectID) (*gitalypb.FindLicenseResponse, error) { diff --git a/internal/gitaly/service/repository/license_test.go b/internal/gitaly/service/repository/license_test.go index 3384d0cad..b02bee340 100644 --- a/internal/gitaly/service/repository/license_test.go +++ b/internal/gitaly/service/repository/license_test.go @@ -11,8 +11,6 @@ import ( "github.com/stretchr/testify/require" "gitlab.com/gitlab-org/gitaly/v15/internal/git/catfile" "gitlab.com/gitlab-org/gitaly/v15/internal/git/gittest" - "gitlab.com/gitlab-org/gitaly/v15/internal/gitaly/config" - "gitlab.com/gitlab-org/gitaly/v15/internal/gitaly/rubyserver" "gitlab.com/gitlab-org/gitaly/v15/internal/metadata/featureflag" "gitlab.com/gitlab-org/gitaly/v15/internal/testhelper" "gitlab.com/gitlab-org/gitaly/v15/internal/testhelper/testcfg" @@ -48,258 +46,215 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.` ) -func testSuccessfulFindLicenseRequest(t *testing.T, cfg config.Cfg, client gitalypb.RepositoryServiceClient, rubySrv *rubyserver.Server) { - testhelper.NewFeatureSets(featureflag.GoFindLicense).Run(t, func(t *testing.T, ctx context.Context) { - for _, tc := range []struct { - desc string - nonExistentRepository bool - setup func(t *testing.T, repoPath string) - // expectedLicenseRuby is used to verify the response received from the Ruby side-car. - // Also is it used if expectedLicenseGo is not set. Because the Licensee gem and - // the github.com/go-enry/go-license-detector go package use different license databases - // and different methods to detect the license, they will not always return the - // same result. So we need to provide different expected results in some cases. - expectedLicenseRuby *gitalypb.FindLicenseResponse - expectedLicenseGo *gitalypb.FindLicenseResponse - errorContains string - }{ - { - desc: "repository does not exist", - setup: func(t *testing.T, repoPath string) { - require.NoError(t, os.RemoveAll(repoPath)) - }, - errorContains: "GetRepoPath: not a git repository", +func TestFindLicense_successful(t *testing.T) { + t.Parallel() + + cfg, client := setupRepositoryServiceWithoutRepo(t) + ctx := testhelper.Context(t) + + for _, tc := range []struct { + desc string + nonExistentRepository bool + setup func(t *testing.T, repoPath string) + expectedLicense *gitalypb.FindLicenseResponse + errorContains string + }{ + { + desc: "repository does not exist", + setup: func(t *testing.T, repoPath string) { + require.NoError(t, os.RemoveAll(repoPath)) }, - { - desc: "empty if no license file in repo", - setup: func(t *testing.T, repoPath string) { - gittest.WriteCommit(t, cfg, repoPath, gittest.WithBranch("main"), - gittest.WithTreeEntries( - gittest.TreeEntry{ - Mode: "100644", - Path: "README.md", - Content: "readme content", - })) - }, - expectedLicenseRuby: &gitalypb.FindLicenseResponse{}, + errorContains: "GetRepoPath: not a git repository", + }, + { + desc: "empty if no license file in repo", + setup: func(t *testing.T, repoPath string) { + gittest.WriteCommit(t, cfg, repoPath, gittest.WithBranch("main"), + gittest.WithTreeEntries( + gittest.TreeEntry{ + Mode: "100644", + Path: "README.md", + Content: "readme content", + })) }, - { - desc: "high confidence mit result and less confident mit-0 result", - setup: func(t *testing.T, repoPath string) { - gittest.WriteCommit(t, cfg, repoPath, gittest.WithBranch("main"), - gittest.WithTreeEntries( - gittest.TreeEntry{ - Mode: "100644", - Path: "LICENSE", - Content: mitLicense, - })) - }, - expectedLicenseRuby: &gitalypb.FindLicenseResponse{ - LicenseShortName: "mit", - LicenseUrl: "http://choosealicense.com/licenses/mit/", - LicenseName: "MIT License", - LicensePath: "LICENSE", - }, - expectedLicenseGo: &gitalypb.FindLicenseResponse{ - LicenseShortName: "mit", - LicenseUrl: "https://opensource.org/licenses/MIT", - LicenseName: "MIT License", - LicensePath: "LICENSE", - }, + expectedLicense: &gitalypb.FindLicenseResponse{}, + }, + { + desc: "high confidence mit result and less confident mit-0 result", + setup: func(t *testing.T, repoPath string) { + gittest.WriteCommit(t, cfg, repoPath, gittest.WithBranch("main"), + gittest.WithTreeEntries( + gittest.TreeEntry{ + Mode: "100644", + Path: "LICENSE", + Content: mitLicense, + })) }, - { - // test for https://gitlab.com/gitlab-org/gitaly/-/issues/4745 - desc: "ignores licenses that don't have further details", - setup: func(t *testing.T, repoPath string) { - licenseText := testhelper.MustReadFile(t, "testdata/linux-license.txt") - - gittest.WriteCommit(t, cfg, repoPath, gittest.WithBranch("main"), - gittest.WithTreeEntries( - gittest.TreeEntry{ - Mode: "100644", - Path: "COPYING", - Content: string(licenseText), - })) - }, - expectedLicenseRuby: &gitalypb.FindLicenseResponse{ - LicenseShortName: "other", - LicenseName: "Other", - LicenseNickname: "LICENSE", - LicensePath: "COPYING", - }, - expectedLicenseGo: &gitalypb.FindLicenseResponse{ - LicenseShortName: "gpl-2.0+", - LicenseName: "GNU General Public License v2.0 or later", - LicenseUrl: "https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html", - LicensePath: "COPYING", - }, + expectedLicense: &gitalypb.FindLicenseResponse{ + LicenseShortName: "mit", + LicenseUrl: "https://opensource.org/licenses/MIT", + LicenseName: "MIT License", + LicensePath: "LICENSE", }, - { - desc: "unknown license", - setup: func(t *testing.T, repoPath string) { - gittest.WriteCommit(t, cfg, repoPath, gittest.WithBranch("main"), - gittest.WithTreeEntries( - gittest.TreeEntry{ - Mode: "100644", - Path: "LICENSE.md", - Content: "this doesn't match any known license", - })) - }, - expectedLicenseRuby: &gitalypb.FindLicenseResponse{ - LicenseShortName: "other", - LicenseName: "Other", - LicenseNickname: "LICENSE", - LicensePath: "LICENSE.md", - }, - expectedLicenseGo: &gitalypb.FindLicenseResponse{ - LicenseShortName: "other", - LicenseName: "Other", - LicenseNickname: "LICENSE", - LicensePath: "LICENSE.md", - }, + }, + { + // test for https://gitlab.com/gitlab-org/gitaly/-/issues/4745 + desc: "ignores licenses that don't have further details", + setup: func(t *testing.T, repoPath string) { + licenseText := testhelper.MustReadFile(t, "testdata/linux-license.txt") + + gittest.WriteCommit(t, cfg, repoPath, gittest.WithBranch("main"), + gittest.WithTreeEntries( + gittest.TreeEntry{ + Mode: "100644", + Path: "COPYING", + Content: string(licenseText), + })) }, - { - desc: "deprecated license", - setup: func(t *testing.T, repoPath string) { - deprecatedLicenseData := testhelper.MustReadFile(t, "testdata/gnu_license.deprecated.txt") - - gittest.WriteCommit(t, cfg, repoPath, gittest.WithBranch("main"), - gittest.WithTreeEntries( - gittest.TreeEntry{ - Mode: "100644", - Path: "LICENSE", - Content: string(deprecatedLicenseData), - })) - }, - expectedLicenseRuby: &gitalypb.FindLicenseResponse{ - LicenseShortName: "gpl-3.0", - LicenseUrl: "http://choosealicense.com/licenses/gpl-3.0/", - LicenseName: "GNU General Public License v3.0", - LicensePath: "LICENSE", - LicenseNickname: "GNU GPLv3", - }, - expectedLicenseGo: &gitalypb.FindLicenseResponse{ - LicenseShortName: "gpl-3.0+", - LicenseUrl: "https://www.gnu.org/licenses/gpl-3.0-standalone.html", - LicenseName: "GNU General Public License v3.0 or later", - LicensePath: "LICENSE", - // The nickname is not set because there is no nickname defined for gpl-3.0+ license. - }, + expectedLicense: &gitalypb.FindLicenseResponse{ + LicenseShortName: "gpl-2.0+", + LicenseName: "GNU General Public License v2.0 or later", + LicenseUrl: "https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html", + LicensePath: "COPYING", }, - { - desc: "license with nickname", - setup: func(t *testing.T, repoPath string) { - licenseText := testhelper.MustReadFile(t, "testdata/gpl-2.0_license.txt") - - gittest.WriteCommit(t, cfg, repoPath, gittest.WithBranch("main"), - gittest.WithTreeEntries( - gittest.TreeEntry{ - Mode: "100644", - Path: "LICENSE", - Content: string(licenseText), - })) - }, - expectedLicenseRuby: &gitalypb.FindLicenseResponse{ - LicenseShortName: "gpl-2.0", - LicenseUrl: "http://choosealicense.com/licenses/gpl-2.0/", - LicenseName: "GNU General Public License v2.0", - LicensePath: "LICENSE", - LicenseNickname: "GNU GPLv2", - }, - expectedLicenseGo: &gitalypb.FindLicenseResponse{ - LicenseShortName: "gpl-2.0", - LicenseUrl: "https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html", - LicenseName: "GNU General Public License v2.0 only", - LicensePath: "LICENSE", - LicenseNickname: "GNU GPLv2", - }, + }, + { + desc: "unknown license", + setup: func(t *testing.T, repoPath string) { + gittest.WriteCommit(t, cfg, repoPath, gittest.WithBranch("main"), + gittest.WithTreeEntries( + gittest.TreeEntry{ + Mode: "100644", + Path: "LICENSE.md", + Content: "this doesn't match any known license", + })) }, - { - desc: "license in subdir", - setup: func(t *testing.T, repoPath string) { - subTree := gittest.WriteTree(t, cfg, repoPath, - []gittest.TreeEntry{{ + expectedLicense: &gitalypb.FindLicenseResponse{ + LicenseShortName: "other", + LicenseName: "Other", + LicenseNickname: "LICENSE", + LicensePath: "LICENSE.md", + }, + }, + { + desc: "deprecated license", + setup: func(t *testing.T, repoPath string) { + deprecatedLicenseData := testhelper.MustReadFile(t, "testdata/gnu_license.deprecated.txt") + + gittest.WriteCommit(t, cfg, repoPath, gittest.WithBranch("main"), + gittest.WithTreeEntries( + gittest.TreeEntry{ Mode: "100644", Path: "LICENSE", + Content: string(deprecatedLicenseData), + })) + }, + expectedLicense: &gitalypb.FindLicenseResponse{ + LicenseShortName: "gpl-3.0+", + LicenseUrl: "https://www.gnu.org/licenses/gpl-3.0-standalone.html", + LicenseName: "GNU General Public License v3.0 or later", + LicensePath: "LICENSE", + // The nickname is not set because there is no nickname defined for gpl-3.0+ license. + }, + }, + { + desc: "license with nickname", + setup: func(t *testing.T, repoPath string) { + licenseText := testhelper.MustReadFile(t, "testdata/gpl-2.0_license.txt") + + gittest.WriteCommit(t, cfg, repoPath, gittest.WithBranch("main"), + gittest.WithTreeEntries( + gittest.TreeEntry{ + Mode: "100644", + Path: "LICENSE", + Content: string(licenseText), + })) + }, + expectedLicense: &gitalypb.FindLicenseResponse{ + LicenseShortName: "gpl-2.0", + LicenseUrl: "https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html", + LicenseName: "GNU General Public License v2.0 only", + LicensePath: "LICENSE", + LicenseNickname: "GNU GPLv2", + }, + }, + { + desc: "license in subdir", + setup: func(t *testing.T, repoPath string) { + subTree := gittest.WriteTree(t, cfg, repoPath, + []gittest.TreeEntry{{ + Mode: "100644", + Path: "LICENSE", + Content: mitLicense, + }}) + + gittest.WriteCommit(t, cfg, repoPath, gittest.WithBranch("main"), + gittest.WithTreeEntries( + gittest.TreeEntry{ + Mode: "040000", + Path: "legal", + OID: subTree, + })) + }, + expectedLicense: &gitalypb.FindLicenseResponse{}, + }, + { + desc: "license pointing to license file", + setup: func(t *testing.T, repoPath string) { + gittest.WriteCommit(t, cfg, repoPath, gittest.WithBranch("main"), + gittest.WithTreeEntries( + gittest.TreeEntry{ + Mode: "100644", + Path: "mit.txt", Content: mitLicense, - }}) - - gittest.WriteCommit(t, cfg, repoPath, gittest.WithBranch("main"), - gittest.WithTreeEntries( - gittest.TreeEntry{ - Mode: "040000", - Path: "legal", - OID: subTree, - })) - }, - expectedLicenseRuby: &gitalypb.FindLicenseResponse{}, + }, + gittest.TreeEntry{ + Mode: "100644", + Path: "LICENSE", + Content: "mit.txt", + }, + )) }, - { - desc: "license pointing to license file", - setup: func(t *testing.T, repoPath string) { - gittest.WriteCommit(t, cfg, repoPath, gittest.WithBranch("main"), - gittest.WithTreeEntries( - gittest.TreeEntry{ - Mode: "100644", - Path: "mit.txt", - Content: mitLicense, - }, - gittest.TreeEntry{ - Mode: "100644", - Path: "LICENSE", - Content: "mit.txt", - }, - )) - }, - expectedLicenseRuby: &gitalypb.FindLicenseResponse{ - LicenseShortName: "other", - LicenseName: "Other", - LicenseNickname: "LICENSE", - LicensePath: "LICENSE", - }, - expectedLicenseGo: &gitalypb.FindLicenseResponse{ - LicenseShortName: "mit", - LicenseUrl: "https://opensource.org/licenses/MIT", - LicenseName: "MIT License", - LicensePath: "mit.txt", - }, + expectedLicense: &gitalypb.FindLicenseResponse{ + LicenseShortName: "mit", + LicenseUrl: "https://opensource.org/licenses/MIT", + LicenseName: "MIT License", + LicensePath: "mit.txt", }, - } { - t.Run(tc.desc, func(t *testing.T) { - repo, repoPath := gittest.CreateRepository(t, ctx, cfg) - tc.setup(t, repoPath) - - if _, err := os.Stat(repoPath); !os.IsNotExist(err) { - gittest.Exec(t, cfg, "-C", repoPath, "symbolic-ref", "HEAD", "refs/heads/main") - } - - resp, err := client.FindLicense(ctx, &gitalypb.FindLicenseRequest{Repository: repo}) - if tc.errorContains != "" { - require.Error(t, err) - require.Contains(t, err.Error(), tc.errorContains) - return - } - - require.NoError(t, err) - if featureflag.GoFindLicense.IsEnabled(ctx) && tc.expectedLicenseGo != nil { - testhelper.ProtoEqual(t, tc.expectedLicenseGo, resp) - } else { - testhelper.ProtoEqual(t, tc.expectedLicenseRuby, resp) - } - }) - } - }) + }, + } { + t.Run(tc.desc, func(t *testing.T) { + repo, repoPath := gittest.CreateRepository(t, ctx, cfg) + tc.setup(t, repoPath) + + if _, err := os.Stat(repoPath); !os.IsNotExist(err) { + gittest.Exec(t, cfg, "-C", repoPath, "symbolic-ref", "HEAD", "refs/heads/main") + } + + resp, err := client.FindLicense(ctx, &gitalypb.FindLicenseRequest{Repository: repo}) + if tc.errorContains != "" { + require.Error(t, err) + require.Contains(t, err.Error(), tc.errorContains) + return + } + + require.NoError(t, err) + testhelper.ProtoEqual(t, tc.expectedLicense, resp) + }) + } } -func testFindLicenseRequestEmptyRepo(t *testing.T, cfg config.Cfg, client gitalypb.RepositoryServiceClient, rubySrv *rubyserver.Server) { - testhelper.NewFeatureSets(featureflag.GoFindLicense).Run(t, func(t *testing.T, ctx context.Context) { - repo, _ := gittest.CreateRepository(t, ctx, cfg) +func TestFindLicense_emptyRepo(t *testing.T) { + t.Parallel() - resp, err := client.FindLicense(ctx, &gitalypb.FindLicenseRequest{Repository: repo}) - require.NoError(t, err) + cfg, client := setupRepositoryServiceWithoutRepo(t) + ctx := testhelper.Context(t) + repo, _ := gittest.CreateRepository(t, ctx, cfg) - require.Empty(t, resp.GetLicenseShortName()) - }) + resp, err := client.FindLicense(ctx, &gitalypb.FindLicenseRequest{Repository: repo}) + require.NoError(t, err) + + require.Empty(t, resp.GetLicenseShortName()) } func TestFindLicense_validate(t *testing.T) { @@ -316,7 +271,6 @@ func TestFindLicense_validate(t *testing.T) { func BenchmarkFindLicense(b *testing.B) { cfg := testcfg.Build(b) ctx := testhelper.Context(b) - ctx = featureflag.ContextWithFeatureFlag(ctx, featureflag.GoFindLicense, true) gitCmdFactory := gittest.NewCountingCommandFactory(b, cfg) client, serverSocketPath := runRepositoryService( @@ -390,7 +344,6 @@ func BenchmarkFindLicense(b *testing.B) { gittest.Exec(b, cfg, "-C", repoStressPath, "symbolic-ref", "HEAD", "refs/heads/main") testhelper.NewFeatureSets(featureflag.LocalrepoReadObjectCached).Bench(b, func(b *testing.B, ctx context.Context) { - ctx = featureflag.ContextWithFeatureFlag(ctx, featureflag.GoFindLicense, true) ctx = correlation.ContextWithCorrelation(ctx, "1") ctx = testhelper.MergeOutgoingMetadata(ctx, metadata.Pairs(catfile.SessionIDField, "1"), diff --git a/internal/gitaly/service/repository/ruby_sidecar_test.go b/internal/gitaly/service/repository/ruby_sidecar_test.go deleted file mode 100644 index b6ac1d450..000000000 --- a/internal/gitaly/service/repository/ruby_sidecar_test.go +++ /dev/null @@ -1,41 +0,0 @@ -//go:build !gitaly_test_sha256 - -package repository - -import ( - "reflect" - "runtime" - "testing" - - "github.com/stretchr/testify/require" - "gitlab.com/gitlab-org/gitaly/v15/internal/git/gittest" - "gitlab.com/gitlab-org/gitaly/v15/internal/gitaly/config" - "gitlab.com/gitlab-org/gitaly/v15/internal/gitaly/rubyserver" - "gitlab.com/gitlab-org/gitaly/v15/internal/testhelper/testcfg" - "gitlab.com/gitlab-org/gitaly/v15/proto/go/gitalypb" -) - -func TestWithRubySidecar(t *testing.T) { - t.Parallel() - cfg := testcfg.Build(t) - - rubySrv := rubyserver.New(cfg, gittest.NewCommandFactory(t, cfg)) - require.NoError(t, rubySrv.Start()) - t.Cleanup(rubySrv.Stop) - - client, serverSocketPath := runRepositoryService(t, cfg, rubySrv) - cfg.SocketPath = serverSocketPath - - testcfg.BuildGitalyHooks(t, cfg) - testcfg.BuildGitalyGit2Go(t, cfg) - - fs := []func(t *testing.T, cfg config.Cfg, client gitalypb.RepositoryServiceClient, rubySrv *rubyserver.Server){ - testSuccessfulFindLicenseRequest, - testFindLicenseRequestEmptyRepo, - } - for _, f := range fs { - t.Run(runtime.FuncForPC(reflect.ValueOf(f).Pointer()).Name(), func(t *testing.T) { - f(t, cfg, client, rubySrv) - }) - } -} diff --git a/internal/metadata/featureflag/ff_go_find_license.go b/internal/metadata/featureflag/ff_go_find_license.go deleted file mode 100644 index a3c63b2c9..000000000 --- a/internal/metadata/featureflag/ff_go_find_license.go +++ /dev/null @@ -1,9 +0,0 @@ -package featureflag - -// GoFindLicense enables Go implementation of FindLicense -var GoFindLicense = NewFeatureFlag( - "go_find_license", - "v14.3.0", - "https://gitlab.com/gitlab-org/gitaly/-/issues/3759", - false, -) |