Skip to content

Commit 5363f0e

Browse files
committed
Merge remote-tracking branch 'giteaofficial/main'
* 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)
2 parents af2a835 + 8c1d988 commit 5363f0e

File tree

3 files changed

+61
-11
lines changed

3 files changed

+61
-11
lines changed

routers/api/packages/pypi/pypi.go

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,19 @@ import (
2121
packages_service "code.gitea.io/gitea/services/packages"
2222
)
2323

24-
// https://www.python.org/dev/peps/pep-0503/#normalized-names
24+
// https://peps.python.org/pep-0426/#name
2525
var normalizer = strings.NewReplacer(".", "-", "_", "-")
26-
var nameMatcher = regexp.MustCompile(`\A[a-zA-Z0-9\.\-_]+\z`)
27-
28-
// https://www.python.org/dev/peps/pep-0440/#appendix-b-parsing-version-strings-with-regular-expressions
29-
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]*))?$`)
26+
var nameMatcher = regexp.MustCompile(`\A(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\.\-_]*[a-zA-Z0-9])\z`)
27+
28+
// https://peps.python.org/pep-0440/#appendix-b-parsing-version-strings-with-regular-expressions
29+
var versionMatcher = regexp.MustCompile(`\Av?` +
30+
`(?:[0-9]+!)?` + // epoch
31+
`[0-9]+(?:\.[0-9]+)*` + // release segment
32+
`(?:[-_\.]?(?:a|b|c|rc|alpha|beta|pre|preview)[-_\.]?[0-9]*)?` + // pre-release
33+
`(?:-[0-9]+|[-_\.]?(?:post|rev|r)[-_\.]?[0-9]*)?` + // post release
34+
`(?:[-_\.]?dev[-_\.]?[0-9]*)?` + // dev release
35+
`(?:\+[a-z0-9]+(?:[-_\.][a-z0-9]+)*)?` + // local version
36+
`\z`)
3037

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

122129
packageName := normalizer.Replace(ctx.Req.FormValue("name"))
123130
packageVersion := ctx.Req.FormValue("version")
124-
if !nameMatcher.MatchString(packageName) || !versionMatcher.MatchString(packageVersion) {
131+
if !isValidNameAndVersion(packageName, packageVersion) {
125132
apiError(ctx, http.StatusBadRequest, "invalid name or version")
126133
return
127134
}
@@ -139,7 +146,7 @@ func UploadPackageFile(ctx *context.Context) {
139146
Name: packageName,
140147
Version: packageVersion,
141148
},
142-
SemverCompatible: true,
149+
SemverCompatible: false,
143150
Creator: ctx.Doer,
144151
Metadata: &pypi_module.Metadata{
145152
Author: ctx.Req.FormValue("author"),
@@ -170,3 +177,7 @@ func UploadPackageFile(ctx *context.Context) {
170177

171178
ctx.Status(http.StatusCreated)
172179
}
180+
181+
func isValidNameAndVersion(packageName, packageVersion string) bool {
182+
return nameMatcher.MatchString(packageName) && versionMatcher.MatchString(packageVersion)
183+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// Copyright 2022 The Gitea Authors. All rights reserved.
2+
// Use of this source code is governed by a MIT-style
3+
// license that can be found in the LICENSE file.
4+
5+
package pypi
6+
7+
import (
8+
"testing"
9+
10+
"github.com/stretchr/testify/assert"
11+
)
12+
13+
func TestIsValidNameAndVersion(t *testing.T) {
14+
// The test cases below were created from the following Python PEPs:
15+
// https://peps.python.org/pep-0426/#name
16+
// https://peps.python.org/pep-0440/#appendix-b-parsing-version-strings-with-regular-expressions
17+
18+
// Valid Cases
19+
assert.True(t, isValidNameAndVersion("A", "1.0.1"))
20+
assert.True(t, isValidNameAndVersion("Test.Name.1234", "1.0.1"))
21+
assert.True(t, isValidNameAndVersion("test_name", "1.0.1"))
22+
assert.True(t, isValidNameAndVersion("test-name", "1.0.1"))
23+
assert.True(t, isValidNameAndVersion("test-name", "v1.0.1"))
24+
assert.True(t, isValidNameAndVersion("test-name", "2012.4"))
25+
assert.True(t, isValidNameAndVersion("test-name", "1.0.1-alpha"))
26+
assert.True(t, isValidNameAndVersion("test-name", "1.0.1a1"))
27+
assert.True(t, isValidNameAndVersion("test-name", "1.0b2.r345.dev456"))
28+
assert.True(t, isValidNameAndVersion("test-name", "1!1.0.1"))
29+
assert.True(t, isValidNameAndVersion("test-name", "1.0.1+local.1"))
30+
31+
// Invalid Cases
32+
assert.False(t, isValidNameAndVersion(".test-name", "1.0.1"))
33+
assert.False(t, isValidNameAndVersion("test!name", "1.0.1"))
34+
assert.False(t, isValidNameAndVersion("-test-name", "1.0.1"))
35+
assert.False(t, isValidNameAndVersion("test-name-", "1.0.1"))
36+
assert.False(t, isValidNameAndVersion("test-name", "a1.0.1"))
37+
assert.False(t, isValidNameAndVersion("test-name", "1.0.1aa"))
38+
assert.False(t, isValidNameAndVersion("test-name", "1.0.0-alpha.beta"))
39+
}

tests/integration/api_packages_pypi_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ func TestPackagePyPI(t *testing.T) {
2929
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
3030

3131
packageName := "test-package"
32-
packageVersion := "1.0.1"
32+
packageVersion := "1!1.0.1+r1234"
3333
packageAuthor := "KN4CK3R"
3434
packageDescription := "Test Description"
3535

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

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

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

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

169169
for _, a := range nodes {
170170
for _, att := range a.Attr {

0 commit comments

Comments
 (0)