gitea源码

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  1. // Copyright 2024 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package migrations
  4. import (
  5. "context"
  6. "errors"
  7. "net/url"
  8. "strconv"
  9. "strings"
  10. git_module "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/structs"
  14. "code.gitea.io/gitea/modules/util"
  15. "github.com/aws/aws-sdk-go-v2/credentials"
  16. "github.com/aws/aws-sdk-go-v2/service/codecommit"
  17. "github.com/aws/aws-sdk-go-v2/service/codecommit/types"
  18. )
  19. var (
  20. _ base.Downloader = &CodeCommitDownloader{}
  21. _ base.DownloaderFactory = &CodeCommitDownloaderFactory{}
  22. )
  23. func init() {
  24. RegisterDownloaderFactory(&CodeCommitDownloaderFactory{})
  25. }
  26. // CodeCommitDownloaderFactory defines a codecommit downloader factory
  27. type CodeCommitDownloaderFactory struct{}
  28. // New returns a Downloader related to this factory according MigrateOptions
  29. func (c *CodeCommitDownloaderFactory) New(ctx context.Context, opts base.MigrateOptions) (base.Downloader, error) {
  30. u, err := url.Parse(opts.CloneAddr)
  31. if err != nil {
  32. return nil, err
  33. }
  34. hostElems := strings.Split(u.Host, ".")
  35. if len(hostElems) != 4 {
  36. return nil, errors.New("cannot get the region from clone URL")
  37. }
  38. region := hostElems[1]
  39. pathElems := strings.Split(u.Path, "/")
  40. if len(pathElems) == 0 {
  41. return nil, errors.New("cannot get the repo name from clone URL")
  42. }
  43. repoName := pathElems[len(pathElems)-1]
  44. baseURL := u.Scheme + "://" + u.Host
  45. return NewCodeCommitDownloader(ctx, repoName, baseURL, opts.AWSAccessKeyID, opts.AWSSecretAccessKey, region), nil
  46. }
  47. // GitServiceType returns the type of git service
  48. func (c *CodeCommitDownloaderFactory) GitServiceType() structs.GitServiceType {
  49. return structs.CodeCommitService
  50. }
  51. func NewCodeCommitDownloader(_ context.Context, repoName, baseURL, accessKeyID, secretAccessKey, region string) *CodeCommitDownloader {
  52. downloader := CodeCommitDownloader{
  53. repoName: repoName,
  54. baseURL: baseURL,
  55. codeCommitClient: codecommit.New(codecommit.Options{
  56. Credentials: credentials.NewStaticCredentialsProvider(accessKeyID, secretAccessKey, ""),
  57. Region: region,
  58. }),
  59. }
  60. return &downloader
  61. }
  62. // CodeCommitDownloader implements a downloader for AWS CodeCommit
  63. type CodeCommitDownloader struct {
  64. base.NullDownloader
  65. codeCommitClient *codecommit.Client
  66. repoName string
  67. baseURL string
  68. allPullRequestIDs []string
  69. }
  70. // GetRepoInfo returns a repository information
  71. func (c *CodeCommitDownloader) GetRepoInfo(ctx context.Context) (*base.Repository, error) {
  72. output, err := c.codeCommitClient.GetRepository(ctx, &codecommit.GetRepositoryInput{
  73. RepositoryName: util.ToPointer(c.repoName),
  74. })
  75. if err != nil {
  76. return nil, err
  77. }
  78. repoMeta := output.RepositoryMetadata
  79. repo := &base.Repository{
  80. Name: *repoMeta.RepositoryName,
  81. Owner: *repoMeta.AccountId,
  82. IsPrivate: true, // CodeCommit repos are always private
  83. CloneURL: *repoMeta.CloneUrlHttp,
  84. }
  85. if repoMeta.DefaultBranch != nil {
  86. repo.DefaultBranch = *repoMeta.DefaultBranch
  87. }
  88. if repoMeta.RepositoryDescription != nil {
  89. repo.DefaultBranch = *repoMeta.RepositoryDescription
  90. }
  91. return repo, nil
  92. }
  93. // GetComments returns comments of an issue or PR
  94. func (c *CodeCommitDownloader) GetComments(ctx context.Context, commentable base.Commentable) ([]*base.Comment, bool, error) {
  95. var (
  96. nextToken *string
  97. comments []*base.Comment
  98. )
  99. for {
  100. resp, err := c.codeCommitClient.GetCommentsForPullRequest(ctx, &codecommit.GetCommentsForPullRequestInput{
  101. NextToken: nextToken,
  102. PullRequestId: util.ToPointer(strconv.FormatInt(commentable.GetForeignIndex(), 10)),
  103. })
  104. if err != nil {
  105. return nil, false, err
  106. }
  107. for _, prComment := range resp.CommentsForPullRequestData {
  108. for _, ccComment := range prComment.Comments {
  109. comment := &base.Comment{
  110. IssueIndex: commentable.GetForeignIndex(),
  111. PosterName: c.getUsernameFromARN(*ccComment.AuthorArn),
  112. Content: *ccComment.Content,
  113. Created: *ccComment.CreationDate,
  114. Updated: *ccComment.LastModifiedDate,
  115. }
  116. comments = append(comments, comment)
  117. }
  118. }
  119. nextToken = resp.NextToken
  120. if nextToken == nil {
  121. break
  122. }
  123. }
  124. return comments, true, nil
  125. }
  126. // GetPullRequests returns pull requests according page and perPage
  127. func (c *CodeCommitDownloader) GetPullRequests(ctx context.Context, page, perPage int) ([]*base.PullRequest, bool, error) {
  128. allPullRequestIDs, err := c.getAllPullRequestIDs(ctx)
  129. if err != nil {
  130. return nil, false, err
  131. }
  132. startIndex := (page - 1) * perPage
  133. endIndex := min(page*perPage, len(allPullRequestIDs))
  134. batch := allPullRequestIDs[startIndex:endIndex]
  135. prs := make([]*base.PullRequest, 0, len(batch))
  136. for _, id := range batch {
  137. output, err := c.codeCommitClient.GetPullRequest(ctx, &codecommit.GetPullRequestInput{
  138. PullRequestId: util.ToPointer(id),
  139. })
  140. if err != nil {
  141. return nil, false, err
  142. }
  143. orig := output.PullRequest
  144. number, err := strconv.ParseInt(*orig.PullRequestId, 10, 64)
  145. if err != nil {
  146. log.Error("CodeCommit pull request id is not a number: %s", *orig.PullRequestId)
  147. continue
  148. }
  149. if len(orig.PullRequestTargets) == 0 {
  150. log.Error("CodeCommit pull request does not contain targets", *orig.PullRequestId)
  151. continue
  152. }
  153. target := orig.PullRequestTargets[0]
  154. description := ""
  155. if orig.Description != nil {
  156. description = *orig.Description
  157. }
  158. pr := &base.PullRequest{
  159. Number: number,
  160. Title: *orig.Title,
  161. PosterName: c.getUsernameFromARN(*orig.AuthorArn),
  162. Content: description,
  163. State: "open",
  164. Created: *orig.CreationDate,
  165. Updated: *orig.LastActivityDate,
  166. Merged: target.MergeMetadata.IsMerged,
  167. Head: base.PullRequestBranch{
  168. Ref: strings.TrimPrefix(*target.SourceReference, git_module.BranchPrefix),
  169. SHA: *target.SourceCommit,
  170. RepoName: c.repoName,
  171. },
  172. Base: base.PullRequestBranch{
  173. Ref: strings.TrimPrefix(*target.DestinationReference, git_module.BranchPrefix),
  174. SHA: *target.DestinationCommit,
  175. RepoName: c.repoName,
  176. },
  177. ForeignIndex: number,
  178. }
  179. if orig.PullRequestStatus == types.PullRequestStatusEnumClosed {
  180. pr.State = "closed"
  181. pr.Closed = orig.LastActivityDate
  182. }
  183. if pr.Merged {
  184. pr.MergeCommitSHA = *target.MergeMetadata.MergeCommitId
  185. pr.MergedTime = orig.LastActivityDate
  186. }
  187. _ = CheckAndEnsureSafePR(pr, c.baseURL, c)
  188. prs = append(prs, pr)
  189. }
  190. return prs, len(prs) < perPage, nil
  191. }
  192. // FormatCloneURL add authentication into remote URLs
  193. func (c *CodeCommitDownloader) FormatCloneURL(opts MigrateOptions, remoteAddr string) (string, error) {
  194. u, err := url.Parse(remoteAddr)
  195. if err != nil {
  196. return "", err
  197. }
  198. u.User = url.UserPassword(opts.AuthUsername, opts.AuthPassword)
  199. return u.String(), nil
  200. }
  201. func (c *CodeCommitDownloader) getAllPullRequestIDs(ctx context.Context) ([]string, error) {
  202. if len(c.allPullRequestIDs) > 0 {
  203. return c.allPullRequestIDs, nil
  204. }
  205. var (
  206. nextToken *string
  207. prIDs []string
  208. )
  209. for {
  210. output, err := c.codeCommitClient.ListPullRequests(ctx, &codecommit.ListPullRequestsInput{
  211. RepositoryName: util.ToPointer(c.repoName),
  212. NextToken: nextToken,
  213. })
  214. if err != nil {
  215. return nil, err
  216. }
  217. prIDs = append(prIDs, output.PullRequestIds...)
  218. nextToken = output.NextToken
  219. if nextToken == nil {
  220. break
  221. }
  222. }
  223. c.allPullRequestIDs = prIDs
  224. return c.allPullRequestIDs, nil
  225. }
  226. func (c *CodeCommitDownloader) getUsernameFromARN(arn string) string {
  227. parts := strings.Split(arn, "/")
  228. if len(parts) > 0 {
  229. return parts[len(parts)-1]
  230. }
  231. return ""
  232. }