package filesystem import ( "errors" "io" "os" "path/filepath" "strings" ) // CopyPath copies file or directory defined by the path to the toDir path func CopyPath(path string, toDir string) error { info, err := os.Stat(path) if err != nil && errors.Is(err, os.ErrNotExist) { // skip copy if file does not exist return nil } if !info.IsDir() { destination := filepath.Join(toDir, info.Name()) return copyFile(path, destination) } return CopyDir(path, toDir, true) } // CopyDir copies contents of fromDir to toDir. // When keepParent is true, contents will be copied with their immediate parent dir, // i.e. given /from/dirA and /to/dirB with keepParent == true, result will be /to/dirB/dirA/ func CopyDir(fromDir, toDir string, keepParent bool) error { cleanedSourcePath := filepath.Clean(fromDir) parentDirectory := filepath.Dir(cleanedSourcePath) err := filepath.Walk(cleanedSourcePath, func(path string, info os.FileInfo, err error) error { if err != nil { return err } var destination string if keepParent { destination = filepath.Join(toDir, strings.TrimPrefix(path, parentDirectory)) } else { destination = filepath.Join(toDir, strings.TrimPrefix(path, cleanedSourcePath)) } if destination == "" { return nil } if info.IsDir() { return nil // skip directory creations } if info.Mode()&os.ModeSymlink != 0 { // entry is a symlink return nil // don't copy symlinks } return copyFile(path, destination) }) return err } // copies regular a file from src to dst func copyFile(src, dst string) error { from, err := os.Open(src) if err != nil { return err } defer from.Close() // has to include 'execute' bit, otherwise fails. MkdirAll follows `mkdir -m` restrictions if err := os.MkdirAll(filepath.Dir(dst), 0744); err != nil { return err } to, err := os.Create(dst) if err != nil { return err } defer to.Close() _, err = io.Copy(to, from) return err }