gitea源码

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  1. // Copyright 2023 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package integration
  4. import (
  5. "archive/tar"
  6. "bytes"
  7. "compress/gzip"
  8. "fmt"
  9. "io"
  10. "net/http"
  11. "strconv"
  12. "strings"
  13. "testing"
  14. "code.gitea.io/gitea/models/packages"
  15. "code.gitea.io/gitea/models/unittest"
  16. user_model "code.gitea.io/gitea/models/user"
  17. "code.gitea.io/gitea/modules/base"
  18. debian_module "code.gitea.io/gitea/modules/packages/debian"
  19. packages_cleanup_service "code.gitea.io/gitea/services/packages/cleanup"
  20. "code.gitea.io/gitea/tests"
  21. "github.com/blakesmith/ar"
  22. "github.com/stretchr/testify/assert"
  23. )
  24. func TestPackageDebian(t *testing.T) {
  25. defer tests.PrepareTestEnv(t)()
  26. user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
  27. packageName := "gitea"
  28. packageVersion := "1.0.3"
  29. packageVersion2 := "1.0.4"
  30. packageDescription := "Package Description"
  31. createArchive := func(name, version, architecture string) io.Reader {
  32. var cbuf bytes.Buffer
  33. zw := gzip.NewWriter(&cbuf)
  34. tw := tar.NewWriter(zw)
  35. tw.WriteHeader(&tar.Header{
  36. Name: "control",
  37. Mode: 0o600,
  38. Size: 50,
  39. })
  40. fmt.Fprintf(tw, "Package: %s\nVersion: %s\nArchitecture: %s\nDescription: %s\n", name, version, architecture, packageDescription)
  41. tw.Close()
  42. zw.Close()
  43. var buf bytes.Buffer
  44. aw := ar.NewWriter(&buf)
  45. aw.WriteGlobalHeader()
  46. hdr := &ar.Header{
  47. Name: "control.tar.gz",
  48. Mode: 0o600,
  49. Size: int64(cbuf.Len()),
  50. }
  51. aw.WriteHeader(hdr)
  52. aw.Write(cbuf.Bytes())
  53. return &buf
  54. }
  55. distributions := []string{"test", "gitea"}
  56. components := []string{"main", "stable"}
  57. architectures := []string{"all", "amd64"}
  58. rootURL := fmt.Sprintf("/api/packages/%s/debian", user.Name)
  59. t.Run("RepositoryKey", func(t *testing.T) {
  60. defer tests.PrintCurrentTest(t)()
  61. req := NewRequest(t, "GET", rootURL+"/repository.key")
  62. resp := MakeRequest(t, req, http.StatusOK)
  63. assert.Equal(t, "application/pgp-keys", resp.Header().Get("Content-Type"))
  64. assert.Contains(t, resp.Body.String(), "-----BEGIN PGP PUBLIC KEY BLOCK-----")
  65. })
  66. for _, distribution := range distributions {
  67. t.Run(fmt.Sprintf("[Distribution:%s]", distribution), func(t *testing.T) {
  68. for _, component := range components {
  69. for _, architecture := range architectures {
  70. t.Run(fmt.Sprintf("[Component:%s,Architecture:%s]", component, architecture), func(t *testing.T) {
  71. uploadURL := fmt.Sprintf("%s/pool/%s/%s/upload", rootURL, distribution, component)
  72. t.Run("Upload", func(t *testing.T) {
  73. defer tests.PrintCurrentTest(t)()
  74. req := NewRequestWithBody(t, "PUT", uploadURL, bytes.NewReader([]byte{}))
  75. MakeRequest(t, req, http.StatusUnauthorized)
  76. req = NewRequestWithBody(t, "PUT", uploadURL, bytes.NewReader([]byte{})).
  77. AddBasicAuth(user.Name)
  78. MakeRequest(t, req, http.StatusBadRequest)
  79. req = NewRequestWithBody(t, "PUT", uploadURL, createArchive("", "", "")).
  80. AddBasicAuth(user.Name)
  81. MakeRequest(t, req, http.StatusBadRequest)
  82. req = NewRequestWithBody(t, "PUT", uploadURL, createArchive(packageName, packageVersion, architecture)).
  83. AddBasicAuth(user.Name)
  84. MakeRequest(t, req, http.StatusCreated)
  85. pv, err := packages.GetVersionByNameAndVersion(t.Context(), user.ID, packages.TypeDebian, packageName, packageVersion)
  86. assert.NoError(t, err)
  87. pd, err := packages.GetPackageDescriptor(t.Context(), pv)
  88. assert.NoError(t, err)
  89. assert.Nil(t, pd.SemVer)
  90. assert.IsType(t, &debian_module.Metadata{}, pd.Metadata)
  91. assert.Equal(t, packageName, pd.Package.Name)
  92. assert.Equal(t, packageVersion, pd.Version.Version)
  93. pfs, err := packages.GetFilesByVersionID(t.Context(), pv.ID)
  94. assert.NoError(t, err)
  95. assert.NotEmpty(t, pfs)
  96. assert.Condition(t, func() bool {
  97. seen := false
  98. expectedFilename := fmt.Sprintf("%s_%s_%s.deb", packageName, packageVersion, architecture)
  99. expectedCompositeKey := fmt.Sprintf("%s|%s", distribution, component)
  100. for _, pf := range pfs {
  101. if pf.Name == expectedFilename && pf.CompositeKey == expectedCompositeKey {
  102. if seen {
  103. return false
  104. }
  105. seen = true
  106. assert.True(t, pf.IsLead)
  107. pfps, err := packages.GetProperties(t.Context(), packages.PropertyTypeFile, pf.ID)
  108. assert.NoError(t, err)
  109. for _, pfp := range pfps {
  110. switch pfp.Name {
  111. case debian_module.PropertyDistribution:
  112. assert.Equal(t, distribution, pfp.Value)
  113. case debian_module.PropertyComponent:
  114. assert.Equal(t, component, pfp.Value)
  115. case debian_module.PropertyArchitecture:
  116. assert.Equal(t, architecture, pfp.Value)
  117. }
  118. }
  119. }
  120. }
  121. return seen
  122. })
  123. req = NewRequestWithBody(t, "PUT", uploadURL, createArchive(packageName, packageVersion, architecture)).
  124. AddBasicAuth(user.Name)
  125. MakeRequest(t, req, http.StatusConflict)
  126. })
  127. t.Run("Download", func(t *testing.T) {
  128. defer tests.PrintCurrentTest(t)()
  129. req := NewRequest(t, "GET", fmt.Sprintf("%s/pool/%s/%s/%s_%s_%s.deb", rootURL, distribution, component, packageName, packageVersion, architecture))
  130. resp := MakeRequest(t, req, http.StatusOK)
  131. assert.Equal(t, "application/vnd.debian.binary-package", resp.Header().Get("Content-Type"))
  132. })
  133. t.Run("Packages", func(t *testing.T) {
  134. defer tests.PrintCurrentTest(t)()
  135. req := NewRequestWithBody(t, "PUT", uploadURL, createArchive(packageName, packageVersion2, architecture)).
  136. AddBasicAuth(user.Name)
  137. MakeRequest(t, req, http.StatusCreated)
  138. url := fmt.Sprintf("%s/dists/%s/%s/binary-%s/Packages", rootURL, distribution, component, architecture)
  139. req = NewRequest(t, "GET", url)
  140. resp := MakeRequest(t, req, http.StatusOK)
  141. body := resp.Body.String()
  142. assert.Contains(t, body, "Package: "+packageName+"\n")
  143. assert.Contains(t, body, "Version: "+packageVersion+"\n")
  144. assert.Contains(t, body, "Version: "+packageVersion2+"\n")
  145. assert.Contains(t, body, "Architecture: "+architecture+"\n")
  146. assert.Contains(t, body, fmt.Sprintf("Filename: pool/%s/%s/%s_%s_%s.deb\n", distribution, component, packageName, packageVersion, architecture))
  147. assert.Contains(t, body, fmt.Sprintf("Filename: pool/%s/%s/%s_%s_%s.deb\n", distribution, component, packageName, packageVersion2, architecture))
  148. req = NewRequest(t, "GET", url+".gz")
  149. MakeRequest(t, req, http.StatusOK)
  150. req = NewRequest(t, "GET", url+".xz")
  151. MakeRequest(t, req, http.StatusOK)
  152. url = fmt.Sprintf("%s/dists/%s/%s/%s/by-hash/SHA256/%s", rootURL, distribution, component, architecture, base.EncodeSha256(body))
  153. req = NewRequest(t, "GET", url)
  154. resp = MakeRequest(t, req, http.StatusOK)
  155. assert.Equal(t, body, resp.Body.String())
  156. })
  157. })
  158. }
  159. }
  160. t.Run("Release", func(t *testing.T) {
  161. defer tests.PrintCurrentTest(t)()
  162. req := NewRequest(t, "GET", fmt.Sprintf("%s/dists/%s/Release", rootURL, distribution))
  163. resp := MakeRequest(t, req, http.StatusOK)
  164. body := resp.Body.String()
  165. assert.Contains(t, body, "Components: "+strings.Join(components, " ")+"\n")
  166. assert.Contains(t, body, "Architectures: "+strings.Join(architectures, " ")+"\n")
  167. for _, component := range components {
  168. for _, architecture := range architectures {
  169. assert.Contains(t, body, fmt.Sprintf("%s/binary-%s/Packages\n", component, architecture))
  170. assert.Contains(t, body, fmt.Sprintf("%s/binary-%s/Packages.gz\n", component, architecture))
  171. assert.Contains(t, body, fmt.Sprintf("%s/binary-%s/Packages.xz\n", component, architecture))
  172. }
  173. }
  174. req = NewRequest(t, "GET", fmt.Sprintf("%s/dists/%s/by-hash/SHA256/%s", rootURL, distribution, base.EncodeSha256(body)))
  175. resp = MakeRequest(t, req, http.StatusOK)
  176. assert.Equal(t, body, resp.Body.String())
  177. req = NewRequest(t, "GET", fmt.Sprintf("%s/dists/%s/Release.gpg", rootURL, distribution))
  178. resp = MakeRequest(t, req, http.StatusOK)
  179. assert.Contains(t, resp.Body.String(), "-----BEGIN PGP SIGNATURE-----")
  180. req = NewRequest(t, "GET", fmt.Sprintf("%s/dists/%s/InRelease", rootURL, distribution))
  181. resp = MakeRequest(t, req, http.StatusOK)
  182. assert.Contains(t, resp.Body.String(), "-----BEGIN PGP SIGNED MESSAGE-----")
  183. })
  184. })
  185. }
  186. t.Run("Delete", func(t *testing.T) {
  187. defer tests.PrintCurrentTest(t)()
  188. distribution := distributions[0]
  189. architecture := architectures[0]
  190. for _, component := range components {
  191. req := NewRequest(t, "DELETE", fmt.Sprintf("%s/pool/%s/%s/%s/%s/%s", rootURL, distribution, component, packageName, packageVersion, architecture))
  192. MakeRequest(t, req, http.StatusUnauthorized)
  193. req = NewRequest(t, "DELETE", fmt.Sprintf("%s/pool/%s/%s/%s/%s/%s", rootURL, distribution, component, packageName, packageVersion, architecture)).
  194. AddBasicAuth(user.Name)
  195. MakeRequest(t, req, http.StatusNoContent)
  196. req = NewRequest(t, "DELETE", fmt.Sprintf("%s/pool/%s/%s/%s/%s/%s", rootURL, distribution, component, packageName, packageVersion2, architecture)).
  197. AddBasicAuth(user.Name)
  198. MakeRequest(t, req, http.StatusNoContent)
  199. req = NewRequest(t, "GET", fmt.Sprintf("%s/dists/%s/%s/binary-%s/Packages", rootURL, distribution, component, architecture))
  200. MakeRequest(t, req, http.StatusNotFound)
  201. }
  202. req := NewRequest(t, "GET", fmt.Sprintf("%s/dists/%s/Release", rootURL, distribution))
  203. resp := MakeRequest(t, req, http.StatusOK)
  204. body := resp.Body.String()
  205. assert.Contains(t, body, "Components: "+strings.Join(components, " ")+"\n")
  206. assert.Contains(t, body, "Architectures: "+architectures[1]+"\n")
  207. })
  208. t.Run("Cleanup", func(t *testing.T) {
  209. defer tests.PrintCurrentTest(t)()
  210. rule := &packages.PackageCleanupRule{
  211. Enabled: true,
  212. RemovePattern: `.*`,
  213. MatchFullName: true,
  214. OwnerID: user.ID,
  215. Type: packages.TypeDebian,
  216. }
  217. _, err := packages.InsertCleanupRule(t.Context(), rule)
  218. assert.NoError(t, err)
  219. // When there were a lot of packages (> 50 or 100) and the code used "Iterate" to get all packages, it ever caused bugs,
  220. // because "Iterate" keeps a dangling SQL session but the callback function still uses the same session to execute statements.
  221. // The "Iterate" problem has been checked by TestContextSafety now, so here we only need to check the cleanup logic with a small number
  222. packagesCount := 2
  223. for i := range packagesCount {
  224. uploadURL := fmt.Sprintf("%s/pool/%s/%s/upload", rootURL, "test", "main")
  225. req := NewRequestWithBody(t, "PUT", uploadURL, createArchive(packageName, "1.0."+strconv.Itoa(i), "all")).AddBasicAuth(user.Name)
  226. MakeRequest(t, req, http.StatusCreated)
  227. }
  228. req := NewRequest(t, "GET", fmt.Sprintf("%s/dists/%s/Release", rootURL, "test"))
  229. MakeRequest(t, req, http.StatusOK)
  230. err = packages_cleanup_service.CleanupTask(t.Context(), 0)
  231. assert.NoError(t, err)
  232. req = NewRequest(t, "GET", fmt.Sprintf("%s/dists/%s/Release", rootURL, "test"))
  233. MakeRequest(t, req, http.StatusNotFound)
  234. })
  235. }