Skip to content

Commit cc95def

Browse files
GiteaBotKN4CK3RBenjamin Heemann
authored
Some NuGet package enhancements (#30280) (#30324)
Backport #30280 by @KN4CK3R Fixes #30265 1. Read second type of dependencies 2. Render `Description` and `ReleaseNotes` old: ![grafik](https://github.com/go-gitea/gitea/assets/1666336/abac057c-11cd-4d25-b196-01ff899d948e) new: ![grafik](https://github.com/go-gitea/gitea/assets/1666336/35302273-740c-481a-a031-1f80d2d7d336) The NuGet spec does not specify what kind of text can be stored in the description but we can best guess markdown. The official NuGet registry just [converts the newlines to html lines](https://www.nuget.org/packages/rb.Firefox#readme-body-tab). 3. Extract and render the readme. This is the new and better place to store larger text than in the description. The content is markdown. ![grafik](https://github.com/go-gitea/gitea/assets/1666336/f442264e-3735-4b55-92c4-3b89a8ebafb0) Co-authored-by: KN4CK3R <[email protected]> Co-authored-by: Benjamin Heemann <[email protected]>
1 parent 10d83ae commit cc95def

File tree

3 files changed

+73
-32
lines changed

3 files changed

+73
-32
lines changed

modules/packages/nuget/metadata.go

+32-2
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ type Package struct {
5858
type Metadata struct {
5959
Description string `json:"description,omitempty"`
6060
ReleaseNotes string `json:"release_notes,omitempty"`
61+
Readme string `json:"readme,omitempty"`
6162
Authors string `json:"authors,omitempty"`
6263
ProjectURL string `json:"project_url,omitempty"`
6364
RepositoryURL string `json:"repository_url,omitempty"`
@@ -71,6 +72,7 @@ type Dependency struct {
7172
Version string `json:"version"`
7273
}
7374

75+
// https://learn.microsoft.com/en-us/nuget/reference/nuspec
7476
type nuspecPackage struct {
7577
Metadata struct {
7678
ID string `xml:"id"`
@@ -80,6 +82,7 @@ type nuspecPackage struct {
8082
ProjectURL string `xml:"projectUrl"`
8183
Description string `xml:"description"`
8284
ReleaseNotes string `xml:"releaseNotes"`
85+
Readme string `xml:"readme"`
8386
PackageTypes struct {
8487
PackageType []struct {
8588
Name string `xml:"name,attr"`
@@ -89,6 +92,11 @@ type nuspecPackage struct {
8992
URL string `xml:"url,attr"`
9093
} `xml:"repository"`
9194
Dependencies struct {
95+
Dependency []struct {
96+
ID string `xml:"id,attr"`
97+
Version string `xml:"version,attr"`
98+
Exclude string `xml:"exclude,attr"`
99+
} `xml:"dependency"`
92100
Group []struct {
93101
TargetFramework string `xml:"targetFramework,attr"`
94102
Dependency []struct {
@@ -122,14 +130,14 @@ func ParsePackageMetaData(r io.ReaderAt, size int64) (*Package, error) {
122130
}
123131
defer f.Close()
124132

125-
return ParseNuspecMetaData(f)
133+
return ParseNuspecMetaData(archive, f)
126134
}
127135
}
128136
return nil, ErrMissingNuspecFile
129137
}
130138

131139
// ParseNuspecMetaData parses a Nuspec file to retrieve the metadata of a Nuget package
132-
func ParseNuspecMetaData(r io.Reader) (*Package, error) {
140+
func ParseNuspecMetaData(archive *zip.Reader, r io.Reader) (*Package, error) {
133141
var p nuspecPackage
134142
if err := xml.NewDecoder(r).Decode(&p); err != nil {
135143
return nil, err
@@ -166,6 +174,28 @@ func ParseNuspecMetaData(r io.Reader) (*Package, error) {
166174
Dependencies: make(map[string][]Dependency),
167175
}
168176

177+
if p.Metadata.Readme != "" {
178+
f, err := archive.Open(p.Metadata.Readme)
179+
if err == nil {
180+
buf, _ := io.ReadAll(f)
181+
m.Readme = string(buf)
182+
_ = f.Close()
183+
}
184+
}
185+
186+
if len(p.Metadata.Dependencies.Dependency) > 0 {
187+
deps := make([]Dependency, 0, len(p.Metadata.Dependencies.Dependency))
188+
for _, dep := range p.Metadata.Dependencies.Dependency {
189+
if dep.ID == "" || dep.Version == "" {
190+
continue
191+
}
192+
deps = append(deps, Dependency{
193+
ID: dep.ID,
194+
Version: dep.Version,
195+
})
196+
}
197+
m.Dependencies[""] = deps
198+
}
169199
for _, group := range p.Metadata.Dependencies.Group {
170200
deps := make([]Dependency, 0, len(group.Dependency))
171201
for _, dep := range group.Dependency {

modules/packages/nuget/metadata_test.go

+37-25
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ package nuget
66
import (
77
"archive/zip"
88
"bytes"
9-
"strings"
109
"testing"
1110

1211
"github.com/stretchr/testify/assert"
@@ -19,6 +18,7 @@ const (
1918
projectURL = "https://gitea.io"
2019
description = "Package Description"
2120
releaseNotes = "Package Release Notes"
21+
readme = "Readme"
2222
repositoryURL = "https://gitea.io/gitea/gitea"
2323
targetFramework = ".NETStandard2.1"
2424
dependencyID = "System.Text.Json"
@@ -36,6 +36,7 @@ const nuspecContent = `<?xml version="1.0" encoding="utf-8"?>
3636
<description>` + description + `</description>
3737
<releaseNotes>` + releaseNotes + `</releaseNotes>
3838
<repository url="` + repositoryURL + `" />
39+
<readme>README.md</readme>
3940
<dependencies>
4041
<group targetFramework="` + targetFramework + `">
4142
<dependency id="` + dependencyID + `" version="` + dependencyVersion + `" exclude="Build,Analyzers" />
@@ -60,75 +61,81 @@ const symbolsNuspecContent = `<?xml version="1.0" encoding="utf-8"?>
6061
</package>`
6162

6263
func TestParsePackageMetaData(t *testing.T) {
63-
createArchive := func(name, content string) []byte {
64+
createArchive := func(files map[string]string) []byte {
6465
var buf bytes.Buffer
6566
archive := zip.NewWriter(&buf)
66-
w, _ := archive.Create(name)
67-
w.Write([]byte(content))
67+
for name, content := range files {
68+
w, _ := archive.Create(name)
69+
w.Write([]byte(content))
70+
}
6871
archive.Close()
6972
return buf.Bytes()
7073
}
7174

7275
t.Run("MissingNuspecFile", func(t *testing.T) {
73-
data := createArchive("dummy.txt", "")
76+
data := createArchive(map[string]string{"dummy.txt": ""})
7477

7578
np, err := ParsePackageMetaData(bytes.NewReader(data), int64(len(data)))
7679
assert.Nil(t, np)
7780
assert.ErrorIs(t, err, ErrMissingNuspecFile)
7881
})
7982

8083
t.Run("MissingNuspecFileInRoot", func(t *testing.T) {
81-
data := createArchive("sub/package.nuspec", "")
84+
data := createArchive(map[string]string{"sub/package.nuspec": ""})
8285

8386
np, err := ParsePackageMetaData(bytes.NewReader(data), int64(len(data)))
8487
assert.Nil(t, np)
8588
assert.ErrorIs(t, err, ErrMissingNuspecFile)
8689
})
8790

8891
t.Run("InvalidNuspecFile", func(t *testing.T) {
89-
data := createArchive("package.nuspec", "")
92+
data := createArchive(map[string]string{"package.nuspec": ""})
9093

9194
np, err := ParsePackageMetaData(bytes.NewReader(data), int64(len(data)))
9295
assert.Nil(t, np)
9396
assert.Error(t, err)
9497
})
9598

9699
t.Run("InvalidPackageId", func(t *testing.T) {
97-
data := createArchive("package.nuspec", `<?xml version="1.0" encoding="utf-8"?>
100+
data := createArchive(map[string]string{"package.nuspec": `<?xml version="1.0" encoding="utf-8"?>
98101
<package xmlns="http://schemas.microsoft.com/packaging/2013/05/nuspec.xsd">
99102
<metadata></metadata>
100-
</package>`)
103+
</package>`})
101104

102105
np, err := ParsePackageMetaData(bytes.NewReader(data), int64(len(data)))
103106
assert.Nil(t, np)
104107
assert.ErrorIs(t, err, ErrNuspecInvalidID)
105108
})
106109

107110
t.Run("InvalidPackageVersion", func(t *testing.T) {
108-
data := createArchive("package.nuspec", `<?xml version="1.0" encoding="utf-8"?>
111+
data := createArchive(map[string]string{"package.nuspec": `<?xml version="1.0" encoding="utf-8"?>
109112
<package xmlns="http://schemas.microsoft.com/packaging/2013/05/nuspec.xsd">
110113
<metadata>
111-
<id>`+id+`</id>
114+
<id>` + id + `</id>
112115
</metadata>
113-
</package>`)
116+
</package>`})
114117

115118
np, err := ParsePackageMetaData(bytes.NewReader(data), int64(len(data)))
116119
assert.Nil(t, np)
117120
assert.ErrorIs(t, err, ErrNuspecInvalidVersion)
118121
})
119122

120-
t.Run("Valid", func(t *testing.T) {
121-
data := createArchive("package.nuspec", nuspecContent)
123+
t.Run("MissingReadme", func(t *testing.T) {
124+
data := createArchive(map[string]string{"package.nuspec": nuspecContent})
122125

123126
np, err := ParsePackageMetaData(bytes.NewReader(data), int64(len(data)))
124127
assert.NoError(t, err)
125128
assert.NotNil(t, np)
129+
assert.Empty(t, np.Metadata.Readme)
126130
})
127-
}
128131

129-
func TestParseNuspecMetaData(t *testing.T) {
130132
t.Run("Dependency Package", func(t *testing.T) {
131-
np, err := ParseNuspecMetaData(strings.NewReader(nuspecContent))
133+
data := createArchive(map[string]string{
134+
"package.nuspec": nuspecContent,
135+
"README.md": readme,
136+
})
137+
138+
np, err := ParsePackageMetaData(bytes.NewReader(data), int64(len(data)))
132139
assert.NoError(t, err)
133140
assert.NotNil(t, np)
134141
assert.Equal(t, DependencyPackage, np.PackageType)
@@ -139,6 +146,7 @@ func TestParseNuspecMetaData(t *testing.T) {
139146
assert.Equal(t, projectURL, np.Metadata.ProjectURL)
140147
assert.Equal(t, description, np.Metadata.Description)
141148
assert.Equal(t, releaseNotes, np.Metadata.ReleaseNotes)
149+
assert.Equal(t, readme, np.Metadata.Readme)
142150
assert.Equal(t, repositoryURL, np.Metadata.RepositoryURL)
143151
assert.Len(t, np.Metadata.Dependencies, 1)
144152
assert.Contains(t, np.Metadata.Dependencies, targetFramework)
@@ -148,21 +156,25 @@ func TestParseNuspecMetaData(t *testing.T) {
148156
assert.Equal(t, dependencyVersion, deps[0].Version)
149157

150158
t.Run("NormalizedVersion", func(t *testing.T) {
151-
np, err := ParseNuspecMetaData(strings.NewReader(`<?xml version="1.0" encoding="utf-8"?>
152-
<package xmlns="http://schemas.microsoft.com/packaging/2013/05/nuspec.xsd">
153-
<metadata>
154-
<id>test</id>
155-
<version>1.04.5.2.5-rc.1+metadata</version>
156-
</metadata>
157-
</package>`))
159+
data := createArchive(map[string]string{"package.nuspec": `<?xml version="1.0" encoding="utf-8"?>
160+
<package xmlns="http://schemas.microsoft.com/packaging/2013/05/nuspec.xsd">
161+
<metadata>
162+
<id>test</id>
163+
<version>1.04.5.2.5-rc.1+metadata</version>
164+
</metadata>
165+
</package>`})
166+
167+
np, err := ParsePackageMetaData(bytes.NewReader(data), int64(len(data)))
158168
assert.NoError(t, err)
159169
assert.NotNil(t, np)
160170
assert.Equal(t, "1.4.5.2-rc.1", np.Version)
161171
})
162172
})
163173

164174
t.Run("Symbols Package", func(t *testing.T) {
165-
np, err := ParseNuspecMetaData(strings.NewReader(symbolsNuspecContent))
175+
data := createArchive(map[string]string{"package.nuspec": symbolsNuspecContent})
176+
177+
np, err := ParsePackageMetaData(bytes.NewReader(data), int64(len(data)))
166178
assert.NoError(t, err)
167179
assert.NotNil(t, np)
168180
assert.Equal(t, SymbolsPackage, np.PackageType)

templates/package/content/nuget.tmpl

+4-5
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,11 @@
1616
</div>
1717
</div>
1818

19-
{{if or .PackageDescriptor.Metadata.Description .PackageDescriptor.Metadata.ReleaseNotes}}
19+
{{if or .PackageDescriptor.Metadata.Description .PackageDescriptor.Metadata.ReleaseNotes .PackageDescriptor.Metadata.Readme}}
2020
<h4 class="ui top attached header">{{ctx.Locale.Tr "packages.about"}}</h4>
21-
<div class="ui attached segment">
22-
{{if .PackageDescriptor.Metadata.Description}}{{.PackageDescriptor.Metadata.Description}}{{end}}
23-
{{if .PackageDescriptor.Metadata.ReleaseNotes}}{{.PackageDescriptor.Metadata.ReleaseNotes}}{{end}}
24-
</div>
21+
{{if .PackageDescriptor.Metadata.Description}}<div class="ui attached segment">{{RenderMarkdownToHtml $.Context .PackageDescriptor.Metadata.Description}}</div>{{end}}
22+
{{if .PackageDescriptor.Metadata.Readme}}<div class="ui attached segment markup markdown">{{RenderMarkdownToHtml $.Context .PackageDescriptor.Metadata.Readme}}</div>{{end}}
23+
{{if .PackageDescriptor.Metadata.ReleaseNotes}}<div class="ui attached segment">{{RenderMarkdownToHtml $.Context .PackageDescriptor.Metadata.ReleaseNotes}}</div>{{end}}
2524
{{end}}
2625

2726
{{if .PackageDescriptor.Metadata.Dependencies}}

0 commit comments

Comments
 (0)