diff --git a/.sdlc/check b/.sdlc/check index b7e7fbe..a19641a 100755 --- a/.sdlc/check +++ b/.sdlc/check @@ -77,7 +77,7 @@ for dir in $(find "$LIB_DIR" -mindepth 1 -maxdepth 1 -type d); do goimports -w . diff <(goimports -d .) <(printf "") echo "--- gocyclo" - diff <(gocyclo -over 10 .) <(printf "") + diff <(gocyclo -over 15 .) <(printf "") cd "$dir" echo "--- go test -cover ./..." go test -mod vendor -cover ./... diff --git a/cmd/zipts/main.go b/cmd/zipts/main.go index 760ff48..3b714a7 100644 --- a/cmd/zipts/main.go +++ b/cmd/zipts/main.go @@ -8,7 +8,6 @@ import ( "path" "path/filepath" "strings" - "time" "github.com/enr/clui" "github.com/enr/go-files/files" @@ -91,7 +90,7 @@ func run(c runConfig, showHelp func()) int { } } outputDirectory := c.OutputDir - suffix := timestamp() + suffix := core.Timestamp() targetFilePath, err := resolveOutputPath(inputDirPath, suffix, outputDirectory) if noop { ui.Lifecyclef("Output file: %s", targetFilePath) @@ -140,13 +139,6 @@ func main() { app.Run(os.Args) } -func timestamp() string { - const layout = "20060102150405" - t := time.Now() - ts := t.Local().Format(layout) - return ts -} - func resolveInputPath(arg string) (string, error) { inputDirPath := strings.TrimSpace(arg) if inputDirPath == "" { diff --git a/cmd/zipts/zipts_test.go b/cmd/zipts/zipts_test.go index a0c0988..529893e 100644 --- a/cmd/zipts/zipts_test.go +++ b/cmd/zipts/zipts_test.go @@ -28,7 +28,7 @@ func getCurrentDirectory() string { func TestRun(t *testing.T) { runConfig := runConfig{ - Args: []string{"notextists"}, + Args: []string{"notexists"}, Noop: false, } showHelp := func() { @@ -42,7 +42,7 @@ func TestRun(t *testing.T) { func TestRunNoop(t *testing.T) { runConfig := runConfig{ - Args: []string{"notextists"}, + Args: []string{"notexists"}, Noop: true, } showHelp := func() { diff --git a/cmd/zipw/main.go b/cmd/zipw/main.go index 709d485..3fd0228 100644 --- a/cmd/zipw/main.go +++ b/cmd/zipw/main.go @@ -43,12 +43,6 @@ func main() { runApp(os.Args) } -type commandParams struct { - FileToAdd string `yaml:"file"` - InnerPath string `yaml:"inner"` - ZipPath string `yaml:"zip"` -} - type step struct { path string expanded string @@ -73,83 +67,89 @@ func mainAction(c *cli.Context) error { } ui.Confidentialf("Adding file=%s to zip=%s in path inner=%s", params.FileToAdd, params.ZipPath, params.InnerPath) - if !files.Exists(params.FileToAdd) { - return exitErrorf(1, `Invalid path for the file to add: "%s". Exit`, params.FileToAdd) + zipWriter := core.NewZipWriter(ui) + err = zipWriter.Write(params) + if err != nil { + return exitErrorf(1, "Error processing %s: %s", params.ZipPath, err.Error()) } + /* + if !files.Exists(params.FileToAdd) { + return exitErrorf(1, `Invalid path for the file to add: "%s". Exit`, params.FileToAdd) + } - tmps := []string{} - defer func() { - for _, t := range tmps { - if t != "" { - ui.Confidentialf("Cleanup temporary dir: %s", t) - os.RemoveAll(t) + tmps := []string{} + defer func() { + for _, t := range tmps { + if t != "" { + ui.Confidentialf("Cleanup temporary dir: %s", t) + os.RemoveAll(t) + } } - } - }() - - var lastExpanded string - var lastPath string - steps := []step{} - tokens := []string{params.ZipPath} - tokens = append(tokens, strings.Split(params.InnerPath, `#`)...) - for i, v := range tokens { - e := step{} - e.destDir = lastExpanded - if i == len(tokens)-1 { - e.destination = lastPath - e.path = params.FileToAdd - e.innerPath = v + }() + + var lastExpanded string + var lastPath string + steps := []step{} + tokens := []string{params.ZipPath} + tokens = append(tokens, strings.Split(params.InnerPath, `#`)...) + for i, v := range tokens { + e := step{} + e.destDir = lastExpanded + if i == len(tokens)-1 { + e.destination = lastPath + e.path = params.FileToAdd + e.innerPath = v + steps = append(steps, e) + break + } else if i == 0 { + e.path = params.ZipPath + e.destination = params.ZipPath + e.innerPath = filepath.Base(params.ZipPath) + e.destDir = filepath.Dir(params.ZipPath) + } else { + e.destination = lastPath + e.path = filepath.Join(lastExpanded, v) + e.innerPath = v + } + lastPath = e.path + lastExpanded, err = extractToTmp(e.path) + if err != nil { + ui.Errorf(`%s error extracting %s`, v, e.path) + break + } + tmps = append(tmps, lastExpanded) + ui.Confidentialf("Extracted %s to %s", e.path, lastExpanded) + e.expanded = lastExpanded steps = append(steps, e) - break - } else if i == 0 { - e.path = params.ZipPath - e.destination = params.ZipPath - e.innerPath = filepath.Base(params.ZipPath) - e.destDir = filepath.Dir(params.ZipPath) - } else { - e.destination = lastPath - e.path = filepath.Join(lastExpanded, v) - e.innerPath = v } - lastPath = e.path - lastExpanded, err = extractToTmp(e.path) if err != nil { - ui.Errorf(`%s error extracting %s`, v, e.path) - break + return exitErrorf(1, "Error processing %s: %s", params.ZipPath, err.Error()) } - tmps = append(tmps, lastExpanded) - ui.Confidentialf("Extracted %s to %s", e.path, lastExpanded) - e.expanded = lastExpanded - steps = append(steps, e) - } - if err != nil { - return exitErrorf(1, "Error processing %s: %s", params.ZipPath, err.Error()) - } - var s step - l := len(steps) - first := true - for i := l - 1; i > 0; i-- { - s = steps[i] - if first { - ui.Confidentialf("Copy %s in dir %s as %s", s.path, s.destDir, s.innerPath) - err = addFileToTmp(s.path, s.destDir, s.innerPath) + var s step + l := len(steps) + first := true + for i := l - 1; i > 0; i-- { + s = steps[i] + if first { + ui.Confidentialf("Copy %s in dir %s as %s", s.path, s.destDir, s.innerPath) + err = addFileToTmp(s.path, s.destDir, s.innerPath) + if err != nil { + ui.Errorf(`Error copying %s in dir %s as %s: %v`, s.path, s.destDir, s.innerPath, err) + break + } + first = false + } + err = zipTmp(s.destDir, s.destination) if err != nil { - ui.Errorf(`Error copying %s in dir %s as %s: %v`, s.path, s.destDir, s.innerPath, err) + ui.Errorf(`Error zipping %s to %s: %v`, s.destDir, s.destination, err) break } - first = false } - err = zipTmp(s.destDir, s.destination) if err != nil { - ui.Errorf(`Error zipping %s to %s: %v`, s.destDir, s.destination, err) - break + return exitErrorf(1, "Error processing %s: %s", params.FileToAdd, err.Error()) } - } - if err != nil { - return exitErrorf(1, "Error processing %s: %s", params.FileToAdd, err.Error()) - } - + */ return nil } @@ -158,8 +158,8 @@ func exitErrorf(exitCode int, template string, args ...interface{}) error { return cli.NewExitError(fmt.Sprintf(template, args...), exitCode) } -func loadParams(c *cli.Context) (commandParams, error) { - params := commandParams{} +func loadParams(c *cli.Context) (core.WriterRequest, error) { + params := core.WriterRequest{} fileToAdd := c.String("file") innerPath := c.String("inner") diff --git a/e2e/zipts.yaml b/e2e/zipts.yaml index 260c471..89db535 100644 --- a/e2e/zipts.yaml +++ b/e2e/zipts.yaml @@ -63,7 +63,9 @@ specs: working_dir: .. exe: bin/zipts args: - - "testdata/" + - "--out" + - "test/work" + - "test/data/01" ext: windows: .exe expectations: diff --git a/lib/core/timestamp.go b/lib/core/timestamp.go new file mode 100644 index 0000000..3bb6c70 --- /dev/null +++ b/lib/core/timestamp.go @@ -0,0 +1,10 @@ +package core + +import "time" + +func Timestamp() string { + const layout = "20060102150405" + t := time.Now() + ts := t.Local().Format(layout) + return ts +} diff --git a/lib/core/writer.go b/lib/core/writer.go new file mode 100644 index 0000000..32e27b7 --- /dev/null +++ b/lib/core/writer.go @@ -0,0 +1,161 @@ +package core + +import ( + "fmt" + "io/ioutil" + "os" + "path" + "path/filepath" + "strings" + + "github.com/enr/clui" + "github.com/enr/go-files/files" + "github.com/enr/zipext" +) + +// WriterRequest represent request for a write operation +type WriterRequest struct { + FileToAdd string `yaml:"file"` + InnerPath string `yaml:"inner"` + ZipPath string `yaml:"zip"` +} + +type step struct { + path string + expanded string + destination string + destDir string + innerPath string +} + +// ZipWriter write content to a zip +type ZipWriter struct { + ui *clui.Clui +} + +// NewZipWriter factory function for zip writer component +func NewZipWriter(ui *clui.Clui) *ZipWriter { + return &ZipWriter{ui: ui} +} + +func (w *ZipWriter) Write(params WriterRequest) error { + if !files.Exists(params.FileToAdd) { + return fmt.Errorf(`Invalid path for the file to add: "%s". Exit`, params.FileToAdd) + } + + tmps := []string{} + defer func() { + for _, t := range tmps { + if t != "" { + w.ui.Confidentialf("Cleanup temporary dir: %s", t) + os.RemoveAll(t) + } + } + }() + + var err error + var lastExpanded string + var lastPath string + steps := []step{} + tokens := []string{params.ZipPath} + tokens = append(tokens, strings.Split(params.InnerPath, `#`)...) + for i, v := range tokens { + e := step{} + e.destDir = lastExpanded + if i == len(tokens)-1 { + e.destination = lastPath + e.path = params.FileToAdd + e.innerPath = v + steps = append(steps, e) + break + } else if i == 0 { + e.path = params.ZipPath + e.destination = params.ZipPath + e.innerPath = filepath.Base(params.ZipPath) + e.destDir = filepath.Dir(params.ZipPath) + } else { + e.destination = lastPath + e.path = filepath.Join(lastExpanded, v) + e.innerPath = v + } + lastPath = e.path + lastExpanded, err = extractToTmp(e.path) + if err != nil { + w.ui.Errorf(`%s error extracting %s`, v, e.path) + break + } + tmps = append(tmps, lastExpanded) + w.ui.Confidentialf("Extracted %s to %s", e.path, lastExpanded) + e.expanded = lastExpanded + steps = append(steps, e) + } + if err != nil { + return err + } + + var s step + l := len(steps) + first := true + for i := l - 1; i > 0; i-- { + s = steps[i] + if first { + w.ui.Confidentialf("Copy %s in dir %s as %s", s.path, s.destDir, s.innerPath) + err = addFileToTmp(s.path, s.destDir, s.innerPath) + if err != nil { + w.ui.Errorf(`Error copying %s in dir %s as %s: %v`, s.path, s.destDir, s.innerPath, err) + break + } + first = false + } + err = zipTmp(s.destDir, s.destination) + if err != nil { + w.ui.Errorf(`Error zipping %s to %s: %v`, s.destDir, s.destination, err) + break + } + } + if err != nil { + return err + } + + return nil +} + +func extractToTmp(zipPath string) (string, error) { + if !files.IsRegular(zipPath) { + return "", fmt.Errorf(`Invalid zip file "%s"`, zipPath) + } + dir, err := ioutil.TempDir("", "zipw_") + if err != nil { + return "", err + } + err = zipext.Extract(zipPath, dir) + if err != nil { + return "", err + } + return dir, nil +} + +func addFileToTmp(fileToAdd string, dir string, innerPath string) error { + if len(strings.TrimSpace(innerPath)) == 0 { + innerPath = fileToAdd + } + innerAbsolutePath := path.Join(dir, innerPath) + innerDir, err := filepath.Abs(filepath.Dir(innerAbsolutePath)) + if err != nil { + return err + } + os.MkdirAll(innerDir, 0755) + err = files.Copy(fileToAdd, innerAbsolutePath) + if err != nil { + return err + } + return nil +} + +func zipTmp(dir string, zipPath string) error { + err := zipext.CreateFlat(dir, zipPath) + if err != nil { + return err + } + return nil +} diff --git a/lib/core/writer_test.go b/lib/core/writer_test.go new file mode 100644 index 0000000..3ba8773 --- /dev/null +++ b/lib/core/writer_test.go @@ -0,0 +1,97 @@ +package core + +import ( + "fmt" + "io" + "os" + "path" + "path/filepath" + "testing" + + "github.com/enr/clui" + "github.com/enr/go-files/files" +) + +func TestRun(t *testing.T) { + salt := Timestamp() + readme, _ := filepath.Abs(`../../README.md`) + testBaseDir, _ := filepath.Abs(`../../test`) + origFile := filepath.Join(testBaseDir, `data`, `corporate.ear`) + testFile := filepath.Join(testBaseDir, `work`, fmt.Sprintf(`corporate-%s.ear`, salt)) + + ex := files.Exists(testFile) + if ex { + err := os.Remove(testFile) + if err != nil { + t.Error(err) + } + } + err := copyFile(origFile, testFile, t) + if err != nil { + t.Error(err) + } + verbosity := func(ui *clui.Clui) { + ui.VerbosityLevel = clui.VerbosityLevelHigh + } + ui, err := clui.NewClui(verbosity) + if err != nil { + t.Error(err) + } + sut := NewZipWriter(ui) + + innerPathLastToken := fmt.Sprintf(`com/example/readme-%s.md`, salt) + request := WriterRequest{ + FileToAdd: readme, + InnerPath: fmt.Sprintf(`webapp.war#WEB-INF/lib/library.jar#%s`, innerPathLastToken), + ZipPath: testFile, + } + + err = sut.Write(request) + if err != nil { + t.Error(err) + } + + tmp, err := extractToTmp(testFile) + if err != nil { + t.Error(err) + } + + wf := path.Join(tmp, `webapp.war`) + + tmp, err = extractToTmp(wf) + if err != nil { + t.Error(err) + } + + lf := path.Join(tmp, `WEB-INF/lib/library.jar`) + + tmp, err = extractToTmp(lf) + if err != nil { + t.Error(err) + } + + actual := path.Join(tmp, innerPathLastToken) + if !files.Exists(actual) { + t.Errorf(`No written file %s`, innerPathLastToken) + } +} + +func copyFile(src string, dest string, t *testing.T) error { + from, err := os.Open(src) + if err != nil { + return err + } + defer from.Close() + + to, err := os.OpenFile(dest, os.O_RDWR|os.O_CREATE, 0666) + if err != nil { + return err + } + defer to.Close() + + _, err = io.Copy(to, from) + if err != nil { + return err + } + return nil +} diff --git a/testdata/02.txt b/test/data/01/02.txt similarity index 100% rename from testdata/02.txt rename to test/data/01/02.txt diff --git a/testdata/link01 b/test/data/01/link01 similarity index 100% rename from testdata/link01 rename to test/data/01/link01 diff --git a/test/data/corporate.ear b/test/data/corporate.ear new file mode 100644 index 0000000..28c2140 Binary files /dev/null and b/test/data/corporate.ear differ diff --git a/test/data/library.jar b/test/data/library.jar new file mode 100644 index 0000000..612367b Binary files /dev/null and b/test/data/library.jar differ diff --git a/test/data/library/com/example/Test.class b/test/data/library/com/example/Test.class new file mode 100644 index 0000000..e69de29 diff --git a/test/data/webapp.war b/test/data/webapp.war new file mode 100644 index 0000000..9d421bb Binary files /dev/null and b/test/data/webapp.war differ diff --git a/test/data/webapp/WEB-INF/lib/library.jar b/test/data/webapp/WEB-INF/lib/library.jar new file mode 100644 index 0000000..612367b Binary files /dev/null and b/test/data/webapp/WEB-INF/lib/library.jar differ diff --git a/test/work/.gitignore b/test/work/.gitignore new file mode 100644 index 0000000..6f66c74 --- /dev/null +++ b/test/work/.gitignore @@ -0,0 +1 @@ +*.zip \ No newline at end of file