github.com/Racer159/jackal@v0.32.7-0.20240401174413-0bd2339e4f2e/src/pkg/packager/lint/validator.go (about) 1 // SPDX-License-Identifier: Apache-2.0 2 // SPDX-FileCopyrightText: 2021-Present The Jackal Authors 3 4 // Package lint contains functions for verifying jackal yaml files are valid 5 package lint 6 7 import ( 8 "fmt" 9 "path/filepath" 10 11 "github.com/Racer159/jackal/src/pkg/message" 12 "github.com/Racer159/jackal/src/types" 13 "github.com/defenseunicorns/pkg/helpers" 14 "github.com/fatih/color" 15 ) 16 17 type category int 18 19 const ( 20 categoryError category = 1 21 categoryWarning category = 2 22 ) 23 24 type validatorMessage struct { 25 yqPath string 26 description string 27 item string 28 packageRelPath string 29 packageName string 30 category category 31 } 32 33 func (c category) String() string { 34 if c == categoryError { 35 return message.ColorWrap("Error", color.FgRed) 36 } else if c == categoryWarning { 37 return message.ColorWrap("Warning", color.FgYellow) 38 } 39 return "" 40 } 41 42 func (vm validatorMessage) String() string { 43 if vm.item != "" { 44 vm.item = fmt.Sprintf(" - %s", vm.item) 45 } 46 return fmt.Sprintf("%s%s", vm.description, vm.item) 47 } 48 49 // Validator holds the warnings/errors and messaging that we get from validation 50 type Validator struct { 51 findings []validatorMessage 52 jsonSchema []byte 53 typedJackalPackage types.JackalPackage 54 untypedJackalPackage interface{} 55 baseDir string 56 } 57 58 // DisplayFormattedMessage message sent to user based on validator results 59 func (v Validator) DisplayFormattedMessage() { 60 if !v.hasFindings() { 61 message.Successf("0 findings for %q", v.typedJackalPackage.Metadata.Name) 62 } 63 v.printValidationTable() 64 } 65 66 // IsSuccess returns true if there are not any errors 67 func (v Validator) IsSuccess() bool { 68 for _, finding := range v.findings { 69 if finding.category == categoryError { 70 return false 71 } 72 } 73 return true 74 } 75 76 func (v Validator) packageRelPathToUser(vm validatorMessage) string { 77 if helpers.IsOCIURL(vm.packageRelPath) { 78 return vm.packageRelPath 79 } 80 return filepath.Join(v.baseDir, vm.packageRelPath) 81 } 82 83 func (v Validator) printValidationTable() { 84 if !v.hasFindings() { 85 return 86 } 87 88 mapOfFindingsByPath := make(map[string][]validatorMessage) 89 for _, finding := range v.findings { 90 mapOfFindingsByPath[finding.packageRelPath] = append(mapOfFindingsByPath[finding.packageRelPath], finding) 91 } 92 93 header := []string{"Type", "Path", "Message"} 94 95 for packageRelPath, findings := range mapOfFindingsByPath { 96 lintData := [][]string{} 97 for _, finding := range findings { 98 lintData = append(lintData, []string{finding.category.String(), finding.getPath(), finding.String()}) 99 } 100 message.Notef("Linting package %q at %s", findings[0].packageName, v.packageRelPathToUser(findings[0])) 101 message.Table(header, lintData) 102 message.Info(v.getFormattedFindingCount(packageRelPath, findings[0].packageName)) 103 } 104 } 105 106 func (v Validator) getFormattedFindingCount(relPath string, packageName string) string { 107 warningCount := 0 108 errorCount := 0 109 for _, finding := range v.findings { 110 if finding.packageRelPath != relPath { 111 continue 112 } 113 if finding.category == categoryWarning { 114 warningCount++ 115 } 116 if finding.category == categoryError { 117 errorCount++ 118 } 119 } 120 wordWarning := "warnings" 121 if warningCount == 1 { 122 wordWarning = "warning" 123 } 124 wordError := "errors" 125 if errorCount == 1 { 126 wordError = "error" 127 } 128 return fmt.Sprintf("%d %s and %d %s in %q", 129 warningCount, wordWarning, errorCount, wordError, packageName) 130 } 131 132 func (vm validatorMessage) getPath() string { 133 if vm.yqPath == "" { 134 return "" 135 } 136 return message.ColorWrap(vm.yqPath, color.FgCyan) 137 } 138 139 func (v Validator) hasFindings() bool { 140 return len(v.findings) > 0 141 } 142 143 func (v *Validator) addWarning(vmessage validatorMessage) { 144 vmessage.category = categoryWarning 145 v.findings = helpers.Unique(append(v.findings, vmessage)) 146 } 147 148 func (v *Validator) addError(vMessage validatorMessage) { 149 vMessage.category = categoryError 150 v.findings = helpers.Unique(append(v.findings, vMessage)) 151 }