-
Notifications
You must be signed in to change notification settings - Fork 26
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- introduces vacation.NopArchive type for non-archive files - splits vacation implementation files up into type-named files - renames vacation test files to remove stutter - adds vacation.Archive.WithName option to allow NopArchives to have specified names - uses vacation.Archive.WithName option in postal.Service.Deliver to deliver file to location matching the dependency URI basename - includes application/jar mime-type as NopArchive type
- Loading branch information
1 parent
ec0062f
commit 0de1e74
Showing
21 changed files
with
806 additions
and
579 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
package vacation | ||
|
||
import ( | ||
"bufio" | ||
"fmt" | ||
"io" | ||
"path/filepath" | ||
|
||
"github.com/gabriel-vasile/mimetype" | ||
) | ||
|
||
type Decompressor interface { | ||
Decompress(destination string) error | ||
} | ||
|
||
// An Archive decompresses tar, gzip, xz, and bzip2 compressed tar, and zip files from | ||
// an input stream. | ||
type Archive struct { | ||
reader io.Reader | ||
components int | ||
name string | ||
} | ||
|
||
// NewArchive returns a new Archive that reads from inputReader. | ||
func NewArchive(inputReader io.Reader) Archive { | ||
return Archive{ | ||
reader: inputReader, | ||
name: "artifact", | ||
} | ||
} | ||
|
||
// Decompress reads from Archive, determines the archive type of the input | ||
// stream, and writes files into the destination specified. | ||
// | ||
// Archive decompression will also handle files that are types "text/plain; | ||
// charset=utf-8" and write the contents of the input stream to a file name | ||
// "artifact" in the destination directory. | ||
func (a Archive) Decompress(destination string) error { | ||
// Convert reader into a buffered read so that the header can be peeked to | ||
// determine the type. | ||
bufferedReader := bufio.NewReader(a.reader) | ||
|
||
// The number 3072 is lifted from the mimetype library and the definition of | ||
// the constant at the time of writing this functionality is listed below. | ||
// https://github.com/gabriel-vasile/mimetype/blob/c64c025a7c2d8d45ba57d3cebb50a1dbedb3ed7e/internal/matchers/matchers.go#L6 | ||
header, err := bufferedReader.Peek(3072) | ||
if err != nil && err != io.EOF { | ||
return err | ||
} | ||
|
||
mime := mimetype.Detect(header) | ||
|
||
// This switch case is reponsible for determining what the decompression | ||
// strategy should be. | ||
var decompressor Decompressor | ||
switch mime.String() { | ||
case "application/x-tar": | ||
decompressor = NewTarArchive(bufferedReader).StripComponents(a.components) | ||
case "application/gzip": | ||
decompressor = NewTarGzipArchive(bufferedReader).StripComponents(a.components) | ||
case "application/x-xz": | ||
decompressor = NewTarXZArchive(bufferedReader).StripComponents(a.components) | ||
case "application/x-bzip2": | ||
decompressor = NewTarBzip2Archive(bufferedReader).StripComponents(a.components) | ||
case "application/zip": | ||
decompressor = NewZipArchive(bufferedReader) | ||
case "text/plain; charset=utf-8", "application/jar": | ||
destination = filepath.Join(destination, a.name) | ||
decompressor = NewNopArchive(bufferedReader) | ||
default: | ||
return fmt.Errorf("unsupported archive type: %s", mime.String()) | ||
} | ||
|
||
return decompressor.Decompress(destination) | ||
} | ||
|
||
// StripComponents behaves like the --strip-components flag on tar command | ||
// removing the first n levels from the final decompression destination. | ||
// Setting this is a no-op for archive types that do not use --strip-components | ||
// (such as zip). | ||
func (a Archive) StripComponents(components int) Archive { | ||
a.components = components | ||
return a | ||
} | ||
|
||
func (a Archive) WithName(name string) Archive { | ||
a.name = name | ||
return a | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
package vacation | ||
|
||
import ( | ||
"io" | ||
"os" | ||
) | ||
|
||
// A NopArchive implements the common archive interface, but acts as a no-op, | ||
// simply copying the reader to the destination. | ||
type NopArchive struct { | ||
reader io.Reader | ||
} | ||
|
||
// NewNopArchive returns a new NopArchive | ||
func NewNopArchive(r io.Reader) NopArchive { | ||
return NopArchive{reader: r} | ||
} | ||
|
||
// Decompress copies the reader contents into the destination specified. | ||
func (na NopArchive) Decompress(destination string) error { | ||
file, err := os.Create(destination) | ||
if err != nil { | ||
return err | ||
} | ||
defer file.Close() | ||
|
||
_, err = io.Copy(file, na.reader) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
package vacation_test | ||
|
||
import ( | ||
"bytes" | ||
"os" | ||
"path/filepath" | ||
"testing" | ||
|
||
"github.com/paketo-buildpacks/packit/vacation" | ||
"github.com/sclevine/spec" | ||
|
||
. "github.com/onsi/gomega" | ||
) | ||
|
||
func testNopArchive(t *testing.T, context spec.G, it spec.S) { | ||
var Expect = NewWithT(t).Expect | ||
|
||
context("Decompress", func() { | ||
var ( | ||
archive vacation.NopArchive | ||
tempDir string | ||
) | ||
|
||
it.Before(func() { | ||
var err error | ||
tempDir, err = os.MkdirTemp("", "vacation") | ||
Expect(err).NotTo(HaveOccurred()) | ||
|
||
buffer := bytes.NewBuffer([]byte(`some contents`)) | ||
|
||
archive = vacation.NewNopArchive(buffer) | ||
}) | ||
|
||
it.After(func() { | ||
Expect(os.RemoveAll(tempDir)).To(Succeed()) | ||
}) | ||
|
||
it("copies the contents of the reader to the destination", func() { | ||
err := archive.Decompress(filepath.Join(tempDir, "some-file")) | ||
Expect(err).NotTo(HaveOccurred()) | ||
|
||
content, err := os.ReadFile(filepath.Join(tempDir, "some-file")) | ||
Expect(err).NotTo(HaveOccurred()) | ||
Expect(content).To(Equal([]byte(`some contents`))) | ||
}) | ||
|
||
context("failure cases", func() { | ||
context("when the destination file cannot be created", func() { | ||
it("returns an error", func() { | ||
err := archive.Decompress("/no/such/path") | ||
Expect(err).To(MatchError(ContainSubstring("no such file or directory"))) | ||
}) | ||
}) | ||
}) | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.