func Unarchive(a Archiver, source, destination string, excludePaths []string) error {
	var files []string
	err := a.Walk(source, func(f archiver.File) error {
		header, ok := f.Header.(*tar.Header)
		if !ok {
			return fmt.Errorf("expected header to be *tar.Header but was %T", f.Header)
		}
		if !shouldExclude(excludePaths, header.Name) && !f.IsDir() {
			files = append(files, header.Name)
		}
		return nil
	})
	if err != nil {
		return err
	}
	for _, f := range files {
		if err := a.Extract(source, f, destination); err != nil {
			return err
		}
	}
	return nil
}
Unarchive will extract files unless excluded to destination directory
package archive
import (
	"archive/tar"
	"fmt"
	"io"
	"io/fs"
	"os"
	"path/filepath"
	"strings"
	"github.com/mholt/archiver/v3"
	"github.com/sirupsen/logrus"
	"github.com/RedHatGov/bundle/pkg/config"
	"github.com/RedHatGov/bundle/pkg/config/v1alpha1"
)
type Archiver interface {
	String() string
	Archive([]string, string) error
	Extract(string, string, string) error
	Unarchive(string, string) error
	Write(archiver.File) error
	Create(io.Writer) error
	Close() error
	Walk(string, archiver.WalkFunc) error
	Open(io.Reader, int64) error
	Read() (archiver.File, error)
	CheckPath(string, string) error
}
type packager struct {
	manifest    map[string]struct{}
	blobs       map[string]struct{}
	packedBlobs map[string]struct{}
	Archiver
}
func NewArchiver() Archiver {
	return &archiver.Tar{
		OverwriteExisting:      true,
		MkdirAll:               true,
		ImplicitTopLevelFolder: false,
		StripComponents:        0,
		ContinueOnError:        false,
	}
}
func NewPackager(manifests []v1alpha1.Manifest, blobs []v1alpha1.Blob) *packager {
	manifestSetToArchive := make(map[string]struct{}, len(manifests))
	blobSetToArchive := make(map[string]struct{}, len(blobs))
	for _, manifest := range manifests {
		manifestSetToArchive[manifest.Name] = struct{}{}
	}
	for _, blob := range blobs {
		blobSetToArchive[blob.ID] = struct{}{}
	}
	return &packager{
		manifest:    manifestSetToArchive,
		blobs:       blobSetToArchive,
		packedBlobs: make(map[string]struct{}, len(blobs)),
		Archiver:    NewArchiver(),
	}
}
func (p *packager) CreateSplitArchive(maxSplitSize int64, destDir, sourceDir, prefix string, skipCleanup bool) error {
	splitNum := 0
	splitSize := int64(0)
	splitPath := filepath.Join(destDir, fmt.Sprintf("%s_%06d.%s", prefix, splitNum, p.String()))
	splitFile, err := p.createArchive(splitPath)
	if err != nil {
		return fmt.Errorf("error creating archive %s: %v", splitPath, err)
	}
	sourceInfo, err := os.Stat(sourceDir)
	if err != nil {
		return fmt.Errorf("%s: stat: %v", sourceDir, err)
	}
	walkErr := filepath.Walk(sourceDir, func(fpath string, info os.FileInfo, err error) error {
		if err != nil {
			return fmt.Errorf("traversing %s: %v", fpath, err)
		}
		if info == nil {
			return fmt.Errorf("no file info")
		}
		if includeFile(fpath) {
			p.manifest[fpath] = struct{}{}
		}
		var nameInArchive string
		switch {
		case pack(p.manifest, fpath):
			nameInArchive, err = archiver.NameInArchive(sourceInfo, sourceDir, fpath)
			if err != nil {
				return fmt.Errorf("creating %s: %v", nameInArchive, err)
			}
		case pack(p.blobs, info.Name()) && !pack(p.packedBlobs, info.Name()):
			nameInArchive = blobInArchive(info.Name())
			p.packedBlobs[info.Name()] = struct{}{}
		default:
			logrus.Debugf("File %s will not be archived, skipping...", fpath)
			return nil
		}
		var file io.ReadCloser
		if info.Mode().IsRegular() {
			file, err = os.Open(fpath)
			if err != nil {
				return fmt.Errorf("%s: opening: %v", fpath, err)
			}
			defer file.Close()
		}
		f := archiver.File{
			FileInfo: archiver.FileInfo{
				FileInfo:   info,
				CustomName: nameInArchive,
			},
			ReadCloser: file,
		}
		if info.Size()+splitSize > maxSplitSize {
			if err := p.Close(); err != nil {
				return err
			}
			if err := splitFile.Close(); err != nil {
				return err
			}
			splitNum += 1
			splitSize = int64(0)
			splitPath = filepath.Join(destDir, fmt.Sprintf("%s_%06d.%s", prefix, splitNum, p.String()))
			splitFile, err = p.createArchive(splitPath)
			if err != nil {
				return fmt.Errorf("error creating archive %s: %v", splitPath, err)
			}
		}
		if err = p.Write(f); err != nil {
			return fmt.Errorf("%s: writing: %s", fpath, err)
		}
		if shouldRemove(fpath, info) && !skipCleanup {
			if err := os.Remove(fpath); err != nil {
				return err
			}
		}
		logrus.Debugf("File %s added to archive", fpath)
		splitSize += info.Size()
		return nil
	})
	if err := p.Close(); err != nil {
		return err
	}
	if err := splitFile.Close(); err != nil {
		return err
	}
	return walkErr
}
func (p *packager) createArchive(splitPath string) (splitFile *os.File, err error) {
	splitFile, err = os.Create(splitPath)
	if err != nil {
		return nil, fmt.Errorf("creating %s: %v", splitPath, err)
	}
	logrus.Infof("Creating archive %s", splitPath)
	if p.Create(splitFile); err != nil {
		return nil, fmt.Errorf("creating archive %s: %v", splitPath, err)
	}
	if err != nil {
		return nil, err
	}
	return splitFile, nil
}
func pack(search map[string]struct{}, file string) bool {
	_, archive := search[file]
	return archive
}
func blobInArchive(file string) string {
	return filepath.Join("blobs", file)
}
func includeFile(fpath string) bool {
	split := strings.Split(filepath.Clean(fpath), string(filepath.Separator))
	return split[0] == config.InternalDir || split[0] == config.PublishDir || split[0] == "catalogs" || split[0] == config.HelmDir
}
func shouldRemove(fpath string, info fs.FileInfo) bool {
	return !includeFile(fpath) && !info.IsDir()
}