github.com/triarius/goreleaser@v1.12.5/internal/gio/copy.go (about) 1 package gio 2 3 import ( 4 "fmt" 5 "io" 6 "os" 7 "path/filepath" 8 "strings" 9 10 "github.com/caarlos0/log" 11 ) 12 13 // Copy recursively copies src into dst with src's file modes. 14 func Copy(src, dst string) error { 15 return CopyWithMode(src, dst, 0) 16 } 17 18 // CopyWithMode recursively copies src into dst with the given mode. 19 // The given mode applies only to files. Their parent dirs will have the same mode as their src counterparts. 20 func CopyWithMode(src, dst string, mode os.FileMode) error { 21 return filepath.Walk(src, func(path string, info os.FileInfo, err error) error { 22 if err != nil { 23 return fmt.Errorf("failed to copy %s to %s: %w", src, dst, err) 24 } 25 // We have the following: 26 // - src = "a/b" 27 // - dst = "dist/linuxamd64/b" 28 // - path = "a/b/c.txt" 29 // So we join "a/b" with "c.txt" and use it as the destination. 30 dst := filepath.Join(dst, strings.Replace(path, src, "", 1)) 31 log.WithFields(log.Fields{ 32 "src": path, 33 "dst": dst, 34 }).Debug("copying file") 35 if info.IsDir() { 36 return os.MkdirAll(dst, info.Mode()) 37 } 38 if info.Mode()&os.ModeSymlink != 0 { 39 return copySymlink(path, dst) 40 } 41 if mode != 0 { 42 return copyFile(path, dst, mode) 43 } 44 return copyFile(path, dst, info.Mode()) 45 }) 46 } 47 48 func copySymlink(src, dst string) error { 49 src, err := os.Readlink(src) 50 if err != nil { 51 return err 52 } 53 return os.Symlink(src, dst) 54 } 55 56 func copyFile(src, dst string, mode os.FileMode) error { 57 original, err := os.Open(src) 58 if err != nil { 59 return fmt.Errorf("failed to open '%s': %w", src, err) 60 } 61 defer original.Close() 62 63 new, err := os.OpenFile(dst, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, mode) 64 if err != nil { 65 return fmt.Errorf("failed to open '%s': %w", dst, err) 66 } 67 defer new.Close() 68 69 if _, err := io.Copy(new, original); err != nil { 70 return fmt.Errorf("failed to copy: %w", err) 71 } 72 return nil 73 }