Skip to content

Commit

Permalink
Merge remote-tracking branch 'giteaofficial/main'
Browse files Browse the repository at this point in the history
* giteaofficial/main:
  Remove semver compatible flag and change pypi to an array of test cases (go-gitea#21708)
  Allow local package identifiers for PyPI packages (go-gitea#21690)
  • Loading branch information
zjjhot committed Nov 8, 2022
2 parents af2a835 + 8c1d988 commit 5363f0e
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 11 deletions.
25 changes: 18 additions & 7 deletions routers/api/packages/pypi/pypi.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,19 @@ import (
packages_service "code.gitea.io/gitea/services/packages"
)

// https://www.python.org/dev/peps/pep-0503/#normalized-names
// https://peps.python.org/pep-0426/#name
var normalizer = strings.NewReplacer(".", "-", "_", "-")
var nameMatcher = regexp.MustCompile(`\A[a-zA-Z0-9\.\-_]+\z`)

// https://www.python.org/dev/peps/pep-0440/#appendix-b-parsing-version-strings-with-regular-expressions
var versionMatcher = regexp.MustCompile(`^([1-9][0-9]*!)?(0|[1-9][0-9]*)(\.(0|[1-9][0-9]*))*((a|b|rc)(0|[1-9][0-9]*))?(\.post(0|[1-9][0-9]*))?(\.dev(0|[1-9][0-9]*))?$`)
var nameMatcher = regexp.MustCompile(`\A(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\.\-_]*[a-zA-Z0-9])\z`)

// https://peps.python.org/pep-0440/#appendix-b-parsing-version-strings-with-regular-expressions
var versionMatcher = regexp.MustCompile(`\Av?` +
`(?:[0-9]+!)?` + // epoch
`[0-9]+(?:\.[0-9]+)*` + // release segment
`(?:[-_\.]?(?:a|b|c|rc|alpha|beta|pre|preview)[-_\.]?[0-9]*)?` + // pre-release
`(?:-[0-9]+|[-_\.]?(?:post|rev|r)[-_\.]?[0-9]*)?` + // post release
`(?:[-_\.]?dev[-_\.]?[0-9]*)?` + // dev release
`(?:\+[a-z0-9]+(?:[-_\.][a-z0-9]+)*)?` + // local version
`\z`)

func apiError(ctx *context.Context, status int, obj interface{}) {
helper.LogAndProcessError(ctx, status, obj, func(message string) {
Expand Down Expand Up @@ -121,7 +128,7 @@ func UploadPackageFile(ctx *context.Context) {

packageName := normalizer.Replace(ctx.Req.FormValue("name"))
packageVersion := ctx.Req.FormValue("version")
if !nameMatcher.MatchString(packageName) || !versionMatcher.MatchString(packageVersion) {
if !isValidNameAndVersion(packageName, packageVersion) {
apiError(ctx, http.StatusBadRequest, "invalid name or version")
return
}
Expand All @@ -139,7 +146,7 @@ func UploadPackageFile(ctx *context.Context) {
Name: packageName,
Version: packageVersion,
},
SemverCompatible: true,
SemverCompatible: false,
Creator: ctx.Doer,
Metadata: &pypi_module.Metadata{
Author: ctx.Req.FormValue("author"),
Expand Down Expand Up @@ -170,3 +177,7 @@ func UploadPackageFile(ctx *context.Context) {

ctx.Status(http.StatusCreated)
}

func isValidNameAndVersion(packageName, packageVersion string) bool {
return nameMatcher.MatchString(packageName) && versionMatcher.MatchString(packageVersion)
}
39 changes: 39 additions & 0 deletions routers/api/packages/pypi/pypi_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package pypi

import (
"testing"

"github.com/stretchr/testify/assert"
)

func TestIsValidNameAndVersion(t *testing.T) {
// The test cases below were created from the following Python PEPs:
// https://peps.python.org/pep-0426/#name
// https://peps.python.org/pep-0440/#appendix-b-parsing-version-strings-with-regular-expressions

// Valid Cases
assert.True(t, isValidNameAndVersion("A", "1.0.1"))
assert.True(t, isValidNameAndVersion("Test.Name.1234", "1.0.1"))
assert.True(t, isValidNameAndVersion("test_name", "1.0.1"))
assert.True(t, isValidNameAndVersion("test-name", "1.0.1"))
assert.True(t, isValidNameAndVersion("test-name", "v1.0.1"))
assert.True(t, isValidNameAndVersion("test-name", "2012.4"))
assert.True(t, isValidNameAndVersion("test-name", "1.0.1-alpha"))
assert.True(t, isValidNameAndVersion("test-name", "1.0.1a1"))
assert.True(t, isValidNameAndVersion("test-name", "1.0b2.r345.dev456"))
assert.True(t, isValidNameAndVersion("test-name", "1!1.0.1"))
assert.True(t, isValidNameAndVersion("test-name", "1.0.1+local.1"))

// Invalid Cases
assert.False(t, isValidNameAndVersion(".test-name", "1.0.1"))
assert.False(t, isValidNameAndVersion("test!name", "1.0.1"))
assert.False(t, isValidNameAndVersion("-test-name", "1.0.1"))
assert.False(t, isValidNameAndVersion("test-name-", "1.0.1"))
assert.False(t, isValidNameAndVersion("test-name", "a1.0.1"))
assert.False(t, isValidNameAndVersion("test-name", "1.0.1aa"))
assert.False(t, isValidNameAndVersion("test-name", "1.0.0-alpha.beta"))
}
8 changes: 4 additions & 4 deletions tests/integration/api_packages_pypi_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ func TestPackagePyPI(t *testing.T) {
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})

packageName := "test-package"
packageVersion := "1.0.1"
packageVersion := "1!1.0.1+r1234"
packageAuthor := "KN4CK3R"
packageDescription := "Test Description"

Expand Down Expand Up @@ -72,7 +72,7 @@ func TestPackagePyPI(t *testing.T) {

pd, err := packages.GetPackageDescriptor(db.DefaultContext, pvs[0])
assert.NoError(t, err)
assert.NotNil(t, pd.SemVer)
assert.Nil(t, pd.SemVer)
assert.IsType(t, &pypi.Metadata{}, pd.Metadata)
assert.Equal(t, packageName, pd.Package.Name)
assert.Equal(t, packageVersion, pd.Version.Version)
Expand Down Expand Up @@ -100,7 +100,7 @@ func TestPackagePyPI(t *testing.T) {

pd, err := packages.GetPackageDescriptor(db.DefaultContext, pvs[0])
assert.NoError(t, err)
assert.NotNil(t, pd.SemVer)
assert.Nil(t, pd.SemVer)
assert.IsType(t, &pypi.Metadata{}, pd.Metadata)
assert.Equal(t, packageName, pd.Package.Name)
assert.Equal(t, packageVersion, pd.Version.Version)
Expand Down Expand Up @@ -164,7 +164,7 @@ func TestPackagePyPI(t *testing.T) {
nodes := htmlDoc.doc.Find("a").Nodes
assert.Len(t, nodes, 2)

hrefMatcher := regexp.MustCompile(fmt.Sprintf(`%s/files/%s/%s/test\..+#sha256-%s`, root, packageName, packageVersion, hashSHA256))
hrefMatcher := regexp.MustCompile(fmt.Sprintf(`%s/files/%s/%s/test\..+#sha256-%s`, root, regexp.QuoteMeta(packageName), regexp.QuoteMeta(packageVersion), hashSHA256))

for _, a := range nodes {
for _, att := range a.Attr {
Expand Down

0 comments on commit 5363f0e

Please sign in to comment.