From 556e46daeb2bff8a7f8857f3fd205e00e6b0023c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fredrik=20L=C3=B6nnegren?= <fredrik.lonnegren@gmail.com>
Date: Wed, 20 Jul 2022 00:29:24 +0200
Subject: [PATCH] Print all not-found packages (#304)

* Print all not-found packages

When trying to install several packages that are not found luet will now
print all packages that are not found, instead of only first one.

* changes to some failing tests
---
 pkg/api/core/image/extract_test.go           | 21 ++++++++++----------
 pkg/api/core/types/artifact/artifact_test.go |  3 ++-
 pkg/installer/installer.go                   | 19 ++++++++++++++++--
 3 files changed, 30 insertions(+), 13 deletions(-)

diff --git a/pkg/api/core/image/extract_test.go b/pkg/api/core/image/extract_test.go
index 331bb001..db3d700c 100644
--- a/pkg/api/core/image/extract_test.go
+++ b/pkg/api/core/image/extract_test.go
@@ -23,11 +23,12 @@ import (
 	"github.com/google/go-containerregistry/pkg/name"
 	v1 "github.com/google/go-containerregistry/pkg/v1"
 	daemon "github.com/google/go-containerregistry/pkg/v1/daemon"
+	. "github.com/onsi/ginkgo/v2"
+	. "github.com/onsi/gomega"
+
 	"github.com/mudler/luet/pkg/api/core/context"
 	. "github.com/mudler/luet/pkg/api/core/image"
 	"github.com/mudler/luet/pkg/helpers/file"
-	. "github.com/onsi/ginkgo/v2"
-	. "github.com/onsi/gomega"
 )
 
 var _ = Describe("Extract", func() {
@@ -61,10 +62,10 @@ var _ = Describe("Extract", func() {
 					ExtractFiles(ctx, "", []string{}, []string{}),
 				)
 				Expect(err).ToNot(HaveOccurred())
-				defer os.RemoveAll(tmpdir) // clean up
+				// defer os.RemoveAll(tmpdir) // clean up
 
-				Expect(file.Exists(filepath.Join(tmpdir, "usr", "bin"))).To(BeTrue())
-				Expect(file.Exists(filepath.Join(tmpdir, "bin", "sh"))).To(BeTrue())
+				Expect(filepath.Join(tmpdir, "usr", "bin")).To(BeADirectory())
+				Expect(filepath.Join(tmpdir, "bin", "busybox")).To(BeARegularFile())
 			})
 
 			It("Extract specific dir", func() {
@@ -75,9 +76,9 @@ var _ = Describe("Extract", func() {
 				)
 				Expect(err).ToNot(HaveOccurred())
 				defer os.RemoveAll(tmpdir) // clean up
-				Expect(file.Exists(filepath.Join(tmpdir, "usr", "sbin"))).To(BeTrue())
-				Expect(file.Exists(filepath.Join(tmpdir, "usr", "bin"))).To(BeTrue())
-				Expect(file.Exists(filepath.Join(tmpdir, "bin", "sh"))).To(BeFalse())
+				Expect(filepath.Join(tmpdir, "usr", "sbin")).To(BeADirectory())
+				Expect(filepath.Join(tmpdir, "usr", "bin")).To(BeADirectory())
+				Expect(filepath.Join(tmpdir, "bin", "busybox")).ToNot(BeARegularFile())
 			})
 
 			It("Extract a dir with includes/excludes", func() {
@@ -103,8 +104,8 @@ var _ = Describe("Extract", func() {
 				Expect(err).ToNot(HaveOccurred())
 				defer os.RemoveAll(tmpdir) // clean up
 
-				Expect(file.Exists(filepath.Join(tmpdir, "usr", "bin"))).To(BeTrue())
-				Expect(file.Exists(filepath.Join(tmpdir, "bin", "sh"))).To(BeFalse())
+				Expect(filepath.Join(tmpdir, "usr", "bin")).To(BeADirectory())
+				Expect(filepath.Join(tmpdir, "bin", "busybox")).ToNot(BeARegularFile())
 			})
 		})
 	})
diff --git a/pkg/api/core/types/artifact/artifact_test.go b/pkg/api/core/types/artifact/artifact_test.go
index 3d6ae131..c71fb142 100644
--- a/pkg/api/core/types/artifact/artifact_test.go
+++ b/pkg/api/core/types/artifact/artifact_test.go
@@ -21,6 +21,7 @@ import (
 	"path/filepath"
 
 	"github.com/mudler/luet/pkg/api/core/types"
+
 	"github.com/mudler/luet/pkg/compiler"
 
 	"github.com/mudler/luet/pkg/api/core/context"
@@ -91,7 +92,7 @@ ENV PACKAGE_CATEGORY=app-admin`))
 			}
 			Expect(b.BuildImage(opts)).ToNot(HaveOccurred())
 			Expect(b.ExportImage(opts)).ToNot(HaveOccurred())
-			Expect(fileHelper.Exists(filepath.Join(tmpdir2, "output1.tar"))).To(BeTrue())
+			Expect(filepath.Join(tmpdir2, "output1.tar")).To(BeARegularFile())
 			Expect(b.BuildImage(opts)).ToNot(HaveOccurred())
 
 			err = lspec.WriteStepImageDefinition(lspec.Image, filepath.Join(tmpdir, "LuetDockerfile"))
diff --git a/pkg/installer/installer.go b/pkg/installer/installer.go
index 6b508f61..bc9751e7 100644
--- a/pkg/installer/installer.go
+++ b/pkg/installer/installer.go
@@ -25,18 +25,20 @@ import (
 	"sync"
 
 	"github.com/hashicorp/go-multierror"
+
 	"github.com/mudler/luet/pkg/api/core/config"
 	"github.com/mudler/luet/pkg/api/core/logger"
 	"github.com/mudler/luet/pkg/helpers"
 	"github.com/mudler/luet/pkg/tree"
 
+	"github.com/pterm/pterm"
+
 	"github.com/mudler/luet/pkg/api/core/bus"
 	"github.com/mudler/luet/pkg/api/core/types"
 	artifact "github.com/mudler/luet/pkg/api/core/types/artifact"
 	pkg "github.com/mudler/luet/pkg/database"
 	fileHelper "github.com/mudler/luet/pkg/helpers/file"
 	"github.com/mudler/luet/pkg/solver"
-	"github.com/pterm/pterm"
 
 	"github.com/pkg/errors"
 )
@@ -556,6 +558,8 @@ func (l *LuetInstaller) Install(cp types.Packages, s *System) error {
 
 	// Resolvers might decide to remove some packages from being installed
 	if !solver.IsRelaxedResolver(l.Options.SolverOptions) {
+		var packagesNotFound []string
+
 		for _, p := range cp {
 			found := false
 
@@ -577,18 +581,29 @@ func (l *LuetInstaller) Install(cp types.Packages, s *System) error {
 			for _, m := range match {
 				if m.Package.GetName() == p.GetName() {
 					found = true
+					break
 				}
 				for _, pack := range m.Package.GetProvides() {
 					if pack.GetName() == p.GetName() {
 						found = true
+						break
 					}
 				}
 			}
 
 			if !found {
-				return fmt.Errorf("package '%s' not found", p.HumanReadableString())
+				packagesNotFound = append(packagesNotFound, p.HumanReadableString())
 			}
 		}
+
+		if len(packagesNotFound) > 0 {
+			prefix := "package"
+			if len(packagesNotFound) > 1 {
+				prefix = "packages"
+			}
+
+			return fmt.Errorf("%s '%s' not found", prefix, strings.Join(packagesNotFound, "', '"))
+		}
 	}
 
 	// Check if we have to process something, or return to the user an error