gitea源码

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  1. // Copyright 2020 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package migrations
  4. import (
  5. "context"
  6. "fmt"
  7. "os"
  8. "path/filepath"
  9. "strconv"
  10. base "code.gitea.io/gitea/modules/migration"
  11. "gopkg.in/yaml.v3"
  12. )
  13. // RepositoryRestorer implements an Downloader from the local directory
  14. type RepositoryRestorer struct {
  15. base.NullDownloader
  16. baseDir string
  17. repoOwner string
  18. repoName string
  19. validation bool
  20. }
  21. // NewRepositoryRestorer creates a repository restorer which could restore repository from a dumped folder
  22. func NewRepositoryRestorer(_ context.Context, baseDir, owner, repoName string, validation bool) (*RepositoryRestorer, error) {
  23. baseDir, err := filepath.Abs(baseDir)
  24. if err != nil {
  25. return nil, err
  26. }
  27. return &RepositoryRestorer{
  28. baseDir: baseDir,
  29. repoOwner: owner,
  30. repoName: repoName,
  31. validation: validation,
  32. }, nil
  33. }
  34. func (r *RepositoryRestorer) commentDir() string {
  35. return filepath.Join(r.baseDir, "comments")
  36. }
  37. func (r *RepositoryRestorer) reviewDir() string {
  38. return filepath.Join(r.baseDir, "reviews")
  39. }
  40. func (r *RepositoryRestorer) getRepoOptions() (map[string]string, error) {
  41. p := filepath.Join(r.baseDir, "repo.yml")
  42. bs, err := os.ReadFile(p)
  43. if err != nil {
  44. return nil, err
  45. }
  46. opts := make(map[string]string)
  47. err = yaml.Unmarshal(bs, &opts)
  48. if err != nil {
  49. return nil, err
  50. }
  51. return opts, nil
  52. }
  53. // GetRepoInfo returns a repository information
  54. func (r *RepositoryRestorer) GetRepoInfo(_ context.Context) (*base.Repository, error) {
  55. opts, err := r.getRepoOptions()
  56. if err != nil {
  57. return nil, err
  58. }
  59. isPrivate, _ := strconv.ParseBool(opts["is_private"])
  60. return &base.Repository{
  61. Owner: r.repoOwner,
  62. Name: r.repoName,
  63. IsPrivate: isPrivate,
  64. Description: opts["description"],
  65. OriginalURL: opts["original_url"],
  66. CloneURL: filepath.Join(r.baseDir, "git"),
  67. DefaultBranch: opts["default_branch"],
  68. }, nil
  69. }
  70. // GetTopics return github topics
  71. func (r *RepositoryRestorer) GetTopics(_ context.Context) ([]string, error) {
  72. p := filepath.Join(r.baseDir, "topic.yml")
  73. topics := struct {
  74. Topics []string `yaml:"topics"`
  75. }{}
  76. bs, err := os.ReadFile(p)
  77. if err != nil {
  78. if os.IsNotExist(err) {
  79. return nil, nil
  80. }
  81. return nil, err
  82. }
  83. err = yaml.Unmarshal(bs, &topics)
  84. if err != nil {
  85. return nil, err
  86. }
  87. return topics.Topics, nil
  88. }
  89. // GetMilestones returns milestones
  90. func (r *RepositoryRestorer) GetMilestones(_ context.Context) ([]*base.Milestone, error) {
  91. milestones := make([]*base.Milestone, 0, 10)
  92. p := filepath.Join(r.baseDir, "milestone.yml")
  93. err := base.Load(p, &milestones, r.validation)
  94. if err != nil {
  95. if os.IsNotExist(err) {
  96. return nil, nil
  97. }
  98. return nil, err
  99. }
  100. return milestones, nil
  101. }
  102. // GetReleases returns releases
  103. func (r *RepositoryRestorer) GetReleases(_ context.Context) ([]*base.Release, error) {
  104. releases := make([]*base.Release, 0, 10)
  105. p := filepath.Join(r.baseDir, "release.yml")
  106. _, err := os.Stat(p)
  107. if err != nil {
  108. if os.IsNotExist(err) {
  109. return nil, nil
  110. }
  111. return nil, err
  112. }
  113. bs, err := os.ReadFile(p)
  114. if err != nil {
  115. return nil, err
  116. }
  117. err = yaml.Unmarshal(bs, &releases)
  118. if err != nil {
  119. return nil, err
  120. }
  121. for _, rel := range releases {
  122. for _, asset := range rel.Assets {
  123. if asset.DownloadURL != nil {
  124. *asset.DownloadURL = "file://" + filepath.Join(r.baseDir, *asset.DownloadURL)
  125. }
  126. }
  127. }
  128. return releases, nil
  129. }
  130. // GetLabels returns labels
  131. func (r *RepositoryRestorer) GetLabels(_ context.Context) ([]*base.Label, error) {
  132. labels := make([]*base.Label, 0, 10)
  133. p := filepath.Join(r.baseDir, "label.yml")
  134. _, err := os.Stat(p)
  135. if err != nil {
  136. if os.IsNotExist(err) {
  137. return nil, nil
  138. }
  139. return nil, err
  140. }
  141. bs, err := os.ReadFile(p)
  142. if err != nil {
  143. return nil, err
  144. }
  145. err = yaml.Unmarshal(bs, &labels)
  146. if err != nil {
  147. return nil, err
  148. }
  149. return labels, nil
  150. }
  151. // GetIssues returns issues according start and limit
  152. func (r *RepositoryRestorer) GetIssues(_ context.Context, _, _ int) ([]*base.Issue, bool, error) {
  153. issues := make([]*base.Issue, 0, 10)
  154. p := filepath.Join(r.baseDir, "issue.yml")
  155. err := base.Load(p, &issues, r.validation)
  156. if err != nil {
  157. if os.IsNotExist(err) {
  158. return nil, true, nil
  159. }
  160. return nil, false, err
  161. }
  162. return issues, true, nil
  163. }
  164. // GetComments returns comments according issueNumber
  165. func (r *RepositoryRestorer) GetComments(_ context.Context, commentable base.Commentable) ([]*base.Comment, bool, error) {
  166. comments := make([]*base.Comment, 0, 10)
  167. p := filepath.Join(r.commentDir(), fmt.Sprintf("%d.yml", commentable.GetForeignIndex()))
  168. _, err := os.Stat(p)
  169. if err != nil {
  170. if os.IsNotExist(err) {
  171. return nil, false, nil
  172. }
  173. return nil, false, err
  174. }
  175. bs, err := os.ReadFile(p)
  176. if err != nil {
  177. return nil, false, err
  178. }
  179. err = yaml.Unmarshal(bs, &comments)
  180. if err != nil {
  181. return nil, false, err
  182. }
  183. return comments, false, nil
  184. }
  185. // GetPullRequests returns pull requests according page and perPage
  186. func (r *RepositoryRestorer) GetPullRequests(_ context.Context, page, perPage int) ([]*base.PullRequest, bool, error) {
  187. pulls := make([]*base.PullRequest, 0, 10)
  188. p := filepath.Join(r.baseDir, "pull_request.yml")
  189. _, err := os.Stat(p)
  190. if err != nil {
  191. if os.IsNotExist(err) {
  192. return nil, true, nil
  193. }
  194. return nil, false, err
  195. }
  196. bs, err := os.ReadFile(p)
  197. if err != nil {
  198. return nil, false, err
  199. }
  200. err = yaml.Unmarshal(bs, &pulls)
  201. if err != nil {
  202. return nil, false, err
  203. }
  204. for _, pr := range pulls {
  205. pr.PatchURL = "file://" + filepath.Join(r.baseDir, pr.PatchURL)
  206. CheckAndEnsureSafePR(pr, "", r)
  207. }
  208. return pulls, true, nil
  209. }
  210. // GetReviews returns pull requests review
  211. func (r *RepositoryRestorer) GetReviews(ctx context.Context, reviewable base.Reviewable) ([]*base.Review, error) {
  212. reviews := make([]*base.Review, 0, 10)
  213. p := filepath.Join(r.reviewDir(), fmt.Sprintf("%d.yml", reviewable.GetForeignIndex()))
  214. _, err := os.Stat(p)
  215. if err != nil {
  216. if os.IsNotExist(err) {
  217. return nil, nil
  218. }
  219. return nil, err
  220. }
  221. bs, err := os.ReadFile(p)
  222. if err != nil {
  223. return nil, err
  224. }
  225. err = yaml.Unmarshal(bs, &reviews)
  226. if err != nil {
  227. return nil, err
  228. }
  229. return reviews, nil
  230. }