gitea源码

metadata.go 5.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. // Copyright 2023 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package swift
  4. import (
  5. "archive/zip"
  6. "fmt"
  7. "io"
  8. "path"
  9. "regexp"
  10. "strings"
  11. "code.gitea.io/gitea/modules/json"
  12. "code.gitea.io/gitea/modules/util"
  13. "code.gitea.io/gitea/modules/validation"
  14. "github.com/hashicorp/go-version"
  15. )
  16. var (
  17. ErrMissingManifestFile = util.NewInvalidArgumentErrorf("Package.swift file is missing")
  18. ErrManifestFileTooLarge = util.NewInvalidArgumentErrorf("Package.swift file is too large")
  19. ErrInvalidManifestVersion = util.NewInvalidArgumentErrorf("manifest version is invalid")
  20. manifestPattern = regexp.MustCompile(`\APackage(?:@swift-(\d+(?:\.\d+)?(?:\.\d+)?))?\.swift\z`)
  21. toolsVersionPattern = regexp.MustCompile(`\A// swift-tools-version:(\d+(?:\.\d+)?(?:\.\d+)?)`)
  22. )
  23. const (
  24. maxManifestFileSize = 128 * 1024
  25. PropertyScope = "swift.scope"
  26. PropertyName = "swift.name"
  27. PropertyRepositoryURL = "swift.repository_url"
  28. )
  29. // Package represents a Swift package
  30. type Package struct {
  31. RepositoryURLs []string
  32. Metadata *Metadata
  33. }
  34. // Metadata represents the metadata of a Swift package
  35. type Metadata struct {
  36. Description string `json:"description,omitempty"`
  37. Keywords []string `json:"keywords,omitempty"`
  38. RepositoryURL string `json:"repository_url,omitempty"`
  39. License string `json:"license,omitempty"`
  40. Author Person `json:"author"`
  41. Manifests map[string]*Manifest `json:"manifests,omitempty"`
  42. }
  43. // Manifest represents a Package.swift file
  44. type Manifest struct {
  45. Content string `json:"content"`
  46. ToolsVersion string `json:"tools_version,omitempty"`
  47. }
  48. // https://schema.org/SoftwareSourceCode
  49. type SoftwareSourceCode struct {
  50. Context []string `json:"@context"`
  51. Type string `json:"@type"`
  52. Name string `json:"name"`
  53. Version string `json:"version"`
  54. Description string `json:"description,omitempty"`
  55. Keywords []string `json:"keywords,omitempty"`
  56. CodeRepository string `json:"codeRepository,omitempty"`
  57. License string `json:"license,omitempty"`
  58. Author Person `json:"author"`
  59. ProgrammingLanguage ProgrammingLanguage `json:"programmingLanguage"`
  60. RepositoryURLs []string `json:"repositoryURLs,omitempty"`
  61. }
  62. // https://schema.org/ProgrammingLanguage
  63. type ProgrammingLanguage struct {
  64. Type string `json:"@type"`
  65. Name string `json:"name"`
  66. URL string `json:"url"`
  67. }
  68. // https://schema.org/Person
  69. type Person struct {
  70. Type string `json:"@type,omitempty"`
  71. Name string `json:"name,omitempty"` // inherited from https://schema.org/Thing
  72. GivenName string `json:"givenName,omitempty"`
  73. MiddleName string `json:"middleName,omitempty"`
  74. FamilyName string `json:"familyName,omitempty"`
  75. }
  76. func (p Person) String() string {
  77. var sb strings.Builder
  78. if p.GivenName != "" {
  79. sb.WriteString(p.GivenName)
  80. }
  81. if p.MiddleName != "" {
  82. if sb.Len() > 0 {
  83. sb.WriteRune(' ')
  84. }
  85. sb.WriteString(p.MiddleName)
  86. }
  87. if p.FamilyName != "" {
  88. if sb.Len() > 0 {
  89. sb.WriteRune(' ')
  90. }
  91. sb.WriteString(p.FamilyName)
  92. }
  93. return sb.String()
  94. }
  95. // ParsePackage parses the Swift package upload
  96. func ParsePackage(sr io.ReaderAt, size int64, mr io.Reader) (*Package, error) {
  97. zr, err := zip.NewReader(sr, size)
  98. if err != nil {
  99. return nil, err
  100. }
  101. p := &Package{
  102. Metadata: &Metadata{
  103. Manifests: make(map[string]*Manifest),
  104. },
  105. }
  106. for _, file := range zr.File {
  107. manifestMatch := manifestPattern.FindStringSubmatch(path.Base(file.Name))
  108. if len(manifestMatch) == 0 {
  109. continue
  110. }
  111. if file.UncompressedSize64 > maxManifestFileSize {
  112. return nil, ErrManifestFileTooLarge
  113. }
  114. f, err := zr.Open(file.Name)
  115. if err != nil {
  116. return nil, err
  117. }
  118. content, err := io.ReadAll(f)
  119. if err := f.Close(); err != nil {
  120. return nil, err
  121. }
  122. if err != nil {
  123. return nil, err
  124. }
  125. swiftVersion := ""
  126. if len(manifestMatch) == 2 && manifestMatch[1] != "" {
  127. v, err := version.NewSemver(manifestMatch[1])
  128. if err != nil {
  129. return nil, ErrInvalidManifestVersion
  130. }
  131. swiftVersion = TrimmedVersionString(v)
  132. }
  133. manifest := &Manifest{
  134. Content: string(content),
  135. }
  136. toolsMatch := toolsVersionPattern.FindStringSubmatch(manifest.Content)
  137. if len(toolsMatch) == 2 {
  138. v, err := version.NewSemver(toolsMatch[1])
  139. if err != nil {
  140. return nil, ErrInvalidManifestVersion
  141. }
  142. manifest.ToolsVersion = TrimmedVersionString(v)
  143. }
  144. p.Metadata.Manifests[swiftVersion] = manifest
  145. }
  146. if _, found := p.Metadata.Manifests[""]; !found {
  147. return nil, ErrMissingManifestFile
  148. }
  149. if mr != nil {
  150. var ssc *SoftwareSourceCode
  151. if err := json.NewDecoder(mr).Decode(&ssc); err != nil {
  152. return nil, err
  153. }
  154. p.Metadata.Description = ssc.Description
  155. p.Metadata.Keywords = ssc.Keywords
  156. p.Metadata.License = ssc.License
  157. author := Person{
  158. Name: ssc.Author.Name,
  159. GivenName: ssc.Author.GivenName,
  160. MiddleName: ssc.Author.MiddleName,
  161. FamilyName: ssc.Author.FamilyName,
  162. }
  163. // If Name is not provided, generate it from individual name components
  164. if author.Name == "" {
  165. author.Name = author.String()
  166. }
  167. p.Metadata.Author = author
  168. p.Metadata.RepositoryURL = ssc.CodeRepository
  169. if !validation.IsValidURL(p.Metadata.RepositoryURL) {
  170. p.Metadata.RepositoryURL = ""
  171. }
  172. p.RepositoryURLs = ssc.RepositoryURLs
  173. }
  174. return p, nil
  175. }
  176. // TrimmedVersionString returns the version string without the patch segment if it is zero
  177. func TrimmedVersionString(v *version.Version) string {
  178. segments := v.Segments64()
  179. var b strings.Builder
  180. fmt.Fprintf(&b, "%d.%d", segments[0], segments[1])
  181. if segments[2] != 0 {
  182. fmt.Fprintf(&b, ".%d", segments[2])
  183. }
  184. return b.String()
  185. }