2020-12-27 06:34:19 +03:00
// Copyright 2020 The Gitea Authors. All rights reserved.
2022-11-27 21:20:29 +03:00
// SPDX-License-Identifier: MIT
2020-12-27 06:34:19 +03:00
package cmd
import (
"context"
"errors"
2022-07-14 16:52:18 +03:00
"fmt"
"os"
2020-12-27 06:34:19 +03:00
"strings"
2022-07-01 02:48:25 +03:00
"code.gitea.io/gitea/modules/git"
2020-12-27 06:34:19 +03:00
"code.gitea.io/gitea/modules/log"
2021-11-16 18:25:33 +03:00
base "code.gitea.io/gitea/modules/migration"
2020-12-27 06:34:19 +03:00
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/structs"
2022-07-14 16:52:18 +03:00
"code.gitea.io/gitea/modules/util"
2022-12-29 05:57:15 +03:00
"code.gitea.io/gitea/services/convert"
2021-11-16 18:25:33 +03:00
"code.gitea.io/gitea/services/migrations"
2020-12-27 06:34:19 +03:00
"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 : "" ,
2021-06-27 03:56:58 +03:00
Usage : ` Which items will be migrated , one or more units should be separated as comma .
2020-12-27 06:34:19 +03:00
wiki , issues , labels , releases , release_assets , milestones , pull_requests , comments are allowed . Empty means all units . ` ,
} ,
} ,
}
func runDumpRepository ( ctx * cli . Context ) error {
2021-11-07 06:11:27 +03:00
stdCtx , cancel := installSignals ( )
defer cancel ( )
if err := initDB ( stdCtx ) ; err != nil {
2020-12-27 06:34:19 +03:00
return err
}
2022-07-01 02:48:25 +03:00
// migrations.GiteaLocalUploader depends on git module
if err := git . InitSimple ( context . Background ( ) ) ; err != nil {
return err
}
2021-06-27 03:56:58 +03:00
log . Info ( "AppPath: %s" , setting . AppPath )
log . Info ( "AppWorkPath: %s" , setting . AppWorkPath )
log . Info ( "Custom path: %s" , setting . CustomPath )
log . Info ( "Log path: %s" , setting . LogRootPath )
2021-09-14 04:24:57 +03:00
log . Info ( "Configuration file: %s" , setting . CustomConf )
2020-12-27 06:34:19 +03:00
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 )
2022-01-20 20:46:10 +03:00
opts := base . MigrateOptions {
2020-12-27 06:34:19 +03:00
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 {
2022-07-01 10:47:44 +03:00
switch strings . ToLower ( strings . TrimSpace ( unit ) ) {
case "" :
continue
2020-12-27 06:34:19 +03:00
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
2022-07-01 10:47:44 +03:00
default :
return errors . New ( "invalid unit: " + unit )
2020-12-27 06:34:19 +03:00
}
}
}
2022-07-14 16:52:18 +03:00
// the repo_dir will be removed if error occurs in DumpRepository
// make sure the directory doesn't exist or is empty, prevent from deleting user files
repoDir := ctx . String ( "repo_dir" )
if exists , err := util . IsExist ( repoDir ) ; err != nil {
2022-10-24 22:29:17 +03:00
return fmt . Errorf ( "unable to stat repo_dir %q: %w" , repoDir , err )
2022-07-14 16:52:18 +03:00
} else if exists {
if isDir , _ := util . IsDir ( repoDir ) ; ! isDir {
return fmt . Errorf ( "repo_dir %q already exists but it's not a directory" , repoDir )
}
if dir , _ := os . ReadDir ( repoDir ) ; len ( dir ) > 0 {
return fmt . Errorf ( "repo_dir %q is not empty" , repoDir )
}
}
2020-12-27 06:34:19 +03:00
if err := migrations . DumpRepository (
context . Background ( ) ,
2022-07-14 16:52:18 +03:00
repoDir ,
2020-12-27 06:34:19 +03:00
ctx . String ( "owner_name" ) ,
opts ,
) ; err != nil {
log . Fatal ( "Failed to dump repository: %v" , err )
return err
}
log . Trace ( "Dump finished!!!" )
return nil
}