Merge pull request #216 from wagoodman/include-inefficient-files-in-ci-output
Add file report to CI results
This commit is contained in:
commit
d89bfed1fe
@ -1,8 +1,10 @@
|
||||
package ci
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/dustin/go-humanize"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
@ -11,12 +13,13 @@ import (
|
||||
"github.com/wagoodman/dive/image"
|
||||
)
|
||||
|
||||
type Evaluator struct {
|
||||
Rules []Rule
|
||||
Results map[string]RuleResult
|
||||
Tally ResultTally
|
||||
Pass bool
|
||||
Misconfigured bool
|
||||
type CiEvaluator struct {
|
||||
Rules []CiRule
|
||||
Results map[string]RuleResult
|
||||
Tally ResultTally
|
||||
Pass bool
|
||||
Misconfigured bool
|
||||
InefficientFiles []ReferenceFile
|
||||
}
|
||||
|
||||
type ResultTally struct {
|
||||
@ -27,19 +30,19 @@ type ResultTally struct {
|
||||
Total int
|
||||
}
|
||||
|
||||
func NewEvaluator(config *viper.Viper) *Evaluator {
|
||||
return &Evaluator{
|
||||
func NewCiEvaluator(config *viper.Viper) *CiEvaluator {
|
||||
return &CiEvaluator{
|
||||
Rules: loadCiRules(config),
|
||||
Results: make(map[string]RuleResult),
|
||||
Pass: true,
|
||||
}
|
||||
}
|
||||
|
||||
func (ci *Evaluator) isRuleEnabled(rule Rule) bool {
|
||||
func (ci *CiEvaluator) isRuleEnabled(rule CiRule) bool {
|
||||
return rule.Configuration() != "disabled"
|
||||
}
|
||||
|
||||
func (ci *Evaluator) Evaluate(analysis *image.AnalysisResult) bool {
|
||||
func (ci *CiEvaluator) Evaluate(analysis *image.AnalysisResult) bool {
|
||||
canEvaluate := true
|
||||
for _, rule := range ci.Rules {
|
||||
if !ci.isRuleEnabled(rule) {
|
||||
@ -72,6 +75,18 @@ func (ci *Evaluator) Evaluate(analysis *image.AnalysisResult) bool {
|
||||
return ci.Pass
|
||||
}
|
||||
|
||||
// capture inefficient files
|
||||
for idx := 0; idx < len(analysis.Inefficiencies); idx++ {
|
||||
fileData := analysis.Inefficiencies[len(analysis.Inefficiencies)-1-idx]
|
||||
|
||||
ci.InefficientFiles = append(ci.InefficientFiles, ReferenceFile{
|
||||
References: len(fileData.Nodes),
|
||||
SizeBytes: uint64(fileData.CumulativeSize),
|
||||
Path: fileData.Path,
|
||||
})
|
||||
}
|
||||
|
||||
// evaluate results against the configured CI rules
|
||||
for _, rule := range ci.Rules {
|
||||
if !ci.isRuleEnabled(rule) {
|
||||
ci.Results[rule.Key()] = RuleResult{
|
||||
@ -117,7 +132,22 @@ func (ci *Evaluator) Evaluate(analysis *image.AnalysisResult) bool {
|
||||
return ci.Pass
|
||||
}
|
||||
|
||||
func (ci *Evaluator) Report() {
|
||||
func (ci *CiEvaluator) Report() {
|
||||
fmt.Println(title("Inefficient Files:"))
|
||||
|
||||
template := "%5s %12s %-s\n"
|
||||
fmt.Printf(template, "Count", "Wasted Space", "File Path")
|
||||
|
||||
if len(ci.InefficientFiles) == 0 {
|
||||
fmt.Println("None")
|
||||
} else {
|
||||
for _, file := range ci.InefficientFiles {
|
||||
fmt.Printf(template, strconv.Itoa(file.References), humanize.Bytes(uint64(file.SizeBytes)), file.Path)
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Println(title("Results:"))
|
||||
|
||||
status := "PASS"
|
||||
|
||||
rules := make([]string, 0, len(ci.Results))
|
@ -1,4 +1,4 @@
|
||||
package ci
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"strings"
|
||||
@ -10,7 +10,7 @@ import (
|
||||
|
||||
func Test_Evaluator(t *testing.T) {
|
||||
|
||||
result, err := image.TestLoadDockerImageTar("../../.data/test-docker-image.tar")
|
||||
result, err := image.TestLoadDockerImageTar("../.data/test-docker-image.tar")
|
||||
if err != nil {
|
||||
t.Fatalf("Test_Export: unable to fetch analysis: %v", err)
|
||||
}
|
||||
@ -35,7 +35,7 @@ func Test_Evaluator(t *testing.T) {
|
||||
ciConfig.SetDefault("rules.highestWastedBytes", test.wastedBytes)
|
||||
ciConfig.SetDefault("rules.highestUserWastedPercent", test.wastedPercent)
|
||||
|
||||
evaluator := NewEvaluator(ciConfig)
|
||||
evaluator := NewCiEvaluator(ciConfig)
|
||||
|
||||
pass := evaluator.Evaluate(result)
|
||||
|
@ -1,4 +1,4 @@
|
||||
package ci
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@ -21,7 +21,7 @@ const (
|
||||
RuleConfigured
|
||||
)
|
||||
|
||||
type Rule interface {
|
||||
type CiRule interface {
|
||||
Key() string
|
||||
Configuration() string
|
||||
Validate() error
|
||||
@ -86,8 +86,8 @@ func (status RuleStatus) String() string {
|
||||
}
|
||||
}
|
||||
|
||||
func loadCiRules(config *viper.Viper) []Rule {
|
||||
var rules = make([]Rule, 0)
|
||||
func loadCiRules(config *viper.Viper) []CiRule {
|
||||
var rules = make([]CiRule, 0)
|
||||
var ruleKey = "lowestEfficiency"
|
||||
rules = append(rules, newGenericCiRule(
|
||||
ruleKey,
|
@ -7,10 +7,29 @@ import (
|
||||
"github.com/wagoodman/dive/image"
|
||||
)
|
||||
|
||||
type export struct {
|
||||
Layer []exportLayer `json:"layer"`
|
||||
Image exportImage `json:"image"`
|
||||
}
|
||||
|
||||
type exportLayer struct {
|
||||
Index int `json:"index"`
|
||||
DigestID string `json:"digestId"`
|
||||
SizeBytes uint64 `json:"sizeBytes"`
|
||||
Command string `json:"command"`
|
||||
}
|
||||
|
||||
type exportImage struct {
|
||||
SizeBytes uint64 `json:"sizeBytes"`
|
||||
InefficientBytes uint64 `json:"inefficientBytes"`
|
||||
EfficiencyScore float64 `json:"efficiencyScore"`
|
||||
InefficientFiles []ReferenceFile `json:"ReferenceFile"`
|
||||
}
|
||||
|
||||
func newExport(analysis *image.AnalysisResult) *export {
|
||||
data := export{}
|
||||
data.Layer = make([]exportLayer, len(analysis.Layers))
|
||||
data.Image.InefficientFiles = make([]inefficientFiles, len(analysis.Inefficiencies))
|
||||
data.Image.InefficientFiles = make([]ReferenceFile, len(analysis.Inefficiencies))
|
||||
|
||||
// export layers in order
|
||||
for revIdx := len(analysis.Layers) - 1; revIdx >= 0; revIdx-- {
|
||||
@ -32,10 +51,10 @@ func newExport(analysis *image.AnalysisResult) *export {
|
||||
for idx := 0; idx < len(analysis.Inefficiencies); idx++ {
|
||||
fileData := analysis.Inefficiencies[len(analysis.Inefficiencies)-1-idx]
|
||||
|
||||
data.Image.InefficientFiles[idx] = inefficientFiles{
|
||||
Count: len(fileData.Nodes),
|
||||
SizeBytes: uint64(fileData.CumulativeSize),
|
||||
File: fileData.Path,
|
||||
data.Image.InefficientFiles[idx] = ReferenceFile{
|
||||
References: len(fileData.Nodes),
|
||||
SizeBytes: uint64(fileData.CumulativeSize),
|
||||
Path: fileData.Path,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -109,7 +109,7 @@ func Test_Export(t *testing.T) {
|
||||
"sizeBytes": 1220598,
|
||||
"inefficientBytes": 32025,
|
||||
"efficiencyScore": 0.9844212134184309,
|
||||
"inefficientFiles": [
|
||||
"ReferenceFile": [
|
||||
{
|
||||
"count": 2,
|
||||
"sizeBytes": 12810,
|
||||
|
13
runtime/options.go
Normal file
13
runtime/options.go
Normal file
@ -0,0 +1,13 @@
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
type Options struct {
|
||||
Ci bool
|
||||
ImageId string
|
||||
ExportFile string
|
||||
CiConfig *viper.Viper
|
||||
BuildArgs []string
|
||||
}
|
7
runtime/reference_file.go
Normal file
7
runtime/reference_file.go
Normal file
@ -0,0 +1,7 @@
|
||||
package runtime
|
||||
|
||||
type ReferenceFile struct {
|
||||
References int `json:"count"`
|
||||
SizeBytes uint64 `json:"sizeBytes"`
|
||||
Path string `json:"file"`
|
||||
}
|
@ -10,7 +10,6 @@ import (
|
||||
"github.com/logrusorgru/aurora"
|
||||
"github.com/wagoodman/dive/filetree"
|
||||
"github.com/wagoodman/dive/image"
|
||||
"github.com/wagoodman/dive/runtime/ci"
|
||||
"github.com/wagoodman/dive/ui"
|
||||
"github.com/wagoodman/dive/utils"
|
||||
)
|
||||
@ -24,9 +23,7 @@ func runCi(analysis *image.AnalysisResult, options Options) {
|
||||
fmt.Printf(" wastedBytes: %d bytes (%s)\n", analysis.WastedBytes, humanize.Bytes(analysis.WastedBytes))
|
||||
fmt.Printf(" userWastedPercent: %2.4f %%\n", analysis.WastedUserPercent*100)
|
||||
|
||||
evaluator := ci.NewEvaluator(options.CiConfig)
|
||||
|
||||
fmt.Println(title("Run CI Validations..."))
|
||||
evaluator := NewCiEvaluator(options.CiConfig)
|
||||
|
||||
pass := evaluator.Evaluate(analysis)
|
||||
evaluator.Report()
|
||||
|
@ -1,38 +0,0 @@
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
type Options struct {
|
||||
Ci bool
|
||||
ImageId string
|
||||
ExportFile string
|
||||
CiConfig *viper.Viper
|
||||
BuildArgs []string
|
||||
}
|
||||
|
||||
type export struct {
|
||||
Layer []exportLayer `json:"layer"`
|
||||
Image exportImage `json:"image"`
|
||||
}
|
||||
|
||||
type exportLayer struct {
|
||||
Index int `json:"index"`
|
||||
DigestID string `json:"digestId"`
|
||||
SizeBytes uint64 `json:"sizeBytes"`
|
||||
Command string `json:"command"`
|
||||
}
|
||||
|
||||
type exportImage struct {
|
||||
SizeBytes uint64 `json:"sizeBytes"`
|
||||
InefficientBytes uint64 `json:"inefficientBytes"`
|
||||
EfficiencyScore float64 `json:"efficiencyScore"`
|
||||
InefficientFiles []inefficientFiles `json:"inefficientFiles"`
|
||||
}
|
||||
|
||||
type inefficientFiles struct {
|
||||
Count int `json:"count"`
|
||||
SizeBytes uint64 `json:"sizeBytes"`
|
||||
File string `json:"file"`
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user