Alex Goodman d6512456b3 improve testing a docs
Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
2025-10-29 10:08:29 -04:00

151 lines
5.5 KiB
Go

// this is the entry point for regenerating the packages.yaml file, which orchestrates discovery, merging, and validation of cataloger capabilities.
package main
import (
"fmt"
"log"
"os"
"path/filepath"
"github.com/charmbracelet/lipgloss"
"github.com/anchore/syft/internal/capabilities"
)
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 := RepoRoot()
if err != nil {
log.Fatalf("failed to find repo root: %v", err)
}
yamlPath := filepath.Join(repoRoot, "internal/capabilities/packages.yaml")
fmt.Println("Regenerating packages.yaml...")
fmt.Println()
stats, err := RegenerateCapabilities(yamlPath, repoRoot)
if err != nil {
log.Fatalf("failed to regenerate capabilities: %v", err)
}
printSummary(stats)
checkIncompleteCapabilities(yamlPath)
printMetadataTypeCoverageWarning(yamlPath, repoRoot)
printPackageTypeCoverageWarning(yamlPath, 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 packages.yaml successfully"))
}
func checkIncompleteCapabilities(yamlPath string) {
doc, _, err := loadCapabilities(yamlPath)
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 packages.yaml 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()
}