mirror of
				https://github.com/go-gitea/gitea
				synced 2025-10-30 19:08:37 +00:00 
			
		
		
		
	Normalize NuGet package version on upload (#22186)
Fixes #22178 After this change upload versions with different semver metadata are treated as the same version and trigger a duplicated version error. Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
This commit is contained in:
		| @@ -5,8 +5,10 @@ package nuget | |||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"archive/zip" | 	"archive/zip" | ||||||
|  | 	"bytes" | ||||||
| 	"encoding/xml" | 	"encoding/xml" | ||||||
| 	"errors" | 	"errors" | ||||||
|  | 	"fmt" | ||||||
| 	"io" | 	"io" | ||||||
| 	"path/filepath" | 	"path/filepath" | ||||||
| 	"regexp" | 	"regexp" | ||||||
| @@ -182,7 +184,23 @@ func ParseNuspecMetaData(r io.Reader) (*Package, error) { | |||||||
| 	return &Package{ | 	return &Package{ | ||||||
| 		PackageType: packageType, | 		PackageType: packageType, | ||||||
| 		ID:          p.Metadata.ID, | 		ID:          p.Metadata.ID, | ||||||
| 		Version:     v.String(), | 		Version:     toNormalizedVersion(v), | ||||||
| 		Metadata:    m, | 		Metadata:    m, | ||||||
| 	}, nil | 	}, nil | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // https://learn.microsoft.com/en-us/nuget/concepts/package-versioning#normalized-version-numbers | ||||||
|  | // https://github.com/NuGet/NuGet.Client/blob/dccbd304b11103e08b97abf4cf4bcc1499d9235a/src/NuGet.Core/NuGet.Versioning/VersionFormatter.cs#L121 | ||||||
|  | func toNormalizedVersion(v *version.Version) string { | ||||||
|  | 	var buf bytes.Buffer | ||||||
|  | 	segments := v.Segments64() | ||||||
|  | 	fmt.Fprintf(&buf, "%d.%d.%d", segments[0], segments[1], segments[2]) | ||||||
|  | 	if len(segments) > 3 && segments[3] > 0 { | ||||||
|  | 		fmt.Fprintf(&buf, ".%d", segments[3]) | ||||||
|  | 	} | ||||||
|  | 	pre := v.Prerelease() | ||||||
|  | 	if pre != "" { | ||||||
|  | 		fmt.Fprint(&buf, "-", pre) | ||||||
|  | 	} | ||||||
|  | 	return buf.String() | ||||||
|  | } | ||||||
|   | |||||||
| @@ -146,6 +146,19 @@ func TestParseNuspecMetaData(t *testing.T) { | |||||||
| 		assert.Len(t, deps, 1) | 		assert.Len(t, deps, 1) | ||||||
| 		assert.Equal(t, dependencyID, deps[0].ID) | 		assert.Equal(t, dependencyID, deps[0].ID) | ||||||
| 		assert.Equal(t, dependencyVersion, deps[0].Version) | 		assert.Equal(t, dependencyVersion, deps[0].Version) | ||||||
|  |  | ||||||
|  | 		t.Run("NormalizedVersion", func(t *testing.T) { | ||||||
|  | 			np, err := ParseNuspecMetaData(strings.NewReader(`<?xml version="1.0" encoding="utf-8"?> | ||||||
|  | <package xmlns="http://schemas.microsoft.com/packaging/2013/05/nuspec.xsd"> | ||||||
|  |   <metadata> | ||||||
|  | 	<id>test</id> | ||||||
|  | 	<version>1.04.5.2.5-rc.1+metadata</version> | ||||||
|  |   </metadata> | ||||||
|  | </package>`)) | ||||||
|  | 			assert.NoError(t, err) | ||||||
|  | 			assert.NotNil(t, np) | ||||||
|  | 			assert.Equal(t, "1.4.5.2-rc.1", np.Version) | ||||||
|  | 		}) | ||||||
| 	}) | 	}) | ||||||
|  |  | ||||||
| 	t.Run("Symbols Package", func(t *testing.T) { | 	t.Run("Symbols Package", func(t *testing.T) { | ||||||
|   | |||||||
| @@ -344,7 +344,7 @@ func createEntry(l *linkBuilder, pd *packages_model.PackageDescriptor, withNames | |||||||
| 		Content: content, | 		Content: content, | ||||||
| 		Properties: &FeedEntryProperties{ | 		Properties: &FeedEntryProperties{ | ||||||
| 			Version:                  pd.Version.Version, | 			Version:                  pd.Version.Version, | ||||||
| 			NormalizedVersion:        normalizeVersion(pd.SemVer), | 			NormalizedVersion:        pd.Version.Version, | ||||||
| 			Authors:                  metadata.Authors, | 			Authors:                  metadata.Authors, | ||||||
| 			Dependencies:             buildDependencyString(metadata), | 			Dependencies:             buildDependencyString(metadata), | ||||||
| 			Description:              metadata.Description, | 			Description:              metadata.Description, | ||||||
|   | |||||||
| @@ -4,15 +4,11 @@ | |||||||
| package nuget | package nuget | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"bytes" |  | ||||||
| 	"fmt" |  | ||||||
| 	"sort" | 	"sort" | ||||||
| 	"time" | 	"time" | ||||||
|  |  | ||||||
| 	packages_model "code.gitea.io/gitea/models/packages" | 	packages_model "code.gitea.io/gitea/models/packages" | ||||||
| 	nuget_module "code.gitea.io/gitea/modules/packages/nuget" | 	nuget_module "code.gitea.io/gitea/modules/packages/nuget" | ||||||
|  |  | ||||||
| 	"github.com/hashicorp/go-version" |  | ||||||
| ) | ) | ||||||
|  |  | ||||||
| // https://docs.microsoft.com/en-us/nuget/api/service-index#resources | // https://docs.microsoft.com/en-us/nuget/api/service-index#resources | ||||||
| @@ -95,8 +91,8 @@ func createRegistrationIndexResponse(l *linkBuilder, pds []*packages_model.Packa | |||||||
| 			{ | 			{ | ||||||
| 				RegistrationPageURL: l.GetRegistrationIndexURL(pds[0].Package.Name), | 				RegistrationPageURL: l.GetRegistrationIndexURL(pds[0].Package.Name), | ||||||
| 				Count:               len(pds), | 				Count:               len(pds), | ||||||
| 				Lower:               normalizeVersion(pds[0].SemVer), | 				Lower:               pds[0].Version.Version, | ||||||
| 				Upper:               normalizeVersion(pds[len(pds)-1].SemVer), | 				Upper:               pds[len(pds)-1].Version.Version, | ||||||
| 				Items:               items, | 				Items:               items, | ||||||
| 			}, | 			}, | ||||||
| 		}, | 		}, | ||||||
| @@ -173,7 +169,7 @@ type PackageVersionsResponse struct { | |||||||
| func createPackageVersionsResponse(pds []*packages_model.PackageDescriptor) *PackageVersionsResponse { | func createPackageVersionsResponse(pds []*packages_model.PackageDescriptor) *PackageVersionsResponse { | ||||||
| 	versions := make([]string, 0, len(pds)) | 	versions := make([]string, 0, len(pds)) | ||||||
| 	for _, pd := range pds { | 	for _, pd := range pds { | ||||||
| 		versions = append(versions, normalizeVersion(pd.SemVer)) | 		versions = append(versions, pd.Version.Version) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	return &PackageVersionsResponse{ | 	return &PackageVersionsResponse{ | ||||||
| @@ -248,15 +244,3 @@ func createSearchResult(l *linkBuilder, pds []*packages_model.PackageDescriptor) | |||||||
| 		RegistrationIndexURL: l.GetRegistrationIndexURL(latest.Package.Name), | 		RegistrationIndexURL: l.GetRegistrationIndexURL(latest.Package.Name), | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| // normalizeVersion removes the metadata |  | ||||||
| func normalizeVersion(v *version.Version) string { |  | ||||||
| 	var buf bytes.Buffer |  | ||||||
| 	segments := v.Segments64() |  | ||||||
| 	fmt.Fprintf(&buf, "%d.%d.%d", segments[0], segments[1], segments[2]) |  | ||||||
| 	pre := v.Prerelease() |  | ||||||
| 	if pre != "" { |  | ||||||
| 		fmt.Fprintf(&buf, "-%s", pre) |  | ||||||
| 	} |  | ||||||
| 	return buf.String() |  | ||||||
| } |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user