Skip to content

Commit 9dd95f5

Browse files
committed
add DownloaderFactory and docs about how to create a new Downloader
1 parent 7040908 commit 9dd95f5

File tree

6 files changed

+157
-63
lines changed

6 files changed

+157
-63
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
---
2+
date: "2019-04-15T17:29:00+08:00"
3+
title: "Advanced: Migrations Interfaces"
4+
slug: "migrations-interfaces"
5+
weight: 30
6+
toc: true
7+
draft: false
8+
menu:
9+
sidebar:
10+
parent: "advanced"
11+
name: "Migrations Interfaces"
12+
weight: 55
13+
identifier: "migrations-interfaces"
14+
---
15+
16+
# Migration Features
17+
18+
The new migration features introduced in Gitea 1.9.0. It defined two interfaces to support migrating
19+
repositories data from other git host platforms to gitea or in future migrating gitea data to other
20+
git host platform. Currently, it only implements to migrate from github via APIv3 to Gitea.
21+
22+
First of all, Gitea defines some standard objects, `Repository`, `Milestone`, `Release`, `Label`, `Issue`,
23+
`Comment`, `PullRequest`.
24+
25+
## Downloader Interfaces
26+
27+
To migrate from a new git host platform, there are two steps to be updated.
28+
29+
- You should implement a Downloader which could get repository all kinds of informations.
30+
- You should implement a DownloaderFactory which could detect if the url should match the
31+
factory and new a Downloader
32+
- You should RegisterDownloaderFactory when init
33+
34+
```Go
35+
type Downloader interface {
36+
GetRepoInfo() (*Repository, error)
37+
GetMilestones() ([]*Milestone, error)
38+
GetReleases() ([]*Release, error)
39+
GetLabels() ([]*Label, error)
40+
GetIssues(start, limit int) ([]*Issue, error)
41+
GetComments(issueNumber int64) ([]*Comment, error)
42+
GetPullRequests(start, limit int) ([]*PullRequest, error)
43+
}
44+
```
45+
46+
```Go
47+
type DownloaderFactory interface {
48+
Match(opts MigrateOptions) (bool, error)
49+
New(opts MigrateOptions) (Downloader, error)
50+
}
51+
```
52+
53+
## Uploader Interface
54+
55+
Currently, we only implemented an Uploader `GiteaLocalUploader` so we only save downloaded
56+
data via this `Uploader` and we haven't supported a new Uploader that will be in future.
57+
58+
```Go
59+
// Uploader uploads all the informations
60+
type Uploader interface {
61+
CreateRepo(repo *Repository, includeWiki bool) error
62+
CreateMilestone(milestone *Milestone) error
63+
CreateRelease(release *Release) error
64+
CreateLabel(label *Label) error
65+
CreateIssue(issue *Issue) error
66+
CreateComment(issueNumber int64, comment *Comment) error
67+
CreatePullRequest(pr *PullRequest) error
68+
Rollback() error
69+
}
70+
71+
```

modules/migrations/base/downloader.go

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// Copyright 2019 The Gitea Authors. All rights reserved.
2+
// Copyright 2018 Jonas Franz. All rights reserved.
3+
// Use of this source code is governed by a MIT-style
4+
// license that can be found in the LICENSE file.
5+
6+
package base
7+
8+
// Downloader downloads the site repo informations
9+
type Downloader interface {
10+
GetRepoInfo() (*Repository, error)
11+
GetMilestones() ([]*Milestone, error)
12+
GetReleases() ([]*Release, error)
13+
GetLabels() ([]*Label, error)
14+
GetIssues(start, limit int) ([]*Issue, error)
15+
GetComments(issueNumber int64) ([]*Comment, error)
16+
GetPullRequests(start, limit int) ([]*PullRequest, error)
17+
}
18+
19+
// DownloaderFactory defines an interface to match a downloader implementation and create a downloader
20+
type DownloaderFactory interface {
21+
Match(opts MigrateOptions) (bool, error)
22+
New(opts MigrateOptions) (Downloader, error)
23+
}

modules/migrations/options.go renamed to modules/migrations/base/options.go

+1-31
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,7 @@
33
// Use of this source code is governed by a MIT-style
44
// license that can be found in the LICENSE file.
55

6-
package migrations
7-
8-
import "net/url"
9-
10-
// MigrationSource represents a migration source
11-
type MigrationSource string
12-
13-
// enumerates all MigrationSources
14-
const (
15-
MigrateFromPlainGit = "git"
16-
MigrateFromGithub MigrationSource = "github"
17-
)
6+
package base
187

198
// MigrateOptions defines the way a repository gets migrated
209
type MigrateOptions struct {
@@ -35,22 +24,3 @@ type MigrateOptions struct {
3524
Mirror bool
3625
IgnoreIssueAuthor bool // if true will not add original author information before issues or comments content.
3726
}
38-
39-
// Source returns the migration source
40-
func (opts MigrateOptions) Source() (MigrationSource, error) {
41-
u, err := url.Parse(opts.RemoteURL)
42-
if err != nil {
43-
return "", err
44-
}
45-
46-
switch u.Host {
47-
case "github.com":
48-
return MigrateFromGithub, nil
49-
}
50-
return MigrateFromPlainGit, nil
51-
}
52-
53-
// URL return remote URL
54-
func (opts MigrateOptions) URL() (*url.URL, error) {
55-
return url.Parse(opts.RemoteURL)
56-
}

modules/migrations/base/interface.go renamed to modules/migrations/base/uploader.go

-11
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,6 @@
55

66
package base
77

8-
// Downloader downloads the site repo informations
9-
type Downloader interface {
10-
GetRepoInfo() (*Repository, error)
11-
GetMilestones() ([]*Milestone, error)
12-
GetReleases() ([]*Release, error)
13-
GetLabels() ([]*Label, error)
14-
GetIssues(start, limit int) ([]*Issue, error)
15-
GetComments(issueNumber int64) ([]*Comment, error)
16-
GetPullRequests(start, limit int) ([]*PullRequest, error)
17-
}
18-
198
// Uploader uploads all the informations
209
type Uploader interface {
2110
CreateRepo(repo *Repository, includeWiki bool) error

modules/migrations/github.go

+38-1
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,54 @@ import (
1010
"fmt"
1111
"net/http"
1212
"net/url"
13+
"strings"
1314

15+
"code.gitea.io/gitea/modules/log"
1416
"code.gitea.io/gitea/modules/migrations/base"
1517

1618
"github.com/google/go-github/v24/github"
1719
"golang.org/x/oauth2"
1820
)
1921

2022
var (
21-
_ base.Downloader = &GithubDownloaderV3{}
23+
_ base.Downloader = &GithubDownloaderV3{}
24+
_ base.DownloaderFactory = &GithubDownloaderV3Factory{}
2225
)
2326

27+
func init() {
28+
RegisterDownloaderFactory(&GithubDownloaderV3Factory{})
29+
}
30+
31+
// GithubDownloaderV3Factory defines a github downloader v3 factory
32+
type GithubDownloaderV3Factory struct {
33+
}
34+
35+
// Match returns ture if the migration remote URL matched this downloader factory
36+
func (f *GithubDownloaderV3Factory) Match(opts base.MigrateOptions) (bool, error) {
37+
u, err := url.Parse(opts.RemoteURL)
38+
if err != nil {
39+
return false, err
40+
}
41+
42+
return u.Host == "github.com" && opts.AuthUsername != "", nil
43+
}
44+
45+
// New returns a Downloader related to this factory according MigrateOptions
46+
func (f *GithubDownloaderV3Factory) New(opts base.MigrateOptions) (base.Downloader, error) {
47+
u, err := url.Parse(opts.RemoteURL)
48+
if err != nil {
49+
return nil, err
50+
}
51+
52+
fields := strings.Split(u.Path, "/")
53+
oldOwner := fields[1]
54+
oldName := strings.TrimSuffix(fields[2], ".git")
55+
56+
log.Trace("Create github downloader: %s/%s", oldOwner, oldName)
57+
58+
return NewGithubDownloaderV3(opts.AuthUsername, opts.AuthPassword, oldOwner, oldName), nil
59+
}
60+
2461
// GithubDownloaderV3 implements a Downloader interface to get repository informations
2562
// from github via APIv3
2663
type GithubDownloaderV3 struct {

modules/migrations/migrate.go

+24-20
Original file line numberDiff line numberDiff line change
@@ -7,39 +7,43 @@ package migrations
77

88
import (
99
"fmt"
10-
"strings"
1110

1211
"code.gitea.io/gitea/models"
1312
"code.gitea.io/gitea/modules/log"
1413
"code.gitea.io/gitea/modules/migrations/base"
1514
)
1615

17-
// MigrateRepository migrate repository according MigrateOptions
18-
func MigrateRepository(doer *models.User, ownerName string, opts MigrateOptions) (*models.Repository, error) {
19-
source, err := opts.Source()
20-
if err != nil {
21-
return nil, err
22-
}
23-
url, err := opts.URL()
24-
if err != nil {
25-
return nil, err
26-
}
16+
// MigrateOptions is equal to base.MigrateOptions
17+
type MigrateOptions = base.MigrateOptions
2718

19+
var (
20+
factorys []base.DownloaderFactory
21+
)
22+
23+
// RegisterDownloaderFactory registers a downloader factory
24+
func RegisterDownloaderFactory(factory base.DownloaderFactory) {
25+
factorys = append(factorys, factory)
26+
}
27+
28+
// MigrateRepository migrate repository according MigrateOptions
29+
func MigrateRepository(doer *models.User, ownerName string, opts base.MigrateOptions) (*models.Repository, error) {
2830
var (
2931
downloader base.Downloader
3032
uploader = NewGiteaLocalUploader(doer, ownerName, opts.Name)
3133
)
3234

33-
switch source {
34-
case MigrateFromGithub:
35-
if opts.AuthUsername != "" {
36-
fields := strings.Split(url.Path, "/")
37-
oldOwner := fields[1]
38-
oldName := strings.TrimSuffix(fields[2], ".git")
39-
downloader = NewGithubDownloaderV3(opts.AuthUsername, opts.AuthPassword, oldOwner, oldName)
40-
log.Trace("Will migrate from github: %s/%s", oldOwner, oldName)
35+
for _, factory := range factorys {
36+
if match, err := factory.Match(opts); err != nil {
37+
return nil, err
38+
} else if match {
39+
downloader, err = factory.New(opts)
40+
if err != nil {
41+
return nil, err
42+
}
43+
break
4144
}
4245
}
46+
4347
if downloader == nil {
4448
opts.Wiki = true
4549
opts.Milestones = false
@@ -65,7 +69,7 @@ func MigrateRepository(doer *models.User, ownerName string, opts MigrateOptions)
6569
// migrateRepository will download informations and upload to Uploader, this is a simple
6670
// process for small repository. For a big repository, save all the data to disk
6771
// before upload is better
68-
func migrateRepository(downloader base.Downloader, uploader base.Uploader, opts MigrateOptions) error {
72+
func migrateRepository(downloader base.Downloader, uploader base.Uploader, opts base.MigrateOptions) error {
6973
repo, err := downloader.GetRepoInfo()
7074
if err != nil {
7175
return err

0 commit comments

Comments
 (0)