diff options
author | 6543 <6543@obermui.de> | 2020-10-14 06:06:00 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-10-14 07:06:00 +0300 |
commit | 49b1948cb11f46b8dabc6c977079ca9a187c92e0 (patch) | |
tree | 4c582fd4a53ce6ee3d24b1ef902b3f242187eb50 /modules | |
parent | dfa7291f8fafd2aac032ef73a78b588e4d0e9a36 (diff) | |
download | gitea-49b1948cb11f46b8dabc6c977079ca9a187c92e0.tar.gz gitea-49b1948cb11f46b8dabc6c977079ca9a187c92e0.zip |
Gitea 2 Gitea migration (#12657)
* first draft
* update gitea sdk to 9e280adb4da
* adapt feat of updated sdk
* releases now works
* break the Reactions loop
* use convertGiteaLabel
* fix endless loop because paggination is not supported there !!!
* rename gitea local uploader files
* pagination can bite you in the ass
* Version Checks
* lint
* docs
* rename gitea sdk import to miss future conficts
* go-swagger: dont scan the sdk structs
* make sure gitea can shutdown gracefully
* make GetPullRequests and GetIssues similar
* rm useles
* Add Test: started ...
* ... add tests ...
* Add tests and Fixing things
* Workaround missing SHA
* Adapt: Ensure that all migration requests are cancellable
(714ab71ddc4260937b1480519d453d2dc4e77dd6)
* LINT: fix misspells in test set
* adapt ListMergeRequestAwardEmoji
* update sdk
* Return error when creating giteadownloader failed
* update sdk
* adapt new sdk
* adopt new features
* check version before err
* adapt: 'migrate service type switch page'
* optimize
* Fix DefaultBranch
* impruve
* handle subPath
* fix test
* Fix ReviewCommentPosition
* test GetReviews
* add DefaultBranch int test set
* rm unused
* Update SDK to v0.13.0
* addopt sdk changes
* found better link
* format template
* Update Docs
* Update Gitea SDK (v0.13.1)
Diffstat (limited to 'modules')
-rw-r--r-- | modules/migrations/base/downloader.go | 15 | ||||
-rw-r--r-- | modules/migrations/base/issue.go | 1 | ||||
-rw-r--r-- | modules/migrations/base/pullrequest.go | 1 | ||||
-rw-r--r-- | modules/migrations/base/release.go | 1 | ||||
-rw-r--r-- | modules/migrations/base/repo.go | 2 | ||||
-rw-r--r-- | modules/migrations/base/review.go | 1 | ||||
-rw-r--r-- | modules/migrations/git.go | 6 | ||||
-rw-r--r-- | modules/migrations/gitea_downloader.go | 671 | ||||
-rw-r--r-- | modules/migrations/gitea_downloader_test.go | 365 | ||||
-rw-r--r-- | modules/migrations/gitea_uploader.go (renamed from modules/migrations/gitea.go) | 23 | ||||
-rw-r--r-- | modules/migrations/gitea_uploader_test.go (renamed from modules/migrations/gitea_test.go) | 0 | ||||
-rw-r--r-- | modules/migrations/github.go | 10 | ||||
-rw-r--r-- | modules/migrations/github_test.go | 2 | ||||
-rw-r--r-- | modules/migrations/gitlab.go | 10 | ||||
-rw-r--r-- | modules/migrations/gitlab_test.go | 2 | ||||
-rw-r--r-- | modules/migrations/migrate.go | 4 | ||||
-rw-r--r-- | modules/structs/repo.go | 1 |
17 files changed, 1083 insertions, 32 deletions
diff --git a/modules/migrations/base/downloader.go b/modules/migrations/base/downloader.go index 036abf22c9..5c47ed5305 100644 --- a/modules/migrations/base/downloader.go +++ b/modules/migrations/base/downloader.go @@ -15,7 +15,7 @@ import ( // AssetDownloader downloads an asset (attachment) for a release type AssetDownloader interface { - GetAsset(tag string, id int64) (io.ReadCloser, error) + GetAsset(relTag string, relID, id int64) (io.ReadCloser, error) } // Downloader downloads the site repo informations @@ -29,7 +29,7 @@ type Downloader interface { GetLabels() ([]*Label, error) GetIssues(page, perPage int) ([]*Issue, bool, error) GetComments(issueNumber int64) ([]*Comment, error) - GetPullRequests(page, perPage int) ([]*PullRequest, error) + GetPullRequests(page, perPage int) ([]*PullRequest, bool, error) GetReviews(pullRequestNumber int64) ([]*Review, error) } @@ -209,23 +209,24 @@ func (d *RetryDownloader) GetComments(issueNumber int64) ([]*Comment, error) { } // GetPullRequests returns a repository's pull requests with retry -func (d *RetryDownloader) GetPullRequests(page, perPage int) ([]*PullRequest, error) { +func (d *RetryDownloader) GetPullRequests(page, perPage int) ([]*PullRequest, bool, error) { var ( times = d.RetryTimes prs []*PullRequest err error + isEnd bool ) for ; times > 0; times-- { - if prs, err = d.Downloader.GetPullRequests(page, perPage); err == nil { - return prs, nil + if prs, isEnd, err = d.Downloader.GetPullRequests(page, perPage); err == nil { + return prs, isEnd, nil } select { case <-d.ctx.Done(): - return nil, d.ctx.Err() + return nil, false, d.ctx.Err() case <-time.After(time.Second * time.Duration(d.RetryDelay)): } } - return nil, err + return nil, false, err } // GetReviews returns pull requests reviews diff --git a/modules/migrations/base/issue.go b/modules/migrations/base/issue.go index 4e2bf25f17..b9625a23f6 100644 --- a/modules/migrations/base/issue.go +++ b/modules/migrations/base/issue.go @@ -23,4 +23,5 @@ type Issue struct { Closed *time.Time Labels []*Label Reactions []*Reaction + Assignees []string } diff --git a/modules/migrations/base/pullrequest.go b/modules/migrations/base/pullrequest.go index 964512e137..ee612fbb8e 100644 --- a/modules/migrations/base/pullrequest.go +++ b/modules/migrations/base/pullrequest.go @@ -31,7 +31,6 @@ type PullRequest struct { MergeCommitSHA string Head PullRequestBranch Base PullRequestBranch - Assignee string Assignees []string IsLocked bool Reactions []*Reaction diff --git a/modules/migrations/base/release.go b/modules/migrations/base/release.go index 2a223920c7..c9b26ab1da 100644 --- a/modules/migrations/base/release.go +++ b/modules/migrations/base/release.go @@ -15,6 +15,7 @@ type ReleaseAsset struct { DownloadCount *int Created time.Time Updated time.Time + DownloadURL *string } // Release represents a release diff --git a/modules/migrations/base/repo.go b/modules/migrations/base/repo.go index d2052da90d..d26a911854 100644 --- a/modules/migrations/base/repo.go +++ b/modules/migrations/base/repo.go @@ -12,8 +12,6 @@ type Repository struct { IsPrivate bool IsMirror bool Description string - AuthUsername string - AuthPassword string CloneURL string OriginalURL string DefaultBranch string diff --git a/modules/migrations/base/review.go b/modules/migrations/base/review.go index 8051fed653..0a9d03dae9 100644 --- a/modules/migrations/base/review.go +++ b/modules/migrations/base/review.go @@ -36,6 +36,7 @@ type ReviewComment struct { TreePath string DiffHunk string Position int + Line int CommitID string PosterID int64 Reactions []*Reaction diff --git a/modules/migrations/git.go b/modules/migrations/git.go index 5c9acb2533..0aad8dbef5 100644 --- a/modules/migrations/git.go +++ b/modules/migrations/git.go @@ -66,7 +66,7 @@ func (g *PlainGitDownloader) GetReleases() ([]*base.Release, error) { } // GetAsset returns an asset -func (g *PlainGitDownloader) GetAsset(_ string, _ int64) (io.ReadCloser, error) { +func (g *PlainGitDownloader) GetAsset(_ string, _, _ int64) (io.ReadCloser, error) { return nil, ErrNotSupported } @@ -81,8 +81,8 @@ func (g *PlainGitDownloader) GetComments(issueNumber int64) ([]*base.Comment, er } // GetPullRequests returns pull requests according page and perPage -func (g *PlainGitDownloader) GetPullRequests(start, limit int) ([]*base.PullRequest, error) { - return nil, ErrNotSupported +func (g *PlainGitDownloader) GetPullRequests(start, limit int) ([]*base.PullRequest, bool, error) { + return nil, false, ErrNotSupported } // GetReviews returns reviews according issue number diff --git a/modules/migrations/gitea_downloader.go b/modules/migrations/gitea_downloader.go new file mode 100644 index 0000000000..827cbef5c2 --- /dev/null +++ b/modules/migrations/gitea_downloader.go @@ -0,0 +1,671 @@ +// Copyright 2020 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package migrations + +import ( + "context" + "errors" + "fmt" + "io" + "net/http" + "net/url" + "strings" + "time" + + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/migrations/base" + "code.gitea.io/gitea/modules/structs" + + gitea_sdk "code.gitea.io/sdk/gitea" +) + +var ( + _ base.Downloader = &GiteaDownloader{} + _ base.DownloaderFactory = &GiteaDownloaderFactory{} +) + +func init() { + RegisterDownloaderFactory(&GiteaDownloaderFactory{}) +} + +// GiteaDownloaderFactory defines a gitea downloader factory +type GiteaDownloaderFactory struct { +} + +// New returns a Downloader related to this factory according MigrateOptions +func (f *GiteaDownloaderFactory) New(ctx context.Context, opts base.MigrateOptions) (base.Downloader, error) { + u, err := url.Parse(opts.CloneAddr) + if err != nil { + return nil, err + } + + baseURL := u.Scheme + "://" + u.Host + repoNameSpace := strings.TrimPrefix(u.Path, "/") + repoNameSpace = strings.TrimSuffix(repoNameSpace, ".git") + + path := strings.Split(repoNameSpace, "/") + if len(path) < 2 { + return nil, fmt.Errorf("invalid path") + } + + repoPath := strings.Join(path[len(path)-2:], "/") + if len(path) > 2 { + subPath := strings.Join(path[:len(path)-2], "/") + baseURL += "/" + subPath + } + + log.Trace("Create gitea downloader. BaseURL: %s RepoName: %s", baseURL, repoNameSpace) + + return NewGiteaDownloader(ctx, baseURL, repoPath, opts.AuthUsername, opts.AuthPassword, opts.AuthToken) +} + +// GitServiceType returns the type of git service +func (f *GiteaDownloaderFactory) GitServiceType() structs.GitServiceType { + return structs.GiteaService +} + +// GiteaDownloader implements a Downloader interface to get repository information's +type GiteaDownloader struct { + ctx context.Context + client *gitea_sdk.Client + repoOwner string + repoName string + pagination bool + maxPerPage int +} + +// NewGiteaDownloader creates a gitea Downloader via gitea API +// Use either a username/password or personal token. token is preferred +// Note: Public access only allows very basic access +func NewGiteaDownloader(ctx context.Context, baseURL, repoPath, username, password, token string) (*GiteaDownloader, error) { + giteaClient, err := gitea_sdk.NewClient( + baseURL, + gitea_sdk.SetToken(token), + gitea_sdk.SetBasicAuth(username, password), + gitea_sdk.SetContext(ctx), + ) + if err != nil { + log.Error(fmt.Sprintf("NewGiteaDownloader: %s", err.Error())) + return nil, err + } + + path := strings.Split(repoPath, "/") + + paginationSupport := true + if err := giteaClient.CheckServerVersionConstraint(">=1.12"); err != nil { + paginationSupport = false + } + + // set small maxPerPage since we can only guess + // (default would be 50 but this can differ) + maxPerPage := 10 + // new gitea instances can tell us what maximum they have + if giteaClient.CheckServerVersionConstraint(">=1.13.0") == nil { + apiConf, _, err := giteaClient.GetGlobalAPISettings() + if err != nil { + return nil, err + } + maxPerPage = apiConf.MaxResponseItems + } + + return &GiteaDownloader{ + ctx: ctx, + client: giteaClient, + repoOwner: path[0], + repoName: path[1], + pagination: paginationSupport, + maxPerPage: maxPerPage, + }, nil +} + +// SetContext set context +func (g *GiteaDownloader) SetContext(ctx context.Context) { + g.ctx = ctx +} + +// GetRepoInfo returns a repository information +func (g *GiteaDownloader) GetRepoInfo() (*base.Repository, error) { + if g == nil { + return nil, errors.New("error: GiteaDownloader is nil") + } + + repo, _, err := g.client.GetRepo(g.repoOwner, g.repoName) + if err != nil { + return nil, err + } + + return &base.Repository{ + Name: repo.Name, + Owner: repo.Owner.UserName, + IsPrivate: repo.Private, + Description: repo.Description, + CloneURL: repo.CloneURL, + OriginalURL: repo.HTMLURL, + DefaultBranch: repo.DefaultBranch, + }, nil +} + +// GetTopics return gitea topics +func (g *GiteaDownloader) GetTopics() ([]string, error) { + topics, _, err := g.client.ListRepoTopics(g.repoOwner, g.repoName, gitea_sdk.ListRepoTopicsOptions{}) + return topics, err +} + +// GetMilestones returns milestones +func (g *GiteaDownloader) GetMilestones() ([]*base.Milestone, error) { + var milestones = make([]*base.Milestone, 0, g.maxPerPage) + + for i := 1; ; i++ { + // make sure gitea can shutdown gracefully + select { + case <-g.ctx.Done(): + return nil, nil + default: + } + + ms, _, err := g.client.ListRepoMilestones(g.repoOwner, g.repoName, gitea_sdk.ListMilestoneOption{ + ListOptions: gitea_sdk.ListOptions{ + PageSize: g.maxPerPage, + Page: i, + }, + State: gitea_sdk.StateAll, + }) + if err != nil { + return nil, err + } + + for i := range ms { + // old gitea instances dont have this information + createdAT := time.Now() + var updatedAT *time.Time + if ms[i].Closed != nil { + createdAT = *ms[i].Closed + updatedAT = ms[i].Closed + } + + // new gitea instances (>=1.13) do + if !ms[i].Created.IsZero() { + createdAT = ms[i].Created + } + if ms[i].Updated != nil && !ms[i].Updated.IsZero() { + updatedAT = ms[i].Updated + } + + milestones = append(milestones, &base.Milestone{ + Title: ms[i].Title, + Description: ms[i].Description, + Deadline: ms[i].Deadline, + Created: createdAT, + Updated: updatedAT, + Closed: ms[i].Closed, + State: string(ms[i].State), + }) + } + if !g.pagination || len(ms) < g.maxPerPage { + break + } + } + return milestones, nil +} + +func (g *GiteaDownloader) convertGiteaLabel(label *gitea_sdk.Label) *base.Label { + return &base.Label{ + Name: label.Name, + Color: label.Color, + Description: label.Description, + } +} + +// GetLabels returns labels +func (g *GiteaDownloader) GetLabels() ([]*base.Label, error) { + var labels = make([]*base.Label, 0, g.maxPerPage) + + for i := 1; ; i++ { + // make sure gitea can shutdown gracefully + select { + case <-g.ctx.Done(): + return nil, nil + default: + } + + ls, _, err := g.client.ListRepoLabels(g.repoOwner, g.repoName, gitea_sdk.ListLabelsOptions{ListOptions: gitea_sdk.ListOptions{ + PageSize: g.maxPerPage, + Page: i, + }}) + if err != nil { + return nil, err + } + + for i := range ls { + labels = append(labels, g.convertGiteaLabel(ls[i])) + } + if !g.pagination || len(ls) < g.maxPerPage { + break + } + } + return labels, nil +} + +func (g *GiteaDownloader) convertGiteaRelease(rel *gitea_sdk.Release) *base.Release { + r := &base.Release{ + TagName: rel.TagName, + TargetCommitish: rel.Target, + Name: rel.Title, + Body: rel.Note, + Draft: rel.IsDraft, + Prerelease: rel.IsPrerelease, + PublisherID: rel.Publisher.ID, + PublisherName: rel.Publisher.UserName, + PublisherEmail: rel.Publisher.Email, + Published: rel.PublishedAt, + Created: rel.CreatedAt, + } + + for _, asset := range rel.Attachments { + size := int(asset.Size) + dlCount := int(asset.DownloadCount) + r.Assets = append(r.Assets, base.ReleaseAsset{ + ID: asset.ID, + Name: asset.Name, + Size: &size, + DownloadCount: &dlCount, + Created: asset.Created, + DownloadURL: &asset.DownloadURL, + }) + } + return r +} + +// GetReleases returns releases +func (g *GiteaDownloader) GetReleases() ([]*base.Release, error) { + var releases = make([]*base.Release, 0, g.maxPerPage) + + for i := 1; ; i++ { + // make sure gitea can shutdown gracefully + select { + case <-g.ctx.Done(): + return nil, nil + default: + } + + rl, _, err := g.client.ListReleases(g.repoOwner, g.repoName, gitea_sdk.ListReleasesOptions{ListOptions: gitea_sdk.ListOptions{ + PageSize: g.maxPerPage, + Page: i, + }}) + if err != nil { + return nil, err + } + + for i := range rl { + releases = append(releases, g.convertGiteaRelease(rl[i])) + } + if !g.pagination || len(rl) < g.maxPerPage { + break + } + } + return releases, nil +} + +// GetAsset returns an asset +func (g *GiteaDownloader) GetAsset(_ string, relID, id int64) (io.ReadCloser, error) { + asset, _, err := g.client.GetReleaseAttachment(g.repoOwner, g.repoName, relID, id) + if err != nil { + return nil, err + } + resp, err := http.Get(asset.DownloadURL) + if err != nil { + return nil, err + } + + // resp.Body is closed by the uploader + return resp.Body, nil +} + +func (g *GiteaDownloader) getIssueReactions(index int64) ([]*base.Reaction, error) { + var reactions []*base.Reaction + if err := g.client.CheckServerVersionConstraint(">=1.11"); err != nil { + log.Info("GiteaDownloader: instance to old, skip getIssueReactions") + return reactions, nil + } + rl, _, err := g.client.GetIssueReactions(g.repoOwner, g.repoName, index) + if err != nil { + return nil, err + } + + for _, reaction := range rl { + reactions = append(reactions, &base.Reaction{ + UserID: reaction.User.ID, + UserName: reaction.User.UserName, + Content: reaction.Reaction, + }) + } + return reactions, nil +} + +func (g *GiteaDownloader) getCommentReactions(commentID int64) ([]*base.Reaction, error) { + var reactions []*base.Reaction + if err := g.client.CheckServerVersionConstraint(">=1.11"); err != nil { + log.Info("GiteaDownloader: instance to old, skip getCommentReactions") + return reactions, nil + } + rl, _, err := g.client.GetIssueCommentReactions(g.repoOwner, g.repoName, commentID) + if err != nil { + return nil, err + } + + for i := range rl { + reactions = append(reactions, &base.Reaction{ + UserID: rl[i].User.ID, + UserName: rl[i].User.UserName, + Content: rl[i].Reaction, + }) + } + return reactions, nil +} + +// GetIssues returns issues according start and limit +func (g *GiteaDownloader) GetIssues(page, perPage int) ([]*base.Issue, bool, error) { + if perPage > g.maxPerPage { + perPage = g.maxPerPage + } + var allIssues = make([]*base.Issue, 0, perPage) + + issues, _, err := g.client.ListRepoIssues(g.repoOwner, g.repoName, gitea_sdk.ListIssueOption{ + ListOptions: gitea_sdk.ListOptions{Page: page, PageSize: perPage}, + State: gitea_sdk.StateAll, + Type: gitea_sdk.IssueTypeIssue, + }) + if err != nil { + return nil, false, fmt.Errorf("error while listing issues: %v", err) + } + for _, issue := range issues { + + var labels = make([]*base.Label, 0, len(issue.Labels)) + for i := range issue.Labels { + labels = append(labels, g.convertGiteaLabel(issue.Labels[i])) + } + + var milestone string + if issue.Milestone != nil { + milestone = issue.Milestone.Title + } + + reactions, err := g.getIssueReactions(issue.Index) + if err != nil { + return nil, false, fmt.Errorf("error while loading reactions: %v", err) + } + + var assignees []string + for i := range issue.Assignees { + assignees = append(assignees, issue.Assignees[i].UserName) + } + + allIssues = append(allIssues, &base.Issue{ + Title: issue.Title, + Number: issue.Index, + PosterID: issue.Poster.ID, + PosterName: issue.Poster.UserName, + PosterEmail: issue.Poster.Email, + Content: issue.Body, + Milestone: milestone, + State: string(issue.State), + Created: issue.Created, + Updated: issue.Updated, + Closed: issue.Closed, + Reactions: reactions, + Labels: labels, + Assignees: assignees, + IsLocked: issue.IsLocked, + }) + } + + isEnd := len(issues) < perPage + if !g.pagination { + isEnd = len(issues) == 0 + } + return allIssues, isEnd, nil +} + +// GetComments returns comments according issueNumber +func (g *GiteaDownloader) GetComments(index int64) ([]*base.Comment, error) { + var allComments = make([]*base.Comment, 0, g.maxPerPage) + + // for i := 1; ; i++ { + // make sure gitea can shutdown gracefully + select { + case <-g.ctx.Done(): + return nil, nil + default: + } + + comments, _, err := g.client.ListIssueComments(g.repoOwner, g.repoName, index, gitea_sdk.ListIssueCommentOptions{ListOptions: gitea_sdk.ListOptions{ + // PageSize: g.maxPerPage, + // Page: i, + }}) + if err != nil { + return nil, fmt.Errorf("error while listing comments: %v", err) + } + + for _, comment := range comments { + reactions, err := g.getCommentReactions(comment.ID) + if err != nil { + return nil, fmt.Errorf("error while listing comment creactions: %v", err) + } + + allComments = append(allComments, &base.Comment{ + IssueIndex: index, + PosterID: comment.Poster.ID, + PosterName: comment.Poster.UserName, + PosterEmail: comment.Poster.Email, + Content: comment.Body, + Created: comment.Created, + Updated: comment.Updated, + Reactions: reactions, + }) + } + + // TODO enable pagination vor (gitea >= 1.14) when it got implemented + // if !g.pagination || len(comments) < g.maxPerPage { + // break + // } + //} + return allComments, nil +} + +// GetPullRequests returns pull requests according page and perPage +func (g *GiteaDownloader) GetPullRequests(page, perPage int) ([]*base.PullRequest, bool, error) { + if perPage > g.maxPerPage { + perPage = g.maxPerPage + } + var allPRs = make([]*base.PullRequest, 0, perPage) + + prs, _, err := g.client.ListRepoPullRequests(g.repoOwner, g.repoName, gitea_sdk.ListPullRequestsOptions{ + ListOptions: gitea_sdk.ListOptions{ + Page: page, + PageSize: perPage, + }, + State: gitea_sdk.StateAll, + }) + if err != nil { + return nil, false, fmt.Errorf("error while listing repos: %v", err) + } + for _, pr := range prs { + var milestone string + if pr.Milestone != nil { + milestone = pr.Milestone.Title + } + + var labels = make([]*base.Label, 0, len(pr.Labels)) + for i := range pr.Labels { + labels = append(labels, g.convertGiteaLabel(pr.Labels[i])) + } + + var ( + headUserName string + headRepoName string + headCloneURL string + headRef string + headSHA string + ) + if pr.Head != nil { + if pr.Head.Repository != nil { + headUserName = pr.Head.Repository.Owner.UserName + headRepoName = pr.Head.Repository.Name + headCloneURL = pr.Head.Repository.CloneURL + } + headSHA = pr.Head.Sha + headRef = pr.Head.Ref + if headSHA == "" { + headCommit, _, err := g.client.GetSingleCommit(g.repoOwner, g.repoName, url.PathEscape(pr.Head.Ref)) + if err != nil { + return nil, false, fmt.Errorf("error while resolving git ref: %v", err) + } + headSHA = headCommit.SHA + } + } + + var mergeCommitSHA string + if pr.MergedCommitID != nil { + mergeCommitSHA = *pr.MergedCommitID + } + + reactions, err := g.getIssueReactions(pr.Index) + if err != nil { + return nil, false, fmt.Errorf("error while loading reactions: %v", err) + } + + var assignees []string + for i := range pr.Assignees { + assignees = append(assignees, pr.Assignees[i].UserName) + } + + createdAt := time.Now() + if pr.Created != nil { + createdAt = *pr.Created + } + updatedAt := time.Now() + if pr.Created != nil { + updatedAt = *pr.Updated + } + + closedAt := pr.Closed + if pr.Merged != nil && closedAt == nil { + closedAt = pr.Merged + } + + allPRs = append(allPRs, &base.PullRequest{ + Title: pr.Title, + Number: pr.Index, + PosterID: pr.Poster.ID, + PosterName: pr.Poster.UserName, + PosterEmail: pr.Poster.Email, + Content: pr.Body, + State: string(pr.State), + Created: createdAt, + Updated: updatedAt, + Closed: closedAt, + Labels: labels, + Milestone: milestone, + Reactions: reactions, + Assignees: assignees, + Merged: pr.HasMerged, + MergedTime: pr.Merged, + MergeCommitSHA: mergeCommitSHA, + IsLocked: pr.IsLocked, + PatchURL: pr.PatchURL, + Head: base.PullRequestBranch{ + Ref: headRef, + SHA: headSHA, + RepoName: headRepoName, + OwnerName: headUserName, + CloneURL: headCloneURL, + }, + Base: base.PullRequestBranch{ + Ref: pr.Base.Ref, + SHA: pr.Base.Sha, + RepoName: g.repoName, + OwnerName: g.repoOwner, + }, + }) + } + + isEnd := len(prs) < perPage + if !g.pagination { + isEnd = len(prs) == 0 + } + return allPRs, isEnd, nil +} + +// GetReviews returns pull requests review +func (g *GiteaDownloader) GetReviews(index int64) ([]*base.Review, error) { + if err := g.client.CheckServerVersionConstraint(">=1.12"); err != nil { + log.Info("GiteaDownloader: instance to old, skip GetReviews") + return nil, nil + } + + var allReviews = make([]*base.Review, 0, g.maxPerPage) + + for i := 1; ; i++ { + // make sure gitea can shutdown gracefully + select { + case <-g.ctx.Done(): + return nil, nil + default: + } + + prl, _, err := g.client.ListPullReviews(g.repoOwner, g.repoName, index, gitea_sdk.ListPullReviewsOptions{ListOptions: gitea_sdk.ListOptions{ + Page: i, + PageSize: g.maxPerPage, + }}) + if err != nil { + return nil, err + } + + for _, pr := range prl { + + rcl, _, err := g.client.ListPullReviewComments(g.repoOwner, g.repoName, index, pr.ID) + if err != nil { + return nil, err + } + var reviewComments []*base.ReviewComment + for i := range rcl { + line := int(rcl[i].LineNum) + if rcl[i].OldLineNum > 0 { + line = int(rcl[i].OldLineNum) * -1 + } + + reviewComments = append(reviewComments, &base.ReviewComment{ + ID: rcl[i].ID, + Content: rcl[i].Body, + TreePath: rcl[i].Path, + DiffHunk: rcl[i].DiffHunk, + Line: line, + CommitID: rcl[i].CommitID, + PosterID: rcl[i].Reviewer.ID, + CreatedAt: rcl[i].Created, + UpdatedAt: rcl[i].Updated, + }) + } + + allReviews = append(allReviews, &base.Review{ + ID: pr.ID, + IssueIndex: index, + ReviewerID: pr.Reviewer.ID, + ReviewerName: pr.Reviewer.UserName, + Official: pr.Official, + CommitID: pr.CommitID, + Content: pr.Body, + CreatedAt: pr.Submitted, + State: string(pr.State), + Comments: reviewComments, + }) + } + + if len(prl) < g.maxPerPage { + break + } + } + return allReviews, nil +} diff --git a/modules/migrations/gitea_downloader_test.go b/modules/migrations/gitea_downloader_test.go new file mode 100644 index 0000000000..c52c1225f4 --- /dev/null +++ b/modules/migrations/gitea_downloader_test.go @@ -0,0 +1,365 @@ +// Copyright 2020 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package migrations + +import ( + "context" + "fmt" + "net/http" + "os" + "sort" + "testing" + "time" + + "code.gitea.io/gitea/modules/migrations/base" + + "github.com/stretchr/testify/assert" +) + +func assertEqualIssue(t *testing.T, issueExp, issueGet *base.Issue) { + assert.EqualValues(t, issueExp.Number, issueGet.Number) + assert.EqualValues(t, issueExp.Title, issueGet.Title) + assert.EqualValues(t, issueExp.Content, issueGet.Content) + assert.EqualValues(t, issueExp.Milestone, issueGet.Milestone) + assert.EqualValues(t, issueExp.PosterID, issueGet.PosterID) + assert.EqualValues(t, issueExp.PosterName, issueGet.PosterName) + assert.EqualValues(t, issueExp.PosterEmail, issueGet.PosterEmail) + assert.EqualValues(t, issueExp.IsLocked, issueGet.IsLocked) + assert.EqualValues(t, issueExp.Created.Unix(), issueGet.Created.Unix()) + assert.EqualValues(t, issueExp.Updated.Unix(), issueGet.Updated.Unix()) + if issueExp.Closed != nil { + assert.EqualValues(t, issueExp.Closed.Unix(), issueGet.Closed.Unix()) + } else { + assert.True(t, issueGet.Closed == nil) + } + sort.Strings(issueExp.Assignees) + sort.Strings(issueGet.Assignees) + assert.EqualValues(t, issueExp.Assignees, issueGet.Assignees) + assert.EqualValues(t, issueExp.Labels, issueGet.Labels) + assert.EqualValues(t, issueExp.Reactions, issueGet.Reactions) +} + +func TestGiteaDownloadRepo(t *testing.T) { + // Skip tests if Gitea token is not found + giteaToken := os.Getenv("GITEA_TOKEN") + if giteaToken == "" { + t.Skip("skipped test because GITEA_TOKEN was not in the environment") + } + + resp, err := http.Get("https://gitea.com/gitea") + if err != nil || resp.StatusCode != 200 { + t.Skipf("Can't reach https://gitea.com, skipping %s", t.Name()) + } + + downloader, err := NewGiteaDownloader(context.Background(), "https://gitea.com", "gitea/test_repo", "", "", giteaToken) + if downloader == nil { + t.Fatal("NewGitlabDownloader is nil") + } + if !assert.NoError(t, err) { + t.Fatal("NewGitlabDownloader error occur") + } + + repo, err := downloader.GetRepoInfo() + assert.NoError(t, err) + assert.EqualValues(t, &base.Repository{ + Name: "test_repo", + Owner: "gitea", + IsPrivate: false, + Description: "Test repository for testing migration from gitea to gitea", + CloneURL: "https://gitea.com/gitea/test_repo.git", + OriginalURL: "https://gitea.com/gitea/test_repo", + DefaultBranch: "master", + }, repo) + + topics, err := downloader.GetTopics() + assert.NoError(t, err) + sort.Strings(topics) + assert.EqualValues(t, []string{"ci", "gitea", "migration", "test"}, topics) + + labels, err := downloader.GetLabels() + assert.NoError(t, err) + assert.Len(t, labels, 6) + for _, l := range labels { + switch l.Name { + case "Bug": + assertLabelEqual(t, "Bug", "e11d21", "", l) + case "documentation": + assertLabelEqual(t, "Enhancement", "207de5", "", l) + case "confirmed": + assertLabelEqual(t, "Feature", "0052cc", "a feature request", l) + case "enhancement": + assertLabelEqual(t, "Invalid", "d4c5f9", "", l) + case "critical": + assertLabelEqual(t, "Question", "fbca04", "", l) + case "discussion": + assertLabelEqual(t, "Valid", "53e917", "", l) + default: + assert.Error(t, fmt.Errorf("unexpected label: %s", l.Name)) + } + } + + milestones, err := downloader.GetMilestones() + assert.NoError(t, err) + assert.Len(t, milestones, 2) + + for _, milestone := range milestones { + switch milestone.Title { + case "V1": + assert.EqualValues(t, "Generate Content", milestone.Description) + // assert.EqualValues(t, "ToDo", milestone.Created) + // assert.EqualValues(t, "ToDo", milestone.Updated) + assert.EqualValues(t, 1598985406, milestone.Closed.Unix()) + assert.True(t, milestone.Deadline == nil) + assert.EqualValues(t, "closed", milestone.State) + case "V2 Finalize": + assert.EqualValues(t, "", milestone.Description) + // assert.EqualValues(t, "ToDo", milestone.Created) + // assert.EqualValues(t, "ToDo", milestone.Updated) + assert.True(t, milestone.Closed == nil) + assert.EqualValues(t, 1599263999, milestone.Deadline.Unix()) + assert.EqualValues(t, "open", milestone.State) + default: + assert.Error(t, fmt.Errorf("unexpected milestone: %s", milestone.Title)) + } + } + + releases, err := downloader.GetReleases() + assert.NoError(t, err) + assert.EqualValues(t, []*base.Release{ + { + Name: "Second Release", + TagName: "v2-rc1", + TargetCommitish: "master", + Body: "this repo has:\r\n* reactions\r\n* wiki\r\n* issues (open/closed)\r\n* pulls (open/closed/merged) (external/internal)\r\n* pull reviews\r\n* projects\r\n* milestones\r\n* labels\r\n* releases\r\n\r\nto test migration against", + Draft: false, + Prerelease: true, + Created: time.Date(2020, 9, 1, 18, 2, 43, 0, time.UTC), + Published: time.Date(2020, 9, 1, 18, 2, 43, 0, time.UTC), + PublisherID: 689, + PublisherName: "6543", + PublisherEmail: "6543@noreply.gitea.io", + }, + { + Name: "First Release", + TagName: "V1", + TargetCommitish: "master", + Body: "as title", + Draft: false, + Prerelease: false, + Created: time.Date(2020, 9, 1, 17, 30, 32, 0, time.UTC), + Published: time.Date(2020, 9, 1, 17, 30, 32, 0, time.UTC), + PublisherID: 689, + PublisherName: "6543", + PublisherEmail: "6543@noreply.gitea.io", + }, + }, releases) + + issues, isEnd, err := downloader.GetIssues(1, 50) + assert.NoError(t, err) + assert.EqualValues(t, 7, len(issues)) + assert.True(t, isEnd) + assert.EqualValues(t, "open", issues[0].State) + + issues, isEnd, err = downloader.GetIssues(3, 2) + assert.NoError(t, err) + assert.EqualValues(t, 2, len(issues)) + assert.False(t, isEnd) + + var ( + closed4 = time.Date(2020, 9, 1, 15, 49, 34, 0, time.UTC) + closed2 = time.Unix(1598969497, 0) + ) + + assertEqualIssue(t, &base.Issue{ + Number: 4, + Title: "what is this repo about?", + Content: "", + Milestone: "V1", + PosterID: -1, + PosterName: "Ghost", + PosterEmail: "", + State: "closed", + IsLocked: true, + Created: time.Unix(1598975321, 0), + Updated: time.Unix(1598975400, 0), + Labels: []*base.Label{{ + Name: "Question", + Color: "fbca04", + Description: "", + }}, + Reactions: []*base.Reaction{ + { + UserID: 689, + UserName: "6543", + Content: "gitea", + }, + { + UserID: 689, + UserName: "6543", + Content: "laugh", + }, + }, + Closed: &closed4, + }, issues[0]) + assertEqualIssue(t, &base.Issue{ + Number: 2, + Title: "Spam", + Content: ":(", + Milestone: "", + PosterID: 689, + PosterName: "6543", + PosterEmail: "6543@noreply.gitea.io", + State: "closed", + IsLocked: false, + Created: time.Unix(1598919780, 0), + Updated: closed2, + Labels: []*base.Label{{ + Name: "Invalid", + Color: "d4c5f9", + Description: "", + }}, + Reactions: nil, + Closed: &closed2, + }, issues[1]) + + comments, err := downloader.GetComments(4) + assert.NoError(t, err) + assert.Len(t, comments, 2) + assert.EqualValues(t, 1598975370, comments[0].Created.Unix()) + assert.EqualValues(t, 1599070865, comments[0].Updated.Unix()) + assert.EqualValues(t, 1598975393, comments[1].Created.Unix()) + assert.EqualValues(t, 1598975393, comments[1].Updated.Unix()) + assert.EqualValues(t, []*base.Comment{ + { + IssueIndex: 4, + PosterID: 689, + PosterName: "6543", + PosterEmail: "6543@noreply.gitea.io", + Created: comments[0].Created, + Updated: comments[0].Updated, + Content: "a really good question!\n\nIt is the used as TESTSET for gitea2gitea repo migration function", + }, + { + IssueIndex: 4, + PosterID: -1, + PosterName: "Ghost", + PosterEmail: "", + Created: comments[1].Created, + Updated: comments[1].Updated, + Content: "Oh!", + }, + }, comments) + + prs, isEnd, err := downloader.GetPullRequests(1, 50) + assert.NoError(t, err) + assert.True(t, isEnd) + assert.Len(t, prs, 6) + prs, isEnd, err = downloader.GetPullRequests(1, 3) + assert.NoError(t, err) + assert.False(t, isEnd) + assert.Len(t, prs, 3) + merged12 := time.Unix(1598982934, 0) + assertEqualPulls(t, &base.PullRequest{ + Number: 12, + PosterID: 689, + PosterName: "6543", + PosterEmail: "6543@noreply.gitea.io", + Title: "Dont Touch", + Content: "\r\nadd dont touch note", + Milestone: "V2 Finalize", + State: "closed", + IsLocked: false, + Created: time.Unix(1598982759, 0), + Updated: time.Unix(1599023425, 0), + Closed: &merged12, + Assignees: []string{"techknowlogick"}, + Labels: []*base.Label{}, + + Base: base.PullRequestBranch{ + CloneURL: "", + Ref: "master", + SHA: "827aa28a907853e5ddfa40c8f9bc52471a2685fd", + RepoName: "test_repo", + OwnerName: "gitea", + }, + Head: base.PullRequestBranch{ + CloneURL: "https://gitea.com/6543-forks/test_repo.git", + Ref: "refs/pull/12/head", + SHA: "b6ab5d9ae000b579a5fff03f92c486da4ddf48b6", + RepoName: "test_repo", + OwnerName: "6543-forks", + }, + Merged: true, + MergedTime: &merged12, + MergeCommitSHA: "827aa28a907853e5ddfa40c8f9bc52471a2685fd", + PatchURL: "https://gitea.com/gitea/test_repo/pulls/12.patch", + }, prs[1]) + + reviews, err := downloader.GetReviews(7) + assert.NoError(t, err) + if assert.Len(t, reviews, 3) { + assert.EqualValues(t, 689, reviews[0].ReviewerID) + assert.EqualValues(t, "6543", reviews[0].ReviewerName) + assert.EqualValues(t, "techknowlogick", reviews[1].ReviewerName) + assert.EqualValues(t, "techknowlogick", reviews[2].ReviewerName) + assert.False(t, reviews[1].Official) + assert.EqualValues(t, "I think this needs some changes", reviews[1].Content) + assert.EqualValues(t, "REQUEST_CHANGES", reviews[1].State) + assert.True(t, reviews[2].Official) + assert.EqualValues(t, "looks good", reviews[2].Content) + assert.EqualValues(t, "APPROVED", reviews[2].State) + + // TODO: https://github.com/go-gitea/gitea/issues/12846 + // assert.EqualValues(t, 9, reviews[1].ReviewerID) + // assert.EqualValues(t, 9, reviews[2].ReviewerID) + + assert.Len(t, reviews[0].Comments, 1) + assert.EqualValues(t, &base.ReviewComment{ + ID: 116561, + InReplyTo: 0, + Content: "is one `\\newline` to less?", + TreePath: "README.md", + DiffHunk: "@@ -2,3 +2,3 @@\n \n-Test repository for testing migration from gitea 2 gitea\n\\ No newline at end of file\n+Test repository for testing migration from gitea 2 gitea", + Position: 0, + Line: 4, + CommitID: "187ece0cb6631e2858a6872e5733433bb3ca3b03", + PosterID: 689, + Reactions: nil, + CreatedAt: time.Date(2020, 9, 1, 16, 12, 58, 0, time.UTC), + UpdatedAt: time.Date(2020, 9, 1, 16, 12, 58, 0, time.UTC), + }, reviews[0].Comments[0]) + } +} + +func assertEqualPulls(t *testing.T, pullExp, pullGet *base.PullRequest) { + assertEqualIssue(t, pull2issue(pullExp), pull2issue(pullGet)) + assert.EqualValues(t, 0, pullGet.OriginalNumber) + assert.EqualValues(t, pullExp.PatchURL, pullGet.PatchURL) + assert.EqualValues(t, pullExp.Merged, pullGet.Merged) + assert.EqualValues(t, pullExp.MergedTime.Unix(), pullGet.MergedTime.Unix()) + assert.EqualValues(t, pullExp.MergeCommitSHA, pullGet.MergeCommitSHA) + assert.EqualValues(t, pullExp.Base, pullGet.Base) + assert.EqualValues(t, pullExp.Head, pullGet.Head) +} + +func pull2issue(pull *base.PullRequest) *base.Issue { + return &base.Issue{ + Number: pull.Number, + PosterID: pull.PosterID, + PosterName: pull.PosterName, + PosterEmail: pull.PosterEmail, + Title: pull.Title, + Content: pull.Content, + Milestone: pull.Milestone, + State: pull.State, + IsLocked: pull.IsLocked, + Created: pull.Created, + Updated: pull.Updated, + Closed: pull.Closed, + Labels: pull.Labels, + Reactions: pull.Reactions, + Assignees: pull.Assignees, + } +} diff --git a/modules/migrations/gitea.go b/modules/migrations/gitea_uploader.go index d4ba66fd38..cd1fd5cb8d 100644 --- a/modules/migrations/gitea.go +++ b/modules/migrations/gitea_uploader.go @@ -273,9 +273,18 @@ func (g *GiteaLocalUploader) CreateReleases(downloader base.Downloader, releases // download attachment err = func() error { - rc, err := downloader.GetAsset(rel.TagName, asset.ID) - if err != nil { - return err + var rc io.ReadCloser + if asset.DownloadURL == nil { + rc, err = downloader.GetAsset(rel.TagName, rel.ID, asset.ID) + if err != nil { + return err + } + } else { + resp, err := http.Get(*asset.DownloadURL) + if err != nil { + return err + } + rc = resp.Body } _, err = storage.Attachments.Save(attach.RelativePath(), rc) return err @@ -779,8 +788,12 @@ func (g *GiteaLocalUploader) CreateReviews(reviews ...*base.Review) error { } for _, comment := range review.Comments { - _, _, line, _ := git.ParseDiffHunkString(comment.DiffHunk) - + line := comment.Line + if line != 0 { + comment.Position = 1 + } else { + _, _, line, _ = git.ParseDiffHunkString(comment.DiffHunk) + } headCommitID, err := g.gitRepo.GetRefCommitID(pr.GetGitRefName()) if err != nil { return fmt.Errorf("GetRefCommitID[%s]: %v", pr.GetGitRefName(), err) diff --git a/modules/migrations/gitea_test.go b/modules/migrations/gitea_uploader_test.go index 8432a1eecd..8432a1eecd 100644 --- a/modules/migrations/gitea_test.go +++ b/modules/migrations/gitea_uploader_test.go diff --git a/modules/migrations/github.go b/modules/migrations/github.go index c053596c17..088e54744d 100644 --- a/modules/migrations/github.go +++ b/modules/migrations/github.go @@ -329,7 +329,7 @@ func (g *GithubDownloaderV3) GetReleases() ([]*base.Release, error) { } // GetAsset returns an asset -func (g *GithubDownloaderV3) GetAsset(_ string, id int64) (io.ReadCloser, error) { +func (g *GithubDownloaderV3) GetAsset(_ string, _, id int64) (io.ReadCloser, error) { asset, redir, err := g.client.Repositories.DownloadReleaseAsset(g.ctx, g.repoOwner, g.repoName, id, http.DefaultClient) if err != nil { return nil, err @@ -496,7 +496,7 @@ func (g *GithubDownloaderV3) GetComments(issueNumber int64) ([]*base.Comment, er } // GetPullRequests returns pull requests according page and perPage -func (g *GithubDownloaderV3) GetPullRequests(page, perPage int) ([]*base.PullRequest, error) { +func (g *GithubDownloaderV3) GetPullRequests(page, perPage int) ([]*base.PullRequest, bool, error) { opt := &github.PullRequestListOptions{ Sort: "created", Direction: "asc", @@ -510,7 +510,7 @@ func (g *GithubDownloaderV3) GetPullRequests(page, perPage int) ([]*base.PullReq g.sleep() prs, resp, err := g.client.PullRequests.List(g.ctx, g.repoOwner, g.repoName, opt) if err != nil { - return nil, fmt.Errorf("error while listing repos: %v", err) + return nil, false, fmt.Errorf("error while listing repos: %v", err) } g.rate = &resp.Rate for _, pr := range prs { @@ -576,7 +576,7 @@ func (g *GithubDownloaderV3) GetPullRequests(page, perPage int) ([]*base.PullReq PerPage: perPage, }) if err != nil { - return nil, err + return nil, false, err } g.rate = &resp.Rate if len(res) == 0 { @@ -626,7 +626,7 @@ func (g *GithubDownloaderV3) GetPullRequests(page, perPage int) ([]*base.PullReq }) } - return allPRs, nil + return allPRs, len(prs) < perPage, nil } func convertGithubReview(r *github.PullRequestReview) *base.Review { diff --git a/modules/migrations/github_test.go b/modules/migrations/github_test.go index 3a1affbc2f..efa8b6ba9b 100644 --- a/modules/migrations/github_test.go +++ b/modules/migrations/github_test.go @@ -271,7 +271,7 @@ func TestGitHubDownloadRepo(t *testing.T) { }, comments[:2]) // downloader.GetPullRequests() - prs, err := downloader.GetPullRequests(1, 2) + prs, _, err := downloader.GetPullRequests(1, 2) assert.NoError(t, err) assert.EqualValues(t, 2, len(prs)) diff --git a/modules/migrations/gitlab.go b/modules/migrations/gitlab.go index 379a2f2b60..23cd90c747 100644 --- a/modules/migrations/gitlab.go +++ b/modules/migrations/gitlab.go @@ -303,7 +303,7 @@ func (g *GitlabDownloader) GetReleases() ([]*base.Release, error) { } // GetAsset returns an asset -func (g *GitlabDownloader) GetAsset(tag string, id int64) (io.ReadCloser, error) { +func (g *GitlabDownloader) GetAsset(tag string, _, id int64) (io.ReadCloser, error) { link, _, err := g.client.ReleaseLinks.GetReleaseLink(g.repoID, tag, int(id), gitlab.WithContext(g.ctx)) if err != nil { return nil, err @@ -464,7 +464,7 @@ func (g *GitlabDownloader) GetComments(issueNumber int64) ([]*base.Comment, erro } // GetPullRequests returns pull requests according page and perPage -func (g *GitlabDownloader) GetPullRequests(page, perPage int) ([]*base.PullRequest, error) { +func (g *GitlabDownloader) GetPullRequests(page, perPage int) ([]*base.PullRequest, bool, error) { opt := &gitlab.ListProjectMergeRequestsOptions{ ListOptions: gitlab.ListOptions{ PerPage: perPage, @@ -479,7 +479,7 @@ func (g *GitlabDownloader) GetPullRequests(page, perPage int) ([]*base.PullReque prs, _, err := g.client.MergeRequests.ListProjectMergeRequests(g.repoID, opt, nil, gitlab.WithContext(g.ctx)) if err != nil { - return nil, fmt.Errorf("error while listing merge requests: %v", err) + return nil, false, fmt.Errorf("error while listing merge requests: %v", err) } for _, pr := range prs { @@ -521,7 +521,7 @@ func (g *GitlabDownloader) GetPullRequests(page, perPage int) ([]*base.PullReque for { awards, _, err := g.client.AwardEmoji.ListMergeRequestAwardEmoji(g.repoID, pr.IID, &gitlab.ListAwardEmojiOptions{Page: awardPage, PerPage: perPage}, gitlab.WithContext(g.ctx)) if err != nil { - return nil, fmt.Errorf("error while listing merge requests awards: %v", err) + return nil, false, fmt.Errorf("error while listing merge requests awards: %v", err) } if len(awards) < perPage { break @@ -569,7 +569,7 @@ func (g *GitlabDownloader) GetPullRequests(page, perPage int) ([]*base.PullReque }) } - return allPRs, nil + return allPRs, len(prs) < perPage, nil } // GetReviews returns pull requests review diff --git a/modules/migrations/gitlab_test.go b/modules/migrations/gitlab_test.go index 11c3aefeaf..f64d72147c 100644 --- a/modules/migrations/gitlab_test.go +++ b/modules/migrations/gitlab_test.go @@ -242,7 +242,7 @@ func TestGitlabDownloadRepo(t *testing.T) { }, }, comments[:4]) - prs, err := downloader.GetPullRequests(1, 1) + prs, _, err := downloader.GetPullRequests(1, 1) assert.NoError(t, err) assert.Len(t, prs, 1) diff --git a/modules/migrations/migrate.go b/modules/migrations/migrate.go index 8543a3fc09..191f2a5550 100644 --- a/modules/migrations/migrate.go +++ b/modules/migrations/migrate.go @@ -229,7 +229,7 @@ func migrateRepository(downloader base.Downloader, uploader base.Uploader, opts log.Trace("migrating pull requests and comments") var prBatchSize = uploader.MaxBatchInsertSize("pullrequest") for i := 1; ; i++ { - prs, err := downloader.GetPullRequests(i, prBatchSize) + prs, isEnd, err := downloader.GetPullRequests(i, prBatchSize) if err != nil { return err } @@ -300,7 +300,7 @@ func migrateRepository(downloader base.Downloader, uploader base.Uploader, opts } } - if len(prs) < prBatchSize { + if isEnd { break } } diff --git a/modules/structs/repo.go b/modules/structs/repo.go index a8ca823ef6..c12f8e1c18 100644 --- a/modules/structs/repo.go +++ b/modules/structs/repo.go @@ -275,5 +275,6 @@ var ( SupportedFullGitService = []GitServiceType{ GithubService, GitlabService, + GiteaService, } ) |