github.com/goreleaser/goreleaser@v1.25.1/pkg/archive/tar/tar.go (about)

     1  // Package tar implements the Archive interface providing tar archiving.
     2  package tar
     3  
     4  import (
     5  	"archive/tar"
     6  	"fmt"
     7  	"io"
     8  	"io/fs"
     9  	"os"
    10  
    11  	"github.com/goreleaser/goreleaser/pkg/config"
    12  )
    13  
    14  // Archive as tar.
    15  type Archive struct {
    16  	tw    *tar.Writer
    17  	files map[string]bool
    18  }
    19  
    20  // New tar archive.
    21  func New(target io.Writer) Archive {
    22  	return Archive{
    23  		tw:    tar.NewWriter(target),
    24  		files: map[string]bool{},
    25  	}
    26  }
    27  
    28  // Copying creates a new tar with the contents of the given tar.
    29  func Copying(source io.Reader, target io.Writer) (Archive, error) {
    30  	w := New(target)
    31  	r := tar.NewReader(source)
    32  	for {
    33  		header, err := r.Next()
    34  		if err == io.EOF || header == nil {
    35  			break
    36  		}
    37  		if err != nil {
    38  			return Archive{}, err
    39  		}
    40  		w.files[header.Name] = true
    41  		if err := w.tw.WriteHeader(header); err != nil {
    42  			return w, err
    43  		}
    44  		if _, err := io.Copy(w.tw, r); err != nil {
    45  			return w, err
    46  		}
    47  	}
    48  	return w, nil
    49  }
    50  
    51  // Close all closeables.
    52  func (a Archive) Close() error {
    53  	return a.tw.Close()
    54  }
    55  
    56  // Add file to the archive.
    57  func (a Archive) Add(f config.File) error {
    58  	if _, ok := a.files[f.Destination]; ok {
    59  		return &fs.PathError{Err: fs.ErrExist, Path: f.Destination, Op: "add"}
    60  	}
    61  	a.files[f.Destination] = true
    62  	info, err := os.Lstat(f.Source) // #nosec
    63  	if err != nil {
    64  		return fmt.Errorf("%s: %w", f.Source, err)
    65  	}
    66  	var link string
    67  	if info.Mode()&os.ModeSymlink != 0 {
    68  		link, err = os.Readlink(f.Source) // #nosec
    69  		if err != nil {
    70  			return fmt.Errorf("%s: %w", f.Source, err)
    71  		}
    72  	}
    73  	header, err := tar.FileInfoHeader(info, link)
    74  	if err != nil {
    75  		return fmt.Errorf("%s: %w", f.Source, err)
    76  	}
    77  	header.Name = f.Destination
    78  	if !f.Info.ParsedMTime.IsZero() {
    79  		header.ModTime = f.Info.ParsedMTime
    80  	}
    81  	if f.Info.Mode != 0 {
    82  		header.Mode = int64(f.Info.Mode)
    83  	}
    84  	if f.Info.Owner != "" {
    85  		header.Uid = 0
    86  		header.Uname = f.Info.Owner
    87  	}
    88  	if f.Info.Group != "" {
    89  		header.Gid = 0
    90  		header.Gname = f.Info.Group
    91  	}
    92  	if err = a.tw.WriteHeader(header); err != nil {
    93  		return fmt.Errorf("%s: %w", f.Source, err)
    94  	}
    95  	if info.IsDir() || info.Mode()&os.ModeSymlink != 0 {
    96  		return nil
    97  	}
    98  	file, err := os.Open(f.Source) // #nosec
    99  	if err != nil {
   100  		return fmt.Errorf("%s: %w", f.Source, err)
   101  	}
   102  	defer file.Close()
   103  	if _, err := io.Copy(a.tw, file); err != nil {
   104  		return fmt.Errorf("%s: %w", f.Source, err)
   105  	}
   106  	return nil
   107  }