gitea源码

license.go 4.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. // Copyright 2024 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package repository
  4. import (
  5. "context"
  6. "fmt"
  7. "io"
  8. "code.gitea.io/gitea/models/db"
  9. repo_model "code.gitea.io/gitea/models/repo"
  10. "code.gitea.io/gitea/modules/container"
  11. "code.gitea.io/gitea/modules/git"
  12. "code.gitea.io/gitea/modules/gitrepo"
  13. "code.gitea.io/gitea/modules/graceful"
  14. "code.gitea.io/gitea/modules/log"
  15. "code.gitea.io/gitea/modules/options"
  16. "code.gitea.io/gitea/modules/queue"
  17. licenseclassifier "github.com/google/licenseclassifier/v2"
  18. )
  19. var (
  20. classifier *licenseclassifier.Classifier
  21. LicenseFileName = "LICENSE"
  22. // licenseUpdaterQueue represents a queue to handle update repo licenses
  23. licenseUpdaterQueue *queue.WorkerPoolQueue[*LicenseUpdaterOptions]
  24. )
  25. func AddRepoToLicenseUpdaterQueue(opts *LicenseUpdaterOptions) error {
  26. if opts == nil {
  27. return nil
  28. }
  29. return licenseUpdaterQueue.Push(opts)
  30. }
  31. func InitLicenseClassifier() error {
  32. // threshold should be 0.84~0.86 or the test will be failed
  33. classifier = licenseclassifier.NewClassifier(.85)
  34. licenseFiles, err := options.AssetFS().ListFiles("license", true)
  35. if err != nil {
  36. return err
  37. }
  38. for _, licenseFile := range licenseFiles {
  39. licenseName := licenseFile
  40. data, err := options.License(licenseFile)
  41. if err != nil {
  42. return err
  43. }
  44. classifier.AddContent("License", licenseName, licenseName, data)
  45. }
  46. return nil
  47. }
  48. type LicenseUpdaterOptions struct {
  49. RepoID int64
  50. }
  51. func repoLicenseUpdater(items ...*LicenseUpdaterOptions) []*LicenseUpdaterOptions {
  52. ctx := graceful.GetManager().ShutdownContext()
  53. for _, opts := range items {
  54. repo, err := repo_model.GetRepositoryByID(ctx, opts.RepoID)
  55. if err != nil {
  56. log.Error("repoLicenseUpdater [%d] failed: GetRepositoryByID: %v", opts.RepoID, err)
  57. continue
  58. }
  59. if repo.IsEmpty {
  60. continue
  61. }
  62. gitRepo, err := gitrepo.OpenRepository(ctx, repo)
  63. if err != nil {
  64. log.Error("repoLicenseUpdater [%d] failed: OpenRepository: %v", opts.RepoID, err)
  65. continue
  66. }
  67. defer gitRepo.Close()
  68. commit, err := gitRepo.GetBranchCommit(repo.DefaultBranch)
  69. if err != nil {
  70. log.Error("repoLicenseUpdater [%d] failed: GetBranchCommit: %v", opts.RepoID, err)
  71. continue
  72. }
  73. if err = UpdateRepoLicenses(ctx, repo, commit); err != nil {
  74. log.Error("repoLicenseUpdater [%d] failed: updateRepoLicenses: %v", opts.RepoID, err)
  75. }
  76. }
  77. return nil
  78. }
  79. func SyncRepoLicenses(ctx context.Context) error {
  80. log.Trace("Doing: SyncRepoLicenses")
  81. if err := db.Iterate(
  82. ctx,
  83. nil,
  84. func(ctx context.Context, repo *repo_model.Repository) error {
  85. select {
  86. case <-ctx.Done():
  87. return db.ErrCancelledf("before sync repo licenses for %s", repo.FullName())
  88. default:
  89. }
  90. return AddRepoToLicenseUpdaterQueue(&LicenseUpdaterOptions{RepoID: repo.ID})
  91. },
  92. ); err != nil {
  93. log.Trace("Error: SyncRepoLicenses: %v", err)
  94. return err
  95. }
  96. log.Trace("Finished: SyncReposLicenses")
  97. return nil
  98. }
  99. // UpdateRepoLicenses will update repository licenses col if license file exists
  100. func UpdateRepoLicenses(ctx context.Context, repo *repo_model.Repository, commit *git.Commit) error {
  101. if commit == nil {
  102. return nil
  103. }
  104. b, err := commit.GetBlobByPath(LicenseFileName)
  105. if err != nil && !git.IsErrNotExist(err) {
  106. return fmt.Errorf("GetBlobByPath: %w", err)
  107. }
  108. if git.IsErrNotExist(err) {
  109. return repo_model.CleanRepoLicenses(ctx, repo)
  110. }
  111. licenses := make([]string, 0)
  112. if b != nil {
  113. r, err := b.DataAsync()
  114. if err != nil {
  115. return err
  116. }
  117. defer r.Close()
  118. licenses, err = detectLicense(r)
  119. if err != nil {
  120. return fmt.Errorf("detectLicense: %w", err)
  121. }
  122. }
  123. return repo_model.UpdateRepoLicenses(ctx, repo, commit.ID.String(), licenses)
  124. }
  125. // detectLicense returns the licenses detected by the given content buff
  126. func detectLicense(r io.Reader) ([]string, error) {
  127. if r == nil {
  128. return nil, nil
  129. }
  130. matches, err := classifier.MatchFrom(r)
  131. if err != nil {
  132. return nil, err
  133. }
  134. if len(matches.Matches) > 0 {
  135. results := make(container.Set[string], len(matches.Matches))
  136. for _, r := range matches.Matches {
  137. if r.MatchType == "License" && !results.Contains(r.Variant) {
  138. results.Add(r.Variant)
  139. }
  140. }
  141. return results.Values(), nil
  142. }
  143. return nil, nil
  144. }