mirror of https://github.com/harness/drone.git
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 importunified-ui
parent
30dfa92023
commit
5427130a42
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -63,5 +63,6 @@ func (c *Controller) getRepoCheckAccess(
|
|||
session,
|
||||
repoRef,
|
||||
reqPermission,
|
||||
repo.ActiveRepoStates,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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;
|
|
@ -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;
|
|
@ -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;
|
|
@ -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;
|
|
@ -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:
|
||||
|
|
|
@ -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)
|
||||
|
|
8
go.mod
8
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
|
||||
|
|
22
go.sum
22
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=
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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:"-"`
|
||||
|
|
Loading…
Reference in New Issue