From 5427130a425385bf59a67655a31d81f06cb0601e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20Ga=C4=87e=C5=A1a?= Date: Sat, 6 Jul 2024 00:46:36 +0000 Subject: [PATCH] pull request import (#2121) * bypass lint * bump golang version * using types from migrator * moving Importing to the RepositoryOutput * minor changes; improved unit tests * pr comments * pr import * pull request import --- .github/workflows/ci-lint.yml | 2 +- Dockerfile | 2 +- app/api/controller/githook/pre_receive.go | 5 + app/api/controller/pullreq/controller.go | 8 +- app/api/controller/pullreq/wire.go | 5 +- app/api/controller/repo/controller.go | 24 +- app/api/controller/repo/create.go | 5 +- app/api/controller/repo/git_info_refs.go | 2 +- app/api/controller/repo/git_service_pack.go | 2 +- app/api/controller/repo/helper.go | 27 +- app/api/controller/repo/import.go | 5 +- app/api/controller/repo/move.go | 4 +- app/api/controller/repo/purge.go | 2 +- app/api/controller/repo/restore.go | 5 +- app/api/controller/repo/soft_delete.go | 2 +- .../controller/repo/update_public_access.go | 10 +- app/api/controller/reposettings/controller.go | 1 + .../controller/space/import_repositories.go | 20 +- app/services/importer/provider.go | 3 +- app/services/importer/pullreq.go | 538 ++++++++++++++++++ app/services/importer/pullreq_test.go | 72 +++ app/services/importer/pullreq_types.go | 27 + app/services/importer/repository.go | 12 +- app/services/importer/wire.go | 23 + ...0057_alter_repositories_add_state.down.sql | 7 + .../0057_alter_repositories_add_state.up.sql | 7 + ...0057_alter_repositories_add_state.down.sql | 7 + .../0057_alter_repositories_add_state.up.sql | 7 + app/store/database/repo.go | 18 +- cmd/gitness/wire_gen.go | 3 +- go.mod | 8 +- go.sum | 22 +- types/enum/repo.go | 31 +- types/repo.go | 4 +- 34 files changed, 843 insertions(+), 77 deletions(-) create mode 100644 app/services/importer/pullreq.go create mode 100644 app/services/importer/pullreq_test.go create mode 100644 app/services/importer/pullreq_types.go create mode 100644 app/store/database/migrate/postgres/0057_alter_repositories_add_state.down.sql create mode 100644 app/store/database/migrate/postgres/0057_alter_repositories_add_state.up.sql create mode 100644 app/store/database/migrate/sqlite/0057_alter_repositories_add_state.down.sql create mode 100644 app/store/database/migrate/sqlite/0057_alter_repositories_add_state.up.sql diff --git a/.github/workflows/ci-lint.yml b/.github/workflows/ci-lint.yml index 84f4acf02..fe0df2e75 100644 --- a/.github/workflows/ci-lint.yml +++ b/.github/workflows/ci-lint.yml @@ -35,7 +35,7 @@ jobs: - uses: actions/checkout@v3 - uses: actions/setup-go@v4 with: - go-version: '1.20' + go-version: '1.22' - name: get dependencies run: | mkdir -p ./web/dist diff --git a/Dockerfile b/Dockerfile index 6fe7c6051..c63bba83e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -18,7 +18,7 @@ RUN yarn && yarn build && yarn cache clean # ---------------------------------------------------------# # Build gitness image # # ---------------------------------------------------------# -FROM --platform=$BUILDPLATFORM golang:1.20-alpine3.18 as builder +FROM --platform=$BUILDPLATFORM golang:1.22-alpine3.18 as builder RUN apk update \ && apk add --no-cache protoc build-base git diff --git a/app/api/controller/githook/pre_receive.go b/app/api/controller/githook/pre_receive.go index 95100d64c..35b3bf433 100644 --- a/app/api/controller/githook/pre_receive.go +++ b/app/api/controller/githook/pre_receive.go @@ -47,6 +47,11 @@ func (c *Controller) PreReceive( return hook.Output{}, err } + if !in.Internal && repo.State != enum.RepoStateActive && repo.State != enum.RepoStateMigrateGitPush { + output.Error = ptr.String("Push not allowed in the current repository state") + return output, nil + } + if err := c.limiter.RepoSize(ctx, in.RepoID); err != nil { return hook.Output{}, fmt.Errorf( "resource limit exceeded: %w", diff --git a/app/api/controller/pullreq/controller.go b/app/api/controller/pullreq/controller.go index 4ec6106e3..6599b19f8 100644 --- a/app/api/controller/pullreq/controller.go +++ b/app/api/controller/pullreq/controller.go @@ -25,6 +25,7 @@ import ( pullreqevents "github.com/harness/gitness/app/events/pullreq" "github.com/harness/gitness/app/services/codecomments" "github.com/harness/gitness/app/services/codeowners" + "github.com/harness/gitness/app/services/importer" locker "github.com/harness/gitness/app/services/locker" "github.com/harness/gitness/app/services/protection" "github.com/harness/gitness/app/services/pullreq" @@ -62,6 +63,7 @@ type Controller struct { sseStreamer sse.Streamer codeOwners *codeowners.Service locker *locker.Locker + importer *importer.PullReq } func NewController( @@ -87,6 +89,7 @@ func NewController( sseStreamer sse.Streamer, codeowners *codeowners.Service, locker *locker.Locker, + importer *importer.PullReq, ) *Controller { return &Controller{ tx: tx, @@ -111,6 +114,7 @@ func NewController( sseStreamer: sseStreamer, codeOwners: codeowners, locker: locker, + importer: importer, } } @@ -152,8 +156,8 @@ func (c *Controller) getRepoCheckAccess(ctx context.Context, return nil, fmt.Errorf("failed to find repository: %w", err) } - if repo.Importing { - return nil, usererror.BadRequest("Repository import is in progress.") + if repo.State != enum.RepoStateActive { + return nil, usererror.BadRequest("Repository is not ready to use.") } if err = apiauth.CheckRepo(ctx, c.authorizer, session, repo, reqPermission); err != nil { diff --git a/app/api/controller/pullreq/wire.go b/app/api/controller/pullreq/wire.go index eb2d34e52..dca5631c8 100644 --- a/app/api/controller/pullreq/wire.go +++ b/app/api/controller/pullreq/wire.go @@ -19,6 +19,7 @@ import ( pullreqevents "github.com/harness/gitness/app/events/pullreq" "github.com/harness/gitness/app/services/codecomments" "github.com/harness/gitness/app/services/codeowners" + "github.com/harness/gitness/app/services/importer" "github.com/harness/gitness/app/services/locker" "github.com/harness/gitness/app/services/protection" "github.com/harness/gitness/app/services/pullreq" @@ -45,7 +46,7 @@ func ProvideController(tx dbtx.Transactor, urlProvider url.Provider, authorizer checkStore store.CheckStore, rpcClient git.Interface, eventReporter *pullreqevents.Reporter, codeCommentMigrator *codecomments.Migrator, pullreqService *pullreq.Service, ruleManager *protection.Manager, sseStreamer sse.Streamer, - codeOwners *codeowners.Service, locker *locker.Locker, + codeOwners *codeowners.Service, locker *locker.Locker, importer *importer.PullReq, ) *Controller { return NewController(tx, urlProvider, authorizer, pullReqStore, pullReqActivityStore, @@ -56,5 +57,5 @@ func ProvideController(tx dbtx.Transactor, urlProvider url.Provider, authorizer checkStore, rpcClient, eventReporter, codeCommentMigrator, - pullreqService, ruleManager, sseStreamer, codeOwners, locker) + pullreqService, ruleManager, sseStreamer, codeOwners, locker, importer) } diff --git a/app/api/controller/repo/controller.go b/app/api/controller/repo/controller.go index 11ed4b721..200e533be 100644 --- a/app/api/controller/repo/controller.go +++ b/app/api/controller/repo/controller.go @@ -49,7 +49,8 @@ var errPublicRepoCreationDisabled = usererror.BadRequestf("Public repository cre type RepositoryOutput struct { types.Repository - IsPublic bool `json:"is_public" yaml:"is_public"` + IsPublic bool `json:"is_public" yaml:"is_public"` + Importing bool `json:"importing" yaml:"-"` } // TODO [CODE-1363]: remove after identifier migration. @@ -156,6 +157,7 @@ func (c *Controller) getRepo( ctx, c.repoStore, repoRef, + ActiveRepoStates, ) } @@ -174,6 +176,26 @@ func (c *Controller) getRepoCheckAccess( session, repoRef, reqPermission, + ActiveRepoStates, + ) +} + +// getRepoCheckAccessForGit fetches a repo +// and checks if the current user has permission to access it. +func (c *Controller) getRepoCheckAccessForGit( + ctx context.Context, + session *auth.Session, + repoRef string, + reqPermission enum.Permission, +) (*types.Repository, error) { + return GetRepoCheckAccess( + ctx, + c.repoStore, + c.authorizer, + session, + repoRef, + reqPermission, + nil, // Any state allowed - we'll block in the pre-receive hook. ) } diff --git a/app/api/controller/repo/create.go b/app/api/controller/repo/create.go index 363b65d56..b8b235718 100644 --- a/app/api/controller/repo/create.go +++ b/app/api/controller/repo/create.go @@ -149,10 +149,7 @@ func (c *Controller) Create(ctx context.Context, session *auth.Session, in *Crea repo.GitURL = c.urlProvider.GenerateGITCloneURL(repo.Path) repo.GitSSHURL = c.urlProvider.GenerateGITCloneSSHURL(repo.Path) - repoOutput := &RepositoryOutput{ - Repository: *repo, - IsPublic: in.IsPublic, - } + repoOutput := GetRepoOutputWithAccess(ctx, in.IsPublic, repo) err = c.auditService.Log(ctx, session.Principal, diff --git a/app/api/controller/repo/git_info_refs.go b/app/api/controller/repo/git_info_refs.go index 3d9fbe09f..97e88ed82 100644 --- a/app/api/controller/repo/git_info_refs.go +++ b/app/api/controller/repo/git_info_refs.go @@ -33,7 +33,7 @@ func (c *Controller) GitInfoRefs( gitProtocol string, w io.Writer, ) error { - repo, err := c.getRepoCheckAccess(ctx, session, repoRef, enum.PermissionRepoView) + repo, err := c.getRepoCheckAccessForGit(ctx, session, repoRef, enum.PermissionRepoView) if err != nil { return fmt.Errorf("failed to verify repo access: %w", err) } diff --git a/app/api/controller/repo/git_service_pack.go b/app/api/controller/repo/git_service_pack.go index 08f164af4..6529cae08 100644 --- a/app/api/controller/repo/git_service_pack.go +++ b/app/api/controller/repo/git_service_pack.go @@ -40,7 +40,7 @@ func (c *Controller) GitServicePack( permission = enum.PermissionRepoPush } - repo, err := c.getRepoCheckAccess(ctx, session, repoRef, permission) + repo, err := c.getRepoCheckAccessForGit(ctx, session, repoRef, permission) if err != nil { return fmt.Errorf("failed to verify repo access: %w", err) } diff --git a/app/api/controller/repo/helper.go b/app/api/controller/repo/helper.go index 97cce0e48..b3ba15ce8 100644 --- a/app/api/controller/repo/helper.go +++ b/app/api/controller/repo/helper.go @@ -26,13 +26,18 @@ import ( "github.com/harness/gitness/app/store" "github.com/harness/gitness/types" "github.com/harness/gitness/types/enum" + + "golang.org/x/exp/slices" ) -// GetRepo fetches an active repo (not one that is currently being imported). +var ActiveRepoStates = []enum.RepoState{enum.RepoStateActive} + +// GetRepo fetches an repository. func GetRepo( ctx context.Context, repoStore store.RepoStore, repoRef string, + allowedStates []enum.RepoState, ) (*types.Repository, error) { if repoRef == "" { return nil, usererror.BadRequest("A valid repository reference must be provided.") @@ -43,8 +48,8 @@ func GetRepo( return nil, fmt.Errorf("failed to find repository: %w", err) } - if repo.Importing { - return nil, usererror.BadRequest("Repository import is in progress.") + if len(allowedStates) > 0 && !slices.Contains(allowedStates, repo.State) { + return nil, usererror.BadRequest("Repository is not ready to use.") } return repo, nil @@ -59,8 +64,9 @@ func GetRepoCheckAccess( session *auth.Session, repoRef string, reqPermission enum.Permission, + allowedStates []enum.RepoState, ) (*types.Repository, error) { - repo, err := GetRepo(ctx, repoStore, repoRef) + repo, err := GetRepo(ctx, repoStore, repoRef, allowedStates) if err != nil { return nil, fmt.Errorf("failed to find repo: %w", err) } @@ -85,5 +91,18 @@ func GetRepoOutput( return &RepositoryOutput{ Repository: *repo, IsPublic: isPublic, + Importing: repo.State != enum.RepoStateActive, }, nil } + +func GetRepoOutputWithAccess( + _ context.Context, + isPublic bool, + repo *types.Repository, +) *RepositoryOutput { + return &RepositoryOutput{ + Repository: *repo, + IsPublic: isPublic, + Importing: repo.State != enum.RepoStateActive, + } +} diff --git a/app/api/controller/repo/import.go b/app/api/controller/repo/import.go index 0f988295c..e9dd5a39f 100644 --- a/app/api/controller/repo/import.go +++ b/app/api/controller/repo/import.go @@ -114,10 +114,7 @@ func (c *Controller) Import(ctx context.Context, session *auth.Session, in *Impo log.Warn().Msgf("failed to insert audit log for import repository operation: %s", err) } - return &RepositoryOutput{ - Repository: *repo, - IsPublic: false, - }, nil + return GetRepoOutputWithAccess(ctx, false, repo), nil } func (c *Controller) sanitizeImportInput(in *ImportInput) error { diff --git a/app/api/controller/repo/move.go b/app/api/controller/repo/move.go index 1568551c4..c7f7fc4b9 100644 --- a/app/api/controller/repo/move.go +++ b/app/api/controller/repo/move.go @@ -58,8 +58,8 @@ func (c *Controller) Move(ctx context.Context, return nil, err } - if repo.Importing { - return nil, usererror.BadRequest("can't move a repo that is being imported") + if repo.State != enum.RepoStateActive { + return nil, usererror.BadRequest("Can't move a repo that isn't ready to use.") } if err = apiauth.CheckRepo(ctx, c.authorizer, session, repo, enum.PermissionRepoEdit); err != nil { diff --git a/app/api/controller/repo/purge.go b/app/api/controller/repo/purge.go index 5591820f1..37cd16aab 100644 --- a/app/api/controller/repo/purge.go +++ b/app/api/controller/repo/purge.go @@ -64,7 +64,7 @@ func (c *Controller) PurgeNoAuth( session *auth.Session, repo *types.Repository, ) error { - if repo.Importing { + if repo.State == enum.RepoStateGitImport { log.Ctx(ctx).Info().Msg("repository is importing. cancelling the import job.") err := c.importer.Cancel(ctx, repo) if err != nil { diff --git a/app/api/controller/repo/restore.go b/app/api/controller/repo/restore.go index 606bf3359..86520effc 100644 --- a/app/api/controller/repo/restore.go +++ b/app/api/controller/repo/restore.go @@ -94,8 +94,5 @@ func (c *Controller) RestoreNoAuth( } // Repos restored as private since public access data has been deleted upon deletion. - return &RepositoryOutput{ - Repository: *repo, - IsPublic: false, - }, nil + return GetRepoOutputWithAccess(ctx, false, repo), nil } diff --git a/app/api/controller/repo/soft_delete.go b/app/api/controller/repo/soft_delete.go index fc8670c50..f3ae42619 100644 --- a/app/api/controller/repo/soft_delete.go +++ b/app/api/controller/repo/soft_delete.go @@ -97,7 +97,7 @@ func (c *Controller) SoftDeleteNoAuth( return fmt.Errorf("failed to delete public access for repo: %w", err) } - if repo.Importing { + if repo.State != enum.RepoStateActive { return c.PurgeNoAuth(ctx, session, repo) } diff --git a/app/api/controller/repo/update_public_access.go b/app/api/controller/repo/update_public_access.go index e2d60893c..90e418c29 100644 --- a/app/api/controller/repo/update_public_access.go +++ b/app/api/controller/repo/update_public_access.go @@ -63,10 +63,7 @@ func (c *Controller) UpdatePublicAccess(ctx context.Context, // no op if isPublic == in.IsPublic { - return &RepositoryOutput{ - Repository: *repo, - IsPublic: isPublic, - }, nil + return GetRepoOutputWithAccess(ctx, isPublic, repo), nil } if err = c.publicAccess.Set(ctx, enum.PublicResourceTypeRepo, repo.Path, in.IsPublic); err != nil { @@ -95,8 +92,5 @@ func (c *Controller) UpdatePublicAccess(ctx context.Context, log.Ctx(ctx).Warn().Msgf("failed to insert audit log for update repository operation: %s", err) } - return &RepositoryOutput{ - Repository: *repo, - IsPublic: in.IsPublic, - }, nil + return GetRepoOutputWithAccess(ctx, in.IsPublic, repo), nil } diff --git a/app/api/controller/reposettings/controller.go b/app/api/controller/reposettings/controller.go index f59a87573..e3632ee2e 100644 --- a/app/api/controller/reposettings/controller.go +++ b/app/api/controller/reposettings/controller.go @@ -63,5 +63,6 @@ func (c *Controller) getRepoCheckAccess( session, repoRef, reqPermission, + repo.ActiveRepoStates, ) } diff --git a/app/api/controller/space/import_repositories.go b/app/api/controller/space/import_repositories.go index 7791547c7..2320e65f9 100644 --- a/app/api/controller/space/import_repositories.go +++ b/app/api/controller/space/import_repositories.go @@ -21,7 +21,7 @@ import ( apiauth "github.com/harness/gitness/app/api/auth" "github.com/harness/gitness/app/api/controller/limiter" - repoCtrl "github.com/harness/gitness/app/api/controller/repo" + repoctrl "github.com/harness/gitness/app/api/controller/repo" "github.com/harness/gitness/app/api/usererror" "github.com/harness/gitness/app/auth" "github.com/harness/gitness/app/paths" @@ -39,8 +39,8 @@ type ImportRepositoriesInput struct { } type ImportRepositoriesOutput struct { - ImportingRepos []*repoCtrl.RepositoryOutput `json:"importing_repos"` - DuplicateRepos []*repoCtrl.RepositoryOutput `json:"duplicate_repos"` // repos which already exist in the space. + ImportingRepos []*repoctrl.RepositoryOutput `json:"importing_repos"` + DuplicateRepos []*repoctrl.RepositoryOutput `json:"duplicate_repos"` // repos which already exist in the space. } // getSpaceCheckAuthRepoCreation checks whether the user has permissions to create repos @@ -165,12 +165,9 @@ func (c *Controller) ImportRepositories( return ImportRepositoriesOutput{}, err } - reposOut := make([]*repoCtrl.RepositoryOutput, len(repos)) + reposOut := make([]*repoctrl.RepositoryOutput, len(repos)) for i, repo := range repos { - reposOut[i] = &repoCtrl.RepositoryOutput{ - Repository: *repo, - IsPublic: false, - } + reposOut[i] = repoctrl.GetRepoOutputWithAccess(ctx, false, repo) err = c.auditService.Log(ctx, session.Principal, @@ -187,12 +184,9 @@ func (c *Controller) ImportRepositories( } } - duplicateReposOut := make([]*repoCtrl.RepositoryOutput, len(duplicateRepos)) + duplicateReposOut := make([]*repoctrl.RepositoryOutput, len(duplicateRepos)) for i, dupRepo := range duplicateRepos { - duplicateReposOut[i] = &repoCtrl.RepositoryOutput{ - Repository: *dupRepo, - IsPublic: false, - } + duplicateReposOut[i] = repoctrl.GetRepoOutputWithAccess(ctx, false, dupRepo) } return ImportRepositoriesOutput{ImportingRepos: reposOut, DuplicateRepos: duplicateReposOut}, nil diff --git a/app/services/importer/provider.go b/app/services/importer/provider.go index 5e44473e0..d876133d0 100644 --- a/app/services/importer/provider.go +++ b/app/services/importer/provider.go @@ -27,6 +27,7 @@ import ( "github.com/harness/gitness/app/api/usererror" "github.com/harness/gitness/app/paths" "github.com/harness/gitness/types" + "github.com/harness/gitness/types/enum" "github.com/drone/go-scm/scm" "github.com/drone/go-scm/scm/driver/azure" @@ -100,7 +101,7 @@ func (r *RepositoryInfo) ToRepo( Updated: now, ForkID: 0, DefaultBranch: r.DefaultBranch, - Importing: true, + State: enum.RepoStateGitImport, Path: paths.Concatenate(spacePath, identifier), }, r.IsPublic } diff --git a/app/services/importer/pullreq.go b/app/services/importer/pullreq.go new file mode 100644 index 000000000..3cbae23ec --- /dev/null +++ b/app/services/importer/pullreq.go @@ -0,0 +1,538 @@ +// Copyright 2023 Harness, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package importer + +import ( + "context" + "encoding/json" + "fmt" + "sort" + "strings" + "time" + + "github.com/harness/gitness/app/store" + "github.com/harness/gitness/app/url" + "github.com/harness/gitness/errors" + "github.com/harness/gitness/git" + "github.com/harness/gitness/git/parser" + "github.com/harness/gitness/store/database/dbtx" + "github.com/harness/gitness/types" + "github.com/harness/gitness/types/enum" + + "github.com/rs/zerolog/log" +) + +// PullReq is pull request importer. +type PullReq struct { + urlProvider url.Provider + git git.Interface + principalStore store.PrincipalStore + repoStore store.RepoStore + pullReqStore store.PullReqStore + pullReqActStore store.PullReqActivityStore + tx dbtx.Transactor +} + +type repoImportState struct { + git git.Interface + readParams git.ReadParams + principalStore store.PrincipalStore + pullReqActivityStore store.PullReqActivityStore + branchCheck map[string]*git.Branch + principals map[string]*types.Principal +} + +// Import load provided pull requests in go-scm format and imports them. +// +//nolint:gocognit +func (importer PullReq) Import( + ctx context.Context, + repo *types.Repository, + extPullReqs []*ExternalPullRequest, +) ([]*types.PullReq, error) { + if repo.State != enum.RepoStateMigrateDataImport { + return nil, errors.PreconditionFailed("Repository data can't be imported at this point") + } + + readParams := git.ReadParams{RepoUID: repo.GitUID} + + repoState := repoImportState{ + git: importer.git, + readParams: readParams, + principalStore: importer.principalStore, + pullReqActivityStore: importer.pullReqActStore, + branchCheck: map[string]*git.Branch{}, + principals: map[string]*types.Principal{}, + } + + pullReqUnique := map[int]struct{}{} + pullReqComments := map[*types.PullReq][]ExternalComment{} + + pullReqs := make([]*types.PullReq, 0, len(extPullReqs)) + // create the PR objects, one by one. Each pull request will mutate the repository object (to update the counters). + for _, extPullReqData := range extPullReqs { + extPullReq := &extPullReqData.PullRequest + + if _, exists := pullReqUnique[extPullReq.Number]; exists { + return nil, errors.Conflict("duplicate pull request number %d", extPullReq.Number) + } + pullReqUnique[extPullReq.Number] = struct{}{} + + pr, err := repoState.convertPullReq(ctx, repo, extPullReqData) + if err != nil { + return nil, fmt.Errorf("failed to import pull request %d: %w", extPullReq.Number, err) + } + + pullReqs = append(pullReqs, pr) + pullReqComments[pr] = extPullReqData.Comments + } + + if len(pullReqs) == 0 { // nothing to do: exit early to avoid accessing the database + return nil, nil + } + + err := importer.tx.WithTx(ctx, func(ctx context.Context) error { + var deltaOpen, deltaClosed, deltaMerged int + var maxNumber int64 + + // Store the pull request objects and the comments. + for _, pullReq := range pullReqs { + if err := importer.pullReqStore.Create(ctx, pullReq); err != nil { + return fmt.Errorf("failed to import the pull request %d: %w", pullReq.Number, err) + } + + switch pullReq.State { + case enum.PullReqStateOpen: + deltaOpen++ + case enum.PullReqStateClosed: + deltaClosed++ + case enum.PullReqStateMerged: + deltaMerged++ + } + + if maxNumber < pullReq.Number { + maxNumber = pullReq.Number + } + + comments, err := repoState.createComments(ctx, repo, pullReq, pullReqComments[pullReq]) + if err != nil { + return fmt.Errorf("failed to import pull request comments: %w", err) + } + + if len(comments) == 0 { // no need to update the pull request object in the DB if there are no comments. + continue + } + + if err := importer.pullReqStore.Update(ctx, pullReq); err != nil { + return fmt.Errorf("failed to update pull request after importing of the comments: %w", err) + } + } + + // Update the repository + + repoUpdate, err := importer.repoStore.Find(ctx, repo.ID) + if err != nil { + return fmt.Errorf("failed to fetch repo in pull request import: %w", err) + } + + if repoUpdate.PullReqSeq < maxNumber { + repoUpdate.PullReqSeq = maxNumber + } + repoUpdate.NumPulls += len(pullReqs) + repoUpdate.NumOpenPulls += deltaOpen + repoUpdate.NumClosedPulls += deltaClosed + repoUpdate.NumMergedPulls += deltaMerged + + if err := importer.repoStore.Update(ctx, repoUpdate); err != nil { + return fmt.Errorf("failed to update repo in pull request import: %w", err) + } + + return nil + }) + if err != nil { + return nil, err + } + + return pullReqs, nil +} + +// convertPullReq analyses external pull request object and creates types.PullReq object out of it. +func (r *repoImportState) convertPullReq( + ctx context.Context, + repo *types.Repository, + extPullReqData *ExternalPullRequest, +) (*types.PullReq, error) { + extPullReq := extPullReqData.PullRequest + + log := log.Ctx(ctx).With(). + Str("repo.identifier", repo.Identifier). + Int("pullreq.number", extPullReq.Number). + Logger() + + author, err := r.getPrincipalByEmail(ctx, extPullReq.Author.Email) + if err != nil { + return nil, fmt.Errorf("failed to get pull request author: %w", err) + } + + now := time.Now().UnixMilli() + createdAt := timestampMillis(extPullReq.Created, now) + updatedAt := timestampMillis(extPullReq.Updated, now) + + pr := &types.PullReq{ + ID: 0, // the ID will be populated in the data layer + Version: 0, + Number: int64(extPullReq.Number), + CreatedBy: author.ID, + Created: createdAt, + Updated: updatedAt, + Edited: updatedAt, + Closed: nil, + State: enum.PullReqStateOpen, + IsDraft: false, + CommentCount: 0, + UnresolvedCount: 0, + Title: extPullReq.Title, + Description: extPullReq.Body, + SourceRepoID: repo.ID, + SourceBranch: extPullReq.Head.Name, + SourceSHA: extPullReq.Head.SHA, + TargetRepoID: repo.ID, + TargetBranch: extPullReq.Base.Name, + ActivitySeq: 0, + // Merge related fields are all left unset and will be set depending on the PR state + } + + params := git.ReadParams{RepoUID: repo.GitUID} + + // Set the state of the PR + switch { + case extPullReq.Merged: + pr.State = enum.PullReqStateMerged + case extPullReq.Closed: + pr.State = enum.PullReqStateClosed + default: + pr.State = enum.PullReqStateOpen + } + + // Update the PR depending on its state + switch pr.State { + case enum.PullReqStateMerged: + // For merged PR's assume the Head.Sha and Base.Sha point to commits at the time of merging. + + pr.Merged = &pr.Edited + pr.MergedBy = &author.ID // Don't have real info for this - use the author. + mergeMethod := enum.MergeMethodMerge // Don't know + pr.MergeMethod = &mergeMethod + + pr.MergeCheckStatus = enum.MergeCheckStatusMergeable + pr.SourceSHA = extPullReq.Head.SHA + pr.MergeTargetSHA = &extPullReq.Base.SHA + pr.MergeBaseSHA = extPullReq.Head.SHA // Don't have the real value. Set the value to SourceSHA. + pr.MergeSHA = nil // Don't have this. + pr.MergeConflicts = nil + + case enum.PullReqStateClosed: + // For closed PR's it's not important to verify existence of branches and commits. + // If these don't exist the PR will be impossible to open. + + pr.MergeCheckStatus = enum.MergeCheckStatusUnchecked + pr.MergeSHA = nil + pr.MergeConflicts = nil + pr.MergeTargetSHA = nil + pr.Closed = &pr.Edited + + case enum.PullReqStateOpen: + // For open PR we need to verify existence of branches and find to merge base. + + sourceBranch, err := r.git.GetBranch(ctx, &git.GetBranchParams{ + ReadParams: params, + BranchName: extPullReq.Head.Name, + }) + if err != nil { + return nil, fmt.Errorf("failed to fetch source branch of an open pull request: %w", err) + } + + // TODO: Cache this in the repoImportState - it's very likely that it will be the same for other PRs + targetBranch, err := r.git.GetBranch(ctx, &git.GetBranchParams{ + ReadParams: params, + BranchName: extPullReq.Base.Name, + }) + if err != nil { + return nil, fmt.Errorf("failed to fetch target branch of an open pull request: %w", err) + } + + mergeBase, err := r.git.MergeBase(ctx, git.MergeBaseParams{ + ReadParams: params, + Ref1: sourceBranch.Branch.SHA.String(), + Ref2: targetBranch.Branch.SHA.String(), + }) + if err != nil { + return nil, fmt.Errorf("failed to find merge base an open pull request: %w", err) + } + + sourceSHA := sourceBranch.Branch.SHA.String() + targetSHA := targetBranch.Branch.SHA.String() + pr.SourceSHA = sourceSHA + pr.MergeTargetSHA = &targetSHA + pr.MergeBaseSHA = mergeBase.MergeBaseSHA.String() + pr.MergeCheckStatus = enum.MergeCheckStatusUnchecked + } + + log.Debug().Str("pullreq.state", string(pr.State)).Msg("importing pull request") + + return pr, nil +} + +// createComments analyses external pull request comment objects and stores types.PullReqActivity object to the DB. +// It will mutate the pull request object to update counter fields. +func (r *repoImportState) createComments( + ctx context.Context, + repo *types.Repository, + pullReq *types.PullReq, + extComments []ExternalComment, +) ([]*types.PullReqActivity, error) { + log := log.Ctx(ctx).With(). + Str("repo.id", repo.Identifier). + Int("pullreq.number", int(pullReq.Number)). + Logger() + + extThreads := generateThreads(extComments) + + comments := make([]*types.PullReqActivity, 0, len(extComments)) + for idxTopLevel, extThread := range extThreads { + order := idxTopLevel + 1 + + // Create the top level comment with the correct value of Order, SubOrder and ReplySeq. + commentTopLevel, err := r.createComment(ctx, repo, pullReq, nil, + order, 0, len(extThread.Replies), &extThread.TopLevel) + if err != nil { + return nil, fmt.Errorf("failed to create top level comment: %w", err) + } + + comments = append(comments, commentTopLevel) + + for idxReply, extReply := range extThread.Replies { + subOrder := idxReply + 1 + + // Create the reply comment with the correct value of Order, SubOrder and ReplySeq. + //nolint:gosec + commentReply, err := r.createComment(ctx, repo, pullReq, &commentTopLevel.ID, + order, subOrder, 0, &extReply) + if err != nil { + return nil, fmt.Errorf("failed to create reply comment: %w", err) + } + + comments = append(comments, commentReply) + } + } + + log.Debug().Int("count", len(comments)).Msg("imported pull request comments") + + return comments, nil +} + +// createComment analyses an external pull request comment object and creates types.PullReqActivity object out of it. +// It will mutate the pull request object to update counter fields. +func (r *repoImportState) createComment( + ctx context.Context, + repo *types.Repository, + pullReq *types.PullReq, + parentID *int64, + order, subOrder, replySeq int, + extComment *ExternalComment, +) (*types.PullReqActivity, error) { + commenter, err := r.getPrincipalByEmail(ctx, extComment.Author.Email) + if err != nil { + return nil, fmt.Errorf("failed to get comment ID=%d author: %w", extComment.ID, err) + } + + commentedAt := extComment.Created.UnixMilli() + + // Mark comments as resolved if the PR is merged, otherwise they are unresolved. + var resolved, resolvedBy *int64 + if pullReq.State == enum.PullReqStateMerged { + resolved = &commentedAt + resolvedBy = &commenter.ID + } + + comment := &types.PullReqActivity{ + CreatedBy: commenter.ID, + Created: commentedAt, + Updated: commentedAt, + Edited: commentedAt, + Deleted: nil, + ParentID: parentID, + RepoID: repo.ID, + PullReqID: pullReq.ID, + Order: int64(order), + SubOrder: int64(subOrder), + ReplySeq: int64(replySeq), + Type: enum.PullReqActivityTypeComment, + Kind: enum.PullReqActivityKindComment, + Text: extComment.Body, + PayloadRaw: json.RawMessage("{}"), + Metadata: nil, + ResolvedBy: resolvedBy, + Resolved: resolved, + CodeComment: nil, + Mentions: nil, + } + + if cc := extComment.CodeComment; cc != nil && cc.HunkHeader != "" && extComment.ParentID == 0 { + // a code comment must have a valid HunkHeader and must not be a reply + hunkHeader, ok := parser.ParseDiffHunkHeader(cc.HunkHeader) + if !ok { + return nil, errors.InvalidArgument("Invalid hunk header for code comment: %s", cc.HunkHeader) + } + + comment.Kind = enum.PullReqActivityKindChangeComment + comment.Type = enum.PullReqActivityTypeCodeComment + + comment.CodeComment = &types.CodeCommentFields{ + Outdated: cc.SourceSHA != pullReq.SourceSHA, + MergeBaseSHA: cc.MergeBaseSHA, + SourceSHA: cc.SourceSHA, + Path: cc.Path, + LineNew: hunkHeader.NewLine, + SpanNew: hunkHeader.NewSpan, + LineOld: hunkHeader.OldLine, + SpanOld: hunkHeader.OldSpan, + } + + sideNew := !strings.EqualFold(cc.Side, "OLD") // cc.Side can be either OLD or NEW + _ = comment.SetPayload(&types.PullRequestActivityPayloadCodeComment{ + Title: cc.CodeSnippet.Header, + Lines: cc.CodeSnippet.Lines, + LineStartNew: sideNew, + LineEndNew: sideNew, + }) + } + + // store the comment + + if err := r.pullReqActivityStore.Create(ctx, comment); err != nil { + return nil, fmt.Errorf("failed to store the external comment ID=%d author: %w", extComment.ID, err) + } + + // update the pull request's counter fields + + pullReq.CommentCount++ + if comment.IsBlocking() { + pullReq.UnresolvedCount++ + } + + if pullReq.ActivitySeq < comment.Order { + pullReq.ActivitySeq = comment.Order + } + + return comment, nil +} + +func (r *repoImportState) getPrincipalByEmail(ctx context.Context, emailAddress string) (*types.Principal, error) { + if principal, exists := r.principals[emailAddress]; exists { + return principal, nil + } + + principal, err := r.principalStore.FindByEmail(ctx, emailAddress) + if err != nil { + return nil, fmt.Errorf("failed to load principal by email: %w", err) + } + + return principal, nil +} + +func timestampMillis(t time.Time, def int64) int64 { + if t.IsZero() { + return def + } + + return t.UnixMilli() +} + +func generateThreads(extComments []ExternalComment) []*externalCommentThread { + extCommentParents := make(map[int]int, len(extComments)) + extCommentMap := make(map[int]ExternalComment, len(extComments)) + for _, extComment := range extComments { + extCommentParents[extComment.ID] = extComment.ParentID + extCommentMap[extComment.ID] = extComment + } + + // Make flat list of reply comment IDs: create map[topLevelCommentID]->[]commentID + extCommentIDReplyMap := make(map[int][]int) + for _, extComment := range extComments { + topLevelParentID := getTopLevelParentID(extComment.ID, extCommentParents) + if topLevelParentID < 0 { + continue + } + if topLevelParentID == extComment.ID { + // Make sure the item with topLevelParentID exist in the map, at least as a nil entry. + extCommentIDReplyMap[topLevelParentID] = extCommentIDReplyMap[topLevelParentID] //nolint:staticcheck + continue + } + extCommentIDReplyMap[topLevelParentID] = append(extCommentIDReplyMap[topLevelParentID], extComment.ID) + } + + countTopLevel := len(extCommentIDReplyMap) + if countTopLevel == 0 { + return nil + } + + extCommentThreads := make([]*externalCommentThread, 0, countTopLevel) + for topLevelID, replyIDs := range extCommentIDReplyMap { + expReplyComments := make([]ExternalComment, len(replyIDs)) + for i, replyID := range replyIDs { + expReplyComments[i] = extCommentMap[replyID] + } + thread := &externalCommentThread{ + TopLevel: extCommentMap[topLevelID], + Replies: expReplyComments, + } + extCommentThreads = append(extCommentThreads, thread) + } + + // order top level comments + + sort.Slice(extCommentThreads, func(i, j int) bool { + created1 := extCommentThreads[i].TopLevel.Created + created2 := extCommentThreads[j].TopLevel.Created + return created1.Before(created2) + }) + + // order reply comments + + for _, thread := range extCommentThreads { + sort.Slice(thread.Replies, func(i, j int) bool { + created1 := thread.Replies[i].Created + created2 := thread.Replies[j].Created + return created1.Before(created2) + }) + } + + return extCommentThreads +} + +func getTopLevelParentID(id int, tree map[int]int) int { + const maxDepth = 20 + for currID, depth := id, 0; depth < maxDepth; depth++ { + parentID := tree[currID] + if parentID == 0 { + return currID + } + + currID = parentID + } + + return -1 +} diff --git a/app/services/importer/pullreq_test.go b/app/services/importer/pullreq_test.go new file mode 100644 index 000000000..bfd21941d --- /dev/null +++ b/app/services/importer/pullreq_test.go @@ -0,0 +1,72 @@ +// Copyright 2023 Harness, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package importer + +import ( + "testing" + "time" + + "github.com/google/go-cmp/cmp" +) + +func TestGenerateThreads(t *testing.T) { + // comments with treelike structure + t0 := time.Now() + comments := []ExternalComment{ + /* 0 */ {ID: 1, Body: "A", ParentID: 0}, + /* 1 */ {ID: 2, Body: "B", ParentID: 0}, + /* 2 */ {ID: 3, Body: "A1", ParentID: 1}, + /* 3 */ {ID: 4, Body: "B1", ParentID: 2}, + /* 4 */ {ID: 5, Body: "A2", ParentID: 1}, + /* 5 */ {ID: 6, Body: "A2X", ParentID: 5}, + /* 6 */ {ID: 7, Body: "A1X", ParentID: 3}, + /* 7 */ {ID: 8, Body: "B1X", ParentID: 4}, + /* 8 */ {ID: 9, Body: "C", ParentID: 0}, + /* 9 */ {ID: 10, Body: "D1", ParentID: 11}, // Wrong order - a reply before its parent + /* 10 */ {ID: 11, Body: "D", ParentID: 0}, + {ID: 20, Body: "Self-parent", ParentID: 20}, // Invalid + {ID: 30, Body: "Crosslinked-X", ParentID: 31}, // Invalid + {ID: 31, Body: "Crosslinked-Y", ParentID: 30}, // Invalid + } + + for i := range comments { + comments[i].Created = t0.Add(time.Duration(i) * time.Minute) + } + + // flattened threads with top level comments and a list of replies to each of them + wantThreads := []*externalCommentThread{ + { + TopLevel: comments[0], // A + Replies: []ExternalComment{comments[2], comments[4], comments[5], comments[6]}, // A1, A2, A2X, A1X + }, + { + TopLevel: comments[1], // B + Replies: []ExternalComment{comments[3], comments[7]}, // B1, B1X + }, + { + TopLevel: comments[8], // C + Replies: []ExternalComment{}, + }, + { + TopLevel: comments[10], // D + Replies: []ExternalComment{comments[9]}, // D1 + }, + } + + gotThreads := generateThreads(comments) + if diff := cmp.Diff(gotThreads, wantThreads); diff != "" { + t.Errorf(diff) + } +} diff --git a/app/services/importer/pullreq_types.go b/app/services/importer/pullreq_types.go new file mode 100644 index 000000000..577449496 --- /dev/null +++ b/app/services/importer/pullreq_types.go @@ -0,0 +1,27 @@ +// Copyright 2023 Harness, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package importer + +import ( + migratetypes "github.com/harness/harness-migrate/types" +) + +type ExternalPullRequest = migratetypes.PullRequestData +type ExternalComment = migratetypes.Comment + +type externalCommentThread struct { + TopLevel ExternalComment + Replies []ExternalComment +} diff --git a/app/services/importer/repository.go b/app/services/importer/repository.go index 96010f306..98e173f5e 100644 --- a/app/services/importer/repository.go +++ b/app/services/importer/repository.go @@ -238,7 +238,7 @@ func (r *Repository) Handle(ctx context.Context, data string, _ job.ProgressRepo return "", fmt.Errorf("failed to find repo by id: %w", err) } - if !repo.Importing { + if repo.State != enum.RepoStateGitImport { return "", fmt.Errorf("repository %s is not being imported", repo.Identifier) } @@ -302,7 +302,7 @@ func (r *Repository) Handle(ctx context.Context, data string, _ job.ProgressRepo err = func() error { repo, err = r.repoStore.UpdateOptLock(ctx, repo, func(repo *types.Repository) error { - if !repo.Importing { + if repo.State != enum.RepoStateGitImport { return errors.New("repository has already finished importing") } repo.GitUID = gitUID @@ -328,13 +328,13 @@ func (r *Repository) Handle(ctx context.Context, data string, _ job.ProgressRepo log.Info().Msg("update repo in DB") repo, err = r.repoStore.UpdateOptLock(ctx, repo, func(repo *types.Repository) error { - if !repo.Importing { + if repo.State != enum.RepoStateGitImport { return errors.New("repository has already finished importing") } repo.GitUID = gitUID repo.DefaultBranch = defaultBranch - repo.Importing = false + repo.State = enum.RepoStateActive return nil }) @@ -385,7 +385,7 @@ func (r *Repository) Handle(ctx context.Context, data string, _ job.ProgressRepo func (r *Repository) GetProgress(ctx context.Context, repo *types.Repository) (job.Progress, error) { progress, err := r.scheduler.GetJobProgress(ctx, JobIDFromRepoID(repo.ID)) if errors.Is(err, gitness_store.ErrResourceNotFound) { - if repo.Importing { + if repo.State == enum.RepoStateGitImport { // if the job is not found but repo is marked as importing, return state=failed return job.FailProgress(), nil } @@ -401,7 +401,7 @@ func (r *Repository) GetProgress(ctx context.Context, repo *types.Repository) (j } func (r *Repository) Cancel(ctx context.Context, repo *types.Repository) error { - if !repo.Importing { + if repo.State != enum.RepoStateGitImport { return nil } diff --git a/app/services/importer/wire.go b/app/services/importer/wire.go index bab7fa4f2..2358ce957 100644 --- a/app/services/importer/wire.go +++ b/app/services/importer/wire.go @@ -32,6 +32,7 @@ import ( var WireSet = wire.NewSet( ProvideRepoImporter, + ProvidePullReqImporter, ) func ProvideRepoImporter( @@ -73,3 +74,25 @@ func ProvideRepoImporter( return importer, nil } + +func ProvidePullReqImporter( + urlProvider url.Provider, + git git.Interface, + principalStore store.PrincipalStore, + repoStore store.RepoStore, + pullReqStore store.PullReqStore, + pullReqActStore store.PullReqActivityStore, + tx dbtx.Transactor, +) *PullReq { + importer := &PullReq{ + urlProvider: urlProvider, + git: git, + principalStore: principalStore, + repoStore: repoStore, + pullReqStore: pullReqStore, + pullReqActStore: pullReqActStore, + tx: tx, + } + + return importer +} diff --git a/app/store/database/migrate/postgres/0057_alter_repositories_add_state.down.sql b/app/store/database/migrate/postgres/0057_alter_repositories_add_state.down.sql new file mode 100644 index 000000000..f024d84d5 --- /dev/null +++ b/app/store/database/migrate/postgres/0057_alter_repositories_add_state.down.sql @@ -0,0 +1,7 @@ +ALTER TABLE repositories + ADD COLUMN repo_importing BOOLEAN DEFAULT FALSE; + +UPDATE repositories SET repo_importing = TRUE WHERE repo_state = 1; + +ALTER TABLE repositories + DROP COLUMN repo_state; diff --git a/app/store/database/migrate/postgres/0057_alter_repositories_add_state.up.sql b/app/store/database/migrate/postgres/0057_alter_repositories_add_state.up.sql new file mode 100644 index 000000000..c92ab11a6 --- /dev/null +++ b/app/store/database/migrate/postgres/0057_alter_repositories_add_state.up.sql @@ -0,0 +1,7 @@ +ALTER TABLE repositories + ADD COLUMN repo_state INTEGER DEFAULT 0; + +UPDATE repositories SET repo_state = 1 WHERE repo_importing = TRUE; + +ALTER TABLE repositories + DROP COLUMN repo_importing; diff --git a/app/store/database/migrate/sqlite/0057_alter_repositories_add_state.down.sql b/app/store/database/migrate/sqlite/0057_alter_repositories_add_state.down.sql new file mode 100644 index 000000000..f024d84d5 --- /dev/null +++ b/app/store/database/migrate/sqlite/0057_alter_repositories_add_state.down.sql @@ -0,0 +1,7 @@ +ALTER TABLE repositories + ADD COLUMN repo_importing BOOLEAN DEFAULT FALSE; + +UPDATE repositories SET repo_importing = TRUE WHERE repo_state = 1; + +ALTER TABLE repositories + DROP COLUMN repo_state; diff --git a/app/store/database/migrate/sqlite/0057_alter_repositories_add_state.up.sql b/app/store/database/migrate/sqlite/0057_alter_repositories_add_state.up.sql new file mode 100644 index 000000000..c92ab11a6 --- /dev/null +++ b/app/store/database/migrate/sqlite/0057_alter_repositories_add_state.up.sql @@ -0,0 +1,7 @@ +ALTER TABLE repositories + ADD COLUMN repo_state INTEGER DEFAULT 0; + +UPDATE repositories SET repo_state = 1 WHERE repo_importing = TRUE; + +ALTER TABLE repositories + DROP COLUMN repo_importing; diff --git a/app/store/database/repo.go b/app/store/database/repo.go index a8f594659..b14912fc9 100644 --- a/app/store/database/repo.go +++ b/app/store/database/repo.go @@ -86,8 +86,8 @@ type repository struct { NumOpenPulls int `db:"repo_num_open_pulls"` NumMergedPulls int `db:"repo_num_merged_pulls"` - Importing bool `db:"repo_importing"` - IsEmpty bool `db:"repo_is_empty"` + State enum.RepoState `db:"repo_state"` + IsEmpty bool `db:"repo_is_empty"` } const ( @@ -112,7 +112,7 @@ const ( ,repo_num_closed_pulls ,repo_num_open_pulls ,repo_num_merged_pulls - ,repo_importing + ,repo_state ,repo_is_empty` ) @@ -236,7 +236,7 @@ func (s *RepoStore) Create(ctx context.Context, repo *types.Repository) error { ,repo_num_closed_pulls ,repo_num_open_pulls ,repo_num_merged_pulls - ,repo_importing + ,repo_state ,repo_is_empty ) values ( :repo_version @@ -258,7 +258,7 @@ func (s *RepoStore) Create(ctx context.Context, repo *types.Repository) error { ,:repo_num_closed_pulls ,:repo_num_open_pulls ,:repo_num_merged_pulls - ,:repo_importing + ,:repo_state ,:repo_is_empty ) RETURNING repo_id` @@ -301,7 +301,7 @@ func (s *RepoStore) Update(ctx context.Context, repo *types.Repository) error { ,repo_num_closed_pulls = :repo_num_closed_pulls ,repo_num_open_pulls = :repo_num_open_pulls ,repo_num_merged_pulls = :repo_num_merged_pulls - ,repo_importing = :repo_importing + ,repo_state = :repo_state ,repo_is_empty = :repo_is_empty WHERE repo_id = :repo_id AND repo_version = :repo_version - 1` @@ -744,7 +744,7 @@ func (s *RepoStore) mapToRepo( NumClosedPulls: in.NumClosedPulls, NumOpenPulls: in.NumOpenPulls, NumMergedPulls: in.NumMergedPulls, - Importing: in.Importing, + State: in.State, IsEmpty: in.IsEmpty, // Path: is set below } @@ -827,7 +827,7 @@ func mapToInternalRepo(in *types.Repository) *repository { NumClosedPulls: in.NumClosedPulls, NumOpenPulls: in.NumOpenPulls, NumMergedPulls: in.NumMergedPulls, - Importing: in.Importing, + State: in.State, IsEmpty: in.IsEmpty, } } @@ -857,7 +857,7 @@ func applySortFilter(stmt squirrel.SelectBuilder, filter *types.RepoFilter) squi // NOTE: string concatenation is safe because the // order attribute is an enum and is not user-defined, // and is therefore not subject to injection attacks. - stmt = stmt.OrderBy("repo_importing desc, repo_uid " + filter.Order.String()) + stmt = stmt.OrderBy("repo_state desc, repo_uid " + filter.Order.String()) case enum.RepoAttrCreated: stmt = stmt.OrderBy("repo_created " + filter.Order.String()) case enum.RepoAttrUpdated: diff --git a/cmd/gitness/wire_gen.go b/cmd/gitness/wire_gen.go index e719ca269..a92e752c4 100644 --- a/cmd/gitness/wire_gen.go +++ b/cmd/gitness/wire_gen.go @@ -266,7 +266,8 @@ func initSystem(ctx context.Context, config *types.Config) (*server.System, erro if err != nil { return nil, err } - pullreqController := pullreq2.ProvideController(transactor, provider, authorizer, pullReqStore, pullReqActivityStore, codeCommentView, pullReqReviewStore, pullReqReviewerStore, repoStore, principalStore, principalInfoCache, pullReqFileViewStore, membershipStore, checkStore, gitInterface, eventsReporter, migrator, pullreqService, protectionManager, streamer, codeownersService, lockerLocker) + pullReq := importer.ProvidePullReqImporter(provider, gitInterface, principalStore, repoStore, pullReqStore, pullReqActivityStore, transactor) + pullreqController := pullreq2.ProvideController(transactor, provider, authorizer, pullReqStore, pullReqActivityStore, codeCommentView, pullReqReviewStore, pullReqReviewerStore, repoStore, principalStore, principalInfoCache, pullReqFileViewStore, membershipStore, checkStore, gitInterface, eventsReporter, migrator, pullreqService, protectionManager, streamer, codeownersService, lockerLocker, pullReq) webhookConfig := server.ProvideWebhookConfig(config) webhookStore := database.ProvideWebhookStore(db) webhookExecutionStore := database.ProvideWebhookExecutionStore(db) diff --git a/go.mod b/go.mod index 8fa78d34e..4c9b4388e 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/harness/gitness -go 1.20 +go 1.22 replace github.com/docker/docker => github.com/docker/engine v17.12.0-ce-rc1.0.20200309214505-aa6a9891b09c+incompatible @@ -12,6 +12,8 @@ require ( github.com/bmatcuk/doublestar/v4 v4.6.0 github.com/coreos/go-semver v0.3.0 github.com/dchest/uniuri v0.0.0-20200228104902-7aecb25e1fe5 + github.com/docker/docker v23.0.3+incompatible + github.com/docker/go-connections v0.4.0 github.com/drone-runners/drone-runner-docker v1.8.4-0.20240109154718-47375e234554 github.com/drone/drone-go v1.7.1 github.com/drone/drone-yaml v1.2.3 @@ -36,7 +38,7 @@ require ( github.com/gorhill/cronexpr v0.0.0-20180427100037-88b0669f7d75 github.com/gotidy/ptr v1.4.0 github.com/guregu/null v4.0.0+incompatible - github.com/harness/harness-migrate v0.21.1-0.20240624210736-65c7e9fbe930 + github.com/harness/harness-migrate v0.21.1-0.20240703163651-0641dc7290d8 github.com/hashicorp/go-multierror v1.1.1 github.com/jmoiron/sqlx v1.3.3 github.com/joho/godotenv v1.3.0 @@ -88,8 +90,6 @@ require ( github.com/containerd/containerd v1.7.6 // indirect github.com/distribution/reference v0.5.0 // indirect github.com/docker/distribution v2.8.3+incompatible // indirect - github.com/docker/docker v23.0.3+incompatible // indirect - github.com/docker/go-connections v0.4.0 // indirect github.com/docker/go-units v0.5.0 // indirect github.com/drone/envsubst v1.0.3 // indirect github.com/fatih/semgroup v1.2.0 // indirect diff --git a/go.sum b/go.sum index 2f16b4018..7b4495103 100644 --- a/go.sum +++ b/go.sum @@ -56,6 +56,7 @@ github.com/99designs/basicauth-go v0.0.0-20160802081356-2a93ba0f464d/go.mod h1:3 github.com/99designs/httpsignatures-go v0.0.0-20170731043157-88528bf4ca7e h1:rl2Aq4ZODqTDkeSqQBy+fzpZPamacO1Srp8zq7jf2Sc= github.com/99designs/httpsignatures-go v0.0.0-20170731043157-88528bf4ca7e/go.mod h1:Xa6lInWHNQnuWoF0YPSsx+INFA9qk7/7pTjwb3PInkY= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8= +github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= github.com/BobuSumisu/aho-corasick v1.0.3 h1:uuf+JHwU9CHP2Vx+wAy6jcksJThhJS9ehR8a+4nPE9g= github.com/BobuSumisu/aho-corasick v1.0.3/go.mod h1:hm4jLcvZKI2vRF2WDU1N4p/jpWtpOzp3nLmi9AzX/XE= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= @@ -81,6 +82,7 @@ github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRF github.com/alecthomas/units v0.0.0-20231202071711-9a357b53e9c9 h1:ez/4by2iGztzR4L0zgAOR8lTQK9VlyBVVd7G4omaOQs= github.com/alecthomas/units v0.0.0-20231202071711-9a357b53e9c9/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs= +github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= @@ -111,7 +113,9 @@ github.com/bmatcuk/doublestar v1.3.4/go.mod h1:wiQtGV+rzVYxB7WIlirSN++5HPtPlXEo9 github.com/bmatcuk/doublestar/v4 v4.6.0 h1:HTuxyug8GyFbRkrffIpzNCSK4luc0TY3wzXvzIZhEXc= github.com/bmatcuk/doublestar/v4 v4.6.0/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc= github.com/bool64/dev v0.2.32 h1:DRZtloaoH1Igky3zphaUHV9+SLIV2H3lsf78JsJHFg0= +github.com/bool64/dev v0.2.32/go.mod h1:iJbh1y/HkunEPhgebWRNcs8wfGq7sjvJ6W5iabL8ACg= github.com/bool64/shared v0.1.5 h1:fp3eUhBsrSjNCQPcSdQqZxxh9bBwrYiZ+zOKFkM0/2E= +github.com/bool64/shared v0.1.5/go.mod h1:081yz68YC9jeFB3+Bbmno2RFWvGKv1lPKkMP6MHJlPs= github.com/buildkite/yaml v2.1.0+incompatible h1:xirI+ql5GzfikVNDmt+yeiXpf/v1Gt03qXTtT5WXdr8= github.com/buildkite/yaml v2.1.0+incompatible/go.mod h1:UoU8vbcwu1+vjZq01+KrpSeLBgQQIjL/H7Y6KwikUrI= github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= @@ -268,6 +272,7 @@ github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc= +github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= @@ -298,6 +303,7 @@ github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= +github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -346,6 +352,7 @@ github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXi github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/martian/v3 v3.3.2 h1:IqNFLAmvJOgVlpdEBiQbDc2EwKW77amAycfTuWKdfvw= +github.com/google/martian/v3 v3.3.2/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= @@ -402,9 +409,11 @@ github.com/h2non/filetype v1.1.3 h1:FKkx9QbD7HR/zjK1Ia5XiBsq9zdLi5Kf3zGyFTAFkGg= github.com/h2non/filetype v1.1.3/go.mod h1:319b3zT68BvV+WRj7cwy856M2ehB3HqNOt6sy1HndBY= github.com/h2non/gock v1.0.9/go.mod h1:CZMcB0Lg5IWnr9bF79pPMg9WeV6WumxQiUJ1UvdO1iE= github.com/h2non/gock v1.2.0 h1:K6ol8rfrRkUOefooBC8elXoaNGYkpp7y2qcxGG6BzUE= +github.com/h2non/gock v1.2.0/go.mod h1:tNhoxHYW2W42cYkYb1WqzdbYIieALC99kpYr7rH/BQk= github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw= -github.com/harness/harness-migrate v0.21.1-0.20240624210736-65c7e9fbe930 h1:NboKpEf78oc/8bOmh7Gk8uILbIYrD5nswyX9+K6iuKg= -github.com/harness/harness-migrate v0.21.1-0.20240624210736-65c7e9fbe930/go.mod h1:HiUX39ZfhBGY+OkEEN6JpglbodNJlbzayOt/l3nkk6w= +github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI= +github.com/harness/harness-migrate v0.21.1-0.20240703163651-0641dc7290d8 h1:X0/Nk9aCkIKoF3kavxZcr9EtfARSBqnJK33c59m+xmw= +github.com/harness/harness-migrate v0.21.1-0.20240703163651-0641dc7290d8/go.mod h1:HiUX39ZfhBGY+OkEEN6JpglbodNJlbzayOt/l3nkk6w= github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= @@ -437,6 +446,7 @@ github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/J github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= github.com/iancoleman/orderedmap v0.2.0 h1:sq1N/TFpYH++aViPcaKjys3bDClUEU7s5B+z6jq8pNA= +github.com/iancoleman/orderedmap v0.2.0/go.mod h1:N0Wam8K1arqPXNWjMo21EXnBPOPp36vB07FNRdD2geA= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= @@ -531,6 +541,7 @@ github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= @@ -607,6 +618,7 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= +github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/muesli/reflow v0.2.1-0.20210115123740-9e1d0d53df68 h1:y1p/ycavWjGT9FnmSjdbWUlLGvcxrY0Rw3ATltrxOhk= github.com/muesli/reflow v0.2.1-0.20210115123740-9e1d0d53df68/go.mod h1:Xk+z4oIWdQqJzsxyjgl3P22oYZnHdZ8FFTHAQQt5BMQ= github.com/muesli/termenv v0.11.1-0.20220204035834-5ac8409525e0/go.mod h1:Bd5NYQ7pd+SrtBSrSNoBBmXlcY8+Xj4BMJgh8qcZrvs= @@ -733,6 +745,7 @@ github.com/sercand/kuberesolver/v5 v5.1.0 h1:YLqreB1vUFbZHSidcqI5ChMp+RIRmop0brQ github.com/sercand/kuberesolver/v5 v5.1.0/go.mod h1:Fs1KbKhVRnB2aDWN12NjKCB+RgYMWZJ294T3BtmVCpQ= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8= +github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I= github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= github.com/shopspring/decimal v0.0.0-20200227202807-02e2044944cc/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= @@ -785,6 +798,7 @@ github.com/stvp/tempredis v0.0.0-20181119212430-b82af8480203/go.mod h1:oqN97ltKN github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/swaggest/assertjson v1.7.0 h1:SKw5Rn0LQs6UvmGrIdaKQbMR1R3ncXm5KNon+QJ7jtw= +github.com/swaggest/assertjson v1.7.0/go.mod h1:vxMJMehbSVJd+dDWFCKv3QRZKNTpy/ktZKTz9LOEDng= github.com/swaggest/jsonschema-go v0.3.40 h1:9EqQ9RvtdW69xfYODmyEKWOSZ12x5eiK+wGD2EVh/L4= github.com/swaggest/jsonschema-go v0.3.40/go.mod h1:ipIOmoFP64QyRUgyPyU/P9tayq2m2TlvUhyZHrhe3S4= github.com/swaggest/openapi-go v0.2.23 h1:DYUezSTyw180z1bL51wUnalYYbTMwHBjp1Itvji8/rs= @@ -805,7 +819,9 @@ github.com/vearutop/statigz v1.4.0/go.mod h1:LYTolBLiz9oJISwiVKnOQoIwhO1LWX1A7OE github.com/vinzenz/yaml v0.0.0-20170920082545-91409cdd725d/go.mod h1:mb5taDqMnJiZNRQ3+02W2IFG+oEz1+dTuCXkp4jpkfo= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/yudai/gojsondiff v1.0.0 h1:27cbfqXLVEJ1o8I6v3y9lg8Ydm53EKqHXAOMxEGlCOA= +github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg= github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 h1:BHyfKlQyqbsFN5p3IfnEUduWvb9is428/nNb5L3U01M= +github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -1307,6 +1323,7 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= @@ -1335,6 +1352,7 @@ gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= +gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/types/enum/repo.go b/types/enum/repo.go index 32ce3c018..8177bd03f 100644 --- a/types/enum/repo.go +++ b/types/enum/repo.go @@ -18,10 +18,10 @@ import ( "strings" ) -// Defines repo attributes that can be used for sorting and filtering. +// RepoAttr defines repo attributes that can be used for sorting and filtering. type RepoAttr int -// Order enumeration. +// RepoAttr enumeration. const ( RepoAttrNone RepoAttr = iota // TODO [CODE-1363]: remove after identifier migration. @@ -72,3 +72,30 @@ func (a RepoAttr) String() string { return undefined } } + +// RepoState defines repo state. +type RepoState int + +// RepoState enumeration. +const ( + RepoStateActive RepoState = iota + RepoStateGitImport + RepoStateMigrateGitPush + RepoStateMigrateDataImport +) + +// String returns the string representation of the RepoState. +func (state RepoState) String() string { + switch state { + case RepoStateActive: + return "active" + case RepoStateGitImport: + return "git-import" + case RepoStateMigrateGitPush: + return "migrate-git-push" + case RepoStateMigrateDataImport: + return "migrate-data-import" + default: + return undefined + } +} diff --git a/types/repo.go b/types/repo.go index fdde014a9..5e9d2491b 100644 --- a/types/repo.go +++ b/types/repo.go @@ -48,8 +48,8 @@ type Repository struct { NumOpenPulls int `json:"num_open_pulls" yaml:"num_open_pulls"` NumMergedPulls int `json:"num_merged_pulls" yaml:"num_merged_pulls"` - Importing bool `json:"importing" yaml:"-"` - IsEmpty bool `json:"is_empty,omitempty" yaml:"is_empty"` + State enum.RepoState `json:"state" yaml:"-"` + IsEmpty bool `json:"is_empty,omitempty" yaml:"is_empty"` // git urls GitURL string `json:"git_url" yaml:"-"`