diff --git a/go.mod b/go.mod index 970e65f3a65b3..8d9cf83a390eb 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ go 1.14 require ( cloud.google.com/go v0.45.0 // indirect + code.gitea.io/sdk/gitea v0.12.0 gitea.com/jolheiser/gitea-vet v0.1.0 gitea.com/lunny/levelqueue v0.3.0 gitea.com/macaron/binding v0.0.0-20190822013154-a5f53841ed2b diff --git a/go.sum b/go.sum index df82fcc43cb1a..3280677f6bfe0 100644 --- a/go.sum +++ b/go.sum @@ -9,6 +9,9 @@ cloud.google.com/go v0.45.0 h1:bALuGBSgE+BD4rxsopAYlqjcwqcQtye6pWG4bC3N/k0= cloud.google.com/go v0.45.0/go.mod h1:452BcPOeI9AZfbvDw0Tbo7D32wA+WX9WME8AZwMEDZU= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +code.gitea.io/sdk v0.11.0 h1:R3VdjBCxObyLKnv4Svd/TM6oGsXzN8JORbzgkEFb83w= +code.gitea.io/sdk/gitea v0.12.0 h1:hvDCz4wtFvo7rf5Ebj8tGd4aJ4wLPKX3BKFX9Dk1Pgs= +code.gitea.io/sdk/gitea v0.12.0/go.mod h1:z3uwDV/b9Ls47NGukYM9XhnHtqPh/J+t40lsUrR6JDY= gitea.com/jolheiser/gitea-vet v0.1.0 h1:gJEms9YWbIcrPOEmDOJ+5JZXCYFxNpwxlI73uRulAi4= gitea.com/jolheiser/gitea-vet v0.1.0/go.mod h1:2Oa6TAdEp1N/38oBNh3ZeiSEER60D/CeDaBFv2sdH58= gitea.com/lunny/levelqueue v0.3.0 h1:MHn1GuSZkxvVEDMyAPqlc7A3cOW+q8RcGhRgH/xtm6I= diff --git a/modules/migrations/gitea_downloader.go b/modules/migrations/gitea_downloader.go new file mode 100644 index 0000000000000..b1c132e360ad1 --- /dev/null +++ b/modules/migrations/gitea_downloader.go @@ -0,0 +1,541 @@ +package migrations + +import ( + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/migrations/base" + "code.gitea.io/gitea/modules/structs" + sdk "code.gitea.io/sdk/gitea" + "errors" + "net/http" + "time" + + "context" + "fmt" + "net/url" + "strings" +) + +var ( + _ base.Downloader = &GiteaDownloader{} + _ base.DownloaderFactory = &GiteaDownloaderFactory{} +) + +func init() { + RegisterDownloaderFactory(&GiteaDownloaderFactory{}) +} + +// GiteaDownloaderFactory defines a gitea downloader factory +type GiteaDownloaderFactory struct { +} + +// Match returns true if the migration remote URL matched this downloader factory +func (f *GiteaDownloaderFactory) Match(opts base.MigrateOptions) (bool, error) { + var matched bool + + u, err := url.Parse(opts.CloneAddr) + if err != nil { + return false, err + } + if strings.EqualFold(u.Host, "gitea.com") && opts.AuthUsername != "" { + matched = true + } + + return matched, nil +} + +// New returns a Downloader related to this factory according MigrateOptions +func (f *GiteaDownloaderFactory) New(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, "/") + + log.Trace("Create gitea downloader. BaseURL: %s RepoName: %s", baseURL, repoNameSpace) + + return NewGiteaDownloader(baseURL, repoNameSpace, opts.AuthUsername, opts.AuthPassword), nil +} + +// GitServiceType returns the type of git service +func (f *GiteaDownloaderFactory) GitServiceType() structs.GitServiceType { + return structs.GiteaService +} + +// GiteaDownloader implements a Downloader interface to get repository informations +// from gitlab via go-gitlab +// - issueCount is incremented in GetIssues() to ensure PR and Issue numbers do not overlap, +// because Gitea has individual Issue and Pull Request numbers. +// - issueSeen, working alongside issueCount, is checked in GetComments() to see whether we +// need to fetch the Issue or PR comments, as Gitea stores them separately. +type GiteaDownloader struct { + ctx context.Context + client *sdk.Client + owner string + repoName string + fetchPRcomments bool +} + +// NewGiteaDownloader creates a gitlab Downloader via gitlab API +// Use either a username/password, personal token entered into the username field, or anonymous/public access +// Note: Public access only allows very basic access +func NewGiteaDownloader(baseURL, repoPath, username, password string) *GiteaDownloader { + var giteaClient *sdk.Client + var err error + + if username != "" { + if password == "" { + giteaClient = sdk.NewClient(baseURL, username) + } else { + // NewClient initializes and returns a API client. + giteaClient = sdk.NewClientWithHTTP(baseURL, &http.Client{}) + giteaClient.SetBasicAuth(username, password) + } + } + + _, err = giteaClient.ListMyRepos(sdk.ListReposOptions{}) + if err != nil { + log.Trace("Error logging into gitlab: %v", err) + return nil + } + + // Grab and store project/repo ID here, due to issues using the URL escaped path + var splitRepoPath = strings.Split(repoPath, "/") + gr, err := giteaClient.GetRepo(splitRepoPath[0], splitRepoPath[1]) + if err != nil { + log.Trace("Error retrieving project: %v", err) + return nil + } + + if gr == nil { + log.Trace("Error getting project, project is nil") + return nil + } + + return &GiteaDownloader{ + ctx: context.Background(), + client: giteaClient, + repoName: gr.Name, + owner: gr.Owner.UserName, + } +} + +// 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") + } + + gr, err := g.client.GetRepo(g.owner, g.repoName) + if err != nil { + return nil, err + } + + var owner string + if gr.Owner == nil { + log.Trace("gr.Owner is nil, using owner from Client") + owner = g.owner + } else { + owner = gr.Owner.UserName + } + + // convert gitlab repo to stand Repo + return &base.Repository{ + Owner: owner, + Name: gr.Name, + IsPrivate: gr.Private, + Description: gr.Description, + // TODO: Is this right? + OriginalURL: gr.HTMLURL, + CloneURL: gr.CloneURL, + }, nil +} + +// GetTopics return gitlab topics +func (g *GiteaDownloader) GetTopics() ([]string, error) { + if g == nil { + return nil, errors.New("error: GiteaDownloader is nil") + } + ts, err := g.client.ListRepoTopics(g.owner, g.repoName, sdk.ListRepoTopicsOptions{}) + if err != nil { + return nil, err + } + return ts, nil +} + +// GetMilestones returns milestones +func (g *GiteaDownloader) GetMilestones() ([]*base.Milestone, error) { + if g == nil { + return nil, errors.New("error: GiteaDownloader is nil") + } + ms, err := g.client.ListRepoMilestones(g.owner, g.repoName, sdk.ListMilestoneOption{State: sdk.StateAll}) + if err != nil { + return nil, err + } + var milestones []*base.Milestone + + now := time.Now() + for _, m := range ms { + milestones = append(milestones, &base.Milestone{ + Title: m.Title, + Description: m.Description, + Deadline: m.Deadline, + State: string(m.State), + // TODO + Created: now, + // TODO + Updated: &now, + Closed: m.Closed, + }) + } + return milestones, nil +} + +func (g *GiteaDownloader) convertGiteaLabel(label *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) { + if g == nil { + return nil, errors.New("error: GiteaDownloader is nil") + } + ls, err := g.client.ListRepoLabels(g.owner, g.repoName, sdk.ListLabelsOptions{}) + if err != nil { + return nil, err + } + var labels []*base.Label + for _, label := range ls { + labels = append(labels, g.convertGiteaLabel(label)) + } + return labels, nil +} + +func (g *GiteaDownloader) convertGiteaRelease(rel *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, + Created: rel.CreatedAt, + Published: rel.PublishedAt, + } + + for _, asset := range rel.Attachments { + r.Assets = append(r.Assets, base.ReleaseAsset{ + URL: asset.DownloadURL, + Name: asset.Name, + // TODO + ContentType: nil, + }) + } + return r +} + +// GetReleases returns releases +func (g *GiteaDownloader) GetReleases() ([]*base.Release, error) { + ls, err := g.client.ListReleases(g.owner, g.repoName, sdk.ListReleasesOptions{}) + if err != nil { + return nil, err + } + var releases []*base.Release + for _, release := range ls { + releases = append(releases, g.convertGiteaRelease(release)) + } + return releases, nil +} + +func (g *GiteaDownloader) GetReactionsToIssue(issueNumber int64) ([]*base.Reaction, error) { + var allReactions []*base.Reaction + + rs, err := g.client.GetIssueReactions(g.owner, g.repoName, issueNumber) + + if err != nil { + return nil, err + } + + for _, r := range rs { + allReactions = append(allReactions, &base.Reaction{ + UserID: r.User.ID, + UserName: r.User.UserName, + Content: r.Reaction, + }) + } + + return allReactions, nil +} + +// GetIssues returns issues according start and limit +// Note: issue label description and colors are not supported by the go-gitlab library at this time +func (g *GiteaDownloader) GetIssues(page, perPage int) ([]*base.Issue, bool, error) { + opt := sdk.ListIssueOption{ + State: sdk.StateAll, + Type: sdk.IssueTypeIssue, + ListOptions: sdk.ListOptions{ + Page: page, + PageSize: perPage, + }, + } + + issues, err := g.client.ListRepoIssues(g.owner, g.repoName, opt) + if err != nil { + return nil, false, fmt.Errorf("error while listing issues: %v", err) + } + var allIssues []*base.Issue + for _, issue := range issues { + + issueNumber := issue.Index + var labels []*base.Label + + for _, l := range issue.Labels { + labels = append(labels, &base.Label{ + Name: l.Name, + Color: l.Color, + Description: l.Description, + }) + } + + var milestone string + if issue.Milestone != nil { + milestone = issue.Milestone.Title + } + + reactions, _ := g.GetReactionsToIssue(issueNumber) + + allIssues = append(allIssues, &base.Issue{ + Number: issueNumber, + PosterID: issue.Poster.ID, + PosterName: issue.Poster.UserName, + PosterEmail: issue.Poster.Email, + Title: issue.Title, + Content: issue.Body, + Milestone: milestone, + State: string(issue.State), + // TODO + IsLocked: false, + Created: issue.Created, + Updated: issue.Updated, + Closed: issue.Closed, + Labels: labels, + Reactions: reactions, + }) + } + + return allIssues, len(issues) < perPage, nil +} + +func (g *GiteaDownloader) GetReactionsToComment(commentID int64) ([]*base.Reaction, error) { + var allReactions []*base.Reaction + + rs, err := g.client.GetIssueCommentReactions(g.owner, g.repoName, commentID) + + if err != nil { + return nil, err + } + + for _, r := range rs { + allReactions = append(allReactions, &base.Reaction{ + UserID: r.User.ID, + UserName: r.User.UserName, + Content: r.Reaction, + }) + } + + return allReactions, nil +} + +// GetComments returns comments according issueNumber +func (g *GiteaDownloader) GetComments(issueNumber int64) ([]*base.Comment, error) { + var allComments []*base.Comment + + comments, err := g.client.ListIssueComments(g.owner, g.repoName, issueNumber, sdk.ListIssueCommentOptions{}) + + if err != nil { + return nil, fmt.Errorf("error while listing comments: %v %v %v", g.owner, g.repoName, err) + } + for _, comment := range comments { + reactions, _ := g.GetReactionsToComment(comment.ID) + + allComments = append(allComments, &base.Comment{ + IssueIndex: issueNumber, + PosterID: comment.Poster.ID, + PosterName: comment.Poster.UserName, + PosterEmail: comment.Poster.Email, + Content: comment.Body, + Created: comment.Created, + Updated: comment.Updated, + Reactions: reactions, + }) + } + return allComments, nil +} + +func (g *GiteaDownloader) convertGiteaPullRequestBranch(branchInfo *sdk.PRBranchInfo) base.PullRequestBranch { + return base.PullRequestBranch{ + Ref: branchInfo.Ref, + SHA: branchInfo.Sha, + RepoName: branchInfo.Repository.Name, + OwnerName: branchInfo.Repository.Owner.UserName, + CloneURL: branchInfo.Repository.CloneURL, + } +} + +// GetPullRequests returns pull requests according page and perPage +func (g *GiteaDownloader) GetPullRequests(page, perPage int) ([]*base.PullRequest, error) { + opt := sdk.ListPullRequestsOptions{ + State: sdk.StateAll, + ListOptions: sdk.ListOptions{ + Page: page, + PageSize: perPage, + }, + } + + // Set fetchPRcomments to true here, so PR comments are fetched instead of Issue comments + g.fetchPRcomments = true + + var allPRs []*base.PullRequest + + prs, err := g.client.ListRepoPullRequests(g.owner, g.repoName, opt) + if err != nil { + return nil, fmt.Errorf("error while listing merge requests: %v", err) + } + for _, pr := range prs { + + issueNumber := pr.Index + var labels []*base.Label + + for _, l := range pr.Labels { + labels = append(labels, g.convertGiteaLabel(l)) + } + + var mergeCommitSHA string + if pr.MergedCommitID != nil { + mergeCommitSHA = *pr.MergedCommitID + } + var updated time.Time + if pr.Updated != nil { + updated = *pr.Updated + } + + reactions, _ := g.GetReactionsToIssue(issueNumber) + var assignee string + if pr.Assignee != nil { + assignee = pr.Assignee.UserName + } + + allPRs = append(allPRs, &base.PullRequest{ + Number: pr.Index, + Title: pr.Title, + PosterName: pr.Poster.UserName, + PosterID: pr.Poster.ID, + PosterEmail: pr.Poster.Email, + Content: pr.Body, + Milestone: pr.Milestone.Title, + State: string(pr.State), + Created: *pr.Created, + Updated: updated, + Closed: pr.Closed, + Labels: labels, + PatchURL: pr.PatchURL, + Merged: pr.HasMerged, + MergedTime: pr.Merged, + MergeCommitSHA: mergeCommitSHA, + Head: g.convertGiteaPullRequestBranch(pr.Head), + Base: g.convertGiteaPullRequestBranch(pr.Base), + Assignee: assignee, + // TODO + Assignees: nil, + // TODO + IsLocked: false, + Reactions: reactions, + }) + } + + return allPRs, nil +} + +func mapReviewState(stateType sdk.ReviewStateType) string { + switch stateType { + case sdk.ReviewStatePending: + return base.ReviewStatePending + case sdk.ReviewStateApproved: + return base.ReviewStateApproved + case sdk.ReviewStateRequestChanges: + return base.ReviewStateChangesRequested + case sdk.ReviewStateComment: + return base.ReviewStateCommented + default: + return "" + } +} + +// GetReviews returns pull requests review +func (g *GiteaDownloader) GetReviews(pullRequestNumber int64) ([]*base.Review, error) { + prrs, err := g.client.ListPullReviews(g.owner, g.repoName, pullRequestNumber, sdk.ListPullReviewsOptions{}) + if err != nil { + return nil, err + } + + var allPRReviews []*base.Review + + for _, prr := range prrs { + id := prr.ID + var currentReviewComments []*base.ReviewComment + + prrcs, err := g.client.ListPullReviewComments(g.owner, g.repoName, pullRequestNumber, id, sdk.ListPullReviewsCommentsOptions{}) + if err != nil { + currentReviewComments = make([]*base.ReviewComment, 0) + } else { + for _, prrc := range prrcs { + id := prrc.ID + reactions, _ := g.GetReactionsToComment(id) + + currentReviewComments = append(currentReviewComments, &base.ReviewComment{ + ID: id, + // TODO: Find out + InReplyTo: 0, + Content: prrc.Body, + TreePath: prrc.Path, + DiffHunk: prrc.DiffHunk, + Position: int(prrc.LineNum), + CommitID: prrc.CommitID, + PosterID: prrc.Reviewer.ID, + Reactions: reactions, + CreatedAt: prrc.Created, + UpdatedAt: prrc.Updated, + }) + } + } + + allPRReviews = append(allPRReviews, &base.Review{ + ID: prr.ID, + IssueIndex: pullRequestNumber, + ReviewerID: prr.Reviewer.ID, + ReviewerName: prr.Reviewer.UserName, + Official: prr.Official, + CommitID: prr.CommitID, + Content: prr.Body, + CreatedAt: prr.Submitted, + State: mapReviewState(prr.State), + Comments: currentReviewComments, + }) + } + + return allPRReviews, nil +} diff --git a/modules/migrations/gitea_downloader_test.go b/modules/migrations/gitea_downloader_test.go new file mode 100644 index 0000000000000..68612f7f272fd --- /dev/null +++ b/modules/migrations/gitea_downloader_test.go @@ -0,0 +1,431 @@ +// Copyright 2019 The Gitea Authors. All rights reserved. +// Copyright 2018 Jonas Franz. 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 ( + "net/http" + "os" + "testing" + "time" + + "code.gitea.io/gitea/modules/migrations/base" + + "github.com/stretchr/testify/assert" + + _ "github.com/mattn/go-sqlite3" +) + +func TestGiteaDownloadRepo(t *testing.T) { + // Skip tests if Gitea token is not found + giteaPersonalAccessToken := os.Getenv("GITEA_ACCESS_TOKEN") + if giteaPersonalAccessToken == "" { + t.Skip("skipped test because GITEA_ACCESS_TOKEN was not in the environment") + } + + resp, err := http.Get("https://gitea.com/j-be/test_repo") + if err != nil || resp.StatusCode != 200 { + t.Skipf("Can't access test repo, skipping %s", t.Name()) + } + + downloader := NewGiteaDownloader("https://gitea.com", "j-be/test_repo", giteaPersonalAccessToken, "") + if downloader == nil { + t.Fatal("NewGiteaDownloader is nil") + } + repo, err := downloader.GetRepoInfo() + assert.NoError(t, err) + assert.EqualValues(t, &base.Repository{ + Name: "test_repo", + Owner: "j-be", + Description: "Test repository for testing migration from gitea to gitea", + CloneURL: "https://gitea.com/j-be/test_repo.git", + OriginalURL: "https://gitea.com/j-be/test_repo", + }, repo) + + topics, err := downloader.GetTopics() + assert.NoError(t, err) + assert.Contains(t, topics, "gitea") + assert.Contains(t, topics, "test") + assert.Contains(t, topics, "migration") + + milestones, err := downloader.GetMilestones() + assert.NoError(t, err) + assert.True(t, len(milestones) >= 2) + + for _, milestone := range milestones { + switch milestone.Title { + case "1.0.0": + assertMilestoneEqual(t, "Milestone 1.0.0", "1.0.0", "2019-11-11 08:00:00 +0000 UTC", + "2019-11-12 19:37:08 +0000 UTC", + "2019-11-12 21:56:17 +0000 UTC", + "2019-11-12 19:45:49 +0000 UTC", + "closed", milestone) + case "1.1.0": + assertMilestoneEqual(t, "Milestone 1.1.0", "1.1.0", "2019-11-12 08:00:00 +0000 UTC", + "2019-11-12 19:37:25 +0000 UTC", + "2019-11-12 21:39:27 +0000 UTC", + "2019-11-12 19:45:46 +0000 UTC", + "closed", milestone) + } + } + + labels, err := downloader.GetLabels() + assert.NoError(t, err) + assert.True(t, len(labels) >= 8) + for _, l := range labels { + switch l.Name { + case "bug": + assertLabelEqual(t, "bug", "d73a4a", "Something isn't working", l) + case "documentation": + assertLabelEqual(t, "documentation", "0075ca", "Improvements or additions to documentation", l) + case "duplicate": + assertLabelEqual(t, "duplicate", "cfd3d7", "This issue or pull request already exists", l) + case "enhancement": + assertLabelEqual(t, "enhancement", "a2eeef", "New feature or request", l) + case "good first issue": + assertLabelEqual(t, "good first issue", "7057ff", "Good for newcomers", l) + case "help wanted": + assertLabelEqual(t, "help wanted", "008672", "Extra attention is needed", l) + case "invalid": + assertLabelEqual(t, "invalid", "e4e669", "This doesn't seem right", l) + case "question": + assertLabelEqual(t, "question", "d876e3", "Further information is requested", l) + } + } + + releases, err := downloader.GetReleases() + assert.NoError(t, err) + assert.EqualValues(t, []*base.Release{ + { + TagName: "v0.9.99", + TargetCommitish: "master", + Name: "First Release", + Body: "A test release", + Created: time.Date(2019, 11, 9, 16, 49, 21, 0, time.UTC), + Published: time.Date(2019, 11, 12, 20, 12, 10, 0, time.UTC), + PublisherID: -1, + PublisherName: "mrsdizzie(github)", + PublisherEmail: "", + }, + }, releases[len(releases)-1:]) + + // downloader.GetIssues() + issues, isEnd, err := downloader.GetIssues(1, 2) + assert.NoError(t, err) + assert.EqualValues(t, 2, len(issues)) + assert.False(t, isEnd) + + var ( + closed1 = time.Date(2019, 11, 12, 20, 22, 22, 0, time.UTC) + closed2 = time.Date(2019, 11, 12, 21, 1, 31, 0, time.UTC) + ) + assert.EqualValues(t, []*base.Issue{ + { + Number: 2, + Title: "Test issue", + Content: "This is test issue 2, do not touch!", + Milestone: "1.1.0", + PosterID: -1, + PosterName: "mrsdizzie(github)", + PosterEmail: "", + State: "closed", + Created: time.Date(2019, 11, 12, 21, 0, 6, 0, time.UTC), + Updated: time.Date(2019, 11, 12, 22, 7, 14, 0, time.UTC), + Labels: []*base.Label{ + { + Name: "duplicate", + Color: "cfd3d7", + Description: "This issue or pull request already exists", + }, + }, + Reactions: []*base.Reaction{ + { + UserID: -1, + UserName: "mrsdizzie(github)", + Content: "heart", + }, + { + UserID: -1, + UserName: "mrsdizzie(github)", + Content: "laugh", + }, + { + UserID: -1, + UserName: "mrsdizzie(github)", + Content: "-1", + }, + { + UserID: -1, + UserName: "mrsdizzie(github)", + Content: "confused", + }, + { + UserID: -1, + UserName: "mrsdizzie(github)", + Content: "hooray", + }, + { + UserID: -1, + UserName: "mrsdizzie(github)", + Content: "+1", + }, + }, + Closed: &closed2, + }, + { + Number: 1, + Title: "Please add an animated gif icon to the merge button", + Content: "I just want the merge button to hurt my eyes a little. \xF0\x9F\x98\x9D ", + Milestone: "1.0.0", + PosterID: 18600385, + PosterName: "guillep2k", + PosterEmail: "", + State: "closed", + Created: time.Date(2019, 11, 9, 17, 0, 29, 0, time.UTC), + Updated: time.Date(2019, 11, 12, 20, 29, 53, 0, time.UTC), + Labels: []*base.Label{ + { + Name: "bug", + Color: "d73a4a", + Description: "Something isn't working", + }, + { + Name: "good first issue", + Color: "7057ff", + Description: "Good for newcomers", + }, + }, + Reactions: []*base.Reaction{ + { + UserID: -1, + UserName: "mrsdizzie(github)", + Content: "+1", + }, + }, + Closed: &closed1, + }, + }, issues) + + // downloader.GetComments() + comments, err := downloader.GetComments(2) + assert.NoError(t, err) + assert.EqualValues(t, 2, len(comments)) + assert.EqualValues(t, []*base.Comment{ + { + IssueIndex: 2, + PosterID: -1, + PosterName: "mrsdizzie(github)", + PosterEmail: "", + Created: time.Date(2019, 11, 12, 21, 0, 13, 0, time.UTC), + Updated: time.Date(2019, 11, 12, 21, 0, 13, 0, time.UTC), + Content: "This is a comment", + Reactions: []*base.Reaction{ + { + UserID: -1, + UserName: "mrsdizzie(github)", + Content: "+1", + }, + }, + }, + { + IssueIndex: 2, + PosterID: -1, + PosterName: "mrsdizzie(github)", + PosterEmail: "", + Created: time.Date(2019, 11, 12, 22, 7, 14, 0, time.UTC), + Updated: time.Date(2019, 11, 12, 22, 7, 14, 0, time.UTC), + Content: "A second comment", + Reactions: nil, + }, + }, comments[:2]) + + // downloader.GetPullRequests() + prs, err := downloader.GetPullRequests(1, 2) + assert.NoError(t, err) + assert.EqualValues(t, 2, len(prs)) + + closed1 = time.Date(2019, 11, 12, 21, 39, 27, 0, time.UTC) + var merged1 = time.Date(2019, 11, 12, 21, 39, 27, 0, time.UTC) + + assert.EqualValues(t, []*base.PullRequest{ + { + Number: 4, + Title: "Test branch", + Content: "do not merge this PR", + Milestone: "1.0.0", + PosterID: -1, + PosterName: "mrsdizzie(github)", + PosterEmail: "", + State: "open", + Created: time.Date(2019, 11, 12, 21, 54, 18, 0, time.UTC), + Updated: time.Date(2020, 1, 4, 11, 30, 1, 0, time.UTC), + Labels: []*base.Label{ + { + Name: "bug", + Color: "d73a4a", + Description: "Something isn't working", + }, + }, + PatchURL: "https://gitea.com/j-be/test_repo/pulls/4.patch", + Head: base.PullRequestBranch{ + Ref: "test-branch", + SHA: "2be9101c543658591222acbee3eb799edfc3853d", + RepoName: "test_repo", + OwnerName: "mrsdizzie(github)", + CloneURL: "https://github.com/mrsdizzie/test_repo.git", + }, + Base: base.PullRequestBranch{ + CloneURL: "https://gitea.com/j-be/test_repo.git", + Ref: "master", + SHA: "f32b0a9dfd09a60f616f29158f772cedd89942d2", + OwnerName: "j-be", + RepoName: "test_repo", + }, + Merged: false, + MergeCommitSHA: "565d1208f5fffdc1c5ae1a2436491eb9a5e4ebae", + Reactions: []*base.Reaction{ + { + UserID: 1, + UserName: "lunny", + Content: "heart", + }, + { + UserID: 1, + UserName: "lunny", + Content: "+1", + }, + }, + }, + { + Number: 3, + Title: "Update README.md", + Content: "add warning to readme", + Milestone: "1.1.0", + PosterID: -1, + PosterName: "mrsdizzie(github)", + PosterEmail: "", + State: "closed", + Created: time.Date(2019, 11, 12, 21, 21, 43, 0, time.UTC), + Updated: time.Date(2019, 11, 12, 21, 39, 28, 0, time.UTC), + Labels: []*base.Label{ + { + Name: "documentation", + Color: "0075ca", + Description: "Improvements or additions to documentation", + }, + }, + PatchURL: "https://gitea.com/j-be/test_repo/pulls/3.patch", + Head: base.PullRequestBranch{ + Ref: "master", + CloneURL: "https://github.com/mrsdizzie/test_repo.git", + SHA: "076160cf0b039f13e5eff19619932d181269414b", + RepoName: "test_repo", + OwnerName: "mrsdizzie(github)", + }, + Base: base.PullRequestBranch{ + CloneURL: "https://gitea.com/j-be/test_repo.git", + Ref: "master", + SHA: "72866af952e98d02a73003501836074b286a78f6", + OwnerName: "j-be", + RepoName: "test_repo", + }, + Closed: &closed1, + Merged: true, + MergedTime: &merged1, + MergeCommitSHA: "f32b0a9dfd09a60f616f29158f772cedd89942d2", + }, + }, prs) + + reviews, err := downloader.GetReviews(3) + assert.NoError(t, err) + assert.EqualValues(t, []*base.Review{ + { + ID: 1453, + IssueIndex: 3, + ReviewerID: 42128690, + ReviewerName: "jolheiser", + CommitID: "076160cf0b039f13e5eff19619932d181269414b", + CreatedAt: time.Date(2019, 11, 12, 21, 35, 24, 0, time.UTC), + State: base.ReviewStateApproved, + }, + { + ID: 1454, + IssueIndex: 3, + ReviewerID: 1824502, + ReviewerName: "zeripath", + CommitID: "076160cf0b039f13e5eff19619932d181269414b", + CreatedAt: time.Date(2019, 11, 12, 21, 35, 36, 0, time.UTC), + State: base.ReviewStateApproved, + }, + { + ID: 1455, + IssueIndex: 3, + ReviewerID: 165205, + ReviewerName: "lafriks", + CommitID: "076160cf0b039f13e5eff19619932d181269414b", + CreatedAt: time.Date(2019, 11, 12, 21, 38, 00, 0, time.UTC), + State: base.ReviewStateApproved, + }, + }, reviews) + + reviews, err = downloader.GetReviews(4) + assert.NoError(t, err) + assert.EqualValues(t, []*base.Review{ + { + ID: 1456, + IssueIndex: 4, + ReviewerID: 1, + ReviewerName: "lunny", + CommitID: "2be9101c543658591222acbee3eb799edfc3853d", + CreatedAt: time.Date(2020, 01, 04, 05, 33, 18, 0, time.UTC), + State: base.ReviewStateApproved, + Comments: []*base.ReviewComment{ + { + ID: 114540, + Content: "This is a good pull request.", + TreePath: "README.md", + DiffHunk: "@@ -1,2 +1,4 @@\n # test_repo\n Test repository for testing migration from github to gitea\n+", + Position: 3, + CommitID: "2be9101c543658591222acbee3eb799edfc3853d", + PosterID: 1, + CreatedAt: time.Date(2020, 01, 04, 05, 33, 06, 0, time.UTC), + UpdatedAt: time.Date(2020, 01, 04, 05, 33, 18, 0, time.UTC), + }, + }, + }, + { + ID: 1457, + IssueIndex: 4, + ReviewerID: 1, + ReviewerName: "lunny", + CommitID: "2be9101c543658591222acbee3eb799edfc3853d", + CreatedAt: time.Date(2020, 01, 04, 06, 07, 06, 0, time.UTC), + State: base.ReviewStateChangesRequested, + Content: "Don't add more reviews", + }, + { + ID: 1458, + IssueIndex: 4, + ReviewerID: 1, + ReviewerName: "lunny", + CommitID: "2be9101c543658591222acbee3eb799edfc3853d", + CreatedAt: time.Date(2020, 01, 04, 11, 21, 41, 0, time.UTC), + State: base.ReviewStateCommented, + Comments: []*base.ReviewComment{ + { + ID: 114543, + Content: "test a single comment.", + TreePath: "LICENSE", + DiffHunk: "@@ -19,3 +19,5 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.\n+", + Position: 4, + CommitID: "2be9101c543658591222acbee3eb799edfc3853d", + PosterID: 1, + CreatedAt: time.Date(2020, 01, 04, 11, 21, 41, 0, time.UTC), + UpdatedAt: time.Date(2020, 01, 04, 11, 21, 41, 0, time.UTC), + }, + }, + }, + }, reviews) +} diff --git a/vendor/modules.txt b/vendor/modules.txt index bf6aacb5065de..d430b69392c8c 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1,6 +1,9 @@ # cloud.google.com/go v0.45.0 ## explicit cloud.google.com/go/compute/metadata +# code.gitea.io/sdk/gitea v0.12.0 +## explicit +code.gitea.io/sdk/gitea # gitea.com/jolheiser/gitea-vet v0.1.0 ## explicit gitea.com/jolheiser/gitea-vet