sorcery-go/pkg/cast/unpack.go

66 lines
2.4 KiB
Go
Executable File

// Unpack: extract a source tarball.
//
// Supports .tar.gz, .tar.bz2, .tar.xz, .tar.lz, .tgz, .txz, .tbz2, and
// plain .tar. The destination directory is created if it doesn't exist.
// We shell out to `tar` because it's universally available on every
// Source Mage box and is far faster than a pure-Go reimplementation
// (it uses splice() for zero-copy extraction on recent kernels).
package cast
import (
"fmt"
"os"
"os/exec"
"path/filepath"
"strings"
"dcos.net/sorcery-go/pkg/eventbus"
)
// Unpack extracts `archivePath` into `destDir`. destDir is created.
// Returns the path to the top-level source directory inside destDir.
func Unpack(archivePath, destDir string, bus *eventbus.Bus, taskID string) error {
if bus != nil {
bus.Log(taskID, "📦 Unpacking "+filepath.Base(archivePath))
}
if err := os.MkdirAll(destDir, 0755); err != nil {
return err
}
// `tar` auto-detects compression from the file content, so we don't
// need to inspect the extension — `tar -xf` does the right thing.
cmd := exec.Command("tar", "-xf", archivePath, "-C", destDir, "--no-same-owner", "--no-same-permissions")
if out, err := cmd.CombinedOutput(); err != nil {
return fmt.Errorf("unpack: %w (output: %s)", err, string(out))
}
if bus != nil {
bus.Log(taskID, "✓ Unpacked into "+destDir)
}
return nil
}
// GuessSourceDir inspects destDir after extraction and returns the
// single top-level subdirectory if there is one (the typical
// ${SPELL}-${VERSION} directory), otherwise returns destDir itself.
func GuessSourceDir(destDir string) (string, error) {
entries, err := os.ReadDir(destDir)
if err != nil {
return "", err
}
if len(entries) == 1 && entries[0].IsDir() {
return filepath.Join(destDir, entries[0].Name()), nil
}
return destDir, nil
}
// ArchiveExtension returns the recognised archive extension of path.
func ArchiveExtension(path string) string {
low := strings.ToLower(path)
for _, ext := range []string{".tar.gz", ".tar.bz2", ".tar.xz", ".tar.lz",
".tgz", ".txz", ".tbz2", ".tar"} {
if strings.HasSuffix(low, ext) {
return ext
}
}
return ""
}