diff --git a/modules/convert/utils.go b/modules/convert/utils.go index a0463d7b10..52fbcf547f 100644 --- a/modules/convert/utils.go +++ b/modules/convert/utils.go @@ -35,6 +35,8 @@ func ToGitServiceType(value string) structs.GitServiceType { return structs.GogsService case "onedev": return structs.OneDevService + case "gitbucket": + return structs.GitBucketService default: return structs.PlainGitService } diff --git a/modules/migrations/gitbucket.go b/modules/migrations/gitbucket.go new file mode 100644 index 0000000000..72090c2490 --- /dev/null +++ b/modules/migrations/gitbucket.go @@ -0,0 +1,72 @@ +// Copyright 2021 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" + "net/url" + "strings" + + "code.gitea.io/gitea/modules/migrations/base" + "code.gitea.io/gitea/modules/structs" +) + +var ( + _ base.Downloader = &GitBucketDownloader{} + _ base.DownloaderFactory = &GitBucketDownloaderFactory{} +) + +func init() { + RegisterDownloaderFactory(&GitBucketDownloaderFactory{}) +} + +// GitBucketDownloaderFactory defines a GitBucket downloader factory +type GitBucketDownloaderFactory struct { +} + +// New returns a Downloader related to this factory according MigrateOptions +func (f *GitBucketDownloaderFactory) 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 + fields := strings.Split(u.Path, "/") + oldOwner := fields[1] + oldName := strings.TrimSuffix(fields[2], ".git") + + return NewGitBucketDownloader(ctx, baseURL, opts.AuthUsername, opts.AuthPassword, opts.AuthToken, oldOwner, oldName), nil +} + +// GitServiceType returns the type of git service +func (f *GitBucketDownloaderFactory) GitServiceType() structs.GitServiceType { + return structs.GitBucketService +} + +// GitBucketDownloader implements a Downloader interface to get repository information +// from GitBucket via GithubDownloader +type GitBucketDownloader struct { + *GithubDownloaderV3 +} + +// NewGitBucketDownloader creates a GitBucket downloader +func NewGitBucketDownloader(ctx context.Context, baseURL, userName, password, token, repoOwner, repoName string) *GitBucketDownloader { + githubDownloader := NewGithubDownloaderV3(ctx, baseURL, userName, password, token, repoOwner, repoName) + githubDownloader.SkipReactions = true + return &GitBucketDownloader{ + githubDownloader, + } +} + +// SupportGetRepoComments return true if it supports get repo comments +func (g *GitBucketDownloader) SupportGetRepoComments() bool { + return false +} + +// GetReviews is not supported +func (g *GitBucketDownloader) GetReviews(context base.IssueContext) ([]*base.Review, error) { + return nil, &base.ErrNotSupported{Entity: "Reviews"} +} diff --git a/modules/migrations/github.go b/modules/migrations/github.go index 874cd05439..50cffc467a 100644 --- a/modules/migrations/github.go +++ b/modules/migrations/github.go @@ -68,15 +68,16 @@ func (f *GithubDownloaderV3Factory) GitServiceType() structs.GitServiceType { // from github via APIv3 type GithubDownloaderV3 struct { base.NullDownloader - ctx context.Context - clients []*github.Client - repoOwner string - repoName string - userName string - password string - rates []*github.Rate - curClientIdx int - maxPerPage int + ctx context.Context + clients []*github.Client + repoOwner string + repoName string + userName string + password string + rates []*github.Rate + curClientIdx int + maxPerPage int + SkipReactions bool } // NewGithubDownloaderV3 creates a github Downloader via github v3 API @@ -428,25 +429,27 @@ func (g *GithubDownloaderV3) GetIssues(page, perPage int) ([]*base.Issue, bool, // get reactions var reactions []*base.Reaction - for i := 1; ; i++ { - g.waitAndPickClient() - res, resp, err := g.getClient().Reactions.ListIssueReactions(g.ctx, g.repoOwner, g.repoName, issue.GetNumber(), &github.ListOptions{ - Page: i, - PerPage: perPage, - }) - if err != nil { - return nil, false, err - } - g.setRate(&resp.Rate) - if len(res) == 0 { - break - } - for _, reaction := range res { - reactions = append(reactions, &base.Reaction{ - UserID: reaction.User.GetID(), - UserName: reaction.User.GetLogin(), - Content: reaction.GetContent(), + if !g.SkipReactions { + for i := 1; ; i++ { + g.waitAndPickClient() + res, resp, err := g.getClient().Reactions.ListIssueReactions(g.ctx, g.repoOwner, g.repoName, issue.GetNumber(), &github.ListOptions{ + Page: i, + PerPage: perPage, }) + if err != nil { + return nil, false, err + } + g.setRate(&resp.Rate) + if len(res) == 0 { + break + } + for _, reaction := range res { + reactions = append(reactions, &base.Reaction{ + UserID: reaction.User.GetID(), + UserName: reaction.User.GetLogin(), + Content: reaction.GetContent(), + }) + } } } @@ -516,25 +519,27 @@ func (g *GithubDownloaderV3) getComments(issueContext base.IssueContext) ([]*bas for _, comment := range comments { // get reactions var reactions []*base.Reaction - for i := 1; ; i++ { - g.waitAndPickClient() - res, resp, err := g.getClient().Reactions.ListIssueCommentReactions(g.ctx, g.repoOwner, g.repoName, comment.GetID(), &github.ListOptions{ - Page: i, - PerPage: g.maxPerPage, - }) - if err != nil { - return nil, err - } - g.setRate(&resp.Rate) - if len(res) == 0 { - break - } - for _, reaction := range res { - reactions = append(reactions, &base.Reaction{ - UserID: reaction.User.GetID(), - UserName: reaction.User.GetLogin(), - Content: reaction.GetContent(), + if !g.SkipReactions { + for i := 1; ; i++ { + g.waitAndPickClient() + res, resp, err := g.getClient().Reactions.ListIssueCommentReactions(g.ctx, g.repoOwner, g.repoName, comment.GetID(), &github.ListOptions{ + Page: i, + PerPage: g.maxPerPage, }) + if err != nil { + return nil, err + } + g.setRate(&resp.Rate) + if len(res) == 0 { + break + } + for _, reaction := range res { + reactions = append(reactions, &base.Reaction{ + UserID: reaction.User.GetID(), + UserName: reaction.User.GetLogin(), + Content: reaction.GetContent(), + }) + } } } @@ -588,25 +593,27 @@ func (g *GithubDownloaderV3) GetAllComments(page, perPage int) ([]*base.Comment, for _, comment := range comments { // get reactions var reactions []*base.Reaction - for i := 1; ; i++ { - g.waitAndPickClient() - res, resp, err := g.getClient().Reactions.ListIssueCommentReactions(g.ctx, g.repoOwner, g.repoName, comment.GetID(), &github.ListOptions{ - Page: i, - PerPage: g.maxPerPage, - }) - if err != nil { - return nil, false, err - } - g.setRate(&resp.Rate) - if len(res) == 0 { - break - } - for _, reaction := range res { - reactions = append(reactions, &base.Reaction{ - UserID: reaction.User.GetID(), - UserName: reaction.User.GetLogin(), - Content: reaction.GetContent(), + if !g.SkipReactions { + for i := 1; ; i++ { + g.waitAndPickClient() + res, resp, err := g.getClient().Reactions.ListIssueCommentReactions(g.ctx, g.repoOwner, g.repoName, comment.GetID(), &github.ListOptions{ + Page: i, + PerPage: g.maxPerPage, }) + if err != nil { + return nil, false, err + } + g.setRate(&resp.Rate) + if len(res) == 0 { + break + } + for _, reaction := range res { + reactions = append(reactions, &base.Reaction{ + UserID: reaction.User.GetID(), + UserName: reaction.User.GetLogin(), + Content: reaction.GetContent(), + }) + } } } idx := strings.LastIndex(*comment.IssueURL, "/") @@ -656,25 +663,27 @@ func (g *GithubDownloaderV3) GetPullRequests(page, perPage int) ([]*base.PullReq // get reactions var reactions []*base.Reaction - for i := 1; ; i++ { - g.waitAndPickClient() - res, resp, err := g.getClient().Reactions.ListIssueReactions(g.ctx, g.repoOwner, g.repoName, pr.GetNumber(), &github.ListOptions{ - Page: i, - PerPage: perPage, - }) - if err != nil { - return nil, false, err - } - g.setRate(&resp.Rate) - if len(res) == 0 { - break - } - for _, reaction := range res { - reactions = append(reactions, &base.Reaction{ - UserID: reaction.User.GetID(), - UserName: reaction.User.GetLogin(), - Content: reaction.GetContent(), + if !g.SkipReactions { + for i := 1; ; i++ { + g.waitAndPickClient() + res, resp, err := g.getClient().Reactions.ListIssueReactions(g.ctx, g.repoOwner, g.repoName, pr.GetNumber(), &github.ListOptions{ + Page: i, + PerPage: perPage, }) + if err != nil { + return nil, false, err + } + g.setRate(&resp.Rate) + if len(res) == 0 { + break + } + for _, reaction := range res { + reactions = append(reactions, &base.Reaction{ + UserID: reaction.User.GetID(), + UserName: reaction.User.GetLogin(), + Content: reaction.GetContent(), + }) + } } } @@ -737,25 +746,27 @@ func (g *GithubDownloaderV3) convertGithubReviewComments(cs []*github.PullReques for _, c := range cs { // get reactions var reactions []*base.Reaction - for i := 1; ; i++ { - g.waitAndPickClient() - res, resp, err := g.getClient().Reactions.ListPullRequestCommentReactions(g.ctx, g.repoOwner, g.repoName, c.GetID(), &github.ListOptions{ - Page: i, - PerPage: g.maxPerPage, - }) - if err != nil { - return nil, err - } - g.setRate(&resp.Rate) - if len(res) == 0 { - break - } - for _, reaction := range res { - reactions = append(reactions, &base.Reaction{ - UserID: reaction.User.GetID(), - UserName: reaction.User.GetLogin(), - Content: reaction.GetContent(), + if !g.SkipReactions { + for i := 1; ; i++ { + g.waitAndPickClient() + res, resp, err := g.getClient().Reactions.ListPullRequestCommentReactions(g.ctx, g.repoOwner, g.repoName, c.GetID(), &github.ListOptions{ + Page: i, + PerPage: g.maxPerPage, }) + if err != nil { + return nil, err + } + g.setRate(&resp.Rate) + if len(res) == 0 { + break + } + for _, reaction := range res { + reactions = append(reactions, &base.Reaction{ + UserID: reaction.User.GetID(), + UserName: reaction.User.GetLogin(), + Content: reaction.GetContent(), + }) + } } } diff --git a/modules/structs/repo.go b/modules/structs/repo.go index 313a982f43..8482a2128d 100644 --- a/modules/structs/repo.go +++ b/modules/structs/repo.go @@ -242,13 +242,14 @@ type GitServiceType int // enumerate all GitServiceType const ( - NotMigrated GitServiceType = iota // 0 not migrated from external sites - PlainGitService // 1 plain git service - GithubService // 2 github.com - GiteaService // 3 gitea service - GitlabService // 4 gitlab service - GogsService // 5 gogs service - OneDevService // 6 onedev service + NotMigrated GitServiceType = iota // 0 not migrated from external sites + PlainGitService // 1 plain git service + GithubService // 2 github.com + GiteaService // 3 gitea service + GitlabService // 4 gitlab service + GogsService // 5 gogs service + OneDevService // 6 onedev service + GitBucketService // 7 gitbucket service ) // Name represents the service type's name @@ -270,6 +271,8 @@ func (gt GitServiceType) Title() string { return "Gogs" case OneDevService: return "OneDev" + case GitBucketService: + return "GitBucket" case PlainGitService: return "Git" } @@ -326,5 +329,6 @@ var ( GiteaService, GogsService, OneDevService, + GitBucketService, } ) diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 6fad20c87e..7d6c878ad6 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -911,6 +911,7 @@ migrate.gitlab.description = Migrate data from gitlab.com or other GitLab instan migrate.gitea.description = Migrate data from gitea.com or other Gitea instances. migrate.gogs.description = Migrate data from notabug.org or other Gogs instances. migrate.onedev.description = Migrate data from code.onedev.io or other OneDev instances. +migrate.gitbucket.description = Migrating data from GitBucket instances. migrate.migrating_git = Migrating Git Data migrate.migrating_topics = Migrating Topics migrate.migrating_milestones = Migrating Milestones diff --git a/public/img/svg/gitea-gitbucket.svg b/public/img/svg/gitea-gitbucket.svg new file mode 100644 index 0000000000..50ddd44e1b --- /dev/null +++ b/public/img/svg/gitea-gitbucket.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/templates/repo/migrate/gitbucket.tmpl b/templates/repo/migrate/gitbucket.tmpl new file mode 100644 index 0000000000..b554a3a756 --- /dev/null +++ b/templates/repo/migrate/gitbucket.tmpl @@ -0,0 +1,129 @@ +{{template "base/head" .}} +