aboutsummaryrefslogtreecommitdiffstats
path: root/modules
diff options
context:
space:
mode:
authorKN4CK3R <admin@oldschoolhack.me>2023-05-14 17:38:40 +0200
committerGitHub <noreply@github.com>2023-05-14 23:38:40 +0800
commit5968c63a11c94b0fdde0485af194bebb2ea1b8e7 (patch)
tree8ff60c459712755a7346c4f5ed2e31e464af6cbe /modules
parent53a00017bbd89fddab11b323fc39872c44286d96 (diff)
downloadgitea-5968c63a11c94b0fdde0485af194bebb2ea1b8e7.tar.gz
gitea-5968c63a11c94b0fdde0485af194bebb2ea1b8e7.zip
Add Go package registry (#24687)
Fixes #7608 This PR adds a Go package registry usable with the Go proxy protocol. ![grafik](https://github.com/go-gitea/gitea/assets/1666336/328feb5c-3df2-4f9d-8eae-fe3126d14c37)
Diffstat (limited to 'modules')
-rw-r--r--modules/packages/goproxy/metadata.go94
-rw-r--r--modules/packages/goproxy/metadata_test.go75
-rw-r--r--modules/setting/packages.go2
3 files changed, 171 insertions, 0 deletions
diff --git a/modules/packages/goproxy/metadata.go b/modules/packages/goproxy/metadata.go
new file mode 100644
index 0000000000..40f7d20508
--- /dev/null
+++ b/modules/packages/goproxy/metadata.go
@@ -0,0 +1,94 @@
+// Copyright 2023 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package goproxy
+
+import (
+ "archive/zip"
+ "fmt"
+ "io"
+ "path"
+ "strings"
+
+ "code.gitea.io/gitea/modules/util"
+)
+
+const (
+ PropertyGoMod = "go.mod"
+
+ maxGoModFileSize = 16 * 1024 * 1024 // https://go.dev/ref/mod#zip-path-size-constraints
+)
+
+var (
+ ErrInvalidStructure = util.NewInvalidArgumentErrorf("package has invalid structure")
+ ErrGoModFileTooLarge = util.NewInvalidArgumentErrorf("go.mod file is too large")
+)
+
+type Package struct {
+ Name string
+ Version string
+ GoMod string
+}
+
+// ParsePackage parses the Go package file
+// https://go.dev/ref/mod#zip-files
+func ParsePackage(r io.ReaderAt, size int64) (*Package, error) {
+ archive, err := zip.NewReader(r, size)
+ if err != nil {
+ return nil, err
+ }
+
+ var p *Package
+
+ for _, file := range archive.File {
+ nameAndVersion := path.Dir(file.Name)
+
+ parts := strings.SplitN(nameAndVersion, "@", 2)
+ if len(parts) != 2 {
+ continue
+ }
+
+ versionParts := strings.SplitN(parts[1], "/", 2)
+
+ if p == nil {
+ p = &Package{
+ Name: strings.TrimSuffix(nameAndVersion, "@"+parts[1]),
+ Version: versionParts[0],
+ }
+ }
+
+ if len(versionParts) > 1 {
+ // files are expected in the "root" folder
+ continue
+ }
+
+ if path.Base(file.Name) == "go.mod" {
+ if file.UncompressedSize64 > maxGoModFileSize {
+ return nil, ErrGoModFileTooLarge
+ }
+
+ f, err := archive.Open(file.Name)
+ if err != nil {
+ return nil, err
+ }
+ defer f.Close()
+
+ bytes, err := io.ReadAll(&io.LimitedReader{R: f, N: maxGoModFileSize})
+ if err != nil {
+ return nil, err
+ }
+
+ p.GoMod = string(bytes)
+
+ return p, nil
+ }
+ }
+
+ if p == nil {
+ return nil, ErrInvalidStructure
+ }
+
+ p.GoMod = fmt.Sprintf("module %s", p.Name)
+
+ return p, nil
+}
diff --git a/modules/packages/goproxy/metadata_test.go b/modules/packages/goproxy/metadata_test.go
new file mode 100644
index 0000000000..4e7f394f8b
--- /dev/null
+++ b/modules/packages/goproxy/metadata_test.go
@@ -0,0 +1,75 @@
+// Copyright 2023 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package goproxy
+
+import (
+ "archive/zip"
+ "bytes"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+const (
+ packageName = "gitea.com/go-gitea/gitea"
+ packageVersion = "v0.0.1"
+)
+
+func TestParsePackage(t *testing.T) {
+ createArchive := func(files map[string][]byte) *bytes.Reader {
+ var buf bytes.Buffer
+ zw := zip.NewWriter(&buf)
+ for name, content := range files {
+ w, _ := zw.Create(name)
+ w.Write(content)
+ }
+ zw.Close()
+ return bytes.NewReader(buf.Bytes())
+ }
+
+ t.Run("EmptyPackage", func(t *testing.T) {
+ data := createArchive(nil)
+
+ p, err := ParsePackage(data, int64(data.Len()))
+ assert.Nil(t, p)
+ assert.ErrorIs(t, err, ErrInvalidStructure)
+ })
+
+ t.Run("InvalidNameOrVersionStructure", func(t *testing.T) {
+ data := createArchive(map[string][]byte{
+ packageName + "/" + packageVersion + "/go.mod": {},
+ })
+
+ p, err := ParsePackage(data, int64(data.Len()))
+ assert.Nil(t, p)
+ assert.ErrorIs(t, err, ErrInvalidStructure)
+ })
+
+ t.Run("GoModFileInWrongDirectory", func(t *testing.T) {
+ data := createArchive(map[string][]byte{
+ packageName + "@" + packageVersion + "/subdir/go.mod": {},
+ })
+
+ p, err := ParsePackage(data, int64(data.Len()))
+ assert.NotNil(t, p)
+ assert.NoError(t, err)
+ assert.Equal(t, packageName, p.Name)
+ assert.Equal(t, packageVersion, p.Version)
+ assert.Equal(t, "module gitea.com/go-gitea/gitea", p.GoMod)
+ })
+
+ t.Run("Valid", func(t *testing.T) {
+ data := createArchive(map[string][]byte{
+ packageName + "@" + packageVersion + "/subdir/go.mod": []byte("invalid"),
+ packageName + "@" + packageVersion + "/go.mod": []byte("valid"),
+ })
+
+ p, err := ParsePackage(data, int64(data.Len()))
+ assert.NotNil(t, p)
+ assert.NoError(t, err)
+ assert.Equal(t, packageName, p.Name)
+ assert.Equal(t, packageVersion, p.Version)
+ assert.Equal(t, "valid", p.GoMod)
+ })
+}
diff --git a/modules/setting/packages.go b/modules/setting/packages.go
index 3719e2f644..a9b91adf16 100644
--- a/modules/setting/packages.go
+++ b/modules/setting/packages.go
@@ -33,6 +33,7 @@ var (
LimitSizeContainer int64
LimitSizeDebian int64
LimitSizeGeneric int64
+ LimitSizeGo int64
LimitSizeHelm int64
LimitSizeMaven int64
LimitSizeNpm int64
@@ -79,6 +80,7 @@ func loadPackagesFrom(rootCfg ConfigProvider) {
Packages.LimitSizeContainer = mustBytes(sec, "LIMIT_SIZE_CONTAINER")
Packages.LimitSizeDebian = mustBytes(sec, "LIMIT_SIZE_DEBIAN")
Packages.LimitSizeGeneric = mustBytes(sec, "LIMIT_SIZE_GENERIC")
+ Packages.LimitSizeGo = mustBytes(sec, "LIMIT_SIZE_GO")
Packages.LimitSizeHelm = mustBytes(sec, "LIMIT_SIZE_HELM")
Packages.LimitSizeMaven = mustBytes(sec, "LIMIT_SIZE_MAVEN")
Packages.LimitSizeNpm = mustBytes(sec, "LIMIT_SIZE_NPM")