add purl types to cataloger info cmd (#4984)

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
This commit is contained in:
Alex Goodman
2026-06-16 12:13:34 -04:00
committed by GitHub
parent 92ae4d44c5
commit 951fbd454a
40 changed files with 266 additions and 7 deletions
+18 -6
View File
@@ -60,6 +60,7 @@ type (
Packages []detectorPackageInfo `json:"packages,omitempty"`
Comment string `json:"comment,omitempty"`
PackageTypes []string `json:"package_types,omitempty"`
PURLTypes []string `json:"purl_types,omitempty"`
JSONSchemaTypes []string `json:"json_schema_types,omitempty"`
Capabilities capabilities.CapabilitySet `json:"capabilities,omitempty"`
}
@@ -235,7 +236,7 @@ func renderCatalogerInfoJSON(doc *capabilities.Document, catalogers []capabiliti
// if no parsers, use detectors instead
if len(info.Patterns) == 0 {
info.Capabilities = cat.Capabilities
info.Patterns = convertDetectorsToPatterns(cat.Detectors, cat.PackageTypes, cat.JSONSchemaTypes)
info.Patterns = convertDetectorsToPatterns(cat.Detectors, cat.PackageTypes, cat.PURLTypes, cat.JSONSchemaTypes)
}
info.Config = getConfigInfoFromDocument(doc, cat.Config)
@@ -278,6 +279,7 @@ func convertParsersToPatterns(parsers []capabilities.Parser) []patternInfo {
Packages: convertDetectorPackages(parser.Detector.Packages),
Comment: parser.Detector.Comment,
PackageTypes: parser.PackageTypes,
PURLTypes: parser.PURLTypes,
JSONSchemaTypes: parser.JSONSchemaTypes,
Capabilities: parser.Capabilities,
})
@@ -286,7 +288,7 @@ func convertParsersToPatterns(parsers []capabilities.Parser) []patternInfo {
}
// convertDetectorsToPatterns converts detector entries to pattern info for JSON output (for non-parser catalogers)
func convertDetectorsToPatterns(detectors []capabilities.Detector, packageTypes, jsonSchemaTypes []string) []patternInfo {
func convertDetectorsToPatterns(detectors []capabilities.Detector, packageTypes, purlTypes, jsonSchemaTypes []string) []patternInfo {
var patterns []patternInfo
for _, det := range detectors {
patterns = append(patterns, patternInfo{
@@ -296,6 +298,7 @@ func convertDetectorsToPatterns(detectors []capabilities.Detector, packageTypes,
Packages: convertDetectorPackages(det.Packages),
Comment: det.Comment,
PackageTypes: packageTypes,
PURLTypes: purlTypes,
JSONSchemaTypes: jsonSchemaTypes,
})
}
@@ -353,7 +356,7 @@ func renderCatalogerInfoTable(_ *capabilities.Document, catalogers []capabilitie
)
// set headers
table.Header("ECOSYSTEM", "CATALOGER", "CRITERIA", "LICENSE", "NODES", "EDGES", "KINDS", "LISTING", "DIGESTS", "HASH")
table.Header("ECOSYSTEM", "CATALOGER", "CRITERIA", "PURL", "LICENSE", "NODES", "EDGES", "KINDS", "LISTING", "DIGESTS", "HASH")
// build rows for each cataloger
var data [][]string
@@ -367,13 +370,13 @@ func renderCatalogerInfoTable(_ *capabilities.Document, catalogers []capabilitie
// generic catalogers: one row per parser
for _, parser := range cat.Parsers {
criteria := formatCriteria([]capabilities.Detector{parser.Detector})
row := buildTableRowFromCapabilities(ecosystem, cat.Name, criteria, parser.Capabilities)
row := buildTableRowFromCapabilities(ecosystem, cat.Name, criteria, parser.PURLTypes, parser.Capabilities)
data = append(data, row)
}
} else {
// custom catalogers: one row with all detectors
criteria := formatCriteria(cat.Detectors)
row := buildTableRowFromCapabilities(ecosystem, cat.Name, criteria, cat.Capabilities)
row := buildTableRowFromCapabilities(ecosystem, cat.Name, criteria, cat.PURLTypes, cat.Capabilities)
data = append(data, row)
}
}
@@ -385,7 +388,7 @@ func renderCatalogerInfoTable(_ *capabilities.Document, catalogers []capabilitie
}
// buildTableRowFromCapabilities builds a table row from capability values
func buildTableRowFromCapabilities(ecosystem, name, criteria string, caps capabilities.CapabilitySet) []string {
func buildTableRowFromCapabilities(ecosystem, name, criteria string, purlTypes []string, caps capabilities.CapabilitySet) []string {
// extract capability default values
license := extractBoolCapability(caps, "license")
nodes := extractNodesCapability(caps)
@@ -399,6 +402,7 @@ func buildTableRowFromCapabilities(ecosystem, name, criteria string, caps capabi
ecosystem,
name,
criteria,
formatPURLTypes(purlTypes),
license,
nodes,
edges,
@@ -409,6 +413,14 @@ func buildTableRowFromCapabilities(ecosystem, name, criteria string, caps capabi
}
}
// formatPURLTypes renders the PURL types as a comma-separated list, or a placeholder when empty
func formatPURLTypes(purlTypes []string) string {
if len(purlTypes) == 0 {
return noStyle.Render("·")
}
return strings.Join(purlTypes, ", ")
}
// extractBoolCapability extracts a boolean capability value and formats it
func extractBoolCapability(caps capabilities.CapabilitySet, name string) string {
for _, cap := range caps {
@@ -280,12 +280,15 @@ func Test_catalogerInfoReport(t *testing.T) {
Detectors: []capabilities.Detector{
{Method: capabilities.GlobDetection, Criteria: []string{"**/*.test"}},
},
PURLTypes: []string{"npm"},
},
},
assertions: func(t *testing.T, got string) {
assert.Contains(t, got, "test-cataloger")
assert.Contains(t, got, "ECOSYSTEM")
assert.Contains(t, got, "CATALOGER")
assert.Contains(t, got, "PURL")
assert.Contains(t, got, "npm")
},
},
{
@@ -298,12 +301,17 @@ func Test_catalogerInfoReport(t *testing.T) {
Ecosystem: "test",
Type: "custom",
Selectors: []string{"test", "custom"},
Detectors: []capabilities.Detector{
{Method: capabilities.GlobDetection, Criteria: []string{"**/*.test"}},
},
PURLTypes: []string{"npm"},
},
},
assertions: func(t *testing.T, got string) {
assert.Contains(t, got, `"name":"test-cataloger"`)
assert.Contains(t, got, `"ecosystem":"test"`)
assert.Contains(t, got, `"type":"custom"`)
assert.Contains(t, got, `"purl_types":["npm"]`)
},
},
{