Skip to content

Commit c487bbf

Browse files
authored
returns structured errors when origins conflict (#1578)
Generally, this is a user Config error. However, since this can happen both during Config resolution, and building - it is hard for users using chainguard.dev/apko as a library to flag this to the user as a user error. This PR converts these errors to use a type to make it easier for library users to do so. --------- Signed-off-by: Nghia Tran <tcnghia@gmail.com>
1 parent 5963499 commit c487bbf

File tree

3 files changed

+38
-2
lines changed

3 files changed

+38
-2
lines changed

pkg/apk/apk/errors.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,27 @@ func (f FileExistsError) Is(target error) bool {
3232
var targetError FileExistsError
3333
return errors.As(target, &targetError)
3434
}
35+
36+
// FileConflictError is returned when a file has conflicting origins.
37+
//
38+
// Generally, this is a user Config error. However, since this can happen
39+
// both during Config resolution, and building - it is hard for users using
40+
// chainguard.dev/apko as a library to flag this to the user as a user error.
41+
//
42+
// To help with that, we create this structure error.
43+
type FileConflictError struct {
44+
// The full path of the file that has conflicting origins.
45+
Path string
46+
47+
// The origins of the file, as a map from the package name to the origin
48+
Origins map[string]string
49+
}
50+
51+
func (f FileConflictError) Error() string {
52+
return fmt.Sprintf("packages %v has conflicting file: %q", f.Origins, f.Path)
53+
}
54+
55+
func (f FileConflictError) Is(target error) bool {
56+
var targetError FileConflictError
57+
return errors.As(target, &targetError)
58+
}

pkg/apk/apk/install.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,13 @@ func (a *APK) installRegularFile(header *tar.Header, tr *tar.Reader, tmpDir stri
141141
// Otherwise, we can only overwrite the file if it's in the same origin or if it replaces the existing package.
142142
_, isReplaced := replaceMap[pk.Name]
143143
if pk.Origin != pkg.Origin && !isReplaced {
144-
return false, fmt.Errorf("unable to install file over existing one, different contents: %s", header.Name)
144+
return false, FileConflictError{
145+
Path: header.Name,
146+
Origins: map[string]string{
147+
pk.Name: pk.Origin,
148+
pkg.Name: pkg.Origin,
149+
},
150+
}
145151
}
146152

147153
if err := a.writeOneFile(header, r, true); err != nil {

pkg/tarfs/fs.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -512,7 +512,13 @@ func (m *memFS) writeHeader(name string, te tarEntry) (bool, error) {
512512

513513
// At this point we know the files conflict, but it's okay if this file replaces that one.
514514
if !sameOrigin && !replaces {
515-
return false, fmt.Errorf("conflicting file %q in %q has different origin %q != %q in %q", name, got.pkg.Name, got.pkg.Origin, want.pkg.Origin, want.pkg.Name)
515+
return false, apk.FileConflictError{
516+
Path: name,
517+
Origins: map[string]string{
518+
got.pkg.Name: got.pkg.Origin,
519+
want.pkg.Name: want.pkg.Origin,
520+
},
521+
}
516522
}
517523

518524
anode := &node{

0 commit comments

Comments
 (0)