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.

check.go 4.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. // Copyright 2020 The Gitea Authors. All rights reserved.
  2. // Use of this source code is governed by a MIT-style
  3. // license that can be found in the LICENSE file.
  4. package repository
  5. import (
  6. "context"
  7. "fmt"
  8. "time"
  9. "code.gitea.io/gitea/models"
  10. "code.gitea.io/gitea/modules/git"
  11. "code.gitea.io/gitea/modules/log"
  12. "code.gitea.io/gitea/modules/setting"
  13. "github.com/unknwon/com"
  14. "xorm.io/builder"
  15. )
  16. // GitFsck calls 'git fsck' to check repository health.
  17. func GitFsck(ctx context.Context) error {
  18. log.Trace("Doing: GitFsck")
  19. if err := models.Iterate(
  20. models.DefaultDBContext(),
  21. new(models.Repository),
  22. builder.Expr("id>0 AND is_fsck_enabled=?", true),
  23. func(idx int, bean interface{}) error {
  24. select {
  25. case <-ctx.Done():
  26. return fmt.Errorf("Aborted due to shutdown")
  27. default:
  28. }
  29. repo := bean.(*models.Repository)
  30. repoPath := repo.RepoPath()
  31. log.Trace("Running health check on repository %s", repoPath)
  32. if err := git.Fsck(repoPath, setting.Cron.RepoHealthCheck.Timeout, setting.Cron.RepoHealthCheck.Args...); err != nil {
  33. desc := fmt.Sprintf("Failed to health check repository (%s): %v", repoPath, err)
  34. log.Warn(desc)
  35. if err = models.CreateRepositoryNotice(desc); err != nil {
  36. log.Error("CreateRepositoryNotice: %v", err)
  37. }
  38. }
  39. return nil
  40. },
  41. ); err != nil {
  42. return err
  43. }
  44. log.Trace("Finished: GitFsck")
  45. return nil
  46. }
  47. // GitGcRepos calls 'git gc' to remove unnecessary files and optimize the local repository
  48. func GitGcRepos(ctx context.Context) error {
  49. log.Trace("Doing: GitGcRepos")
  50. args := append([]string{"gc"}, setting.Git.GCArgs...)
  51. if err := models.Iterate(
  52. models.DefaultDBContext(),
  53. new(models.Repository),
  54. builder.Gt{"id": 0},
  55. func(idx int, bean interface{}) error {
  56. select {
  57. case <-ctx.Done():
  58. return fmt.Errorf("Aborted due to shutdown")
  59. default:
  60. }
  61. repo := bean.(*models.Repository)
  62. if err := repo.GetOwner(); err != nil {
  63. return err
  64. }
  65. if stdout, err := git.NewCommand(args...).
  66. SetDescription(fmt.Sprintf("Repository Garbage Collection: %s", repo.FullName())).
  67. RunInDirTimeout(
  68. time.Duration(setting.Git.Timeout.GC)*time.Second,
  69. repo.RepoPath()); err != nil {
  70. log.Error("Repository garbage collection failed for %v. Stdout: %s\nError: %v", repo, stdout, err)
  71. return fmt.Errorf("Repository garbage collection failed: Error: %v", err)
  72. }
  73. return nil
  74. },
  75. ); err != nil {
  76. return err
  77. }
  78. log.Trace("Finished: GitGcRepos")
  79. return nil
  80. }
  81. func gatherMissingRepoRecords() ([]*models.Repository, error) {
  82. repos := make([]*models.Repository, 0, 10)
  83. if err := models.Iterate(
  84. models.DefaultDBContext(),
  85. new(models.Repository),
  86. builder.Gt{"id": 0},
  87. func(idx int, bean interface{}) error {
  88. repo := bean.(*models.Repository)
  89. if !com.IsDir(repo.RepoPath()) {
  90. repos = append(repos, repo)
  91. }
  92. return nil
  93. },
  94. ); err != nil {
  95. if err2 := models.CreateRepositoryNotice(fmt.Sprintf("gatherMissingRepoRecords: %v", err)); err2 != nil {
  96. return nil, fmt.Errorf("CreateRepositoryNotice: %v", err)
  97. }
  98. }
  99. return repos, nil
  100. }
  101. // DeleteMissingRepositories deletes all repository records that lost Git files.
  102. func DeleteMissingRepositories(doer *models.User) error {
  103. repos, err := gatherMissingRepoRecords()
  104. if err != nil {
  105. return fmt.Errorf("gatherMissingRepoRecords: %v", err)
  106. }
  107. if len(repos) == 0 {
  108. return nil
  109. }
  110. for _, repo := range repos {
  111. log.Trace("Deleting %d/%d...", repo.OwnerID, repo.ID)
  112. if err := models.DeleteRepository(doer, repo.OwnerID, repo.ID); err != nil {
  113. if err2 := models.CreateRepositoryNotice(fmt.Sprintf("DeleteRepository [%d]: %v", repo.ID, err)); err2 != nil {
  114. return fmt.Errorf("CreateRepositoryNotice: %v", err)
  115. }
  116. }
  117. }
  118. return nil
  119. }
  120. // ReinitMissingRepositories reinitializes all repository records that lost Git files.
  121. func ReinitMissingRepositories() error {
  122. repos, err := gatherMissingRepoRecords()
  123. if err != nil {
  124. return fmt.Errorf("gatherMissingRepoRecords: %v", err)
  125. }
  126. if len(repos) == 0 {
  127. return nil
  128. }
  129. for _, repo := range repos {
  130. log.Trace("Initializing %d/%d...", repo.OwnerID, repo.ID)
  131. if err := git.InitRepository(repo.RepoPath(), true); err != nil {
  132. if err2 := models.CreateRepositoryNotice(fmt.Sprintf("InitRepository [%d]: %v", repo.ID, err)); err2 != nil {
  133. return fmt.Errorf("CreateRepositoryNotice: %v", err)
  134. }
  135. }
  136. }
  137. return nil
  138. }