diff --git a/README.md b/README.md index d9e9310..ce4dc78 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,12 @@ # Archiver A golang library for extracting and creating archives. +[Documentation Link](https://pkg.go.dev/egtyl.xyz/omnibill/archiver) + +## Roadmap +- [ ] Automatically detect the archive format +- [ ] Add mutexes to work with concurrency + ## Supported Formats - `.tar.gz` - `.tar.bz2` diff --git a/archive_fs_test.go b/archive_fs_test.go new file mode 100644 index 0000000..7e5c74b --- /dev/null +++ b/archive_fs_test.go @@ -0,0 +1,114 @@ +package archiver + +import ( + "archive/tar" + "compress/gzip" + "github.com/go-git/go-billy/v5/memfs" + "github.com/stretchr/testify/assert" + "os" + "strconv" + "testing" +) + +func TestArchiveFS(t *testing.T) { + err := os.MkdirAll(testArchiveBaseDir, os.ModePerm) + assert.NoError(t, err) + + memoryFS := memfs.New() + + archiveF, err := memoryFS.OpenFile("archive.tar.gz", os.O_CREATE|os.O_TRUNC|os.O_WRONLY, os.ModePerm) + assert.NoError(t, err) + defer archiveF.Close() + + gzipWriter := gzip.NewWriter(archiveF) + defer gzipWriter.Close() + + tarWriter := tar.NewWriter(gzipWriter) + defer tarWriter.Close() + + for i := 0; i < 10; i++ { + tarHeader := &tar.Header{ + Name: "test" + strconv.Itoa(i) + ".txt", + Size: int64(len([]byte("Hello, World! #" + strconv.Itoa(i)))), + Mode: 0600, + } + err = tarWriter.WriteHeader(tarHeader) + assert.NoError(t, err) + + _, err = tarWriter.Write([]byte("Hello, World! #" + strconv.Itoa(i))) + assert.NoError(t, err) + } + + err = tarWriter.Close() + assert.NoError(t, err) + err = gzipWriter.Close() + assert.NoError(t, err) + err = archiveF.Close() + assert.NoError(t, err) + + t.Log("== OpenArchive ==") + archive, err := OpenFS(WrapBillyFS(memoryFS), TarGzip, "archive.tar.gz") + assert.NoError(t, err) + assert.Equal(t, 10, archive.FileCount()) + + t.Log("== ExtractArchiveFile ==") + archiveFile, err := archive.GetFile("test0.txt") + assert.NoError(t, err) + + err = archiveFile.Extract(ExtractFileOptions{ + Folder: testArchiveBaseDir + "/extracted/fs", + }) + assert.NoError(t, err) + + err = os.RemoveAll(testArchiveBaseDir + "/extracted/fs") + assert.NoError(t, err) + + t.Log("== ExtractArchive ==") + err = archive.Extract(ExtractOptions{ + Folder: testArchiveBaseDir + "/extracted/fs", + }) + assert.NoError(t, err) + + err = os.RemoveAll(testArchiveBaseDir + "/extracted/fs") + assert.NoError(t, err) + + t.Log("== ExtractArchiveFilter ==") + err = archive.Extract(ExtractOptions{ + Folder: testArchiveBaseDir + "/extracted/fs", + Filter: archiveRegex, + }) + + assert.FileExists(t, testArchiveBaseDir+"/extracted/fs/test1.txt") + assert.FileExists(t, testArchiveBaseDir+"/extracted/fs/test5.txt") + + err = os.RemoveAll(testArchiveBaseDir + "/extracted/fs") + assert.NoError(t, err) + + err = archive.Close() + assert.NoError(t, err) +} + +func TestExtractArchiveBillyFS(t *testing.T) { + err := os.MkdirAll(testArchiveBaseDir, os.ModePerm) + assert.NoError(t, err) + testGenerateTar(t) + + archive, err := Open(Tar, testArchiveBaseDir+"/test.tar") + assert.NoError(t, err) + + memoryFS := memfs.New() + + err = archive.ExtractBillyFS(memoryFS, ExtractOptions{ + Folder: memoryFS.Root(), + }) + assert.NoError(t, err) + + files, err := memoryFS.ReadDir(memoryFS.Root()) + assert.NoError(t, err) + + assert.Equal(t, 10, len(files)) + + for _, file := range files { + assert.NotEqual(t, int64(0), file.Size()) + } +} diff --git a/archive_test.go b/archive_test.go index 24a5d74..058f12a 100644 --- a/archive_test.go +++ b/archive_test.go @@ -5,7 +5,6 @@ import ( "archive/zip" "compress/gzip" "github.com/dsnet/compress/bzip2" - "github.com/go-git/go-billy/v5/memfs" "github.com/stretchr/testify/assert" "github.com/ulikunitz/xz" "os" @@ -20,6 +19,48 @@ const ( var archiveRegex = regexp.MustCompile(`(?m)test[1|5]`) +func TestArchiveExtract(t *testing.T) { + err := os.MkdirAll(testArchiveBaseDir, os.ModePerm) + assert.NoError(t, err) + + testGenerateZip(t) + testGenerateTar(t) + testGenerateTarXz(t) + testGenerateTarBzip(t) + TestArchiveTarGzip(t) + + t.Log("== ExtractZip ==") + err = Extract(Zip, testArchiveBaseDir+"/test.zip", ExtractOptions{ + Folder: testArchiveBaseDir + "/extracted/zip", + }) + assert.NoError(t, err) + + t.Log("== ExtractTar ==") + err = Extract(Tar, testArchiveBaseDir+"/test.tar", ExtractOptions{ + Folder: testArchiveBaseDir + "/extracted/tar", + }) + assert.NoError(t, err) + + t.Log("== ExtractTarXz ==") + err = Extract(TarXz, testArchiveBaseDir+"/test.tar.xz", ExtractOptions{ + Folder: testArchiveBaseDir + "/extracted/xz", + }) + assert.NoError(t, err) + + t.Log("== ExtractTarBz ==") + err = Extract(TarBzip, testArchiveBaseDir+"/test.tar.bz2", ExtractOptions{ + Folder: testArchiveBaseDir + "/extracted/bzip", + }) + assert.NoError(t, err) + + t.Log("== ExtractTarGz ==") + err = Extract(TarGzip, testArchiveBaseDir+"/test.tar.gz", ExtractOptions{ + Folder: testArchiveBaseDir + "/extracted/gz", + }) + assert.NoError(t, err) + +} + func TestArchiveZip(t *testing.T) { err := os.MkdirAll(testArchiveBaseDir, os.ModePerm) assert.NoError(t, err) @@ -252,109 +293,6 @@ func TestArchiveTar(t *testing.T) { assert.NoError(t, err) } -func TestArchiveFS(t *testing.T) { - err := os.MkdirAll(testArchiveBaseDir, os.ModePerm) - assert.NoError(t, err) - - memoryFS := memfs.New() - - archiveF, err := memoryFS.OpenFile("archive.tar.gz", os.O_CREATE|os.O_TRUNC|os.O_WRONLY, os.ModePerm) - assert.NoError(t, err) - defer archiveF.Close() - - gzipWriter := gzip.NewWriter(archiveF) - defer gzipWriter.Close() - - tarWriter := tar.NewWriter(gzipWriter) - defer tarWriter.Close() - - for i := 0; i < 10; i++ { - tarHeader := &tar.Header{ - Name: "test" + strconv.Itoa(i) + ".txt", - Size: int64(len([]byte("Hello, World! #" + strconv.Itoa(i)))), - Mode: 0600, - } - err = tarWriter.WriteHeader(tarHeader) - assert.NoError(t, err) - - _, err = tarWriter.Write([]byte("Hello, World! #" + strconv.Itoa(i))) - assert.NoError(t, err) - } - - err = tarWriter.Close() - assert.NoError(t, err) - err = gzipWriter.Close() - assert.NoError(t, err) - err = archiveF.Close() - assert.NoError(t, err) - - t.Log("== OpenArchive ==") - archive, err := OpenFS(WrapBillyFS(memoryFS), TarGzip, "archive.tar.gz") - assert.NoError(t, err) - assert.Equal(t, 10, archive.FileCount()) - - t.Log("== ExtractArchiveFile ==") - archiveFile, err := archive.GetFile("test0.txt") - assert.NoError(t, err) - - err = archiveFile.Extract(ExtractFileOptions{ - Folder: testArchiveBaseDir + "/extracted/fs", - }) - assert.NoError(t, err) - - err = os.RemoveAll(testArchiveBaseDir + "/extracted/fs") - assert.NoError(t, err) - - t.Log("== ExtractArchive ==") - err = archive.Extract(ExtractOptions{ - Folder: testArchiveBaseDir + "/extracted/fs", - }) - assert.NoError(t, err) - - err = os.RemoveAll(testArchiveBaseDir + "/extracted/fs") - assert.NoError(t, err) - - t.Log("== ExtractArchiveFilter ==") - err = archive.Extract(ExtractOptions{ - Folder: testArchiveBaseDir + "/extracted/fs", - Filter: archiveRegex, - }) - - assert.FileExists(t, testArchiveBaseDir+"/extracted/fs/test1.txt") - assert.FileExists(t, testArchiveBaseDir+"/extracted/fs/test5.txt") - - err = os.RemoveAll(testArchiveBaseDir + "/extracted/fs") - assert.NoError(t, err) - - err = archive.Close() - assert.NoError(t, err) -} - -func TestExtractArchiveBillyFS(t *testing.T) { - err := os.MkdirAll(testArchiveBaseDir, os.ModePerm) - assert.NoError(t, err) - testGenerateTar(t) - - archive, err := Open(Tar, testArchiveBaseDir+"/test.tar") - assert.NoError(t, err) - - memoryFS := memfs.New() - - err = archive.ExtractBillyFS(memoryFS, ExtractOptions{ - Folder: memoryFS.Root(), - }) - assert.NoError(t, err) - - files, err := memoryFS.ReadDir(memoryFS.Root()) - assert.NoError(t, err) - - assert.Equal(t, 10, len(files)) - - for _, file := range files { - assert.NotEqual(t, int64(0), file.Size()) - } -} - func testGenerateTar(t *testing.T) { tarFile, err := os.Create(testArchiveBaseDir + "/test.tar") defer tarFile.Close() diff --git a/open.go b/open.go index 38ae046..c119c24 100644 --- a/open.go +++ b/open.go @@ -30,6 +30,24 @@ func Open(archiveType Type, path string) (*Archive, error) { return openArchive(WrapPath(archiveFolderPath), archiveType, path) } +// Extract opens an archive file and extracts the contents. +// It takes a Type, a path, and ExtractOptions for its parameters. +// The function returns an error, if any. +func Extract(archiveType Type, path string, options ExtractOptions) error { + archiveFile, err := Open(archiveType, path) + if err != nil { + return err + } + defer archiveFile.Close() + if err := archiveFile.Extract(options); err != nil { + return err + } + if err := archiveFile.Close(); err != nil { + return err + } + return nil +} + func openArchive(filesystem Filesystem, archiveType Type, path string) (*Archive, error) { archive := new(Archive) archive.files = make(map[string]*File)