loading up the forgejo repo on tangled to test page performance
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

[GITEA] feat(nuget): basic manifest download

Refs: https://codeberg.org/forgejo/forgejo/pulls/2222

(cherry picked from commit 5f837efc15f3d1e0d7fbed7fc569251143266584)

fix: write xml header

(cherry picked from commit a715984a42be9da81c48106d5eae244098ac1108)

fix: optional elements and xml schema

(cherry picked from commit 6ea6895a3616246e7282aa20d8f010fa931b60ea)

fix: pass all other requests to file search

(cherry picked from commit 9bfc74833a3b657453b4519573598432a87e3e3c)

test: add integration test

(cherry picked from commit b798f4ce86daa78e694c5c142e6f5f44938e6cb6)

fix: use xmlResponse

(cherry picked from commit 7f76df0b246c64fac0eeb115642c8cb6eb676f36)
(cherry picked from commit e18d574ca40905aec52fbbe8247ba83fd01874dd)

authored by

Michael Kriese and committed by
Earl Warren
97cc9551 bfe87e30

+170 -64
+114 -45
modules/packages/nuget/metadata.go
··· 71 71 Version string `json:"version"` 72 72 } 73 73 74 + type nuspecPackageType struct { 75 + Name string `xml:"name,attr"` 76 + } 77 + 78 + type nuspecPackageTypes struct { 79 + PackageType []nuspecPackageType `xml:"packageType"` 80 + } 81 + 82 + type nuspecRepository struct { 83 + URL string `xml:"url,attr,omitempty"` 84 + Type string `xml:"type,attr,omitempty"` 85 + } 86 + type nuspecDependency struct { 87 + ID string `xml:"id,attr"` 88 + Version string `xml:"version,attr"` 89 + Exclude string `xml:"exclude,attr,omitempty"` 90 + } 91 + 92 + type nuspecGroup struct { 93 + TargetFramework string `xml:"targetFramework,attr"` 94 + Dependency []nuspecDependency `xml:"dependency"` 95 + } 96 + 97 + type nuspecDependencies struct { 98 + Group []nuspecGroup `xml:"group"` 99 + } 100 + 101 + type nuspeceMetadata struct { 102 + ID string `xml:"id"` 103 + Version string `xml:"version"` 104 + Authors string `xml:"authors"` 105 + RequireLicenseAcceptance bool `xml:"requireLicenseAcceptance,omitempty"` 106 + ProjectURL string `xml:"projectUrl,omitempty"` 107 + Description string `xml:"description"` 108 + ReleaseNotes string `xml:"releaseNotes,omitempty"` 109 + PackageTypes *nuspecPackageTypes `xml:"packageTypes,omitempty"` 110 + Repository *nuspecRepository `xml:"repository,omitempty"` 111 + Dependencies *nuspecDependencies `xml:"dependencies,omitempty"` 112 + } 113 + 74 114 type nuspecPackage struct { 75 - Metadata struct { 76 - ID string `xml:"id"` 77 - Version string `xml:"version"` 78 - Authors string `xml:"authors"` 79 - RequireLicenseAcceptance bool `xml:"requireLicenseAcceptance"` 80 - ProjectURL string `xml:"projectUrl"` 81 - Description string `xml:"description"` 82 - ReleaseNotes string `xml:"releaseNotes"` 83 - PackageTypes struct { 84 - PackageType []struct { 85 - Name string `xml:"name,attr"` 86 - } `xml:"packageType"` 87 - } `xml:"packageTypes"` 88 - Repository struct { 89 - URL string `xml:"url,attr"` 90 - } `xml:"repository"` 91 - Dependencies struct { 92 - Group []struct { 93 - TargetFramework string `xml:"targetFramework,attr"` 94 - Dependency []struct { 95 - ID string `xml:"id,attr"` 96 - Version string `xml:"version,attr"` 97 - Exclude string `xml:"exclude,attr"` 98 - } `xml:"dependency"` 99 - } `xml:"group"` 100 - } `xml:"dependencies"` 101 - } `xml:"metadata"` 115 + XMLName xml.Name `xml:"package"` 116 + Xmlns string `xml:"xmlns,attr"` 117 + Metadata nuspeceMetadata `xml:"metadata"` 102 118 } 103 119 104 120 // ParsePackageMetaData parses the metadata of a Nuget package file ··· 149 165 } 150 166 151 167 packageType := DependencyPackage 152 - for _, pt := range p.Metadata.PackageTypes.PackageType { 153 - if pt.Name == "SymbolsPackage" { 154 - packageType = SymbolsPackage 155 - break 168 + if p.Metadata.PackageTypes != nil { 169 + for _, pt := range p.Metadata.PackageTypes.PackageType { 170 + if pt.Name == "SymbolsPackage" { 171 + packageType = SymbolsPackage 172 + break 173 + } 156 174 } 157 175 } 158 176 ··· 161 179 ReleaseNotes: p.Metadata.ReleaseNotes, 162 180 Authors: p.Metadata.Authors, 163 181 ProjectURL: p.Metadata.ProjectURL, 164 - RepositoryURL: p.Metadata.Repository.URL, 165 182 RequireLicenseAcceptance: p.Metadata.RequireLicenseAcceptance, 166 183 Dependencies: make(map[string][]Dependency), 167 184 } 168 - 169 - for _, group := range p.Metadata.Dependencies.Group { 170 - deps := make([]Dependency, 0, len(group.Dependency)) 171 - for _, dep := range group.Dependency { 172 - if dep.ID == "" || dep.Version == "" { 173 - continue 185 + if p.Metadata.Repository != nil { 186 + m.RepositoryURL = p.Metadata.Repository.URL 187 + } 188 + if p.Metadata.Dependencies != nil { 189 + for _, group := range p.Metadata.Dependencies.Group { 190 + deps := make([]Dependency, 0, len(group.Dependency)) 191 + for _, dep := range group.Dependency { 192 + if dep.ID == "" || dep.Version == "" { 193 + continue 194 + } 195 + deps = append(deps, Dependency{ 196 + ID: dep.ID, 197 + Version: dep.Version, 198 + }) 174 199 } 175 - deps = append(deps, Dependency{ 176 - ID: dep.ID, 177 - Version: dep.Version, 178 - }) 179 - } 180 - if len(deps) > 0 { 181 - m.Dependencies[group.TargetFramework] = deps 200 + if len(deps) > 0 { 201 + m.Dependencies[group.TargetFramework] = deps 202 + } 182 203 } 183 204 } 184 205 return &Package{ ··· 204 225 } 205 226 return buf.String() 206 227 } 228 + 229 + // returning any here because we use a private type and we don't need the type for xml marshalling 230 + func GenerateNuspec(pd *Package) any { 231 + m := nuspeceMetadata{ 232 + ID: pd.ID, 233 + Version: pd.Version, 234 + Authors: pd.Metadata.Authors, 235 + Description: pd.Metadata.Description, 236 + ProjectURL: pd.Metadata.ProjectURL, 237 + RequireLicenseAcceptance: pd.Metadata.RequireLicenseAcceptance, 238 + } 239 + 240 + if pd.Metadata.RepositoryURL != "" { 241 + m.Repository = &nuspecRepository{ 242 + URL: pd.Metadata.RepositoryURL, 243 + } 244 + } 245 + 246 + groups := len(pd.Metadata.Dependencies) 247 + if groups > 0 { 248 + m.Dependencies = &nuspecDependencies{ 249 + Group: make([]nuspecGroup, 0, groups), 250 + } 251 + 252 + for tgf, deps := range pd.Metadata.Dependencies { 253 + if len(deps) == 0 { 254 + continue 255 + } 256 + gDeps := make([]nuspecDependency, 0, len(deps)) 257 + for _, dep := range deps { 258 + gDeps = append(gDeps, nuspecDependency{ 259 + ID: dep.ID, 260 + Version: dep.Version, 261 + }) 262 + } 263 + 264 + m.Dependencies.Group = append(m.Dependencies.Group, nuspecGroup{ 265 + TargetFramework: tgf, 266 + Dependency: gDeps, 267 + }) 268 + } 269 + } 270 + 271 + return &nuspecPackage{ 272 + Xmlns: "http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd", 273 + Metadata: m, 274 + } 275 + }
+41 -19
routers/api/packages/nuget/nuget.go
··· 387 387 ctx.JSON(http.StatusOK, resp) 388 388 } 389 389 390 - // https://docs.microsoft.com/en-us/nuget/api/package-base-address-resource#download-package-content-nupkg 390 + // https://learn.microsoft.com/en-us/nuget/api/package-base-address-resource#download-package-manifest-nuspec 391 + // https://learn.microsoft.com/en-us/nuget/api/package-base-address-resource#download-package-content-nupkg 391 392 func DownloadPackageFile(ctx *context.Context) { 392 393 packageName := ctx.Params("id") 393 394 packageVersion := ctx.Params("version") 394 395 filename := ctx.Params("filename") 395 396 396 - s, u, pf, err := packages_service.GetFileStreamByPackageNameAndVersion( 397 - ctx, 398 - &packages_service.PackageInfo{ 399 - Owner: ctx.Package.Owner, 400 - PackageType: packages_model.TypeNuGet, 401 - Name: packageName, 402 - Version: packageVersion, 403 - }, 404 - &packages_service.PackageFileInfo{ 405 - Filename: filename, 406 - }, 407 - ) 408 - if err != nil { 409 - if err == packages_model.ErrPackageNotExist || err == packages_model.ErrPackageFileNotExist { 397 + if filename == fmt.Sprintf("%s.nuspec", packageName) { 398 + pv, err := packages_model.GetVersionByNameAndVersion(ctx, ctx.Package.Owner.ID, packages_model.TypeNuGet, packageName, packageVersion) 399 + if err != nil { 410 400 apiError(ctx, http.StatusNotFound, err) 411 401 return 412 402 } 413 - apiError(ctx, http.StatusInternalServerError, err) 414 - return 415 - } 416 403 417 - helper.ServePackageFile(ctx, s, u, pf) 404 + pd, err := packages_model.GetPackageDescriptor(ctx, pv) 405 + if err != nil { 406 + apiError(ctx, http.StatusInternalServerError, err) 407 + return 408 + } 409 + pkg := &nuget_module.Package{ 410 + ID: pd.Package.Name, 411 + Version: packageVersion, 412 + Metadata: pd.Metadata.(*nuget_module.Metadata), 413 + } 414 + 415 + xmlResponse(ctx, http.StatusOK, nuget_module.GenerateNuspec(pkg)) 416 + } else { 417 + s, u, pf, err := packages_service.GetFileStreamByPackageNameAndVersion( 418 + ctx, 419 + &packages_service.PackageInfo{ 420 + Owner: ctx.Package.Owner, 421 + PackageType: packages_model.TypeNuGet, 422 + Name: packageName, 423 + Version: packageVersion, 424 + }, 425 + &packages_service.PackageFileInfo{ 426 + Filename: filename, 427 + }, 428 + ) 429 + if err != nil { 430 + if err == packages_model.ErrPackageNotExist || err == packages_model.ErrPackageFileNotExist { 431 + apiError(ctx, http.StatusNotFound, err) 432 + return 433 + } 434 + apiError(ctx, http.StatusInternalServerError, err) 435 + return 436 + } 437 + 438 + helper.ServePackageFile(ctx, s, u, pf) 439 + } 418 440 } 419 441 420 442 // UploadPackage creates a new package with the metadata contained in the uploaded nupgk file
+15
tests/integration/api_packages_nuget_test.go
··· 353 353 354 354 assert.Equal(t, content, resp.Body.Bytes()) 355 355 356 + req = NewRequest(t, "GET", fmt.Sprintf("%s/package/%s/%s/%s.nuspec", url, packageName, packageVersion, packageName)). 357 + AddBasicAuth(user.Name) 358 + resp = MakeRequest(t, req, http.StatusOK) 359 + 360 + nuspec := `<?xml version="1.0" encoding="UTF-8"?>` + "\n" + 361 + `<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd"><metadata>` + 362 + `<id>` + packageName + `</id><version>` + packageVersion + `</version><authors>` + packageAuthors + `</authors><description>` + packageDescription + `</description>` + 363 + `<dependencies><group targetFramework=".NETStandard2.0">` + 364 + // https://github.com/golang/go/issues/21399 go can't generate self-closing tags 365 + `<dependency id="Microsoft.CSharp" version="4.5.0"></dependency>` + 366 + `</group></dependencies>` + 367 + `</metadata></package>` 368 + 369 + assert.Equal(t, nuspec, resp.Body.String()) 370 + 356 371 checkDownloadCount(1) 357 372 358 373 req = NewRequest(t, "GET", fmt.Sprintf("%s/package/%s/%s/%s.%s.snupkg", url, packageName, packageVersion, packageName, packageVersion)).