Skip to content

Commit

Permalink
Add code to generate ZIP files (part of #28)
Browse files Browse the repository at this point in the history
  • Loading branch information
aidansteele committed May 29, 2019
1 parent 8034f50 commit af87eac
Show file tree
Hide file tree
Showing 7 changed files with 157 additions and 0 deletions.
Empty file.
2 changes: 2 additions & 0 deletions pkg/zipper/testdata/executable.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#!/bin/bash
exit 0
1 change: 1 addition & 0 deletions pkg/zipper/testdata/notzip.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
hello
Binary file added pkg/zipper/testdata/realzip.zip
Binary file not shown.
1 change: 1 addition & 0 deletions pkg/zipper/testdata/short.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
he
118 changes: 118 additions & 0 deletions pkg/zipper/zipper.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
package zipper

import (
"archive/zip"
"fmt"
"github.com/pkg/errors"
"io"
"io/ioutil"
"net/http"
"os"
"path/filepath"
)

func Zip(path string) (string, error) {
path, err := filepath.Abs(path)
if err != nil {
return "", errors.Wrap(err, "determining absolute path")
}

fi, err := os.Stat(path)
if os.IsNotExist(err) {
return "", errors.Errorf("no file exists at '%s'", path)
}

if isZip(path) {
return path, nil
}

fw, err := ioutil.TempFile("", fmt.Sprintf("%s*.zip", filepath.Base(path)))
if err != nil {
return "", errors.Wrap(err, "creating temporary file to write to")
}
defer fw.Close()

zw := zip.NewWriter(fw)

if fi.IsDir() {
err = filepath.Walk(path, func(subpath string, info os.FileInfo, err error) error {
if info.IsDir() {
return nil
}

return addFileToZip(zw, path, subpath)
})
} else {
err = addFileToZip(zw, path, path)
}
if err != nil {
return "", err
}

err = zw.Close()
if err != nil {
return "", errors.Wrap(err, "finialising zip file")
}

return fw.Name(), nil
}

func addFileToZip(zw *zip.Writer, topPath, path string) error {
inputFile, err := os.Open(path)
if err != nil {
return err
}
defer inputFile.Close()

info, err := os.Stat(path)
if err != nil {
return err
}

fh, err := zip.FileInfoHeader(info)
if err != nil {
return err
}

relPath, err := filepath.Rel(topPath, path)
if err != nil {
return err
}
if relPath == "." {
relPath = filepath.Base(path)
}

fh.Name = relPath
fh.Method = zip.Deflate

zf, err := zw.CreateHeader(fh)
if err != nil {
return err
}

_, err = io.Copy(zf, inputFile)
return err
}

func isZip(path string) bool {
if fi, err := os.Stat(path); fi != nil && fi.IsDir() {
return false
} else if os.IsNotExist(err) {
return false
} else {
f, err := os.Open(path)
if err != nil {
return false
}
defer f.Close()

buf := make([]byte, 4)
_, err = io.ReadAtLeast(f, buf, 4)
if err != nil {
return false
}

contentType := http.DetectContentType(buf)
return contentType == "application/zip"
}
}
35 changes: 35 additions & 0 deletions pkg/zipper/zipper_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package zipper

import (
"archive/zip"
"github.com/stretchr/testify/assert"
"os"
"testing"
)

func TestIsZip(t *testing.T) {
assert.False(t, isZip("testdata/notzip.txt"))
assert.False(t, isZip("testdata/short.txt"))
assert.False(t, isZip("testdata/dir"))
assert.True(t, isZip("testdata/realzip.zip"))
assert.True(t, isZip("./testdata/../testdata/realzip.zip"))
}

func TestZipMaintainsPermissions(t *testing.T) {
path, err := Zip("testdata")
assert.NoError(t, err)

zf, err := zip.OpenReader(path)
assert.NoError(t, err)

foundExecutable := false

for _, f := range zf.File {
if f.Name == "executable.sh" {
foundExecutable = true
assert.Equal(t, f.Mode().Perm(), os.FileMode(0755))
}
}

assert.True(t, foundExecutable)
}

0 comments on commit af87eac

Please sign in to comment.