Merge pull request #216 from wagoodman/include-inefficient-files-in-ci-output

Add file report to CI results
This commit is contained in:
Alex Goodman 2019-08-16 15:57:30 -04:00 committed by GitHub
commit d89bfed1fe
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 95 additions and 67 deletions

View File

@ -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))

View File

@ -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)

View File

@ -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,

View File

@ -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,
}
}

View File

@ -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
View 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
}

View File

@ -0,0 +1,7 @@
package runtime
type ReferenceFile struct {
References int `json:"count"`
SizeBytes uint64 `json:"sizeBytes"`
Path string `json:"file"`
}

View 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()

View File

@ -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"`
}