Skip to content

Commit 211321f

Browse files
jolheiserlafrikslunny
authored
Git migration UX (#12619)
* Initial work Signed-off-by: jolheiser <[email protected]> * Implementation Signed-off-by: jolheiser <[email protected]> * Fix gitlab and token cloning Signed-off-by: jolheiser <[email protected]> * Imports and JS Signed-off-by: jolheiser <[email protected]> * Fix test Signed-off-by: jolheiser <[email protected]> * Linting Signed-off-by: jolheiser <[email protected]> * Generate swagger Signed-off-by: jolheiser <[email protected]> * Move mirror toggle and rename options Signed-off-by: jolheiser <[email protected]> Co-authored-by: Lauris BH <[email protected]> Co-authored-by: Lunny Xiao <[email protected]>
1 parent ed2f6e1 commit 211321f

File tree

20 files changed

+269
-177
lines changed

20 files changed

+269
-177
lines changed

modules/auth/repo_form.go

+2
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,10 @@ func (f *CreateRepoForm) Validate(ctx *macaron.Context, errs binding.Errors) bin
5656
type MigrateRepoForm struct {
5757
// required: true
5858
CloneAddr string `json:"clone_addr" binding:"Required"`
59+
Service int `json:"service"`
5960
AuthUsername string `json:"auth_username"`
6061
AuthPassword string `json:"auth_password"`
62+
AuthToken string `json:"auth_token"`
6163
// required: true
6264
UID int64 `json:"uid" binding:"Required"`
6365
// required: true

modules/migrations/base/downloader.go

+7-1
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,20 @@ package base
77

88
import (
99
"context"
10+
"io"
1011
"time"
1112

1213
"code.gitea.io/gitea/modules/structs"
1314
)
1415

16+
// AssetDownloader downloads an asset (attachment) for a release
17+
type AssetDownloader interface {
18+
GetAsset(tag string, id int64) (io.ReadCloser, error)
19+
}
20+
1521
// Downloader downloads the site repo informations
1622
type Downloader interface {
23+
AssetDownloader
1724
SetContext(context.Context)
1825
GetRepoInfo() (*Repository, error)
1926
GetTopics() ([]string, error)
@@ -28,7 +35,6 @@ type Downloader interface {
2835

2936
// DownloaderFactory defines an interface to match a downloader implementation and create a downloader
3037
type DownloaderFactory interface {
31-
Match(opts MigrateOptions) (bool, error)
3238
New(opts MigrateOptions) (Downloader, error)
3339
GitServiceType() structs.GitServiceType
3440
}

modules/migrations/base/release.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import "time"
88

99
// ReleaseAsset represents a release asset
1010
type ReleaseAsset struct {
11-
URL string
11+
ID int64
1212
Name string
1313
ContentType *string
1414
Size *int

modules/migrations/base/uploader.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ type Uploader interface {
1111
CreateRepo(repo *Repository, opts MigrateOptions) error
1212
CreateTopics(topic ...string) error
1313
CreateMilestones(milestones ...*Milestone) error
14-
CreateReleases(releases ...*Release) error
14+
CreateReleases(downloader Downloader, releases ...*Release) error
1515
SyncTags() error
1616
CreateLabels(labels ...*Label) error
1717
CreateIssues(issues ...*Issue) error

modules/migrations/git.go

+6
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package migrations
66

77
import (
88
"context"
9+
"io"
910

1011
"code.gitea.io/gitea/modules/migrations/base"
1112
)
@@ -64,6 +65,11 @@ func (g *PlainGitDownloader) GetReleases() ([]*base.Release, error) {
6465
return nil, ErrNotSupported
6566
}
6667

68+
// GetAsset returns an asset
69+
func (g *PlainGitDownloader) GetAsset(_ string, _ int64) (io.ReadCloser, error) {
70+
return nil, ErrNotSupported
71+
}
72+
6773
// GetIssues returns issues according page and perPage
6874
func (g *PlainGitDownloader) GetIssues(page, perPage int) ([]*base.Issue, bool, error) {
6975
return nil, false, ErrNotSupported

modules/migrations/gitea.go

+7-6
Original file line numberDiff line numberDiff line change
@@ -93,12 +93,15 @@ func (g *GiteaLocalUploader) CreateRepo(repo *base.Repository, opts base.Migrate
9393
}
9494

9595
var remoteAddr = repo.CloneURL
96-
if len(opts.AuthUsername) > 0 {
96+
if len(opts.AuthToken) > 0 || len(opts.AuthUsername) > 0 {
9797
u, err := url.Parse(repo.CloneURL)
9898
if err != nil {
9999
return err
100100
}
101101
u.User = url.UserPassword(opts.AuthUsername, opts.AuthPassword)
102+
if len(opts.AuthToken) > 0 {
103+
u.User = url.UserPassword("oauth2", opts.AuthToken)
104+
}
102105
remoteAddr = u.String()
103106
}
104107

@@ -210,7 +213,7 @@ func (g *GiteaLocalUploader) CreateLabels(labels ...*base.Label) error {
210213
}
211214

212215
// CreateReleases creates releases
213-
func (g *GiteaLocalUploader) CreateReleases(releases ...*base.Release) error {
216+
func (g *GiteaLocalUploader) CreateReleases(downloader base.Downloader, releases ...*base.Release) error {
214217
var rels = make([]*models.Release, 0, len(releases))
215218
for _, release := range releases {
216219
var rel = models.Release{
@@ -269,13 +272,11 @@ func (g *GiteaLocalUploader) CreateReleases(releases ...*base.Release) error {
269272

270273
// download attachment
271274
err = func() error {
272-
resp, err := http.Get(asset.URL)
275+
rc, err := downloader.GetAsset(rel.TagName, asset.ID)
273276
if err != nil {
274277
return err
275278
}
276-
defer resp.Body.Close()
277-
278-
_, err = storage.Attachments.Save(attach.RelativePath(), resp.Body)
279+
_, err = storage.Attachments.Save(attach.RelativePath(), rc)
279280
return err
280281
}()
281282
if err != nil {

modules/migrations/gitea_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ func TestGiteaUploadRepo(t *testing.T) {
2626
user := models.AssertExistsAndLoadBean(t, &models.User{ID: 1}).(*models.User)
2727

2828
var (
29-
downloader = NewGithubDownloaderV3("", "", "go-xorm", "builder")
29+
downloader = NewGithubDownloaderV3("", "", "", "go-xorm", "builder")
3030
repoName = "builder-" + time.Now().Format("2006-01-02-15-04-05")
3131
uploader = NewGiteaLocalUploader(graceful.GetManager().HammerContext(), user, user.Name, repoName)
3232
)

modules/migrations/github.go

+31-32
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,11 @@
66
package migrations
77

88
import (
9+
"bytes"
910
"context"
1011
"fmt"
12+
"io"
13+
"io/ioutil"
1114
"net/http"
1215
"net/url"
1316
"strings"
@@ -37,16 +40,6 @@ func init() {
3740
type GithubDownloaderV3Factory struct {
3841
}
3942

40-
// Match returns ture if the migration remote URL matched this downloader factory
41-
func (f *GithubDownloaderV3Factory) Match(opts base.MigrateOptions) (bool, error) {
42-
u, err := url.Parse(opts.CloneAddr)
43-
if err != nil {
44-
return false, err
45-
}
46-
47-
return strings.EqualFold(u.Host, "github.com") && opts.AuthUsername != "", nil
48-
}
49-
5043
// New returns a Downloader related to this factory according MigrateOptions
5144
func (f *GithubDownloaderV3Factory) New(opts base.MigrateOptions) (base.Downloader, error) {
5245
u, err := url.Parse(opts.CloneAddr)
@@ -60,7 +53,7 @@ func (f *GithubDownloaderV3Factory) New(opts base.MigrateOptions) (base.Download
6053

6154
log.Trace("Create github downloader: %s/%s", oldOwner, oldName)
6255

63-
return NewGithubDownloaderV3(opts.AuthUsername, opts.AuthPassword, oldOwner, oldName), nil
56+
return NewGithubDownloaderV3(opts.AuthUsername, opts.AuthPassword, opts.AuthToken, oldOwner, oldName), nil
6457
}
6558

6659
// GitServiceType returns the type of git service
@@ -81,7 +74,7 @@ type GithubDownloaderV3 struct {
8174
}
8275

8376
// NewGithubDownloaderV3 creates a github Downloader via github v3 API
84-
func NewGithubDownloaderV3(userName, password, repoOwner, repoName string) *GithubDownloaderV3 {
77+
func NewGithubDownloaderV3(userName, password, token, repoOwner, repoName string) *GithubDownloaderV3 {
8578
var downloader = GithubDownloaderV3{
8679
userName: userName,
8780
password: password,
@@ -90,23 +83,19 @@ func NewGithubDownloaderV3(userName, password, repoOwner, repoName string) *Gith
9083
repoName: repoName,
9184
}
9285

93-
var client *http.Client
94-
if userName != "" {
95-
if password == "" {
96-
ts := oauth2.StaticTokenSource(
97-
&oauth2.Token{AccessToken: userName},
98-
)
99-
client = oauth2.NewClient(downloader.ctx, ts)
100-
} else {
101-
client = &http.Client{
102-
Transport: &http.Transport{
103-
Proxy: func(req *http.Request) (*url.URL, error) {
104-
req.SetBasicAuth(userName, password)
105-
return nil, nil
106-
},
107-
},
108-
}
109-
}
86+
client := &http.Client{
87+
Transport: &http.Transport{
88+
Proxy: func(req *http.Request) (*url.URL, error) {
89+
req.SetBasicAuth(userName, password)
90+
return nil, nil
91+
},
92+
},
93+
}
94+
if token != "" {
95+
ts := oauth2.StaticTokenSource(
96+
&oauth2.Token{AccessToken: token},
97+
)
98+
client = oauth2.NewClient(downloader.ctx, ts)
11099
}
111100
downloader.client = github.NewClient(client)
112101
return &downloader
@@ -290,10 +279,8 @@ func (g *GithubDownloaderV3) convertGithubRelease(rel *github.RepositoryRelease)
290279
}
291280

292281
for _, asset := range rel.Assets {
293-
u, _ := url.Parse(*asset.BrowserDownloadURL)
294-
u.User = url.UserPassword(g.userName, g.password)
295282
r.Assets = append(r.Assets, base.ReleaseAsset{
296-
URL: u.String(),
283+
ID: *asset.ID,
297284
Name: *asset.Name,
298285
ContentType: asset.ContentType,
299286
Size: asset.Size,
@@ -331,6 +318,18 @@ func (g *GithubDownloaderV3) GetReleases() ([]*base.Release, error) {
331318
return releases, nil
332319
}
333320

321+
// GetAsset returns an asset
322+
func (g *GithubDownloaderV3) GetAsset(_ string, id int64) (io.ReadCloser, error) {
323+
asset, redir, err := g.client.Repositories.DownloadReleaseAsset(g.ctx, g.repoOwner, g.repoName, id, http.DefaultClient)
324+
if err != nil {
325+
return nil, err
326+
}
327+
if asset == nil {
328+
return ioutil.NopCloser(bytes.NewBufferString(redir)), nil
329+
}
330+
return asset, nil
331+
}
332+
334333
// GetIssues returns issues according start and limit
335334
func (g *GithubDownloaderV3) GetIssues(page, perPage int) ([]*base.Issue, bool, error) {
336335
opt := &github.IssueListByRepoOptions{

modules/migrations/github_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ func assertLabelEqual(t *testing.T, name, color, description string, label *base
6464

6565
func TestGitHubDownloadRepo(t *testing.T) {
6666
GithubLimitRateRemaining = 3 //Wait at 3 remaining since we could have 3 CI in //
67-
downloader := NewGithubDownloaderV3(os.Getenv("GITHUB_READ_TOKEN"), "", "go-gitea", "test_repo")
67+
downloader := NewGithubDownloaderV3("", "", os.Getenv("GITHUB_READ_TOKEN"), "go-gitea", "test_repo")
6868
err := downloader.RefreshRate()
6969
assert.NoError(t, err)
7070

modules/migrations/gitlab.go

+30-27
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import (
88
"context"
99
"errors"
1010
"fmt"
11+
"io"
12+
"net/http"
1113
"net/url"
1214
"strings"
1315
"time"
@@ -32,21 +34,6 @@ func init() {
3234
type GitlabDownloaderFactory struct {
3335
}
3436

35-
// Match returns true if the migration remote URL matched this downloader factory
36-
func (f *GitlabDownloaderFactory) Match(opts base.MigrateOptions) (bool, error) {
37-
var matched bool
38-
39-
u, err := url.Parse(opts.CloneAddr)
40-
if err != nil {
41-
return false, err
42-
}
43-
if strings.EqualFold(u.Host, "gitlab.com") && opts.AuthUsername != "" {
44-
matched = true
45-
}
46-
47-
return matched, nil
48-
}
49-
5037
// New returns a Downloader related to this factory according MigrateOptions
5138
func (f *GitlabDownloaderFactory) New(opts base.MigrateOptions) (base.Downloader, error) {
5239
u, err := url.Parse(opts.CloneAddr)
@@ -56,10 +43,11 @@ func (f *GitlabDownloaderFactory) New(opts base.MigrateOptions) (base.Downloader
5643

5744
baseURL := u.Scheme + "://" + u.Host
5845
repoNameSpace := strings.TrimPrefix(u.Path, "/")
46+
repoNameSpace = strings.TrimSuffix(repoNameSpace, ".git")
5947

6048
log.Trace("Create gitlab downloader. BaseURL: %s RepoName: %s", baseURL, repoNameSpace)
6149

62-
return NewGitlabDownloader(baseURL, repoNameSpace, opts.AuthUsername, opts.AuthPassword), nil
50+
return NewGitlabDownloader(baseURL, repoNameSpace, opts.AuthUsername, opts.AuthPassword, opts.AuthToken), nil
6351
}
6452

6553
// GitServiceType returns the type of git service
@@ -85,15 +73,13 @@ type GitlabDownloader struct {
8573
// NewGitlabDownloader creates a gitlab Downloader via gitlab API
8674
// Use either a username/password, personal token entered into the username field, or anonymous/public access
8775
// Note: Public access only allows very basic access
88-
func NewGitlabDownloader(baseURL, repoPath, username, password string) *GitlabDownloader {
76+
func NewGitlabDownloader(baseURL, repoPath, username, password, token string) *GitlabDownloader {
8977
var gitlabClient *gitlab.Client
9078
var err error
91-
if username != "" {
92-
if password == "" {
93-
gitlabClient, err = gitlab.NewClient(username, gitlab.WithBaseURL(baseURL))
94-
} else {
95-
gitlabClient, err = gitlab.NewBasicAuthClient(username, password, gitlab.WithBaseURL(baseURL))
96-
}
79+
if token != "" {
80+
gitlabClient, err = gitlab.NewClient(token, gitlab.WithBaseURL(baseURL))
81+
} else {
82+
gitlabClient, err = gitlab.NewBasicAuthClient(username, password, gitlab.WithBaseURL(baseURL))
9783
}
9884

9985
if err != nil {
@@ -271,7 +257,7 @@ func (g *GitlabDownloader) GetLabels() ([]*base.Label, error) {
271257
}
272258

273259
func (g *GitlabDownloader) convertGitlabRelease(rel *gitlab.Release) *base.Release {
274-
260+
var zero int
275261
r := &base.Release{
276262
TagName: rel.TagName,
277263
TargetCommitish: rel.Commit.ID,
@@ -284,9 +270,11 @@ func (g *GitlabDownloader) convertGitlabRelease(rel *gitlab.Release) *base.Relea
284270

285271
for k, asset := range rel.Assets.Links {
286272
r.Assets = append(r.Assets, base.ReleaseAsset{
287-
URL: asset.URL,
288-
Name: asset.Name,
289-
ContentType: &rel.Assets.Sources[k].Format,
273+
ID: int64(asset.ID),
274+
Name: asset.Name,
275+
ContentType: &rel.Assets.Sources[k].Format,
276+
Size: &zero,
277+
DownloadCount: &zero,
290278
})
291279
}
292280
return r
@@ -315,6 +303,21 @@ func (g *GitlabDownloader) GetReleases() ([]*base.Release, error) {
315303
return releases, nil
316304
}
317305

306+
// GetAsset returns an asset
307+
func (g *GitlabDownloader) GetAsset(tag string, id int64) (io.ReadCloser, error) {
308+
link, _, err := g.client.ReleaseLinks.GetReleaseLink(g.repoID, tag, int(id))
309+
if err != nil {
310+
return nil, err
311+
}
312+
resp, err := http.Get(link.URL)
313+
if err != nil {
314+
return nil, err
315+
}
316+
317+
// resp.Body is closed by the uploader
318+
return resp.Body, nil
319+
}
320+
318321
// GetIssues returns issues according start and limit
319322
// Note: issue label description and colors are not supported by the go-gitlab library at this time
320323
// TODO: figure out how to transfer issue reactions

modules/migrations/gitlab_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ func TestGitlabDownloadRepo(t *testing.T) {
2727
t.Skipf("Can't access test repo, skipping %s", t.Name())
2828
}
2929

30-
downloader := NewGitlabDownloader("https://gitlab.com", "gitea/test_repo", gitlabPersonalAccessToken, "")
30+
downloader := NewGitlabDownloader("https://gitlab.com", "gitea/test_repo", "", "", gitlabPersonalAccessToken)
3131
if downloader == nil {
3232
t.Fatal("NewGitlabDownloader is nil")
3333
}

0 commit comments

Comments
 (0)