Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
This commit is contained in:
Alex Goodman 2024-05-03 16:12:00 -04:00
parent d6604adaaf
commit 3e21379492
3 changed files with 178 additions and 15 deletions

View File

@ -69,13 +69,16 @@ func buildDotNetPackage(versionResources map[string]string, f file.LocationReadC
} }
metadata := pkg.DotnetPortableExecutableEntry{ metadata := pkg.DotnetPortableExecutableEntry{
AssemblyVersion: versionResources["Assembly Version"], AssemblyVersion: versionResources["Assembly Version"],
LegalCopyright: versionResources["LegalCopyright"], LegalCopyright: versionResources["LegalCopyright"],
Comments: versionResources["Comments"], Comments: versionResources["Comments"],
InternalName: versionResources["InternalName"], InternalName: versionResources["InternalName"],
CompanyName: versionResources["CompanyName"], CompanyName: versionResources["CompanyName"],
ProductName: versionResources["ProductName"], ProductName: versionResources["ProductName"],
ProductVersion: versionResources["ProductVersion"], ProductVersion: versionResources["ProductVersion"],
FileDescription: versionResources["FileDescription"],
FileVersion: versionResources["FileVersion"],
OriginalFilename: versionResources["OriginalFilename"],
} }
dnpkg = pkg.Package{ dnpkg = pkg.Package{
@ -215,7 +218,7 @@ func findName(versionResources map[string]string) string {
} }
for _, field := range nameFields { for _, field := range nameFields {
value := spaceNormalize(versionResources[field]) value := resolveValue(versionResources[field], versionResources, nil)
if value == "" { if value == "" {
continue continue
} }
@ -225,6 +228,40 @@ func findName(versionResources map[string]string) string {
return "" return ""
} }
func resolveValue(value string, collection map[string]string, visited map[string]bool) string {
value = spaceNormalize(value)
if value == "" {
return ""
}
if visited == nil {
visited = make(map[string]bool)
}
if visited[value] {
return value
}
visited[value] = true
hasIndirect, nextKey := hasIndirectFieldPrefix(value)
if !hasIndirect {
return value
}
return resolveValue(collection[nextKey], collection, visited)
}
func hasIndirectFieldPrefix(value string) (bool, string) {
for _, prefix := range []string{"f#", "p("} {
cleanValue := strings.TrimPrefix(value, prefix)
if cleanValue != value {
return true, cleanValue
}
}
return false, value
}
// normalizes a string to a trimmed version with all contigous whitespace collapsed to a single space character // normalizes a string to a trimmed version with all contigous whitespace collapsed to a single space character
func spaceNormalize(value string) string { func spaceNormalize(value string) string {
value = strings.TrimSpace(value) value = strings.TrimSpace(value)

View File

@ -346,3 +346,126 @@ func Test_spaceNormalize(t *testing.T) {
}) })
} }
} }
func Test_resolveValue(t *testing.T) {
tests := []struct {
name string
value string
collection map[string]string
want string
}{
{
name: "simple value",
value: "value",
collection: map[string]string{
"key": "value",
},
want: "value",
},
{
name: "simple value with spaces",
value: " value ",
collection: map[string]string{
"key": "value",
},
want: "value",
},
{
name: "indirect value - f#",
value: "f#other",
collection: map[string]string{
"key": "f#other",
"other": "value",
},
want: "value",
},
{
name: "indirect value - p(",
value: "f#other",
collection: map[string]string{
"key": "p(other",
"other": "value",
},
want: "value",
},
{
name: "indirect value with cycles",
value: "f#other",
collection: map[string]string{
"key": "f#other",
"other": "f#key",
},
want: "f#other", // this is NOT ideal, but there is no "good" answer here
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert.Equal(t, tt.want, resolveValue(tt.value, tt.collection, nil))
})
}
}
func Test_findVersion(t *testing.T) {
tests := []struct {
name string
versionResources map[string]string
want string
}{
{
name: "prefer file version over product version (when both semver)",
versionResources: map[string]string{
"FileVersion": "1.2.3",
"ProductVersion": "4.5.6",
},
want: "4.5.6",
},
{
name: "prefer file version over product version (when both semver)",
versionResources: map[string]string{
"FileVersion": "1.2.3",
"ProductVersion": "4.5.6.7",
},
want: "1.2.3",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert.Equal(t, tt.want, findVersion(tt.versionResources))
})
}
}
func Test_keepGreaterSemanticVersion(t *testing.T) {
tests := []struct {
name string
productVersion string
fileVersion string
want string
}{
{
name: "product semver is greater",
productVersion: "3.0.0",
fileVersion: "2.0.0",
want: "3.0.0",
},
{
name: "file semver is greater",
productVersion: "2.0.0",
fileVersion: "3.0.0",
want: "3.0.0",
},
{
name: "semver preferred over non-semver",
productVersion: "3.0.0.2",
fileVersion: "3.0.0",
want: "3.0.0",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert.Equal(t, tt.want, keepGreaterSemanticVersion(tt.productVersion, tt.fileVersion))
})
}
}

View File

@ -11,11 +11,14 @@ type DotnetDepsEntry struct {
// DotnetPortableExecutableEntry is a struct that represents a single entry found within "VersionResources" section of a .NET Portable Executable binary file. // DotnetPortableExecutableEntry is a struct that represents a single entry found within "VersionResources" section of a .NET Portable Executable binary file.
type DotnetPortableExecutableEntry struct { type DotnetPortableExecutableEntry struct {
AssemblyVersion string `json:"assemblyVersion"` AssemblyVersion string `json:"assemblyVersion"`
LegalCopyright string `json:"legalCopyright"` LegalCopyright string `json:"legalCopyright"`
Comments string `json:"comments,omitempty"` Comments string `json:"comments,omitempty"`
InternalName string `json:"internalName,omitempty"` InternalName string `json:"internalName,omitempty"`
CompanyName string `json:"companyName"` CompanyName string `json:"companyName"`
ProductName string `json:"productName"` ProductName string `json:"productName"`
ProductVersion string `json:"productVersion"` ProductVersion string `json:"productVersion"`
FileVersion string `json:"fileVersion,omitempty"`
FileDescription string `json:"fileDescription,omitempty"`
OriginalFilename string `json:"originalFilename,omitempty"`
} }