Skip to content

Dump github/gitlab/gitea repository data to a local directory and restore to gitea #12244

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 47 commits into from
Dec 27, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
7798250
Dump github/gitlab repository data to a local directory
lunny Jul 14, 2020
9fa7df4
Fix lint
lunny Jul 14, 2020
13d4b3a
Adjust directory structure
lunny Jul 18, 2020
8c3f3db
Allow migration special units
lunny Jul 18, 2020
9e7915c
Allow migration ignore release assets
lunny Jul 18, 2020
ed4e376
Fix lint
lunny Jul 19, 2020
c49020b
Add restore repository
lunny Aug 22, 2020
c13e745
stage the changes
lunny Sep 1, 2020
aa5ffb3
Merge
lunny Sep 11, 2020
92fbde7
Fix lint
lunny Sep 11, 2020
3999010
Update the interface
lunny Oct 18, 2020
6d633ef
Add some restore methods
lunny Oct 20, 2020
7006ba2
Finish restore
lunny Oct 21, 2020
d1746bc
Add comments
lunny Oct 21, 2020
08e7770
Fix restore
lunny Oct 21, 2020
3c7b2d2
Add a token flag
lunny Oct 21, 2020
770aac9
Fix bug
lunny Oct 21, 2020
d367872
Fix test
lunny Oct 21, 2020
bbc289c
Fix test
lunny Oct 22, 2020
4f43c44
Fix bug
lunny Oct 23, 2020
0b7786f
Fix bug
lunny Oct 23, 2020
7cfd9d8
Fix lint
lunny Oct 23, 2020
c43966b
Fix restore
lunny Oct 23, 2020
a8bdf48
refactor downloader
lunny Oct 23, 2020
20cbc64
fmt
lunny Oct 23, 2020
79f0a4a
Fix bug isEnd detection on getIssues
lunny Oct 24, 2020
ad60586
Refactor maxPerPage
lunny Oct 24, 2020
d33175d
Remove unused codes
lunny Oct 24, 2020
04bebd6
Remove unused codes
lunny Oct 24, 2020
af0be0b
Fix bug
lunny Oct 27, 2020
e615c19
Fix restore
lunny Oct 29, 2020
ab53820
Fix dump
lunny Nov 3, 2020
96ca8e4
Uploader should not depend downloader
lunny Nov 23, 2020
3075168
use release attachment name but not id
lunny Dec 21, 2020
97cc82c
Fix restore bug
lunny Dec 22, 2020
6c5044d
Fix lint
lunny Dec 22, 2020
c09c7ed
Fix restore bug
lunny Dec 26, 2020
8f4e741
Add a method of DownloadFunc for base.Release to make uploader not de…
lunny Dec 26, 2020
2124a61
fix Release yml marshal
lunny Dec 26, 2020
2e90a9f
Fix trace information
lunny Dec 26, 2020
2b0fe43
Fix bug when dump & restore
lunny Dec 26, 2020
f91ba44
Save relative path on yml file
lunny Dec 26, 2020
eb5da17
Fix bug
lunny Dec 26, 2020
3c5b2e1
Use relative path
lunny Dec 26, 2020
80b5c9a
Update docs
lunny Dec 26, 2020
ceefd98
Use git service string but not int
lunny Dec 27, 2020
695d23e
Recognize clone addr to service type
lunny Dec 27, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
162 changes: 162 additions & 0 deletions cmd/dump_repo.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
// Copyright 2020 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 cmd

import (
"context"
"errors"
"strings"

"code.gitea.io/gitea/modules/convert"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/migrations"
"code.gitea.io/gitea/modules/migrations/base"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/structs"

"github.com/urfave/cli"
)

// CmdDumpRepository represents the available dump repository sub-command.
var CmdDumpRepository = cli.Command{
Name: "dump-repo",
Usage: "Dump the repository from git/github/gitea/gitlab",
Description: "This is a command for dumping the repository data.",
Action: runDumpRepository,
Flags: []cli.Flag{
cli.StringFlag{
Name: "git_service",
Value: "",
Usage: "Git service, git, github, gitea, gitlab. If clone_addr could be recognized, this could be ignored.",
},
cli.StringFlag{
Name: "repo_dir, r",
Value: "./data",
Usage: "Repository dir path to store the data",
},
cli.StringFlag{
Name: "clone_addr",
Value: "",
Usage: "The URL will be clone, currently could be a git/github/gitea/gitlab http/https URL",
},
cli.StringFlag{
Name: "auth_username",
Value: "",
Usage: "The username to visit the clone_addr",
},
cli.StringFlag{
Name: "auth_password",
Value: "",
Usage: "The password to visit the clone_addr",
},
cli.StringFlag{
Name: "auth_token",
Value: "",
Usage: "The personal token to visit the clone_addr",
},
cli.StringFlag{
Name: "owner_name",
Value: "",
Usage: "The data will be stored on a directory with owner name if not empty",
},
cli.StringFlag{
Name: "repo_name",
Value: "",
Usage: "The data will be stored on a directory with repository name if not empty",
},
cli.StringFlag{
Name: "units",
Value: "",
Usage: `Which items will be migrated, one or more units should be separated as comma.
wiki, issues, labels, releases, release_assets, milestones, pull_requests, comments are allowed. Empty means all units.`,
},
},
}

func runDumpRepository(ctx *cli.Context) error {
if err := initDB(); err != nil {
return err
}

log.Trace("AppPath: %s", setting.AppPath)
log.Trace("AppWorkPath: %s", setting.AppWorkPath)
log.Trace("Custom path: %s", setting.CustomPath)
log.Trace("Log path: %s", setting.LogRootPath)
setting.InitDBConfig()

var (
serviceType structs.GitServiceType
cloneAddr = ctx.String("clone_addr")
serviceStr = ctx.String("git_service")
)

if strings.HasPrefix(strings.ToLower(cloneAddr), "https://github.com/") {
serviceStr = "github"
} else if strings.HasPrefix(strings.ToLower(cloneAddr), "https://gitlab.com/") {
serviceStr = "gitlab"
} else if strings.HasPrefix(strings.ToLower(cloneAddr), "https://gitea.com/") {
serviceStr = "gitea"
}
if serviceStr == "" {
return errors.New("git_service missed or clone_addr cannot be recognized")
}
serviceType = convert.ToGitServiceType(serviceStr)

var opts = base.MigrateOptions{
GitServiceType: serviceType,
CloneAddr: cloneAddr,
AuthUsername: ctx.String("auth_username"),
AuthPassword: ctx.String("auth_password"),
AuthToken: ctx.String("auth_token"),
RepoName: ctx.String("repo_name"),
}

if len(ctx.String("units")) == 0 {
opts.Wiki = true
opts.Issues = true
opts.Milestones = true
opts.Labels = true
opts.Releases = true
opts.Comments = true
opts.PullRequests = true
opts.ReleaseAssets = true
} else {
units := strings.Split(ctx.String("units"), ",")
for _, unit := range units {
switch strings.ToLower(unit) {
case "wiki":
opts.Wiki = true
case "issues":
opts.Issues = true
case "milestones":
opts.Milestones = true
case "labels":
opts.Labels = true
case "releases":
opts.Releases = true
case "release_assets":
opts.ReleaseAssets = true
case "comments":
opts.Comments = true
case "pull_requests":
opts.PullRequests = true
}
}
}

if err := migrations.DumpRepository(
context.Background(),
ctx.String("repo_dir"),
ctx.String("owner_name"),
opts,
); err != nil {
log.Fatal("Failed to dump repository: %v", err)
return err
}

log.Trace("Dump finished!!!")

return nil
}
119 changes: 119 additions & 0 deletions cmd/restore_repo.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
// Copyright 2020 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 cmd

import (
"context"
"strings"

"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/migrations"
"code.gitea.io/gitea/modules/migrations/base"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/storage"
pull_service "code.gitea.io/gitea/services/pull"

"github.com/urfave/cli"
)

// CmdRestoreRepository represents the available restore a repository sub-command.
var CmdRestoreRepository = cli.Command{
Name: "restore-repo",
Usage: "Restore the repository from disk",
Description: "This is a command for restoring the repository data.",
Action: runRestoreRepository,
Flags: []cli.Flag{
cli.StringFlag{
Name: "repo_dir, r",
Value: "./data",
Usage: "Repository dir path to restore from",
},
cli.StringFlag{
Name: "owner_name",
Value: "",
Usage: "Restore destination owner name",
},
cli.StringFlag{
Name: "repo_name",
Value: "",
Usage: "Restore destination repository name",
},
cli.StringFlag{
Name: "units",
Value: "",
Usage: `Which items will be restored, one or more units should be separated as comma.
wiki, issues, labels, releases, release_assets, milestones, pull_requests, comments are allowed. Empty means all units.`,
},
},
}

func runRestoreRepository(ctx *cli.Context) error {
if err := initDB(); err != nil {
return err
}

log.Trace("AppPath: %s", setting.AppPath)
log.Trace("AppWorkPath: %s", setting.AppWorkPath)
log.Trace("Custom path: %s", setting.CustomPath)
log.Trace("Log path: %s", setting.LogRootPath)
setting.InitDBConfig()

if err := storage.Init(); err != nil {
return err
}

if err := pull_service.Init(); err != nil {
return err
}

var opts = base.MigrateOptions{
RepoName: ctx.String("repo_name"),
}

if len(ctx.String("units")) == 0 {
opts.Wiki = true
opts.Issues = true
opts.Milestones = true
opts.Labels = true
opts.Releases = true
opts.Comments = true
opts.PullRequests = true
opts.ReleaseAssets = true
} else {
units := strings.Split(ctx.String("units"), ",")
for _, unit := range units {
switch strings.ToLower(unit) {
case "wiki":
opts.Wiki = true
case "issues":
opts.Issues = true
case "milestones":
opts.Milestones = true
case "labels":
opts.Labels = true
case "releases":
opts.Releases = true
case "release_assets":
opts.ReleaseAssets = true
case "comments":
opts.Comments = true
case "pull_requests":
opts.PullRequests = true
}
}
}

if err := migrations.RestoreRepository(
context.Background(),
ctx.String("repo_dir"),
ctx.String("owner_name"),
ctx.String("repo_name"),
); err != nil {
log.Fatal("Failed to restore repository: %v", err)
return err
}

return nil
}
25 changes: 25 additions & 0 deletions docs/content/doc/usage/command-line.en-us.md
Original file line number Diff line number Diff line change
Expand Up @@ -441,3 +441,28 @@ Manage running server operations:
- `--host value`, `-H value`: Mail server host (defaults to: 127.0.0.1:25)
- `--send-to value`, `-s value`: Email address(es) to send to
- `--subject value`, `-S value`: Subject header of sent emails

### dump-repo

Dump-repo dumps repository data from git/github/gitea/gitlab:

- Options:
- `--git_service service` : Git service, it could be `git`, `github`, `gitea`, `gitlab`, If clone_addr could be recognized, this could be ignored.
- `--repo_dir dir`, `-r dir`: Repository dir path to store the data
- `--clone_addr addr`: The URL will be clone, currently could be a git/github/gitea/gitlab http/https URL. i.e. https://github.com/lunny/tango.git
- `--auth_username lunny`: The username to visit the clone_addr
- `--auth_password <password>`: The password to visit the clone_addr
- `--auth_token <token>`: The personal token to visit the clone_addr
- `--owner_name lunny`: The data will be stored on a directory with owner name if not empty
- `--repo_name tango`: The data will be stored on a directory with repository name if not empty
- `--units <units>`: Which items will be migrated, one or more units should be separated as comma. wiki, issues, labels, releases, release_assets, milestones, pull_requests, comments are allowed. Empty means all units.

### restore-repo

Restore-repo restore repository data from disk dir:

- Options:
- `--repo_dir dir`, `-r dir`: Repository dir path to restore from
- `--owner_name lunny`: Restore destination owner name
- `--repo_name tango`: Restore destination repository name
- `--units <units>`: Which items will be restored, one or more units should be separated as comma. wiki, issues, labels, releases, release_assets, milestones, pull_requests, comments are allowed. Empty means all units.
2 changes: 2 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ arguments - which can alternatively be run by running the subcommand web.`
cmd.Cmdembedded,
cmd.CmdMigrateStorage,
cmd.CmdDocs,
cmd.CmdDumpRepository,
cmd.CmdRestoreRepository,
}
// Now adjust these commands to add our global configuration options

Expand Down
13 changes: 13 additions & 0 deletions models/admin.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,3 +132,16 @@ func DeleteNoticesByIDs(ids []int64) error {
Delete(new(Notice))
return err
}

// GetAdminUser returns the first administrator
func GetAdminUser() (*User, error) {
var admin User
has, err := x.Where("is_admin=?", true).Get(&admin)
if err != nil {
return nil, err
} else if !has {
return nil, ErrUserNotExist{}
}

return &admin, nil
}
4 changes: 0 additions & 4 deletions models/task.go
Original file line number Diff line number Diff line change
Expand Up @@ -211,10 +211,6 @@ func FinishMigrateTask(task *Task) error {
if _, err := sess.ID(task.ID).Cols("status", "end_time").Update(task); err != nil {
return err
}
task.Repo.Status = RepositoryReady
if _, err := sess.ID(task.RepoID).Cols("status").Update(task.Repo); err != nil {
return err
}

return sess.Commit()
}
8 changes: 4 additions & 4 deletions modules/migrations/base/comment.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ import "time"

// Comment is a standard comment information
type Comment struct {
IssueIndex int64
PosterID int64
PosterName string
PosterEmail string
IssueIndex int64 `yaml:"issue_index"`
PosterID int64 `yaml:"poster_id"`
PosterName string `yaml:"poster_name"`
PosterEmail string `yaml:"poster_email"`
Created time.Time
Updated time.Time
Content string
Expand Down
7 changes: 0 additions & 7 deletions modules/migrations/base/downloader.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,13 @@ package base

import (
"context"
"io"
"time"

"code.gitea.io/gitea/modules/structs"
)

// AssetDownloader downloads an asset (attachment) for a release
type AssetDownloader interface {
GetAsset(relTag string, relID, id int64) (io.ReadCloser, error)
}

// Downloader downloads the site repo informations
type Downloader interface {
AssetDownloader
SetContext(context.Context)
GetRepoInfo() (*Repository, error)
GetTopics() ([]string, error)
Expand Down
Loading