diff --git a/cmd/dive/cli/internal/command/ci/evaluator.go b/cmd/dive/cli/internal/command/ci/evaluator.go index ee4fea1f..cb66af92 100644 --- a/cmd/dive/cli/internal/command/ci/evaluator.go +++ b/cmd/dive/cli/internal/command/ci/evaluator.go @@ -98,7 +98,7 @@ func (e Evaluator) Evaluate(ctx context.Context, analysis *image.Analysis) Evalu e.InefficientFiles = append(e.InefficientFiles, ReferenceFile{ References: len(fileData.Nodes), - SizeBytes: uint64(fileData.CumulativeSize), + SizeBytes: uint64(fileData.WastedSize()), Path: fileData.Path, }) } diff --git a/cmd/dive/cli/internal/command/export/testdata/snapshots/export_test.snap b/cmd/dive/cli/internal/command/export/testdata/snapshots/export_test.snap index 454c34a9..25d6b10c 100755 --- a/cmd/dive/cli/internal/command/export/testdata/snapshots/export_test.snap +++ b/cmd/dive/cli/internal/command/export/testdata/snapshots/export_test.snap @@ -20,7 +20,7 @@ "sizeBytes": 6405 } ], - "inefficientBytes": 32025, + "inefficientBytes": 19215, "sizeBytes": 1220598 }, "layer": [ diff --git a/cmd/dive/cli/internal/ui/v1/view/image_details.go b/cmd/dive/cli/internal/ui/v1/view/image_details.go index a119ba77..ddcbaa53 100644 --- a/cmd/dive/cli/internal/ui/v1/view/image_details.go +++ b/cmd/dive/cli/internal/ui/v1/view/image_details.go @@ -86,7 +86,7 @@ func (v *ImageDetails) Render() error { var wastedSpace int64 for idx := 0; idx < len(v.inefficiencies); idx++ { data := v.inefficiencies[len(v.inefficiencies)-1-idx] - wastedSpace += data.CumulativeSize + wastedSpace += data.WastedSize() inefficiencyReport += fmt.Sprintf(analysisTemplate, strconv.Itoa(len(data.Nodes)), humanize.Bytes(uint64(data.CumulativeSize)), data.Path) } diff --git a/cmd/dive/cli/testdata/snapshots/cli_build_test.snap b/cmd/dive/cli/testdata/snapshots/cli_build_test.snap index cafccd55..55c3ce70 100755 --- a/cmd/dive/cli/testdata/snapshots/cli_build_test.snap +++ b/cmd/dive/cli/testdata/snapshots/cli_build_test.snap @@ -116,19 +116,19 @@ View build details: docker-desktop:// [Test_Build_CI_gate_fail - 1] Analysis: efficiency: 100.00 % - wastedBytes: 131 bytes (131 B) - userWastedPercent: 71.98 % + wastedBytes: 97 bytes (97 B) + userWastedPercent: 53.30 % Inefficient Files: Count Wasted Space File Path - 3 80 B /root/saved.txt - 2 34 B /root/example/somefile1.txt + 3 63 B /root/saved.txt + 2 17 B /root/example/somefile1.txt 2 17 B /root/example/somefile3.txt 2 0 B /root 10 0 B /etc Evaluation: - FAIL highestUserWastedPercent (too many bytes wasted, relative to the user bytes added (%-user-wasted-bytes=0.72 > threshold=0.1)) + FAIL highestUserWastedPercent (too many bytes wasted, relative to the user bytes added (%-user-wasted-bytes=0.53 > threshold=0.1)) SKIP highestWastedBytes (disabled) PASS lowestEfficiency (0.9) diff --git a/cmd/dive/cli/testdata/snapshots/cli_ci_test.snap b/cmd/dive/cli/testdata/snapshots/cli_ci_test.snap index db9c699b..9d993346 100755 --- a/cmd/dive/cli/testdata/snapshots/cli_ci_test.snap +++ b/cmd/dive/cli/testdata/snapshots/cli_ci_test.snap @@ -2,19 +2,19 @@ [Test_CI_Fail - 1] Analysis: efficiency: 100.00 % - wastedBytes: 131 bytes (131 B) - userWastedPercent: 71.98 % + wastedBytes: 97 bytes (97 B) + userWastedPercent: 53.30 % Inefficient Files: Count Wasted Space File Path - 3 80 B /root/saved.txt - 2 34 B /root/example/somefile1.txt + 3 63 B /root/saved.txt + 2 17 B /root/example/somefile1.txt 2 17 B /root/example/somefile3.txt 2 0 B /root 10 0 B /etc Evaluation: - FAIL highestUserWastedPercent (too many bytes wasted, relative to the user bytes added (%-user-wasted-bytes=0.72 > threshold=0.1)) + FAIL highestUserWastedPercent (too many bytes wasted, relative to the user bytes added (%-user-wasted-bytes=0.53 > threshold=0.1)) SKIP highestWastedBytes (disabled) PASS lowestEfficiency (0.9) diff --git a/dive/filetree/efficiency.go b/dive/filetree/efficiency.go index 02035a63..0fc595a0 100644 --- a/dive/filetree/efficiency.go +++ b/dive/filetree/efficiency.go @@ -14,6 +14,17 @@ type EfficiencyData struct { minDiscoveredSize int64 } +// WastedSize returns the number of bytes that could be reclaimed for this +// path by keeping a single copy. CumulativeSize sums every copy across +// layers, including the smallest one we'd keep, so the recoverable amount +// is the total minus the smallest copy. +func (e *EfficiencyData) WastedSize() int64 { + if e.minDiscoveredSize < 0 { + return 0 + } + return e.CumulativeSize - e.minDiscoveredSize +} + // EfficiencySlice represents an ordered set of EfficiencyData data structures. type EfficiencySlice []*EfficiencyData diff --git a/dive/image/analysis.go b/dive/image/analysis.go index d2b0f04a..eb368246 100644 --- a/dive/image/analysis.go +++ b/dive/image/analysis.go @@ -30,7 +30,7 @@ func Analyze(ctx context.Context, img *Image) (*Analysis, error) { var wastedBytes uint64 for _, file := range inefficiencies { - wastedBytes += uint64(file.CumulativeSize) + wastedBytes += uint64(file.WastedSize()) } return &Analysis{ diff --git a/dive/image/docker/image_archive_analysis_test.go b/dive/image/docker/image_archive_analysis_test.go index afc38254..0652b8ee 100644 --- a/dive/image/docker/image_archive_analysis_test.go +++ b/dive/image/docker/image_archive_analysis_test.go @@ -14,7 +14,7 @@ func Test_Analysis(t *testing.T) { wastedPercent float64 path string }{ - "docker-image": {0.9844212134184309, 1220598, 66237, 32025, 0.4834911001404049, "../../../.data/test-docker-image.tar"}, + "docker-image": {0.9844212134184309, 1220598, 66237, 19215, 0.29009466008424295, "../../../.data/test-docker-image.tar"}, } for name, test := range table {