diff options
author | John Olheiser <john.olheiser@gmail.com> | 2020-08-27 20:36:37 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-08-28 09:36:37 +0800 |
commit | 211321fb936683815c4033fdfb9ac46af8a3b357 (patch) | |
tree | 13f356084062e1827bda3ba0d1c9f4b4b5c2c799 /modules/migrations | |
parent | ed2f6e137be8fe3c85292e8344b5bb5cfb56891d (diff) | |
download | gitea-211321fb936683815c4033fdfb9ac46af8a3b357.tar.gz gitea-211321fb936683815c4033fdfb9ac46af8a3b357.zip |
Git migration UX (#12619)
* Initial work
Signed-off-by: jolheiser <john.olheiser@gmail.com>
* Implementation
Signed-off-by: jolheiser <john.olheiser@gmail.com>
* Fix gitlab and token cloning
Signed-off-by: jolheiser <john.olheiser@gmail.com>
* Imports and JS
Signed-off-by: jolheiser <john.olheiser@gmail.com>
* Fix test
Signed-off-by: jolheiser <john.olheiser@gmail.com>
* Linting
Signed-off-by: jolheiser <john.olheiser@gmail.com>
* Generate swagger
Signed-off-by: jolheiser <john.olheiser@gmail.com>
* Move mirror toggle and rename options
Signed-off-by: jolheiser <john.olheiser@gmail.com>
Co-authored-by: Lauris BH <lauris@nix.lv>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Diffstat (limited to 'modules/migrations')
-rw-r--r-- | modules/migrations/base/downloader.go | 8 | ||||
-rw-r--r-- | modules/migrations/base/release.go | 2 | ||||
-rw-r--r-- | modules/migrations/base/uploader.go | 2 | ||||
-rw-r--r-- | modules/migrations/git.go | 6 | ||||
-rw-r--r-- | modules/migrations/gitea.go | 13 | ||||
-rw-r--r-- | modules/migrations/gitea_test.go | 2 | ||||
-rw-r--r-- | modules/migrations/github.go | 63 | ||||
-rw-r--r-- | modules/migrations/github_test.go | 2 | ||||
-rw-r--r-- | modules/migrations/gitlab.go | 57 | ||||
-rw-r--r-- | modules/migrations/gitlab_test.go | 2 | ||||
-rw-r--r-- | modules/migrations/migrate.go | 13 |
11 files changed, 89 insertions, 81 deletions
diff --git a/modules/migrations/base/downloader.go b/modules/migrations/base/downloader.go index c31f3df1d1..b692969ba5 100644 --- a/modules/migrations/base/downloader.go +++ b/modules/migrations/base/downloader.go @@ -7,13 +7,20 @@ package base import ( "context" + "io" "time" "code.gitea.io/gitea/modules/structs" ) +// AssetDownloader downloads an asset (attachment) for a release +type AssetDownloader interface { + GetAsset(tag string, id int64) (io.ReadCloser, error) +} + // Downloader downloads the site repo informations type Downloader interface { + AssetDownloader SetContext(context.Context) GetRepoInfo() (*Repository, error) GetTopics() ([]string, error) @@ -28,7 +35,6 @@ type Downloader interface { // DownloaderFactory defines an interface to match a downloader implementation and create a downloader type DownloaderFactory interface { - Match(opts MigrateOptions) (bool, error) New(opts MigrateOptions) (Downloader, error) GitServiceType() structs.GitServiceType } diff --git a/modules/migrations/base/release.go b/modules/migrations/base/release.go index b2541f1bf5..2a223920c7 100644 --- a/modules/migrations/base/release.go +++ b/modules/migrations/base/release.go @@ -8,7 +8,7 @@ import "time" // ReleaseAsset represents a release asset type ReleaseAsset struct { - URL string + ID int64 Name string ContentType *string Size *int diff --git a/modules/migrations/base/uploader.go b/modules/migrations/base/uploader.go index 85ad60fe0e..07c2bb0d42 100644 --- a/modules/migrations/base/uploader.go +++ b/modules/migrations/base/uploader.go @@ -11,7 +11,7 @@ type Uploader interface { CreateRepo(repo *Repository, opts MigrateOptions) error CreateTopics(topic ...string) error CreateMilestones(milestones ...*Milestone) error - CreateReleases(releases ...*Release) error + CreateReleases(downloader Downloader, releases ...*Release) error SyncTags() error CreateLabels(labels ...*Label) error CreateIssues(issues ...*Issue) error diff --git a/modules/migrations/git.go b/modules/migrations/git.go index af345808b5..5c9acb2533 100644 --- a/modules/migrations/git.go +++ b/modules/migrations/git.go @@ -6,6 +6,7 @@ package migrations import ( "context" + "io" "code.gitea.io/gitea/modules/migrations/base" ) @@ -64,6 +65,11 @@ func (g *PlainGitDownloader) GetReleases() ([]*base.Release, error) { return nil, ErrNotSupported } +// GetAsset returns an asset +func (g *PlainGitDownloader) GetAsset(_ string, _ int64) (io.ReadCloser, error) { + return nil, ErrNotSupported +} + // GetIssues returns issues according page and perPage func (g *PlainGitDownloader) GetIssues(page, perPage int) ([]*base.Issue, bool, error) { return nil, false, ErrNotSupported diff --git a/modules/migrations/gitea.go b/modules/migrations/gitea.go index 8c097e143c..082ddcd5fb 100644 --- a/modules/migrations/gitea.go +++ b/modules/migrations/gitea.go @@ -93,12 +93,15 @@ func (g *GiteaLocalUploader) CreateRepo(repo *base.Repository, opts base.Migrate } var remoteAddr = repo.CloneURL - if len(opts.AuthUsername) > 0 { + if len(opts.AuthToken) > 0 || len(opts.AuthUsername) > 0 { u, err := url.Parse(repo.CloneURL) if err != nil { return err } u.User = url.UserPassword(opts.AuthUsername, opts.AuthPassword) + if len(opts.AuthToken) > 0 { + u.User = url.UserPassword("oauth2", opts.AuthToken) + } remoteAddr = u.String() } @@ -210,7 +213,7 @@ func (g *GiteaLocalUploader) CreateLabels(labels ...*base.Label) error { } // CreateReleases creates releases -func (g *GiteaLocalUploader) CreateReleases(releases ...*base.Release) error { +func (g *GiteaLocalUploader) CreateReleases(downloader base.Downloader, releases ...*base.Release) error { var rels = make([]*models.Release, 0, len(releases)) for _, release := range releases { var rel = models.Release{ @@ -269,13 +272,11 @@ func (g *GiteaLocalUploader) CreateReleases(releases ...*base.Release) error { // download attachment err = func() error { - resp, err := http.Get(asset.URL) + rc, err := downloader.GetAsset(rel.TagName, asset.ID) if err != nil { return err } - defer resp.Body.Close() - - _, err = storage.Attachments.Save(attach.RelativePath(), resp.Body) + _, err = storage.Attachments.Save(attach.RelativePath(), rc) return err }() if err != nil { diff --git a/modules/migrations/gitea_test.go b/modules/migrations/gitea_test.go index c0d2dcd180..02b2f0a5c9 100644 --- a/modules/migrations/gitea_test.go +++ b/modules/migrations/gitea_test.go @@ -26,7 +26,7 @@ func TestGiteaUploadRepo(t *testing.T) { user := models.AssertExistsAndLoadBean(t, &models.User{ID: 1}).(*models.User) var ( - downloader = NewGithubDownloaderV3("", "", "go-xorm", "builder") + downloader = NewGithubDownloaderV3("", "", "", "go-xorm", "builder") repoName = "builder-" + time.Now().Format("2006-01-02-15-04-05") uploader = NewGiteaLocalUploader(graceful.GetManager().HammerContext(), user, user.Name, repoName) ) diff --git a/modules/migrations/github.go b/modules/migrations/github.go index 97d62b994f..eb73a7e0d4 100644 --- a/modules/migrations/github.go +++ b/modules/migrations/github.go @@ -6,8 +6,11 @@ package migrations import ( + "bytes" "context" "fmt" + "io" + "io/ioutil" "net/http" "net/url" "strings" @@ -37,16 +40,6 @@ func init() { type GithubDownloaderV3Factory struct { } -// Match returns ture if the migration remote URL matched this downloader factory -func (f *GithubDownloaderV3Factory) Match(opts base.MigrateOptions) (bool, error) { - u, err := url.Parse(opts.CloneAddr) - if err != nil { - return false, err - } - - return strings.EqualFold(u.Host, "github.com") && opts.AuthUsername != "", nil -} - // New returns a Downloader related to this factory according MigrateOptions func (f *GithubDownloaderV3Factory) New(opts base.MigrateOptions) (base.Downloader, error) { u, err := url.Parse(opts.CloneAddr) @@ -60,7 +53,7 @@ func (f *GithubDownloaderV3Factory) New(opts base.MigrateOptions) (base.Download log.Trace("Create github downloader: %s/%s", oldOwner, oldName) - return NewGithubDownloaderV3(opts.AuthUsername, opts.AuthPassword, oldOwner, oldName), nil + return NewGithubDownloaderV3(opts.AuthUsername, opts.AuthPassword, opts.AuthToken, oldOwner, oldName), nil } // GitServiceType returns the type of git service @@ -81,7 +74,7 @@ type GithubDownloaderV3 struct { } // NewGithubDownloaderV3 creates a github Downloader via github v3 API -func NewGithubDownloaderV3(userName, password, repoOwner, repoName string) *GithubDownloaderV3 { +func NewGithubDownloaderV3(userName, password, token, repoOwner, repoName string) *GithubDownloaderV3 { var downloader = GithubDownloaderV3{ userName: userName, password: password, @@ -90,23 +83,19 @@ func NewGithubDownloaderV3(userName, password, repoOwner, repoName string) *Gith repoName: repoName, } - var client *http.Client - if userName != "" { - if password == "" { - ts := oauth2.StaticTokenSource( - &oauth2.Token{AccessToken: userName}, - ) - client = oauth2.NewClient(downloader.ctx, ts) - } else { - client = &http.Client{ - Transport: &http.Transport{ - Proxy: func(req *http.Request) (*url.URL, error) { - req.SetBasicAuth(userName, password) - return nil, nil - }, - }, - } - } + client := &http.Client{ + Transport: &http.Transport{ + Proxy: func(req *http.Request) (*url.URL, error) { + req.SetBasicAuth(userName, password) + return nil, nil + }, + }, + } + if token != "" { + ts := oauth2.StaticTokenSource( + &oauth2.Token{AccessToken: token}, + ) + client = oauth2.NewClient(downloader.ctx, ts) } downloader.client = github.NewClient(client) return &downloader @@ -290,10 +279,8 @@ func (g *GithubDownloaderV3) convertGithubRelease(rel *github.RepositoryRelease) } for _, asset := range rel.Assets { - u, _ := url.Parse(*asset.BrowserDownloadURL) - u.User = url.UserPassword(g.userName, g.password) r.Assets = append(r.Assets, base.ReleaseAsset{ - URL: u.String(), + ID: *asset.ID, Name: *asset.Name, ContentType: asset.ContentType, Size: asset.Size, @@ -331,6 +318,18 @@ func (g *GithubDownloaderV3) GetReleases() ([]*base.Release, error) { return releases, nil } +// GetAsset returns an asset +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 + } + if asset == nil { + return ioutil.NopCloser(bytes.NewBufferString(redir)), nil + } + return asset, nil +} + // GetIssues returns issues according start and limit func (g *GithubDownloaderV3) GetIssues(page, perPage int) ([]*base.Issue, bool, error) { opt := &github.IssueListByRepoOptions{ diff --git a/modules/migrations/github_test.go b/modules/migrations/github_test.go index 814c771e8c..0b8c559d30 100644 --- a/modules/migrations/github_test.go +++ b/modules/migrations/github_test.go @@ -64,7 +64,7 @@ func assertLabelEqual(t *testing.T, name, color, description string, label *base func TestGitHubDownloadRepo(t *testing.T) { GithubLimitRateRemaining = 3 //Wait at 3 remaining since we could have 3 CI in // - downloader := NewGithubDownloaderV3(os.Getenv("GITHUB_READ_TOKEN"), "", "go-gitea", "test_repo") + downloader := NewGithubDownloaderV3("", "", os.Getenv("GITHUB_READ_TOKEN"), "go-gitea", "test_repo") err := downloader.RefreshRate() assert.NoError(t, err) diff --git a/modules/migrations/gitlab.go b/modules/migrations/gitlab.go index 4f218c95f1..eec16d2433 100644 --- a/modules/migrations/gitlab.go +++ b/modules/migrations/gitlab.go @@ -8,6 +8,8 @@ import ( "context" "errors" "fmt" + "io" + "net/http" "net/url" "strings" "time" @@ -32,21 +34,6 @@ func init() { type GitlabDownloaderFactory struct { } -// Match returns true if the migration remote URL matched this downloader factory -func (f *GitlabDownloaderFactory) 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, "gitlab.com") && opts.AuthUsername != "" { - matched = true - } - - return matched, nil -} - // New returns a Downloader related to this factory according MigrateOptions func (f *GitlabDownloaderFactory) New(opts base.MigrateOptions) (base.Downloader, error) { u, err := url.Parse(opts.CloneAddr) @@ -56,10 +43,11 @@ func (f *GitlabDownloaderFactory) New(opts base.MigrateOptions) (base.Downloader baseURL := u.Scheme + "://" + u.Host repoNameSpace := strings.TrimPrefix(u.Path, "/") + repoNameSpace = strings.TrimSuffix(repoNameSpace, ".git") log.Trace("Create gitlab downloader. BaseURL: %s RepoName: %s", baseURL, repoNameSpace) - return NewGitlabDownloader(baseURL, repoNameSpace, opts.AuthUsername, opts.AuthPassword), nil + return NewGitlabDownloader(baseURL, repoNameSpace, opts.AuthUsername, opts.AuthPassword, opts.AuthToken), nil } // GitServiceType returns the type of git service @@ -85,15 +73,13 @@ type GitlabDownloader struct { // NewGitlabDownloader 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 NewGitlabDownloader(baseURL, repoPath, username, password string) *GitlabDownloader { +func NewGitlabDownloader(baseURL, repoPath, username, password, token string) *GitlabDownloader { var gitlabClient *gitlab.Client var err error - if username != "" { - if password == "" { - gitlabClient, err = gitlab.NewClient(username, gitlab.WithBaseURL(baseURL)) - } else { - gitlabClient, err = gitlab.NewBasicAuthClient(username, password, gitlab.WithBaseURL(baseURL)) - } + if token != "" { + gitlabClient, err = gitlab.NewClient(token, gitlab.WithBaseURL(baseURL)) + } else { + gitlabClient, err = gitlab.NewBasicAuthClient(username, password, gitlab.WithBaseURL(baseURL)) } if err != nil { @@ -271,7 +257,7 @@ func (g *GitlabDownloader) GetLabels() ([]*base.Label, error) { } func (g *GitlabDownloader) convertGitlabRelease(rel *gitlab.Release) *base.Release { - + var zero int r := &base.Release{ TagName: rel.TagName, TargetCommitish: rel.Commit.ID, @@ -284,9 +270,11 @@ func (g *GitlabDownloader) convertGitlabRelease(rel *gitlab.Release) *base.Relea for k, asset := range rel.Assets.Links { r.Assets = append(r.Assets, base.ReleaseAsset{ - URL: asset.URL, - Name: asset.Name, - ContentType: &rel.Assets.Sources[k].Format, + ID: int64(asset.ID), + Name: asset.Name, + ContentType: &rel.Assets.Sources[k].Format, + Size: &zero, + DownloadCount: &zero, }) } return r @@ -315,6 +303,21 @@ func (g *GitlabDownloader) GetReleases() ([]*base.Release, error) { return releases, nil } +// GetAsset returns an asset +func (g *GitlabDownloader) GetAsset(tag string, id int64) (io.ReadCloser, error) { + link, _, err := g.client.ReleaseLinks.GetReleaseLink(g.repoID, tag, int(id)) + if err != nil { + return nil, err + } + resp, err := http.Get(link.URL) + if err != nil { + return nil, err + } + + // resp.Body is closed by the uploader + return resp.Body, 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 // TODO: figure out how to transfer issue reactions diff --git a/modules/migrations/gitlab_test.go b/modules/migrations/gitlab_test.go index 003da5bbdf..daf05f8e3a 100644 --- a/modules/migrations/gitlab_test.go +++ b/modules/migrations/gitlab_test.go @@ -27,7 +27,7 @@ func TestGitlabDownloadRepo(t *testing.T) { t.Skipf("Can't access test repo, skipping %s", t.Name()) } - downloader := NewGitlabDownloader("https://gitlab.com", "gitea/test_repo", gitlabPersonalAccessToken, "") + downloader := NewGitlabDownloader("https://gitlab.com", "gitea/test_repo", "", "", gitlabPersonalAccessToken) if downloader == nil { t.Fatal("NewGitlabDownloader is nil") } diff --git a/modules/migrations/migrate.go b/modules/migrations/migrate.go index c970ba6920..7858dfc685 100644 --- a/modules/migrations/migrate.go +++ b/modules/migrations/migrate.go @@ -13,7 +13,6 @@ import ( "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/migrations/base" "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/structs" ) // MigrateOptions is equal to base.MigrateOptions @@ -33,18 +32,15 @@ func MigrateRepository(ctx context.Context, doer *models.User, ownerName string, var ( downloader base.Downloader uploader = NewGiteaLocalUploader(ctx, doer, ownerName, opts.RepoName) - theFactory base.DownloaderFactory + err error ) for _, factory := range factories { - if match, err := factory.Match(opts); err != nil { - return nil, err - } else if match { + if factory.GitServiceType() == opts.GitServiceType { downloader, err = factory.New(opts) if err != nil { return nil, err } - theFactory = factory break } } @@ -57,11 +53,8 @@ func MigrateRepository(ctx context.Context, doer *models.User, ownerName string, opts.Comments = false opts.Issues = false opts.PullRequests = false - opts.GitServiceType = structs.PlainGitService downloader = NewPlainGitDownloader(ownerName, opts.RepoName, opts.CloneAddr) log.Trace("Will migrate from git: %s", opts.OriginalURL) - } else if opts.GitServiceType == structs.NotMigrated { - opts.GitServiceType = theFactory.GitServiceType() } uploader.gitServiceType = opts.GitServiceType @@ -169,7 +162,7 @@ func migrateRepository(downloader base.Downloader, uploader base.Uploader, opts relBatchSize = len(releases) } - if err := uploader.CreateReleases(releases[:relBatchSize]...); err != nil { + if err := uploader.CreateReleases(downloader, releases[:relBatchSize]...); err != nil { return err } releases = releases[relBatchSize:] |