Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor vacation.Executable #260

Merged
merged 2 commits into from
Dec 6, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 5 additions & 9 deletions vacation/archive.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"bufio"
"fmt"
"io"
"path/filepath"

"github.com/gabriel-vasile/mimetype"
)
Expand All @@ -25,7 +24,6 @@ type Archive struct {
func NewArchive(inputReader io.Reader) Archive {
return Archive{
reader: inputReader,
name: "artifact",
}
}

Expand All @@ -34,8 +32,7 @@ func NewArchive(inputReader io.Reader) Archive {
//
// 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
// specified by the `Archive.WithName()` option (or defaults to "artifact")
// in the destination directory.
// specified by the `Archive.WithName()` option 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.
Expand All @@ -60,16 +57,15 @@ func (a Archive) Decompress(destination string) error {
case "application/gzip":
decompressor = NewGzipArchive(bufferedReader).StripComponents(a.components).WithName(a.name)
case "application/x-xz":
decompressor = NewXZArchive(bufferedReader).StripComponents(a.components)
decompressor = NewXZArchive(bufferedReader).StripComponents(a.components).WithName(a.name)
case "application/x-bzip2":
decompressor = NewBzip2Archive(bufferedReader).StripComponents(a.components)
decompressor = NewBzip2Archive(bufferedReader).StripComponents(a.components).WithName(a.name)
case "application/zip":
decompressor = NewZipArchive(bufferedReader).StripComponents(a.components)
case "application/x-executable":
decompressor = NewExecutable(bufferedReader, a.name)
decompressor = NewExecutable(bufferedReader).WithName(a.name)
case "text/plain; charset=utf-8", "application/jar":
destination = filepath.Join(destination, a.name)
decompressor = NewNopArchive(bufferedReader)
decompressor = NewNopArchive(bufferedReader).WithName(a.name)
default:
return fmt.Errorf("unsupported archive type: %s", mime.String())
}
Expand Down
14 changes: 4 additions & 10 deletions vacation/archive_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -356,7 +356,6 @@ func testArchive(t *testing.T, context spec.G, it spec.S) {
// Encoding of a very small elf executable from https://github.com/mathiasbynens/small
encodedContents = []byte(`f0VMRgEBAQAAAAAAAAAAAAIAAwABAAAAGUDNgCwAAAAAAAAAAAAAADQAIAABAAAAAAAAAABAzYAAQM2ATAAAAEwAAAAFAAAAABAAAA==`)
literalContents []byte
fileName = "exe"
)

it.Before(func() {
Expand All @@ -367,27 +366,22 @@ func testArchive(t *testing.T, context spec.G, it spec.S) {
literalContents, err = ioutil.ReadAll(base64.NewDecoder(base64.StdEncoding, bytes.NewBuffer(encodedContents)))
Expect(err).NotTo(HaveOccurred())

archive = vacation.NewArchive(bytes.NewBuffer(literalContents)).WithName(fileName)
archive = vacation.NewArchive(bytes.NewBuffer(literalContents))
})

it.After(func() {
Expect(os.RemoveAll(tempDir)).To(Succeed())
})

it("writes the executable in the bin dir", func() {
it("writes the executable in the destination directory and sets the permissions", func() {
err := archive.Decompress(tempDir)
Expect(err).NotTo(HaveOccurred())

content, err := os.ReadFile(filepath.Join(tempDir, "bin", fileName))
content, err := os.ReadFile(filepath.Join(tempDir, "artifact"))
Expect(err).NotTo(HaveOccurred())
Expect(content).To(Equal(literalContents))
})

it("gives the executable execute permission", func() {
err := archive.Decompress(tempDir)
Expect(err).NotTo(HaveOccurred())

info, err := os.Stat(filepath.Join(tempDir, "bin", fileName))
info, err := os.Stat(filepath.Join(tempDir, "artifact"))
Expect(err).NotTo(HaveOccurred())
Expect(info.Mode()).To(Equal(fs.FileMode(0755)))
})
Expand Down
18 changes: 13 additions & 5 deletions vacation/bzip2_archive.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
type Bzip2Archive struct {
reader io.Reader
components int
name string
}

// NewBzip2Archive returns a new Bzip2Archive that reads from inputReader.
Expand All @@ -18,13 +19,20 @@ func NewBzip2Archive(inputReader io.Reader) Bzip2Archive {

// Decompress reads from Bzip2Archive and writes files into the destination
// specified.
func (tbz Bzip2Archive) Decompress(destination string) error {
return NewArchive(bzip2.NewReader(tbz.reader)).StripComponents(tbz.components).Decompress(destination)
func (bz Bzip2Archive) Decompress(destination string) error {
return NewArchive(bzip2.NewReader(bz.reader)).WithName(bz.name).StripComponents(bz.components).Decompress(destination)
}

// StripComponents behaves like the --strip-components flag on tar command
// removing the first n levels from the final decompression destination.
func (tbz Bzip2Archive) StripComponents(components int) Bzip2Archive {
tbz.components = components
return tbz
func (bz Bzip2Archive) StripComponents(components int) Bzip2Archive {
bz.components = components
return bz
}

// WithName provides a way of overriding the name of the file
// that the decompressed file will be copied into.
func (bz Bzip2Archive) WithName(name string) Bzip2Archive {
bz.name = name
return bz
}
140 changes: 24 additions & 116 deletions vacation/example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -316,30 +316,12 @@ func ExampleTarArchive_StripComponents() {
func ExampleGzipArchive() {
buffer := bytes.NewBuffer(nil)
gw := gzip.NewWriter(buffer)
tw := tar.NewWriter(gw)

files := []ArchiveFile{
{Name: "some-dir/"},
{Name: "some-dir/some-other-dir/"},
{Name: "some-dir/some-other-dir/some-file", Content: []byte("some-dir/some-other-dir/some-file")},
{Name: "first", Content: []byte("first")},
{Name: "second", Content: []byte("second")},
{Name: "third", Content: []byte("third")},
}

for _, file := range files {
err := tw.WriteHeader(&tar.Header{Name: file.Name, Mode: 0755, Size: int64(len(file.Content))})
if err != nil {
log.Fatal(err)
}

_, err = tw.Write(file.Content)
if err != nil {
log.Fatal(err)
}
_, err := gw.Write([]byte(`Gzip file contents`))
if err != nil {
log.Fatal(err)
}

tw.Close()
gw.Close()

destination, err := os.MkdirTemp("", "destination")
Expand All @@ -348,32 +330,20 @@ func ExampleGzipArchive() {
}
defer os.RemoveAll(destination)

archive := vacation.NewGzipArchive(bytes.NewReader(buffer.Bytes()))
archive := vacation.NewGzipArchive(bytes.NewReader(buffer.Bytes())).WithName("gzip-file")
if err := archive.Decompress(destination); err != nil {
log.Fatal(err)
}

err = filepath.Walk(destination, func(path string, info os.FileInfo, err error) error {
if !info.IsDir() {
rel, err := filepath.Rel(destination, path)
if err != nil {
log.Fatal(err)
}

fmt.Printf("%s\n", rel)
return nil
}
return nil
})
content, err := os.ReadFile(filepath.Join(destination, "gzip-file"))
if err != nil {
log.Fatal(err)
}

fmt.Println(string(content))

// Output:
// first
// second
// some-dir/some-other-dir/some-file
// third
// Gzip file contents
}

func ExampleGzipArchive_StripComponents() {
Expand Down Expand Up @@ -443,30 +413,11 @@ func ExampleXZArchive() {
log.Fatal(err)
}

tw := tar.NewWriter(xw)

files := []ArchiveFile{
{Name: "some-dir/"},
{Name: "some-dir/some-other-dir/"},
{Name: "some-dir/some-other-dir/some-file", Content: []byte("some-dir/some-other-dir/some-file")},
{Name: "first", Content: []byte("first")},
{Name: "second", Content: []byte("second")},
{Name: "third", Content: []byte("third")},
}

for _, file := range files {
err := tw.WriteHeader(&tar.Header{Name: file.Name, Mode: 0755, Size: int64(len(file.Content))})
if err != nil {
log.Fatal(err)
}

_, err = tw.Write(file.Content)
if err != nil {
log.Fatal(err)
}
_, err = xw.Write([]byte(`XZ file contents`))
if err != nil {
log.Fatal(err)
}

tw.Close()
xw.Close()

destination, err := os.MkdirTemp("", "destination")
Expand All @@ -475,32 +426,20 @@ func ExampleXZArchive() {
}
defer os.RemoveAll(destination)

archive := vacation.NewXZArchive(bytes.NewReader(buffer.Bytes()))
archive := vacation.NewXZArchive(bytes.NewReader(buffer.Bytes())).WithName("xz-file")
if err := archive.Decompress(destination); err != nil {
log.Fatal(err)
}

err = filepath.Walk(destination, func(path string, info os.FileInfo, err error) error {
if !info.IsDir() {
rel, err := filepath.Rel(destination, path)
if err != nil {
log.Fatal(err)
}

fmt.Printf("%s\n", rel)
return nil
}
return nil
})
contents, err := os.ReadFile(filepath.Join(destination, "xz-file"))
if err != nil {
log.Fatal(err)
}

fmt.Println(string(contents))

// Output:
// first
// second
// some-dir/some-other-dir/some-file
// third
// XZ file contents
}

func ExampleXZArchive_StripComponents() {
Expand Down Expand Up @@ -579,30 +518,11 @@ func ExampleBzip2Archive() {
log.Fatal(err)
}

tw := tar.NewWriter(bz)

files := []ArchiveFile{
{Name: "some-dir/"},
{Name: "some-dir/some-other-dir/"},
{Name: "some-dir/some-other-dir/some-file", Content: []byte("some-dir/some-other-dir/some-file")},
{Name: "first", Content: []byte("first")},
{Name: "second", Content: []byte("second")},
{Name: "third", Content: []byte("third")},
}

for _, file := range files {
err := tw.WriteHeader(&tar.Header{Name: file.Name, Mode: 0755, Size: int64(len(file.Content))})
if err != nil {
log.Fatal(err)
}

_, err = tw.Write(file.Content)
if err != nil {
log.Fatal(err)
}
_, err = bz.Write([]byte(`Bzip2 file contents`))
if err != nil {
log.Fatal(err)
}

tw.Close()
bz.Close()

destination, err := os.MkdirTemp("", "destination")
Expand All @@ -611,32 +531,20 @@ func ExampleBzip2Archive() {
}
defer os.RemoveAll(destination)

archive := vacation.NewBzip2Archive(bytes.NewReader(buffer.Bytes()))
archive := vacation.NewBzip2Archive(bytes.NewReader(buffer.Bytes())).WithName("bzip2-file")
if err := archive.Decompress(destination); err != nil {
log.Fatal(err)
}

err = filepath.Walk(destination, func(path string, info os.FileInfo, err error) error {
if !info.IsDir() {
rel, err := filepath.Rel(destination, path)
if err != nil {
log.Fatal(err)
}

fmt.Printf("%s\n", rel)
return nil
}
return nil
})
contents, err := os.ReadFile(filepath.Join(destination, "bzip2-file"))
if err != nil {
log.Fatal(err)
}

fmt.Println(string(contents))

// Output:
// first
// second
// some-dir/some-other-dir/some-file
// third
// Bzip2 file contents
}

func ExampleBzip2Archive_StripComponents() {
Expand Down
38 changes: 25 additions & 13 deletions vacation/executable.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,26 @@ import (
"path/filepath"
)

// An Executable writes an executable files from an input stream to the bin/ directory.
// An Executable writes an executable files from an input stream to the with a
// file name specified by the option `Executable.WithName()` (or defaults to
// `artifact`) in the destination directory with executable permissions (0755).
type Executable struct {
reader io.Reader
name string
reader io.Reader
name string
}

func NewExecutable(inputReader io.Reader, name string) Executable {
return Executable{reader: inputReader, name: name}
// NewExecutable returns a new Executable that reads from inputReader.
func NewExecutable(inputReader io.Reader) Executable {
return Executable{
reader: inputReader,
name: "artifact",
}
}

// Decompress copies the reader contents into the destination specified and
// sets executable permissions.
func (e Executable) Decompress(destination string) error {
err := os.MkdirAll(filepath.Join(destination, "bin"), 0755)
if err != nil {
return err
}

file, err := os.Create(filepath.Join(destination, "bin", e.name))
file, err := os.Create(filepath.Join(destination, e.name))
if err != nil {
return err
}
Expand All @@ -33,10 +36,19 @@ func (e Executable) Decompress(destination string) error {
return err
}

err = os.Chmod(filepath.Join(destination, "bin", e.name), 0755)
err = os.Chmod(filepath.Join(destination, e.name), 0755)
if err != nil {
return err
}

return nil
}
}

// WithName provides a way of overriding the name of the file
// that the decompressed file will be copied into.
func (e Executable) WithName(name string) Executable {
if name != "" {
e.name = name
}
return e
}
Loading