Merge pull request 'Some NuGet package enhancements' (#3246) from viceice/forgejo:feat/nuget/improvements into forgejo
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/3246 Reviewed-by: Earl Warren <earl-warren@noreply.codeberg.org>
This commit is contained in:
commit
df6f84945b
3 changed files with 73 additions and 32 deletions
|
@ -59,6 +59,7 @@ type Package struct {
|
||||||
type Metadata struct {
|
type Metadata struct {
|
||||||
Description string `json:"description,omitempty"`
|
Description string `json:"description,omitempty"`
|
||||||
ReleaseNotes string `json:"release_notes,omitempty"`
|
ReleaseNotes string `json:"release_notes,omitempty"`
|
||||||
|
Readme string `json:"readme,omitempty"`
|
||||||
Authors string `json:"authors,omitempty"`
|
Authors string `json:"authors,omitempty"`
|
||||||
ProjectURL string `json:"project_url,omitempty"`
|
ProjectURL string `json:"project_url,omitempty"`
|
||||||
RepositoryURL string `json:"repository_url,omitempty"`
|
RepositoryURL string `json:"repository_url,omitempty"`
|
||||||
|
@ -72,6 +73,7 @@ type Dependency struct {
|
||||||
Version string `json:"version"`
|
Version string `json:"version"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://learn.microsoft.com/en-us/nuget/reference/nuspec
|
||||||
type nuspecPackage struct {
|
type nuspecPackage struct {
|
||||||
Metadata struct {
|
Metadata struct {
|
||||||
ID string `xml:"id"`
|
ID string `xml:"id"`
|
||||||
|
@ -81,6 +83,7 @@ type nuspecPackage struct {
|
||||||
ProjectURL string `xml:"projectUrl"`
|
ProjectURL string `xml:"projectUrl"`
|
||||||
Description string `xml:"description"`
|
Description string `xml:"description"`
|
||||||
ReleaseNotes string `xml:"releaseNotes"`
|
ReleaseNotes string `xml:"releaseNotes"`
|
||||||
|
Readme string `xml:"readme"`
|
||||||
PackageTypes struct {
|
PackageTypes struct {
|
||||||
PackageType []struct {
|
PackageType []struct {
|
||||||
Name string `xml:"name,attr"`
|
Name string `xml:"name,attr"`
|
||||||
|
@ -90,6 +93,11 @@ type nuspecPackage struct {
|
||||||
URL string `xml:"url,attr"`
|
URL string `xml:"url,attr"`
|
||||||
} `xml:"repository"`
|
} `xml:"repository"`
|
||||||
Dependencies struct {
|
Dependencies struct {
|
||||||
|
Dependency []struct {
|
||||||
|
ID string `xml:"id,attr"`
|
||||||
|
Version string `xml:"version,attr"`
|
||||||
|
Exclude string `xml:"exclude,attr"`
|
||||||
|
} `xml:"dependency"`
|
||||||
Group []struct {
|
Group []struct {
|
||||||
TargetFramework string `xml:"targetFramework,attr"`
|
TargetFramework string `xml:"targetFramework,attr"`
|
||||||
Dependency []struct {
|
Dependency []struct {
|
||||||
|
@ -123,14 +131,14 @@ func ParsePackageMetaData(r io.ReaderAt, size int64) (*Package, error) {
|
||||||
}
|
}
|
||||||
defer f.Close()
|
defer f.Close()
|
||||||
|
|
||||||
return ParseNuspecMetaData(f)
|
return ParseNuspecMetaData(archive, f)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil, ErrMissingNuspecFile
|
return nil, ErrMissingNuspecFile
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseNuspecMetaData parses a Nuspec file to retrieve the metadata of a Nuget package
|
// ParseNuspecMetaData parses a Nuspec file to retrieve the metadata of a Nuget package
|
||||||
func ParseNuspecMetaData(r io.Reader) (*Package, error) {
|
func ParseNuspecMetaData(archive *zip.Reader, r io.Reader) (*Package, error) {
|
||||||
var nuspecBuf bytes.Buffer
|
var nuspecBuf bytes.Buffer
|
||||||
var p nuspecPackage
|
var p nuspecPackage
|
||||||
if err := xml.NewDecoder(io.TeeReader(r, &nuspecBuf)).Decode(&p); err != nil {
|
if err := xml.NewDecoder(io.TeeReader(r, &nuspecBuf)).Decode(&p); err != nil {
|
||||||
|
@ -168,6 +176,28 @@ func ParseNuspecMetaData(r io.Reader) (*Package, error) {
|
||||||
Dependencies: make(map[string][]Dependency),
|
Dependencies: make(map[string][]Dependency),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if p.Metadata.Readme != "" {
|
||||||
|
f, err := archive.Open(p.Metadata.Readme)
|
||||||
|
if err == nil {
|
||||||
|
buf, _ := io.ReadAll(f)
|
||||||
|
m.Readme = string(buf)
|
||||||
|
_ = f.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(p.Metadata.Dependencies.Dependency) > 0 {
|
||||||
|
deps := make([]Dependency, 0, len(p.Metadata.Dependencies.Dependency))
|
||||||
|
for _, dep := range p.Metadata.Dependencies.Dependency {
|
||||||
|
if dep.ID == "" || dep.Version == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
deps = append(deps, Dependency{
|
||||||
|
ID: dep.ID,
|
||||||
|
Version: dep.Version,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
m.Dependencies[""] = deps
|
||||||
|
}
|
||||||
for _, group := range p.Metadata.Dependencies.Group {
|
for _, group := range p.Metadata.Dependencies.Group {
|
||||||
deps := make([]Dependency, 0, len(group.Dependency))
|
deps := make([]Dependency, 0, len(group.Dependency))
|
||||||
for _, dep := range group.Dependency {
|
for _, dep := range group.Dependency {
|
||||||
|
|
|
@ -6,7 +6,6 @@ package nuget
|
||||||
import (
|
import (
|
||||||
"archive/zip"
|
"archive/zip"
|
||||||
"bytes"
|
"bytes"
|
||||||
"strings"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
@ -19,6 +18,7 @@ const (
|
||||||
projectURL = "https://gitea.io"
|
projectURL = "https://gitea.io"
|
||||||
description = "Package Description"
|
description = "Package Description"
|
||||||
releaseNotes = "Package Release Notes"
|
releaseNotes = "Package Release Notes"
|
||||||
|
readme = "Readme"
|
||||||
repositoryURL = "https://gitea.io/gitea/gitea"
|
repositoryURL = "https://gitea.io/gitea/gitea"
|
||||||
targetFramework = ".NETStandard2.1"
|
targetFramework = ".NETStandard2.1"
|
||||||
dependencyID = "System.Text.Json"
|
dependencyID = "System.Text.Json"
|
||||||
|
@ -36,6 +36,7 @@ const nuspecContent = `<?xml version="1.0" encoding="utf-8"?>
|
||||||
<description>` + description + `</description>
|
<description>` + description + `</description>
|
||||||
<releaseNotes>` + releaseNotes + `</releaseNotes>
|
<releaseNotes>` + releaseNotes + `</releaseNotes>
|
||||||
<repository url="` + repositoryURL + `" />
|
<repository url="` + repositoryURL + `" />
|
||||||
|
<readme>README.md</readme>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<group targetFramework="` + targetFramework + `">
|
<group targetFramework="` + targetFramework + `">
|
||||||
<dependency id="` + dependencyID + `" version="` + dependencyVersion + `" exclude="Build,Analyzers" />
|
<dependency id="` + dependencyID + `" version="` + dependencyVersion + `" exclude="Build,Analyzers" />
|
||||||
|
@ -60,17 +61,19 @@ const symbolsNuspecContent = `<?xml version="1.0" encoding="utf-8"?>
|
||||||
</package>`
|
</package>`
|
||||||
|
|
||||||
func TestParsePackageMetaData(t *testing.T) {
|
func TestParsePackageMetaData(t *testing.T) {
|
||||||
createArchive := func(name, content string) []byte {
|
createArchive := func(files map[string]string) []byte {
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
archive := zip.NewWriter(&buf)
|
archive := zip.NewWriter(&buf)
|
||||||
|
for name, content := range files {
|
||||||
w, _ := archive.Create(name)
|
w, _ := archive.Create(name)
|
||||||
w.Write([]byte(content))
|
w.Write([]byte(content))
|
||||||
|
}
|
||||||
archive.Close()
|
archive.Close()
|
||||||
return buf.Bytes()
|
return buf.Bytes()
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Run("MissingNuspecFile", func(t *testing.T) {
|
t.Run("MissingNuspecFile", func(t *testing.T) {
|
||||||
data := createArchive("dummy.txt", "")
|
data := createArchive(map[string]string{"dummy.txt": ""})
|
||||||
|
|
||||||
np, err := ParsePackageMetaData(bytes.NewReader(data), int64(len(data)))
|
np, err := ParsePackageMetaData(bytes.NewReader(data), int64(len(data)))
|
||||||
assert.Nil(t, np)
|
assert.Nil(t, np)
|
||||||
|
@ -78,7 +81,7 @@ func TestParsePackageMetaData(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("MissingNuspecFileInRoot", func(t *testing.T) {
|
t.Run("MissingNuspecFileInRoot", func(t *testing.T) {
|
||||||
data := createArchive("sub/package.nuspec", "")
|
data := createArchive(map[string]string{"sub/package.nuspec": ""})
|
||||||
|
|
||||||
np, err := ParsePackageMetaData(bytes.NewReader(data), int64(len(data)))
|
np, err := ParsePackageMetaData(bytes.NewReader(data), int64(len(data)))
|
||||||
assert.Nil(t, np)
|
assert.Nil(t, np)
|
||||||
|
@ -86,7 +89,7 @@ func TestParsePackageMetaData(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("InvalidNuspecFile", func(t *testing.T) {
|
t.Run("InvalidNuspecFile", func(t *testing.T) {
|
||||||
data := createArchive("package.nuspec", "")
|
data := createArchive(map[string]string{"package.nuspec": ""})
|
||||||
|
|
||||||
np, err := ParsePackageMetaData(bytes.NewReader(data), int64(len(data)))
|
np, err := ParsePackageMetaData(bytes.NewReader(data), int64(len(data)))
|
||||||
assert.Nil(t, np)
|
assert.Nil(t, np)
|
||||||
|
@ -94,10 +97,10 @@ func TestParsePackageMetaData(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("InvalidPackageId", func(t *testing.T) {
|
t.Run("InvalidPackageId", func(t *testing.T) {
|
||||||
data := createArchive("package.nuspec", `<?xml version="1.0" encoding="utf-8"?>
|
data := createArchive(map[string]string{"package.nuspec": `<?xml version="1.0" encoding="utf-8"?>
|
||||||
<package xmlns="http://schemas.microsoft.com/packaging/2013/05/nuspec.xsd">
|
<package xmlns="http://schemas.microsoft.com/packaging/2013/05/nuspec.xsd">
|
||||||
<metadata></metadata>
|
<metadata></metadata>
|
||||||
</package>`)
|
</package>`})
|
||||||
|
|
||||||
np, err := ParsePackageMetaData(bytes.NewReader(data), int64(len(data)))
|
np, err := ParsePackageMetaData(bytes.NewReader(data), int64(len(data)))
|
||||||
assert.Nil(t, np)
|
assert.Nil(t, np)
|
||||||
|
@ -105,30 +108,34 @@ func TestParsePackageMetaData(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("InvalidPackageVersion", func(t *testing.T) {
|
t.Run("InvalidPackageVersion", func(t *testing.T) {
|
||||||
data := createArchive("package.nuspec", `<?xml version="1.0" encoding="utf-8"?>
|
data := createArchive(map[string]string{"package.nuspec": `<?xml version="1.0" encoding="utf-8"?>
|
||||||
<package xmlns="http://schemas.microsoft.com/packaging/2013/05/nuspec.xsd">
|
<package xmlns="http://schemas.microsoft.com/packaging/2013/05/nuspec.xsd">
|
||||||
<metadata>
|
<metadata>
|
||||||
<id>` + id + `</id>
|
<id>` + id + `</id>
|
||||||
</metadata>
|
</metadata>
|
||||||
</package>`)
|
</package>`})
|
||||||
|
|
||||||
np, err := ParsePackageMetaData(bytes.NewReader(data), int64(len(data)))
|
np, err := ParsePackageMetaData(bytes.NewReader(data), int64(len(data)))
|
||||||
assert.Nil(t, np)
|
assert.Nil(t, np)
|
||||||
assert.ErrorIs(t, err, ErrNuspecInvalidVersion)
|
assert.ErrorIs(t, err, ErrNuspecInvalidVersion)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Valid", func(t *testing.T) {
|
t.Run("MissingReadme", func(t *testing.T) {
|
||||||
data := createArchive("package.nuspec", nuspecContent)
|
data := createArchive(map[string]string{"package.nuspec": nuspecContent})
|
||||||
|
|
||||||
np, err := ParsePackageMetaData(bytes.NewReader(data), int64(len(data)))
|
np, err := ParsePackageMetaData(bytes.NewReader(data), int64(len(data)))
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.NotNil(t, np)
|
assert.NotNil(t, np)
|
||||||
|
assert.Empty(t, np.Metadata.Readme)
|
||||||
})
|
})
|
||||||
}
|
|
||||||
|
|
||||||
func TestParseNuspecMetaData(t *testing.T) {
|
|
||||||
t.Run("Dependency Package", func(t *testing.T) {
|
t.Run("Dependency Package", func(t *testing.T) {
|
||||||
np, err := ParseNuspecMetaData(strings.NewReader(nuspecContent))
|
data := createArchive(map[string]string{
|
||||||
|
"package.nuspec": nuspecContent,
|
||||||
|
"README.md": readme,
|
||||||
|
})
|
||||||
|
|
||||||
|
np, err := ParsePackageMetaData(bytes.NewReader(data), int64(len(data)))
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.NotNil(t, np)
|
assert.NotNil(t, np)
|
||||||
assert.Equal(t, DependencyPackage, np.PackageType)
|
assert.Equal(t, DependencyPackage, np.PackageType)
|
||||||
|
@ -139,6 +146,7 @@ func TestParseNuspecMetaData(t *testing.T) {
|
||||||
assert.Equal(t, projectURL, np.Metadata.ProjectURL)
|
assert.Equal(t, projectURL, np.Metadata.ProjectURL)
|
||||||
assert.Equal(t, description, np.Metadata.Description)
|
assert.Equal(t, description, np.Metadata.Description)
|
||||||
assert.Equal(t, releaseNotes, np.Metadata.ReleaseNotes)
|
assert.Equal(t, releaseNotes, np.Metadata.ReleaseNotes)
|
||||||
|
assert.Equal(t, readme, np.Metadata.Readme)
|
||||||
assert.Equal(t, repositoryURL, np.Metadata.RepositoryURL)
|
assert.Equal(t, repositoryURL, np.Metadata.RepositoryURL)
|
||||||
assert.Len(t, np.Metadata.Dependencies, 1)
|
assert.Len(t, np.Metadata.Dependencies, 1)
|
||||||
assert.Contains(t, np.Metadata.Dependencies, targetFramework)
|
assert.Contains(t, np.Metadata.Dependencies, targetFramework)
|
||||||
|
@ -148,13 +156,15 @@ func TestParseNuspecMetaData(t *testing.T) {
|
||||||
assert.Equal(t, dependencyVersion, deps[0].Version)
|
assert.Equal(t, dependencyVersion, deps[0].Version)
|
||||||
|
|
||||||
t.Run("NormalizedVersion", func(t *testing.T) {
|
t.Run("NormalizedVersion", func(t *testing.T) {
|
||||||
np, err := ParseNuspecMetaData(strings.NewReader(`<?xml version="1.0" encoding="utf-8"?>
|
data := createArchive(map[string]string{"package.nuspec": `<?xml version="1.0" encoding="utf-8"?>
|
||||||
<package xmlns="http://schemas.microsoft.com/packaging/2013/05/nuspec.xsd">
|
<package xmlns="http://schemas.microsoft.com/packaging/2013/05/nuspec.xsd">
|
||||||
<metadata>
|
<metadata>
|
||||||
<id>test</id>
|
<id>test</id>
|
||||||
<version>1.04.5.2.5-rc.1+metadata</version>
|
<version>1.04.5.2.5-rc.1+metadata</version>
|
||||||
</metadata>
|
</metadata>
|
||||||
</package>`))
|
</package>`})
|
||||||
|
|
||||||
|
np, err := ParsePackageMetaData(bytes.NewReader(data), int64(len(data)))
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.NotNil(t, np)
|
assert.NotNil(t, np)
|
||||||
assert.Equal(t, "1.4.5.2-rc.1", np.Version)
|
assert.Equal(t, "1.4.5.2-rc.1", np.Version)
|
||||||
|
@ -162,7 +172,9 @@ func TestParseNuspecMetaData(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Symbols Package", func(t *testing.T) {
|
t.Run("Symbols Package", func(t *testing.T) {
|
||||||
np, err := ParseNuspecMetaData(strings.NewReader(symbolsNuspecContent))
|
data := createArchive(map[string]string{"package.nuspec": symbolsNuspecContent})
|
||||||
|
|
||||||
|
np, err := ParsePackageMetaData(bytes.NewReader(data), int64(len(data)))
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.NotNil(t, np)
|
assert.NotNil(t, np)
|
||||||
assert.Equal(t, SymbolsPackage, np.PackageType)
|
assert.Equal(t, SymbolsPackage, np.PackageType)
|
||||||
|
|
|
@ -16,12 +16,11 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{if or .PackageDescriptor.Metadata.Description .PackageDescriptor.Metadata.ReleaseNotes}}
|
{{if or .PackageDescriptor.Metadata.Description .PackageDescriptor.Metadata.ReleaseNotes .PackageDescriptor.Metadata.Readme}}
|
||||||
<h4 class="ui top attached header">{{ctx.Locale.Tr "packages.about"}}</h4>
|
<h4 class="ui top attached header">{{ctx.Locale.Tr "packages.about"}}</h4>
|
||||||
<div class="ui attached segment">
|
{{if .PackageDescriptor.Metadata.Description}}<div class="ui attached segment">{{RenderMarkdownToHtml $.Context .PackageDescriptor.Metadata.Description}}</div>{{end}}
|
||||||
{{if .PackageDescriptor.Metadata.Description}}{{.PackageDescriptor.Metadata.Description}}{{end}}
|
{{if .PackageDescriptor.Metadata.Readme}}<div class="ui attached segment markup markdown">{{RenderMarkdownToHtml $.Context .PackageDescriptor.Metadata.Readme}}</div>{{end}}
|
||||||
{{if .PackageDescriptor.Metadata.ReleaseNotes}}{{.PackageDescriptor.Metadata.ReleaseNotes}}{{end}}
|
{{if .PackageDescriptor.Metadata.ReleaseNotes}}<div class="ui attached segment">{{RenderMarkdownToHtml $.Context .PackageDescriptor.Metadata.ReleaseNotes}}</div>{{end}}
|
||||||
</div>
|
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
{{if .PackageDescriptor.Metadata.Dependencies}}
|
{{if .PackageDescriptor.Metadata.Dependencies}}
|
||||||
|
|
Loading…
Reference in a new issue