github.com/goreleaser/goreleaser@v1.25.1/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  	src = filepath.ToSlash(src)
    22  	dst = filepath.ToSlash(dst)
    23  	return filepath.Walk(src, func(path string, info os.FileInfo, err error) error {
    24  		if err != nil {
    25  			return fmt.Errorf("failed to copy %s to %s: %w", src, dst, err)
    26  		}
    27  		path = filepath.ToSlash(path)
    28  		// We have the following:
    29  		// - src = "a/b"
    30  		// - dst = "dist/linuxamd64/b"
    31  		// - path = "a/b/c.txt"
    32  		// So we join "a/b" with "c.txt" and use it as the destination.
    33  		dst := filepath.ToSlash(filepath.Join(dst, strings.Replace(path, src, "", 1)))
    34  		log.WithField("src", path).WithField("dst", dst).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  	f, 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 f.Close()
    68  
    69  	if _, err := io.Copy(f, original); err != nil {
    70  		return fmt.Errorf("failed to copy: %w", err)
    71  	}
    72  	return nil
    73  }