You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

gitea_uploader.go 22KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869
  1. // Copyright 2019 The Gitea Authors. All rights reserved.
  2. // Copyright 2018 Jonas Franz. All rights reserved.
  3. // Use of this source code is governed by a MIT-style
  4. // license that can be found in the LICENSE file.
  5. package migrations
  6. import (
  7. "context"
  8. "fmt"
  9. "io"
  10. "os"
  11. "path/filepath"
  12. "strings"
  13. "sync"
  14. "time"
  15. "code.gitea.io/gitea/models"
  16. "code.gitea.io/gitea/modules/git"
  17. "code.gitea.io/gitea/modules/log"
  18. "code.gitea.io/gitea/modules/migrations/base"
  19. "code.gitea.io/gitea/modules/repository"
  20. repo_module "code.gitea.io/gitea/modules/repository"
  21. "code.gitea.io/gitea/modules/setting"
  22. "code.gitea.io/gitea/modules/storage"
  23. "code.gitea.io/gitea/modules/structs"
  24. "code.gitea.io/gitea/modules/timeutil"
  25. "code.gitea.io/gitea/modules/uri"
  26. "code.gitea.io/gitea/services/pull"
  27. gouuid "github.com/google/uuid"
  28. )
  29. var (
  30. _ base.Uploader = &GiteaLocalUploader{}
  31. )
  32. // GiteaLocalUploader implements an Uploader to gitea sites
  33. type GiteaLocalUploader struct {
  34. ctx context.Context
  35. doer *models.User
  36. repoOwner string
  37. repoName string
  38. repo *models.Repository
  39. labels sync.Map
  40. milestones sync.Map
  41. issues sync.Map
  42. gitRepo *git.Repository
  43. prHeadCache map[string]struct{}
  44. userMap map[int64]int64 // external user id mapping to user id
  45. prCache map[int64]*models.PullRequest
  46. gitServiceType structs.GitServiceType
  47. }
  48. // NewGiteaLocalUploader creates an gitea Uploader via gitea API v1
  49. func NewGiteaLocalUploader(ctx context.Context, doer *models.User, repoOwner, repoName string) *GiteaLocalUploader {
  50. return &GiteaLocalUploader{
  51. ctx: ctx,
  52. doer: doer,
  53. repoOwner: repoOwner,
  54. repoName: repoName,
  55. prHeadCache: make(map[string]struct{}),
  56. userMap: make(map[int64]int64),
  57. prCache: make(map[int64]*models.PullRequest),
  58. }
  59. }
  60. // MaxBatchInsertSize returns the table's max batch insert size
  61. func (g *GiteaLocalUploader) MaxBatchInsertSize(tp string) int {
  62. switch tp {
  63. case "issue":
  64. return models.MaxBatchInsertSize(new(models.Issue))
  65. case "comment":
  66. return models.MaxBatchInsertSize(new(models.Comment))
  67. case "milestone":
  68. return models.MaxBatchInsertSize(new(models.Milestone))
  69. case "label":
  70. return models.MaxBatchInsertSize(new(models.Label))
  71. case "release":
  72. return models.MaxBatchInsertSize(new(models.Release))
  73. case "pullrequest":
  74. return models.MaxBatchInsertSize(new(models.PullRequest))
  75. }
  76. return 10
  77. }
  78. // CreateRepo creates a repository
  79. func (g *GiteaLocalUploader) CreateRepo(repo *base.Repository, opts base.MigrateOptions) error {
  80. owner, err := models.GetUserByName(g.repoOwner)
  81. if err != nil {
  82. return err
  83. }
  84. var r *models.Repository
  85. if opts.MigrateToRepoID <= 0 {
  86. r, err = repo_module.CreateRepository(g.doer, owner, models.CreateRepoOptions{
  87. Name: g.repoName,
  88. Description: repo.Description,
  89. OriginalURL: repo.OriginalURL,
  90. GitServiceType: opts.GitServiceType,
  91. IsPrivate: opts.Private,
  92. IsMirror: opts.Mirror,
  93. Status: models.RepositoryBeingMigrated,
  94. })
  95. } else {
  96. r, err = models.GetRepositoryByID(opts.MigrateToRepoID)
  97. }
  98. if err != nil {
  99. return err
  100. }
  101. r.DefaultBranch = repo.DefaultBranch
  102. r, err = repository.MigrateRepositoryGitData(g.ctx, owner, r, base.MigrateOptions{
  103. RepoName: g.repoName,
  104. Description: repo.Description,
  105. OriginalURL: repo.OriginalURL,
  106. GitServiceType: opts.GitServiceType,
  107. Mirror: repo.IsMirror,
  108. LFS: opts.LFS,
  109. LFSEndpoint: opts.LFSEndpoint,
  110. CloneAddr: repo.CloneURL,
  111. Private: repo.IsPrivate,
  112. Wiki: opts.Wiki,
  113. Releases: opts.Releases, // if didn't get releases, then sync them from tags
  114. MirrorInterval: opts.MirrorInterval,
  115. })
  116. g.repo = r
  117. if err != nil {
  118. return err
  119. }
  120. g.gitRepo, err = git.OpenRepository(r.RepoPath())
  121. return err
  122. }
  123. // Close closes this uploader
  124. func (g *GiteaLocalUploader) Close() {
  125. if g.gitRepo != nil {
  126. g.gitRepo.Close()
  127. }
  128. }
  129. // CreateTopics creates topics
  130. func (g *GiteaLocalUploader) CreateTopics(topics ...string) error {
  131. // ignore topics to long for the db
  132. c := 0
  133. for i := range topics {
  134. if len(topics[i]) <= 50 {
  135. topics[c] = topics[i]
  136. c++
  137. }
  138. }
  139. topics = topics[:c]
  140. return models.SaveTopics(g.repo.ID, topics...)
  141. }
  142. // CreateMilestones creates milestones
  143. func (g *GiteaLocalUploader) CreateMilestones(milestones ...*base.Milestone) error {
  144. var mss = make([]*models.Milestone, 0, len(milestones))
  145. for _, milestone := range milestones {
  146. var deadline timeutil.TimeStamp
  147. if milestone.Deadline != nil {
  148. deadline = timeutil.TimeStamp(milestone.Deadline.Unix())
  149. }
  150. if deadline == 0 {
  151. deadline = timeutil.TimeStamp(time.Date(9999, 1, 1, 0, 0, 0, 0, setting.DefaultUILocation).Unix())
  152. }
  153. var ms = models.Milestone{
  154. RepoID: g.repo.ID,
  155. Name: milestone.Title,
  156. Content: milestone.Description,
  157. IsClosed: milestone.State == "closed",
  158. DeadlineUnix: deadline,
  159. }
  160. if ms.IsClosed && milestone.Closed != nil {
  161. ms.ClosedDateUnix = timeutil.TimeStamp(milestone.Closed.Unix())
  162. }
  163. mss = append(mss, &ms)
  164. }
  165. err := models.InsertMilestones(mss...)
  166. if err != nil {
  167. return err
  168. }
  169. for _, ms := range mss {
  170. g.milestones.Store(ms.Name, ms.ID)
  171. }
  172. return nil
  173. }
  174. // CreateLabels creates labels
  175. func (g *GiteaLocalUploader) CreateLabels(labels ...*base.Label) error {
  176. var lbs = make([]*models.Label, 0, len(labels))
  177. for _, label := range labels {
  178. lbs = append(lbs, &models.Label{
  179. RepoID: g.repo.ID,
  180. Name: label.Name,
  181. Description: label.Description,
  182. Color: fmt.Sprintf("#%s", label.Color),
  183. })
  184. }
  185. err := models.NewLabels(lbs...)
  186. if err != nil {
  187. return err
  188. }
  189. for _, lb := range lbs {
  190. g.labels.Store(lb.Name, lb)
  191. }
  192. return nil
  193. }
  194. // CreateReleases creates releases
  195. func (g *GiteaLocalUploader) CreateReleases(releases ...*base.Release) error {
  196. var rels = make([]*models.Release, 0, len(releases))
  197. for _, release := range releases {
  198. var rel = models.Release{
  199. RepoID: g.repo.ID,
  200. TagName: release.TagName,
  201. LowerTagName: strings.ToLower(release.TagName),
  202. Target: release.TargetCommitish,
  203. Title: release.Name,
  204. Sha1: release.TargetCommitish,
  205. Note: release.Body,
  206. IsDraft: release.Draft,
  207. IsPrerelease: release.Prerelease,
  208. IsTag: false,
  209. CreatedUnix: timeutil.TimeStamp(release.Created.Unix()),
  210. }
  211. userid, ok := g.userMap[release.PublisherID]
  212. tp := g.gitServiceType.Name()
  213. if !ok && tp != "" {
  214. var err error
  215. userid, err = models.GetUserIDByExternalUserID(tp, fmt.Sprintf("%v", release.PublisherID))
  216. if err != nil {
  217. log.Error("GetUserIDByExternalUserID: %v", err)
  218. }
  219. if userid > 0 {
  220. g.userMap[release.PublisherID] = userid
  221. }
  222. }
  223. if userid > 0 {
  224. rel.PublisherID = userid
  225. } else {
  226. rel.PublisherID = g.doer.ID
  227. rel.OriginalAuthor = release.PublisherName
  228. rel.OriginalAuthorID = release.PublisherID
  229. }
  230. // calc NumCommits
  231. commit, err := g.gitRepo.GetCommit(rel.TagName)
  232. if err != nil {
  233. return fmt.Errorf("GetCommit: %v", err)
  234. }
  235. rel.NumCommits, err = commit.CommitsCount()
  236. if err != nil {
  237. return fmt.Errorf("CommitsCount: %v", err)
  238. }
  239. for _, asset := range release.Assets {
  240. var attach = models.Attachment{
  241. UUID: gouuid.New().String(),
  242. Name: asset.Name,
  243. DownloadCount: int64(*asset.DownloadCount),
  244. Size: int64(*asset.Size),
  245. CreatedUnix: timeutil.TimeStamp(asset.Created.Unix()),
  246. }
  247. // download attachment
  248. err = func() error {
  249. // asset.DownloadURL maybe a local file
  250. var rc io.ReadCloser
  251. if asset.DownloadURL == nil {
  252. rc, err = asset.DownloadFunc()
  253. if err != nil {
  254. return err
  255. }
  256. } else {
  257. rc, err = uri.Open(*asset.DownloadURL)
  258. if err != nil {
  259. return err
  260. }
  261. }
  262. defer rc.Close()
  263. _, err = storage.Attachments.Save(attach.RelativePath(), rc, int64(*asset.Size))
  264. return err
  265. }()
  266. if err != nil {
  267. return err
  268. }
  269. rel.Attachments = append(rel.Attachments, &attach)
  270. }
  271. rels = append(rels, &rel)
  272. }
  273. return models.InsertReleases(rels...)
  274. }
  275. // SyncTags syncs releases with tags in the database
  276. func (g *GiteaLocalUploader) SyncTags() error {
  277. return repository.SyncReleasesWithTags(g.repo, g.gitRepo)
  278. }
  279. // CreateIssues creates issues
  280. func (g *GiteaLocalUploader) CreateIssues(issues ...*base.Issue) error {
  281. var iss = make([]*models.Issue, 0, len(issues))
  282. for _, issue := range issues {
  283. var labels []*models.Label
  284. for _, label := range issue.Labels {
  285. lb, ok := g.labels.Load(label.Name)
  286. if ok {
  287. labels = append(labels, lb.(*models.Label))
  288. }
  289. }
  290. var milestoneID int64
  291. if issue.Milestone != "" {
  292. milestone, ok := g.milestones.Load(issue.Milestone)
  293. if ok {
  294. milestoneID = milestone.(int64)
  295. }
  296. }
  297. var is = models.Issue{
  298. RepoID: g.repo.ID,
  299. Repo: g.repo,
  300. Index: issue.Number,
  301. Title: issue.Title,
  302. Content: issue.Content,
  303. Ref: issue.Ref,
  304. IsClosed: issue.State == "closed",
  305. IsLocked: issue.IsLocked,
  306. MilestoneID: milestoneID,
  307. Labels: labels,
  308. CreatedUnix: timeutil.TimeStamp(issue.Created.Unix()),
  309. UpdatedUnix: timeutil.TimeStamp(issue.Updated.Unix()),
  310. }
  311. userid, ok := g.userMap[issue.PosterID]
  312. tp := g.gitServiceType.Name()
  313. if !ok && tp != "" {
  314. var err error
  315. userid, err = models.GetUserIDByExternalUserID(tp, fmt.Sprintf("%v", issue.PosterID))
  316. if err != nil {
  317. log.Error("GetUserIDByExternalUserID: %v", err)
  318. }
  319. if userid > 0 {
  320. g.userMap[issue.PosterID] = userid
  321. }
  322. }
  323. if userid > 0 {
  324. is.PosterID = userid
  325. } else {
  326. is.PosterID = g.doer.ID
  327. is.OriginalAuthor = issue.PosterName
  328. is.OriginalAuthorID = issue.PosterID
  329. }
  330. if issue.Closed != nil {
  331. is.ClosedUnix = timeutil.TimeStamp(issue.Closed.Unix())
  332. }
  333. // add reactions
  334. for _, reaction := range issue.Reactions {
  335. userid, ok := g.userMap[reaction.UserID]
  336. if !ok && tp != "" {
  337. var err error
  338. userid, err = models.GetUserIDByExternalUserID(tp, fmt.Sprintf("%v", reaction.UserID))
  339. if err != nil {
  340. log.Error("GetUserIDByExternalUserID: %v", err)
  341. }
  342. if userid > 0 {
  343. g.userMap[reaction.UserID] = userid
  344. }
  345. }
  346. var res = models.Reaction{
  347. Type: reaction.Content,
  348. CreatedUnix: timeutil.TimeStampNow(),
  349. }
  350. if userid > 0 {
  351. res.UserID = userid
  352. } else {
  353. res.UserID = g.doer.ID
  354. res.OriginalAuthorID = reaction.UserID
  355. res.OriginalAuthor = reaction.UserName
  356. }
  357. is.Reactions = append(is.Reactions, &res)
  358. }
  359. iss = append(iss, &is)
  360. }
  361. if len(iss) > 0 {
  362. if err := models.InsertIssues(iss...); err != nil {
  363. return err
  364. }
  365. for _, is := range iss {
  366. g.issues.Store(is.Index, is.ID)
  367. }
  368. }
  369. return nil
  370. }
  371. // CreateComments creates comments of issues
  372. func (g *GiteaLocalUploader) CreateComments(comments ...*base.Comment) error {
  373. var cms = make([]*models.Comment, 0, len(comments))
  374. for _, comment := range comments {
  375. var issueID int64
  376. if issueIDStr, ok := g.issues.Load(comment.IssueIndex); !ok {
  377. issue, err := models.GetIssueByIndex(g.repo.ID, comment.IssueIndex)
  378. if err != nil {
  379. return err
  380. }
  381. issueID = issue.ID
  382. g.issues.Store(comment.IssueIndex, issueID)
  383. } else {
  384. issueID = issueIDStr.(int64)
  385. }
  386. userid, ok := g.userMap[comment.PosterID]
  387. tp := g.gitServiceType.Name()
  388. if !ok && tp != "" {
  389. var err error
  390. userid, err = models.GetUserIDByExternalUserID(tp, fmt.Sprintf("%v", comment.PosterID))
  391. if err != nil {
  392. log.Error("GetUserIDByExternalUserID: %v", err)
  393. }
  394. if userid > 0 {
  395. g.userMap[comment.PosterID] = userid
  396. }
  397. }
  398. cm := models.Comment{
  399. IssueID: issueID,
  400. Type: models.CommentTypeComment,
  401. Content: comment.Content,
  402. CreatedUnix: timeutil.TimeStamp(comment.Created.Unix()),
  403. UpdatedUnix: timeutil.TimeStamp(comment.Updated.Unix()),
  404. }
  405. if userid > 0 {
  406. cm.PosterID = userid
  407. } else {
  408. cm.PosterID = g.doer.ID
  409. cm.OriginalAuthor = comment.PosterName
  410. cm.OriginalAuthorID = comment.PosterID
  411. }
  412. // add reactions
  413. for _, reaction := range comment.Reactions {
  414. userid, ok := g.userMap[reaction.UserID]
  415. if !ok && tp != "" {
  416. var err error
  417. userid, err = models.GetUserIDByExternalUserID(tp, fmt.Sprintf("%v", reaction.UserID))
  418. if err != nil {
  419. log.Error("GetUserIDByExternalUserID: %v", err)
  420. }
  421. if userid > 0 {
  422. g.userMap[reaction.UserID] = userid
  423. }
  424. }
  425. var res = models.Reaction{
  426. Type: reaction.Content,
  427. CreatedUnix: timeutil.TimeStampNow(),
  428. }
  429. if userid > 0 {
  430. res.UserID = userid
  431. } else {
  432. res.UserID = g.doer.ID
  433. res.OriginalAuthorID = reaction.UserID
  434. res.OriginalAuthor = reaction.UserName
  435. }
  436. cm.Reactions = append(cm.Reactions, &res)
  437. }
  438. cms = append(cms, &cm)
  439. }
  440. if len(cms) == 0 {
  441. return nil
  442. }
  443. return models.InsertIssueComments(cms)
  444. }
  445. // CreatePullRequests creates pull requests
  446. func (g *GiteaLocalUploader) CreatePullRequests(prs ...*base.PullRequest) error {
  447. var gprs = make([]*models.PullRequest, 0, len(prs))
  448. for _, pr := range prs {
  449. gpr, err := g.newPullRequest(pr)
  450. if err != nil {
  451. return err
  452. }
  453. userid, ok := g.userMap[pr.PosterID]
  454. tp := g.gitServiceType.Name()
  455. if !ok && tp != "" {
  456. var err error
  457. userid, err = models.GetUserIDByExternalUserID(tp, fmt.Sprintf("%v", pr.PosterID))
  458. if err != nil {
  459. log.Error("GetUserIDByExternalUserID: %v", err)
  460. }
  461. if userid > 0 {
  462. g.userMap[pr.PosterID] = userid
  463. }
  464. }
  465. if userid > 0 {
  466. gpr.Issue.PosterID = userid
  467. } else {
  468. gpr.Issue.PosterID = g.doer.ID
  469. gpr.Issue.OriginalAuthor = pr.PosterName
  470. gpr.Issue.OriginalAuthorID = pr.PosterID
  471. }
  472. gprs = append(gprs, gpr)
  473. }
  474. if err := models.InsertPullRequests(gprs...); err != nil {
  475. return err
  476. }
  477. for _, pr := range gprs {
  478. g.issues.Store(pr.Issue.Index, pr.Issue.ID)
  479. pull.AddToTaskQueue(pr)
  480. }
  481. return nil
  482. }
  483. func (g *GiteaLocalUploader) newPullRequest(pr *base.PullRequest) (*models.PullRequest, error) {
  484. var labels []*models.Label
  485. for _, label := range pr.Labels {
  486. lb, ok := g.labels.Load(label.Name)
  487. if ok {
  488. labels = append(labels, lb.(*models.Label))
  489. }
  490. }
  491. var milestoneID int64
  492. if pr.Milestone != "" {
  493. milestone, ok := g.milestones.Load(pr.Milestone)
  494. if ok {
  495. milestoneID = milestone.(int64)
  496. }
  497. }
  498. // download patch file
  499. err := func() error {
  500. // pr.PatchURL maybe a local file
  501. ret, err := uri.Open(pr.PatchURL)
  502. if err != nil {
  503. return err
  504. }
  505. defer ret.Close()
  506. pullDir := filepath.Join(g.repo.RepoPath(), "pulls")
  507. if err = os.MkdirAll(pullDir, os.ModePerm); err != nil {
  508. return err
  509. }
  510. f, err := os.Create(filepath.Join(pullDir, fmt.Sprintf("%d.patch", pr.Number)))
  511. if err != nil {
  512. return err
  513. }
  514. defer f.Close()
  515. _, err = io.Copy(f, ret)
  516. return err
  517. }()
  518. if err != nil {
  519. return nil, err
  520. }
  521. // set head information
  522. pullHead := filepath.Join(g.repo.RepoPath(), "refs", "pull", fmt.Sprintf("%d", pr.Number))
  523. if err := os.MkdirAll(pullHead, os.ModePerm); err != nil {
  524. return nil, err
  525. }
  526. p, err := os.Create(filepath.Join(pullHead, "head"))
  527. if err != nil {
  528. return nil, err
  529. }
  530. _, err = p.WriteString(pr.Head.SHA)
  531. p.Close()
  532. if err != nil {
  533. return nil, err
  534. }
  535. var head = "unknown repository"
  536. if pr.IsForkPullRequest() && pr.State != "closed" {
  537. if pr.Head.OwnerName != "" {
  538. remote := pr.Head.OwnerName
  539. _, ok := g.prHeadCache[remote]
  540. if !ok {
  541. // git remote add
  542. err := g.gitRepo.AddRemote(remote, pr.Head.CloneURL, true)
  543. if err != nil {
  544. log.Error("AddRemote failed: %s", err)
  545. } else {
  546. g.prHeadCache[remote] = struct{}{}
  547. ok = true
  548. }
  549. }
  550. if ok {
  551. _, err = git.NewCommand("fetch", remote, pr.Head.Ref).RunInDir(g.repo.RepoPath())
  552. if err != nil {
  553. log.Error("Fetch branch from %s failed: %v", pr.Head.CloneURL, err)
  554. } else {
  555. headBranch := filepath.Join(g.repo.RepoPath(), "refs", "heads", pr.Head.OwnerName, pr.Head.Ref)
  556. if err := os.MkdirAll(filepath.Dir(headBranch), os.ModePerm); err != nil {
  557. return nil, err
  558. }
  559. b, err := os.Create(headBranch)
  560. if err != nil {
  561. return nil, err
  562. }
  563. _, err = b.WriteString(pr.Head.SHA)
  564. b.Close()
  565. if err != nil {
  566. return nil, err
  567. }
  568. head = pr.Head.OwnerName + "/" + pr.Head.Ref
  569. }
  570. }
  571. }
  572. } else {
  573. head = pr.Head.Ref
  574. }
  575. var issue = models.Issue{
  576. RepoID: g.repo.ID,
  577. Repo: g.repo,
  578. Title: pr.Title,
  579. Index: pr.Number,
  580. Content: pr.Content,
  581. MilestoneID: milestoneID,
  582. IsPull: true,
  583. IsClosed: pr.State == "closed",
  584. IsLocked: pr.IsLocked,
  585. Labels: labels,
  586. CreatedUnix: timeutil.TimeStamp(pr.Created.Unix()),
  587. UpdatedUnix: timeutil.TimeStamp(pr.Updated.Unix()),
  588. }
  589. tp := g.gitServiceType.Name()
  590. userid, ok := g.userMap[pr.PosterID]
  591. if !ok && tp != "" {
  592. var err error
  593. userid, err = models.GetUserIDByExternalUserID(tp, fmt.Sprintf("%v", pr.PosterID))
  594. if err != nil {
  595. log.Error("GetUserIDByExternalUserID: %v", err)
  596. }
  597. if userid > 0 {
  598. g.userMap[pr.PosterID] = userid
  599. }
  600. }
  601. if userid > 0 {
  602. issue.PosterID = userid
  603. } else {
  604. issue.PosterID = g.doer.ID
  605. issue.OriginalAuthor = pr.PosterName
  606. issue.OriginalAuthorID = pr.PosterID
  607. }
  608. // add reactions
  609. for _, reaction := range pr.Reactions {
  610. userid, ok := g.userMap[reaction.UserID]
  611. if !ok && tp != "" {
  612. var err error
  613. userid, err = models.GetUserIDByExternalUserID(tp, fmt.Sprintf("%v", reaction.UserID))
  614. if err != nil {
  615. log.Error("GetUserIDByExternalUserID: %v", err)
  616. }
  617. if userid > 0 {
  618. g.userMap[reaction.UserID] = userid
  619. }
  620. }
  621. var res = models.Reaction{
  622. Type: reaction.Content,
  623. CreatedUnix: timeutil.TimeStampNow(),
  624. }
  625. if userid > 0 {
  626. res.UserID = userid
  627. } else {
  628. res.UserID = g.doer.ID
  629. res.OriginalAuthorID = reaction.UserID
  630. res.OriginalAuthor = reaction.UserName
  631. }
  632. issue.Reactions = append(issue.Reactions, &res)
  633. }
  634. var pullRequest = models.PullRequest{
  635. HeadRepoID: g.repo.ID,
  636. HeadBranch: head,
  637. BaseRepoID: g.repo.ID,
  638. BaseBranch: pr.Base.Ref,
  639. MergeBase: pr.Base.SHA,
  640. Index: pr.Number,
  641. HasMerged: pr.Merged,
  642. Issue: &issue,
  643. }
  644. if pullRequest.Issue.IsClosed && pr.Closed != nil {
  645. pullRequest.Issue.ClosedUnix = timeutil.TimeStamp(pr.Closed.Unix())
  646. }
  647. if pullRequest.HasMerged && pr.MergedTime != nil {
  648. pullRequest.MergedUnix = timeutil.TimeStamp(pr.MergedTime.Unix())
  649. pullRequest.MergedCommitID = pr.MergeCommitSHA
  650. pullRequest.MergerID = g.doer.ID
  651. }
  652. // TODO: assignees
  653. return &pullRequest, nil
  654. }
  655. func convertReviewState(state string) models.ReviewType {
  656. switch state {
  657. case base.ReviewStatePending:
  658. return models.ReviewTypePending
  659. case base.ReviewStateApproved:
  660. return models.ReviewTypeApprove
  661. case base.ReviewStateChangesRequested:
  662. return models.ReviewTypeReject
  663. case base.ReviewStateCommented:
  664. return models.ReviewTypeComment
  665. default:
  666. return models.ReviewTypePending
  667. }
  668. }
  669. // CreateReviews create pull request reviews
  670. func (g *GiteaLocalUploader) CreateReviews(reviews ...*base.Review) error {
  671. var cms = make([]*models.Review, 0, len(reviews))
  672. for _, review := range reviews {
  673. var issueID int64
  674. if issueIDStr, ok := g.issues.Load(review.IssueIndex); !ok {
  675. issue, err := models.GetIssueByIndex(g.repo.ID, review.IssueIndex)
  676. if err != nil {
  677. return err
  678. }
  679. issueID = issue.ID
  680. g.issues.Store(review.IssueIndex, issueID)
  681. } else {
  682. issueID = issueIDStr.(int64)
  683. }
  684. userid, ok := g.userMap[review.ReviewerID]
  685. tp := g.gitServiceType.Name()
  686. if !ok && tp != "" {
  687. var err error
  688. userid, err = models.GetUserIDByExternalUserID(tp, fmt.Sprintf("%v", review.ReviewerID))
  689. if err != nil {
  690. log.Error("GetUserIDByExternalUserID: %v", err)
  691. }
  692. if userid > 0 {
  693. g.userMap[review.ReviewerID] = userid
  694. }
  695. }
  696. var cm = models.Review{
  697. Type: convertReviewState(review.State),
  698. IssueID: issueID,
  699. Content: review.Content,
  700. Official: review.Official,
  701. CreatedUnix: timeutil.TimeStamp(review.CreatedAt.Unix()),
  702. UpdatedUnix: timeutil.TimeStamp(review.CreatedAt.Unix()),
  703. }
  704. if userid > 0 {
  705. cm.ReviewerID = userid
  706. } else {
  707. cm.ReviewerID = g.doer.ID
  708. cm.OriginalAuthor = review.ReviewerName
  709. cm.OriginalAuthorID = review.ReviewerID
  710. }
  711. // get pr
  712. pr, ok := g.prCache[issueID]
  713. if !ok {
  714. var err error
  715. pr, err = models.GetPullRequestByIssueIDWithNoAttributes(issueID)
  716. if err != nil {
  717. return err
  718. }
  719. g.prCache[issueID] = pr
  720. }
  721. for _, comment := range review.Comments {
  722. line := comment.Line
  723. if line != 0 {
  724. comment.Position = 1
  725. } else {
  726. _, _, line, _ = git.ParseDiffHunkString(comment.DiffHunk)
  727. }
  728. headCommitID, err := g.gitRepo.GetRefCommitID(pr.GetGitRefName())
  729. if err != nil {
  730. return fmt.Errorf("GetRefCommitID[%s]: %v", pr.GetGitRefName(), err)
  731. }
  732. var patch string
  733. reader, writer := io.Pipe()
  734. defer func() {
  735. _ = reader.Close()
  736. _ = writer.Close()
  737. }()
  738. go func() {
  739. if err := git.GetRepoRawDiffForFile(g.gitRepo, pr.MergeBase, headCommitID, git.RawDiffNormal, comment.TreePath, writer); err != nil {
  740. // We should ignore the error since the commit maybe removed when force push to the pull request
  741. log.Warn("GetRepoRawDiffForFile failed when migrating [%s, %s, %s, %s]: %v", g.gitRepo.Path, pr.MergeBase, headCommitID, comment.TreePath, err)
  742. }
  743. _ = writer.Close()
  744. }()
  745. patch, _ = git.CutDiffAroundLine(reader, int64((&models.Comment{Line: int64(line + comment.Position - 1)}).UnsignedLine()), line < 0, setting.UI.CodeCommentLines)
  746. var c = models.Comment{
  747. Type: models.CommentTypeCode,
  748. PosterID: comment.PosterID,
  749. IssueID: issueID,
  750. Content: comment.Content,
  751. Line: int64(line + comment.Position - 1),
  752. TreePath: comment.TreePath,
  753. CommitSHA: comment.CommitID,
  754. Patch: patch,
  755. CreatedUnix: timeutil.TimeStamp(comment.CreatedAt.Unix()),
  756. UpdatedUnix: timeutil.TimeStamp(comment.UpdatedAt.Unix()),
  757. }
  758. if userid > 0 {
  759. c.PosterID = userid
  760. } else {
  761. c.PosterID = g.doer.ID
  762. c.OriginalAuthor = review.ReviewerName
  763. c.OriginalAuthorID = review.ReviewerID
  764. }
  765. cm.Comments = append(cm.Comments, &c)
  766. }
  767. cms = append(cms, &cm)
  768. }
  769. return models.InsertReviews(cms)
  770. }
  771. // Rollback when migrating failed, this will rollback all the changes.
  772. func (g *GiteaLocalUploader) Rollback() error {
  773. if g.repo != nil && g.repo.ID > 0 {
  774. if err := models.DeleteRepository(g.doer, g.repo.OwnerID, g.repo.ID); err != nil {
  775. return err
  776. }
  777. }
  778. return nil
  779. }
  780. // Finish when migrating success, this will do some status update things.
  781. func (g *GiteaLocalUploader) Finish() error {
  782. if g.repo == nil || g.repo.ID <= 0 {
  783. return ErrRepoNotCreated
  784. }
  785. g.repo.Status = models.RepositoryReady
  786. return models.UpdateRepositoryCols(g.repo, "status")
  787. }