Alex Goodman b3c70da3ea
Add experimental cataloger capabilities command (#4317)
* add info command from generated capabilities

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* correct gentoo and arch ecosystems

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* rename os pkg types

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* better binary cataloger description

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* expose metadata and pacakge types in json

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* expose json schema types

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* add completeness tests for metadata types

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* latest generation

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* fix linting

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* improve testing a docs

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* fix tests and linting

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* restore goreleaser config

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* tweak diagram

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* fix pdm

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* chore: java binary data

Signed-off-by: Keith Zantow <kzantow@gmail.com>

* new capability descriptions for gguf and python

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* correct poetry lock integrity hash claim

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* fix compile error

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* fix: remove purl version from overrides

Signed-off-by: Keith Zantow <kzantow@gmail.com>

* fix lua deps ref

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* keep gguf as ai ecosystem

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* split packages.yaml to multiple files by go package

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* ensure tests do not use go test cache

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* sort json output for info command

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* docs: fix ocaml, php, and portage capabilities yaml

Signed-off-by: Will Murphy <willmurphyscode@users.noreply.github.com>

* chore: update erlang capabilities

Signed-off-by: Keith Zantow <kzantow@gmail.com>

* chore: update java capabilities

Signed-off-by: Keith Zantow <kzantow@gmail.com>

* chore: update javascript capabilities

Signed-off-by: Keith Zantow <kzantow@gmail.com>

* chore: update linux kernel capabilities

Signed-off-by: Keith Zantow <kzantow@gmail.com>

* remove missing tests

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* fix package.yaml references

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* revert license list change

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* check for drift in capability descriptions

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* regenerate capabilities

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* test cleanup

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* use fixture cache in static analysis

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* claim fixtures pre-req for cap generation

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* update documentation with correct regeneration procedure

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* chore: ruby-gemspec-cataloger finds no dependencies

Signed-off-by: Will Murphy <willmurphyscode@users.noreply.github.com>

* chore: fix python docs and config comment

Signed-off-by: Will Murphy <willmurphyscode@users.noreply.github.com>

* chore: commit re-generated java yaml

Signed-off-by: Will Murphy <willmurphyscode@users.noreply.github.com>

* add cataloger selection to caps command

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* re-generate cap yamls

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* fix tests for cataloger selection

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* fix cli test

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* add missing tests

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* fix linting

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* rename cmd to `cataloger info`

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* [wip] change capability description locations

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* [wip] continued

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* [wip] adjust for import cycles

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* correct docs

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* fix linting

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

---------

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
Signed-off-by: Keith Zantow <kzantow@gmail.com>
Signed-off-by: Will Murphy <willmurphyscode@users.noreply.github.com>
Co-authored-by: Keith Zantow <kzantow@gmail.com>
Co-authored-by: Will Murphy <willmurphyscode@users.noreply.github.com>
2025-12-22 19:34:10 +00:00

151 lines
5.7 KiB
Go

// this is the entry point for regenerating the cataloger/*/capabilities.yaml files, which orchestrates discovery, merging, and validation of cataloger capabilities.
package main
import (
"fmt"
"log"
"os"
"github.com/charmbracelet/lipgloss"
"github.com/anchore/syft/internal/capabilities"
"github.com/anchore/syft/internal/capabilities/internal"
)
var (
successStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("10")).Bold(true) // green
warningStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("11")).Bold(true) // yellow
errorStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("9")).Bold(true) // red
infoStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("14")) // cyan
dimStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("245")) // lighter grey (256-color)
)
func main() {
repoRoot, err := internal.RepoRoot()
if err != nil {
log.Fatalf("failed to find repo root: %v", err)
}
catalogerDir := internal.CatalogerDir(repoRoot)
fmt.Println("Regenerating capabilities files...")
fmt.Println()
stats, err := RegenerateCapabilities(catalogerDir, repoRoot)
if err != nil {
log.Fatalf("failed to regenerate capabilities: %v", err)
}
printSummary(stats)
checkIncompleteCapabilities(catalogerDir, repoRoot)
printMetadataTypeCoverageWarning(catalogerDir, repoRoot)
printPackageTypeCoverageWarning(catalogerDir, repoRoot)
}
func printSummary(stats *Statistics) {
fmt.Println()
fmt.Println(infoStyle.Bold(true).Render("Summary:"))
fmt.Printf(" • Total catalogers: %s (%s generic, %s custom)\n",
infoStyle.Render(fmt.Sprintf("%d", stats.TotalGenericCatalogers+stats.TotalCustomCatalogers)),
infoStyle.Render(fmt.Sprintf("%d", stats.TotalGenericCatalogers)),
infoStyle.Render(fmt.Sprintf("%d", stats.TotalCustomCatalogers)))
fmt.Printf(" • Total parser functions: %s\n", infoStyle.Render(fmt.Sprintf("%d", stats.TotalParserFunctions)))
if len(stats.NewCatalogers) > 0 {
fmt.Printf(" • New catalogers: %s\n", successStyle.Render(fmt.Sprintf("%d", len(stats.NewCatalogers))))
for _, name := range stats.NewCatalogers {
fmt.Printf(" - %s\n", successStyle.Render(name))
}
}
if len(stats.NewParserFunctions) > 0 {
fmt.Printf(" • New parser functions: %s\n", successStyle.Render(fmt.Sprintf("%d", len(stats.NewParserFunctions))))
for _, name := range stats.NewParserFunctions {
fmt.Printf(" - %s\n", successStyle.Render(name))
}
}
if len(stats.UpdatedCatalogers) > 0 {
fmt.Printf(" • Updated catalogers: %s\n", infoStyle.Render(fmt.Sprintf("%d", len(stats.UpdatedCatalogers))))
}
fmt.Println()
fmt.Println(successStyle.Render("✓ Updated capabilities files successfully"))
}
func checkIncompleteCapabilities(catalogerDir, repoRoot string) {
doc, _, err := internal.LoadCapabilities(catalogerDir, repoRoot)
if err != nil {
log.Fatalf("failed to load updated capabilities: %v", err)
}
var needsAttentionGeneric []string
var needsAttentionCustom []string
for _, cataloger := range doc.Catalogers {
switch cataloger.Type {
case genericCatalogerType:
for _, parser := range cataloger.Parsers {
if hasEmptyCapabilities(parser.Capabilities) {
needsAttentionGeneric = append(needsAttentionGeneric, fmt.Sprintf("%s/%s", cataloger.Name, parser.ParserFunction))
}
}
case "custom":
if hasEmptyCapabilities(cataloger.Capabilities) {
needsAttentionCustom = append(needsAttentionCustom, cataloger.Name)
}
}
}
if len(needsAttentionGeneric) > 0 || len(needsAttentionCustom) > 0 {
fmt.Println()
printFailureASCII()
fmt.Println(warningStyle.Render("⚠ WARNING:") + " The following entries have incomplete capabilities:")
if len(needsAttentionGeneric) > 0 {
fmt.Printf(" • %s generic cataloger parser functions need capabilities:\n", errorStyle.Render(fmt.Sprintf("%d", len(needsAttentionGeneric))))
for _, name := range needsAttentionGeneric {
fmt.Printf(" - %s\n", dimStyle.Render(name))
}
}
if len(needsAttentionCustom) > 0 {
fmt.Printf(" • %s custom catalogers need capabilities:\n", errorStyle.Render(fmt.Sprintf("%d", len(needsAttentionCustom))))
for _, name := range needsAttentionCustom {
fmt.Printf(" - %s\n", dimStyle.Render(name))
}
}
fmt.Println()
fmt.Println(dimStyle.Render("Please update these entries in the capabilities files before running tests."))
fmt.Println()
fmt.Println(dimStyle.Render("Exit code: 1"))
os.Exit(1)
}
// show success ASCII art when all validations pass
printSuccessASCII()
}
func hasEmptyCapabilities(caps capabilities.CapabilitySet) bool {
// only flag if capabilities are completely missing (empty array)
// if someone filled out the capabilities section (even with all false/empty values), that's intentional
return len(caps) == 0
}
func printSuccessASCII() {
fmt.Println()
fmt.Println(successStyle.Render("✓ All validations passed!") + " 🎉")
fmt.Println()
fmt.Println(successStyle.Render(" ░█▀▀░█░█░█▀▀░█▀▀░█▀▀░█▀▀░█▀▀"))
fmt.Println(successStyle.Render(" ░▀▀█░█░█░█░░░█░░░█▀▀░▀▀█░▀▀█"))
fmt.Println(successStyle.Render(" ░▀▀▀░▀▀▀░▀▀▀░▀▀▀░▀▀▀░▀▀▀░▀▀▀"))
fmt.Println()
}
func printFailureASCII() {
fmt.Println(errorStyle.Render("✗ Validation failed") + " 😢")
fmt.Println()
fmt.Println(errorStyle.Render(" ░█▀▀░█▀█░▀█▀░█░░░█▀▀░█▀▄"))
fmt.Println(errorStyle.Render(" ░█▀▀░█▀█░░█░░█░░░█▀▀░█░█"))
fmt.Println(errorStyle.Render(" ░▀░░░▀░▀░▀▀▀░▀▀▀░▀▀▀░▀▀░"))
fmt.Println()
}