gitea源码

dump_repo.go 5.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. // Copyright 2020 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package cmd
  4. import (
  5. "context"
  6. "errors"
  7. "fmt"
  8. "os"
  9. "strings"
  10. "code.gitea.io/gitea/modules/git"
  11. "code.gitea.io/gitea/modules/log"
  12. base "code.gitea.io/gitea/modules/migration"
  13. "code.gitea.io/gitea/modules/setting"
  14. "code.gitea.io/gitea/modules/structs"
  15. "code.gitea.io/gitea/modules/util"
  16. "code.gitea.io/gitea/services/convert"
  17. "code.gitea.io/gitea/services/migrations"
  18. "github.com/urfave/cli/v3"
  19. )
  20. // CmdDumpRepository represents the available dump repository sub-command.
  21. var CmdDumpRepository = &cli.Command{
  22. Name: "dump-repo",
  23. Usage: "Dump the repository from git/github/gitea/gitlab",
  24. Description: "This is a command for dumping the repository data.",
  25. Action: runDumpRepository,
  26. Flags: []cli.Flag{
  27. &cli.StringFlag{
  28. Name: "git_service",
  29. Value: "",
  30. Usage: "Git service, git, github, gitea, gitlab. If clone_addr could be recognized, this could be ignored.",
  31. },
  32. &cli.StringFlag{
  33. Name: "repo_dir",
  34. Aliases: []string{"r"},
  35. Value: "./data",
  36. Usage: "Repository dir path to store the data",
  37. },
  38. &cli.StringFlag{
  39. Name: "clone_addr",
  40. Value: "",
  41. Usage: "The URL will be clone, currently could be a git/github/gitea/gitlab http/https URL",
  42. },
  43. &cli.StringFlag{
  44. Name: "auth_username",
  45. Value: "",
  46. Usage: "The username to visit the clone_addr",
  47. },
  48. &cli.StringFlag{
  49. Name: "auth_password",
  50. Value: "",
  51. Usage: "The password to visit the clone_addr",
  52. },
  53. &cli.StringFlag{
  54. Name: "auth_token",
  55. Value: "",
  56. Usage: "The personal token to visit the clone_addr",
  57. },
  58. &cli.StringFlag{
  59. Name: "owner_name",
  60. Value: "",
  61. Usage: "The data will be stored on a directory with owner name if not empty",
  62. },
  63. &cli.StringFlag{
  64. Name: "repo_name",
  65. Value: "",
  66. Usage: "The data will be stored on a directory with repository name if not empty",
  67. },
  68. &cli.StringFlag{
  69. Name: "units",
  70. Value: "",
  71. Usage: `Which items will be migrated, one or more units should be separated as comma.
  72. wiki, issues, labels, releases, release_assets, milestones, pull_requests, comments are allowed. Empty means all units.`,
  73. },
  74. },
  75. }
  76. func runDumpRepository(ctx context.Context, cmd *cli.Command) error {
  77. setupConsoleLogger(log.INFO, log.CanColorStderr, os.Stderr)
  78. setting.DisableLoggerInit()
  79. setting.LoadSettings() // cannot access skip_tls_verify settings otherwise
  80. if err := initDB(ctx); err != nil {
  81. return err
  82. }
  83. // migrations.GiteaLocalUploader depends on git module
  84. if err := git.InitSimple(); err != nil {
  85. return err
  86. }
  87. log.Info("AppPath: %s", setting.AppPath)
  88. log.Info("AppWorkPath: %s", setting.AppWorkPath)
  89. log.Info("Custom path: %s", setting.CustomPath)
  90. log.Info("Log path: %s", setting.Log.RootPath)
  91. log.Info("Configuration file: %s", setting.CustomConf)
  92. var (
  93. serviceType structs.GitServiceType
  94. cloneAddr = cmd.String("clone_addr")
  95. serviceStr = cmd.String("git_service")
  96. )
  97. if strings.HasPrefix(strings.ToLower(cloneAddr), "https://github.com/") {
  98. serviceStr = "github"
  99. } else if strings.HasPrefix(strings.ToLower(cloneAddr), "https://gitlab.com/") {
  100. serviceStr = "gitlab"
  101. } else if strings.HasPrefix(strings.ToLower(cloneAddr), "https://gitea.com/") {
  102. serviceStr = "gitea"
  103. }
  104. if serviceStr == "" {
  105. return errors.New("git_service missed or clone_addr cannot be recognized")
  106. }
  107. serviceType = convert.ToGitServiceType(serviceStr)
  108. opts := base.MigrateOptions{
  109. GitServiceType: serviceType,
  110. CloneAddr: cloneAddr,
  111. AuthUsername: cmd.String("auth_username"),
  112. AuthPassword: cmd.String("auth_password"),
  113. AuthToken: cmd.String("auth_token"),
  114. RepoName: cmd.String("repo_name"),
  115. }
  116. if len(cmd.String("units")) == 0 {
  117. opts.Wiki = true
  118. opts.Issues = true
  119. opts.Milestones = true
  120. opts.Labels = true
  121. opts.Releases = true
  122. opts.Comments = true
  123. opts.PullRequests = true
  124. opts.ReleaseAssets = true
  125. } else {
  126. units := strings.SplitSeq(cmd.String("units"), ",")
  127. for unit := range units {
  128. switch strings.ToLower(strings.TrimSpace(unit)) {
  129. case "":
  130. continue
  131. case "wiki":
  132. opts.Wiki = true
  133. case "issues":
  134. opts.Issues = true
  135. case "milestones":
  136. opts.Milestones = true
  137. case "labels":
  138. opts.Labels = true
  139. case "releases":
  140. opts.Releases = true
  141. case "release_assets":
  142. opts.ReleaseAssets = true
  143. case "comments":
  144. opts.Comments = true
  145. case "pull_requests":
  146. opts.PullRequests = true
  147. default:
  148. return errors.New("invalid unit: " + unit)
  149. }
  150. }
  151. }
  152. // the repo_dir will be removed if error occurs in DumpRepository
  153. // make sure the directory doesn't exist or is empty, prevent from deleting user files
  154. repoDir := cmd.String("repo_dir")
  155. if exists, err := util.IsExist(repoDir); err != nil {
  156. return fmt.Errorf("unable to stat repo_dir %q: %w", repoDir, err)
  157. } else if exists {
  158. if isDir, _ := util.IsDir(repoDir); !isDir {
  159. return fmt.Errorf("repo_dir %q already exists but it's not a directory", repoDir)
  160. }
  161. if dir, _ := os.ReadDir(repoDir); len(dir) > 0 {
  162. return fmt.Errorf("repo_dir %q is not empty", repoDir)
  163. }
  164. }
  165. if err := migrations.DumpRepository(
  166. ctx,
  167. repoDir,
  168. cmd.String("owner_name"),
  169. opts,
  170. ); err != nil {
  171. log.Fatal("Failed to dump repository: %v", err)
  172. return err
  173. }
  174. log.Trace("Dump finished!!!")
  175. return nil
  176. }