aboutsummaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
authorKN4CK3R <admin@oldschoolhack.me>2024-12-05 00:09:07 +0100
committerGitHub <noreply@github.com>2024-12-04 23:09:07 +0000
commit0c3c041c88afc66a5048c1d8cf1b29c8bbbb798f (patch)
tree57b7605040ce7b707f32e45bae443e068c90f664 /tests
parent5ab7aa700f4cafcb33d8ad77708d7419ad2480fa (diff)
downloadgitea-0c3c041c88afc66a5048c1d8cf1b29c8bbbb798f.tar.gz
gitea-0c3c041c88afc66a5048c1d8cf1b29c8bbbb798f.zip
Add Arch package registry (#32692)
Close #25037 Close #31037 This PR adds a Arch package registry usable with pacman. ![grafik](https://github.com/user-attachments/assets/81cdb0c2-02f9-4733-bee2-e48af6b45224) Rewrite of #25396 and #31037. You can follow [this tutorial](https://wiki.archlinux.org/title/Creating_packages) to build a package for testing. Docs PR: https://gitea.com/gitea/docs/pulls/111 Co-authored-by: [d1nch8g@ion.lc](mailto:d1nch8g@ion.lc) Co-authored-by: @ExplodingDragon --------- Co-authored-by: dancheg97 <dancheg97@fmnx.su> Co-authored-by: dragon <ExplodingFKL@gmail.com> Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Diffstat (limited to 'tests')
-rw-r--r--tests/integration/api_packages_arch_test.go302
1 files changed, 302 insertions, 0 deletions
diff --git a/tests/integration/api_packages_arch_test.go b/tests/integration/api_packages_arch_test.go
new file mode 100644
index 0000000000..9c7a9dd19d
--- /dev/null
+++ b/tests/integration/api_packages_arch_test.go
@@ -0,0 +1,302 @@
+// Copyright 2023 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package integration
+
+import (
+ "archive/tar"
+ "bytes"
+ "compress/gzip"
+ "fmt"
+ "io"
+ "net/http"
+ "testing"
+
+ "code.gitea.io/gitea/models/db"
+ "code.gitea.io/gitea/models/packages"
+ "code.gitea.io/gitea/models/unittest"
+ user_model "code.gitea.io/gitea/models/user"
+ arch_module "code.gitea.io/gitea/modules/packages/arch"
+ arch_service "code.gitea.io/gitea/services/packages/arch"
+ "code.gitea.io/gitea/tests"
+
+ "github.com/klauspost/compress/zstd"
+ "github.com/stretchr/testify/assert"
+ "github.com/ulikunitz/xz"
+)
+
+func TestPackageArch(t *testing.T) {
+ defer tests.PrepareTestEnv(t)()
+
+ user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
+
+ packageName := "gitea-test"
+ packageVersion := "1.4.1-r3"
+
+ createPackage := func(compression, name, version, architecture string) []byte {
+ var buf bytes.Buffer
+ var cw io.WriteCloser
+ switch compression {
+ case "zst":
+ cw, _ = zstd.NewWriter(&buf)
+ case "xz":
+ cw, _ = xz.NewWriter(&buf)
+ case "gz":
+ cw = gzip.NewWriter(&buf)
+ }
+ tw := tar.NewWriter(cw)
+
+ info := []byte(`pkgname = ` + name + `
+pkgbase = ` + name + `
+pkgver = ` + version + `
+pkgdesc = Description
+# comment
+builddate = 1678834800
+size = 8
+arch = ` + architecture + `
+license = MIT`)
+
+ hdr := &tar.Header{
+ Name: ".PKGINFO",
+ Mode: 0o600,
+ Size: int64(len(info)),
+ }
+ tw.WriteHeader(hdr)
+ tw.Write(info)
+
+ for _, file := range []string{"etc/dummy", "opt/file/bin"} {
+ hdr := &tar.Header{
+ Name: file,
+ Mode: 0o600,
+ Size: 4,
+ }
+ tw.WriteHeader(hdr)
+ tw.Write([]byte("test"))
+ }
+
+ tw.Close()
+ cw.Close()
+
+ return buf.Bytes()
+ }
+
+ compressions := []string{"gz", "xz", "zst"}
+ repositories := []string{"main", "testing", "with/slash", ""}
+
+ rootURL := fmt.Sprintf("/api/packages/%s/arch", user.Name)
+
+ t.Run("RepositoryKey", func(t *testing.T) {
+ defer tests.PrintCurrentTest(t)()
+
+ req := NewRequest(t, "GET", rootURL+"/repository.key")
+ resp := MakeRequest(t, req, http.StatusOK)
+
+ assert.Equal(t, "application/pgp-keys", resp.Header().Get("Content-Type"))
+ assert.Contains(t, resp.Body.String(), "-----BEGIN PGP PUBLIC KEY BLOCK-----")
+ })
+
+ contentAarch64Gz := createPackage("gz", packageName, packageVersion, "aarch64")
+ for _, compression := range compressions {
+ contentAarch64 := createPackage(compression, packageName, packageVersion, "aarch64")
+ contentAny := createPackage(compression, packageName+"_"+arch_module.AnyArch, packageVersion, arch_module.AnyArch)
+
+ for _, repository := range repositories {
+ t.Run(fmt.Sprintf("[%s,%s]", repository, compression), func(t *testing.T) {
+ t.Run("Upload", func(t *testing.T) {
+ defer tests.PrintCurrentTest(t)()
+
+ uploadURL := fmt.Sprintf("%s/%s", rootURL, repository)
+
+ req := NewRequestWithBody(t, "PUT", uploadURL, bytes.NewReader([]byte{}))
+ MakeRequest(t, req, http.StatusUnauthorized)
+
+ req = NewRequestWithBody(t, "PUT", uploadURL, bytes.NewReader([]byte{})).
+ AddBasicAuth(user.Name)
+ MakeRequest(t, req, http.StatusBadRequest)
+
+ req = NewRequestWithBody(t, "PUT", uploadURL, bytes.NewReader(contentAarch64)).
+ AddBasicAuth(user.Name)
+ MakeRequest(t, req, http.StatusCreated)
+
+ pvs, err := packages.GetVersionsByPackageType(db.DefaultContext, user.ID, packages.TypeArch)
+ assert.NoError(t, err)
+ assert.Len(t, pvs, 1)
+
+ pd, err := packages.GetPackageDescriptor(db.DefaultContext, pvs[0])
+ assert.NoError(t, err)
+ assert.Nil(t, pd.SemVer)
+ assert.IsType(t, &arch_module.VersionMetadata{}, pd.Metadata)
+ assert.Equal(t, packageName, pd.Package.Name)
+ assert.Equal(t, packageVersion, pd.Version.Version)
+
+ pfs, err := packages.GetFilesByVersionID(db.DefaultContext, pvs[0].ID)
+ assert.NoError(t, err)
+ assert.NotEmpty(t, pfs)
+ assert.Condition(t, func() bool {
+ seen := false
+ expectedFilename := fmt.Sprintf("%s-%s-aarch64.pkg.tar.%s", packageName, packageVersion, compression)
+ expectedCompositeKey := fmt.Sprintf("%s|aarch64", repository)
+ for _, pf := range pfs {
+ if pf.Name == expectedFilename && pf.CompositeKey == expectedCompositeKey {
+ if seen {
+ return false
+ }
+ seen = true
+
+ assert.True(t, pf.IsLead)
+
+ pfps, err := packages.GetProperties(db.DefaultContext, packages.PropertyTypeFile, pf.ID)
+ assert.NoError(t, err)
+
+ for _, pfp := range pfps {
+ switch pfp.Name {
+ case arch_module.PropertyRepository:
+ assert.Equal(t, repository, pfp.Value)
+ case arch_module.PropertyArchitecture:
+ assert.Equal(t, "aarch64", pfp.Value)
+ }
+ }
+ }
+ }
+ return seen
+ })
+
+ req = NewRequestWithBody(t, "PUT", uploadURL, bytes.NewReader(contentAarch64)).
+ AddBasicAuth(user.Name)
+ MakeRequest(t, req, http.StatusConflict)
+
+ // Add same package with different compression leads to conflict
+ req = NewRequestWithBody(t, "PUT", uploadURL, bytes.NewReader(contentAarch64Gz)).
+ AddBasicAuth(user.Name)
+ MakeRequest(t, req, http.StatusConflict)
+ })
+
+ readIndexContent := func(r io.Reader) (map[string]string, error) {
+ gzr, err := gzip.NewReader(r)
+ if err != nil {
+ return nil, err
+ }
+
+ content := make(map[string]string)
+
+ tr := tar.NewReader(gzr)
+ for {
+ hd, err := tr.Next()
+ if err == io.EOF {
+ break
+ }
+ if err != nil {
+ return nil, err
+ }
+
+ buf, err := io.ReadAll(tr)
+ if err != nil {
+ return nil, err
+ }
+
+ content[hd.Name] = string(buf)
+ }
+
+ return content, nil
+ }
+
+ t.Run("Index", func(t *testing.T) {
+ defer tests.PrintCurrentTest(t)()
+
+ req := NewRequest(t, "GET", fmt.Sprintf("%s/%s/aarch64/%s", rootURL, repository, arch_service.IndexArchiveFilename))
+ resp := MakeRequest(t, req, http.StatusOK)
+
+ content, err := readIndexContent(resp.Body)
+ assert.NoError(t, err)
+
+ desc, has := content[fmt.Sprintf("%s-%s/desc", packageName, packageVersion)]
+ assert.True(t, has)
+ assert.Contains(t, desc, "%FILENAME%\n"+fmt.Sprintf("%s-%s-aarch64.pkg.tar.%s", packageName, packageVersion, compression)+"\n\n")
+ assert.Contains(t, desc, "%NAME%\n"+packageName+"\n\n")
+ assert.Contains(t, desc, "%VERSION%\n"+packageVersion+"\n\n")
+ assert.Contains(t, desc, "%ARCH%\naarch64\n")
+ assert.NotContains(t, desc, "%ARCH%\n"+arch_module.AnyArch+"\n")
+ assert.Contains(t, desc, "%LICENSE%\nMIT\n")
+
+ files, has := content[fmt.Sprintf("%s-%s/files", packageName, packageVersion)]
+ assert.True(t, has)
+ assert.Contains(t, files, "%FILES%\netc/dummy\nopt/file/bin\n\n")
+
+ for _, indexFile := range []string{
+ arch_service.IndexArchiveFilename,
+ arch_service.IndexArchiveFilename + ".tar.gz",
+ "index.db",
+ "index.db.tar.gz",
+ "index.files",
+ "index.files.tar.gz",
+ } {
+ req := NewRequest(t, "GET", fmt.Sprintf("%s/%s/aarch64/%s", rootURL, repository, indexFile))
+ MakeRequest(t, req, http.StatusOK)
+
+ req = NewRequest(t, "GET", fmt.Sprintf("%s/%s/aarch64/%s.sig", rootURL, repository, indexFile))
+ MakeRequest(t, req, http.StatusOK)
+ }
+ })
+
+ t.Run("Download", func(t *testing.T) {
+ defer tests.PrintCurrentTest(t)()
+
+ req := NewRequest(t, "GET", fmt.Sprintf("%s/%s/aarch64/%s-%s-aarch64.pkg.tar.%s", rootURL, repository, packageName, packageVersion, compression))
+ MakeRequest(t, req, http.StatusOK)
+
+ req = NewRequest(t, "GET", fmt.Sprintf("%s/%s/aarch64/%s-%s-aarch64.pkg.tar.%s.sig", rootURL, repository, packageName, packageVersion, compression))
+ MakeRequest(t, req, http.StatusOK)
+ })
+
+ t.Run("Any", func(t *testing.T) {
+ defer tests.PrintCurrentTest(t)()
+
+ req := NewRequestWithBody(t, "PUT", fmt.Sprintf("%s/%s", rootURL, repository), bytes.NewReader(contentAny)).
+ AddBasicAuth(user.Name)
+ MakeRequest(t, req, http.StatusCreated)
+
+ req = NewRequest(t, "GET", fmt.Sprintf("%s/%s/aarch64/%s", rootURL, repository, arch_service.IndexArchiveFilename))
+ resp := MakeRequest(t, req, http.StatusOK)
+
+ content, err := readIndexContent(resp.Body)
+ assert.NoError(t, err)
+
+ desc, has := content[fmt.Sprintf("%s-%s/desc", packageName, packageVersion)]
+ assert.True(t, has)
+ assert.Contains(t, desc, "%NAME%\n"+packageName+"\n\n")
+ assert.Contains(t, desc, "%ARCH%\naarch64\n")
+
+ desc, has = content[fmt.Sprintf("%s-%s/desc", packageName+"_"+arch_module.AnyArch, packageVersion)]
+ assert.True(t, has)
+ assert.Contains(t, desc, "%NAME%\n"+packageName+"_any\n\n")
+ assert.Contains(t, desc, "%ARCH%\n"+arch_module.AnyArch+"\n")
+
+ // "any" architecture package should be available with every architecture requested
+ for _, arch := range []string{arch_module.AnyArch, "aarch64", "myarch"} {
+ req := NewRequest(t, "GET", fmt.Sprintf("%s/%s/%s/%s-%s-any.pkg.tar.%s", rootURL, repository, arch, packageName+"_"+arch_module.AnyArch, packageVersion, compression))
+ MakeRequest(t, req, http.StatusOK)
+ }
+
+ req = NewRequest(t, "DELETE", fmt.Sprintf("%s/%s/%s/%s/any", rootURL, repository, packageName+"_"+arch_module.AnyArch, packageVersion)).
+ AddBasicAuth(user.Name)
+ MakeRequest(t, req, http.StatusNoContent)
+ })
+
+ t.Run("Delete", func(t *testing.T) {
+ defer tests.PrintCurrentTest(t)()
+
+ req := NewRequest(t, "DELETE", fmt.Sprintf("%s/%s/%s/%s/aarch64", rootURL, repository, packageName, packageVersion))
+ MakeRequest(t, req, http.StatusUnauthorized)
+
+ req = NewRequest(t, "DELETE", fmt.Sprintf("%s/%s/%s/%s/aarch64", rootURL, repository, packageName, packageVersion)).
+ AddBasicAuth(user.Name)
+ MakeRequest(t, req, http.StatusNoContent)
+
+ // Deleting the last file of an architecture should remove that index
+ req = NewRequest(t, "GET", fmt.Sprintf("%s/%s/aarch64/%s", rootURL, repository, arch_service.IndexArchiveFilename))
+ MakeRequest(t, req, http.StatusNotFound)
+ })
+ })
+ }
+ }
+}