Skip to content

Commit

Permalink
Adding walkSymlink function to support walking symlinks when zippin…
Browse files Browse the repository at this point in the history
…g dirs. (#136)

* Adding `walkSymlink` function to support walking symlinks when zipping directories.

* Making walkSymlink work with nested symlinks.
  • Loading branch information
g4ndr4 authored Mar 10, 2023
1 parent 1a76184 commit 15dc9d3
Showing 1 changed file with 68 additions and 2 deletions.
70 changes: 68 additions & 2 deletions zip/zip.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,22 @@ func Zip(dirPath string, insidePath string) ([]string, []byte, error) {
var files []string

err := filepath.WalkDir(dirPath, func(path string, dirEntry os.DirEntry, err error) error {
destPath := getDestPath(path, dirPath, insidePath)

if dirEntry == nil {
return fmt.Errorf("directory missing %s", dirPath)
}
if dirEntry.IsDir() {
return nil
}
if dirEntry.Type() == os.ModeSymlink {
err := walkSymlink(writer, &files, path, destPath)
if err != nil {
return errors.Wrap(err, fmt.Sprintf("failed to walk symlinked dir %s", dirPath))
}

return nil
}
if err != nil {
return errors.Wrap(err, fmt.Sprintf("failed to read path %s", path))
}
Expand All @@ -34,8 +44,6 @@ func Zip(dirPath string, insidePath string) ([]string, []byte, error) {
return errors.Wrap(err, fmt.Sprintf("failed reading %s", path))
}

relativePath := strings.TrimPrefix(path, dirPath)
destPath := filepath.Join(insidePath, relativePath)
destFile, err := writer.Create(destPath)
if err != nil {
return errors.Wrap(err, fmt.Sprintf("failed creating %s", destPath))
Expand All @@ -59,3 +67,61 @@ func Zip(dirPath string, insidePath string) ([]string, []byte, error) {

return files, buf.Bytes(), nil
}

func walkSymlink(writer *zip.Writer, files *[]string, path string, destBasePath string) error {
evaluatedDirPath, err := filepath.EvalSymlinks(path)
if err != nil {
return errors.Wrap(err, fmt.Sprintf("failed to eval symlink %s", path))
}

return filepath.WalkDir(evaluatedDirPath, func(path string, dirEntry os.DirEntry, err error) error {
destPath := getDestPath(path, evaluatedDirPath, destBasePath)
if dirEntry == nil {
return fmt.Errorf("directory missing %s", evaluatedDirPath)
}
if dirEntry.IsDir() {
return nil
}
if dirEntry.Type() == os.ModeSymlink {
err := walkSymlink(writer, files, path, destPath)
if err != nil {
return errors.Wrap(err, fmt.Sprintf("failed to walk symlinked dir %s", evaluatedDirPath))
}

return nil
}
if err != nil {
return errors.Wrap(err, fmt.Sprintf("failed to read path %s", path))
}

dat, err := os.ReadFile(path)
if err != nil {
return errors.Wrap(err, fmt.Sprintf("failed reading %s", path))
}

destFile, err := writer.Create(destPath)
if err != nil {
return errors.Wrap(err, fmt.Sprintf("failed creating %s", destPath))
}
_, err = destFile.Write(dat)
if err != nil {
return errors.Wrap(err, fmt.Sprintf("failed writing %s", destPath))
}
*files = append(*files, path)

return nil
})
}

/*
Provides path that filePath will have in the output zip.
filePath - Represents full path to the file (e.g. `node_modules/x/y.js`
dirPath - Represents path to the directory which contains the file (e.g. `node_modules/x/`)
destBasePath - Represents desired base path that the file will have in output zip (e.g. `nodejs/node_modules/x`)
For the example above, the output will be `nodejs/node_modules/x/y.js`.
*/
func getDestPath(filePath string, dirPath string, destBasePath string) string {
relativePath := strings.TrimPrefix(filePath, dirPath)
return filepath.Join(destBasePath, relativePath)
}

0 comments on commit 15dc9d3

Please sign in to comment.