@@ -10,6 +10,7 @@ import (
1010 "regexp"
1111 "sort"
1212 "strings"
13+ "unicode"
1314
1415 packages_model "code.gitea.io/gitea/models/packages"
1516 packages_module "code.gitea.io/gitea/modules/packages"
@@ -139,9 +140,28 @@ func UploadPackageFile(ctx *context.Context) {
139140 return
140141 }
141142
142- projectURL := ctx .Req .FormValue ("home_page" )
143- if ! validation .IsValidURL (projectURL ) {
144- projectURL = ""
143+ // Ensure ctx.Req.Form exists.
144+ _ = ctx .Req .ParseForm ()
145+
146+ // TODO: Home-page is a deprecated metadata field. Remove this form lookup once it's no longer apart of the spec.
147+ homepageURL := ctx .Req .FormValue ("home_page" )
148+ if len (homepageURL ) == 0 {
149+ projectURLs := ctx .Req .Form ["project_urls" ]
150+ for _ , url := range projectURLs {
151+ label , url , found := strings .Cut (url , "," )
152+ if ! found {
153+ continue
154+ }
155+ if normalizeLabel (label ) != "homepage" {
156+ continue
157+ }
158+ homepageURL = strings .TrimSpace (url )
159+ break
160+ }
161+ }
162+
163+ if ! validation .IsValidURL (homepageURL ) {
164+ homepageURL = ""
145165 }
146166
147167 _ , _ , err = packages_service .CreatePackageOrAddFileToExisting (
@@ -160,7 +180,7 @@ func UploadPackageFile(ctx *context.Context) {
160180 Description : ctx .Req .FormValue ("description" ),
161181 LongDescription : ctx .Req .FormValue ("long_description" ),
162182 Summary : ctx .Req .FormValue ("summary" ),
163- ProjectURL : projectURL ,
183+ ProjectURL : homepageURL ,
164184 License : ctx .Req .FormValue ("license" ),
165185 RequiresPython : ctx .Req .FormValue ("requires_python" ),
166186 },
@@ -189,6 +209,23 @@ func UploadPackageFile(ctx *context.Context) {
189209 ctx .Status (http .StatusCreated )
190210}
191211
212+ // Normalizes a Project-URL label.
213+ // See https://packaging.python.org/en/latest/specifications/well-known-project-urls/#label-normalization.
214+ func normalizeLabel (label string ) string {
215+ var builder strings.Builder
216+
217+ // "A label is normalized by deleting all ASCII punctuation and whitespace, and then converting the result
218+ // to lowercase."
219+ for _ , r := range label {
220+ if unicode .IsPunct (r ) || unicode .IsSpace (r ) {
221+ continue
222+ }
223+ builder .WriteRune (unicode .ToLower (r ))
224+ }
225+
226+ return builder .String ()
227+ }
228+
192229func isValidNameAndVersion (packageName , packageVersion string ) bool {
193230 return nameMatcher .MatchString (packageName ) && versionMatcher .MatchString (packageVersion )
194231}
0 commit comments