github.com/goreleaser/goreleaser@v1.25.1/pkg/archive/zip/zip.go (about) 1 // Package zip implements the Archive interface providing zip archiving 2 // and compression. 3 package zip 4 5 import ( 6 "archive/zip" 7 "compress/flate" 8 "fmt" 9 "io" 10 "io/fs" 11 "os" 12 "path/filepath" 13 14 "github.com/goreleaser/goreleaser/pkg/config" 15 ) 16 17 // Archive zip struct. 18 type Archive struct { 19 z *zip.Writer 20 files map[string]bool 21 } 22 23 // New zip archive. 24 func New(target io.Writer) Archive { 25 compressor := zip.NewWriter(target) 26 compressor.RegisterCompressor(zip.Deflate, func(out io.Writer) (io.WriteCloser, error) { 27 return flate.NewWriter(out, flate.BestCompression) 28 }) 29 return Archive{ 30 z: compressor, 31 files: map[string]bool{}, 32 } 33 } 34 35 // New zip archive. 36 func Copying(source *os.File, target io.Writer) (Archive, error) { 37 info, err := source.Stat() 38 if err != nil { 39 return Archive{}, err 40 } 41 r, err := zip.NewReader(source, info.Size()) 42 if err != nil { 43 return Archive{}, err 44 } 45 w := New(target) 46 for _, zf := range r.File { 47 48 w.files[zf.Name] = true 49 hdr := zip.FileHeader{ 50 Name: zf.Name, 51 UncompressedSize64: zf.UncompressedSize64, 52 UncompressedSize: zf.UncompressedSize, 53 CreatorVersion: zf.CreatorVersion, 54 ExternalAttrs: zf.ExternalAttrs, 55 } 56 ww, err := w.z.CreateHeader(&hdr) 57 if err != nil { 58 return Archive{}, fmt.Errorf("creating %q header in target: %w", zf.Name, err) 59 } 60 if zf.Mode().IsDir() { 61 continue 62 } 63 rr, err := zf.Open() 64 if err != nil { 65 return Archive{}, fmt.Errorf("opening %q from source: %w", zf.Name, err) 66 } 67 defer rr.Close() 68 if _, err = io.Copy(ww, rr); err != nil { 69 return Archive{}, fmt.Errorf("copy from %q source to target: %w", zf.Name, err) 70 } 71 _ = rr.Close() 72 } 73 return w, nil 74 } 75 76 // Close all closeables. 77 func (a Archive) Close() error { 78 return a.z.Close() 79 } 80 81 // Add a file to the zip archive. 82 func (a Archive) Add(f config.File) error { 83 if _, ok := a.files[f.Destination]; ok { 84 return &fs.PathError{Err: fs.ErrExist, Path: f.Destination, Op: "add"} 85 } 86 a.files[f.Destination] = true 87 info, err := os.Lstat(f.Source) // #nosec 88 if err != nil { 89 return err 90 } 91 if info.IsDir() { 92 return err 93 } 94 header, err := zip.FileInfoHeader(info) 95 if err != nil { 96 return err 97 } 98 header.Name = f.Destination 99 header.Method = zip.Deflate 100 if !f.Info.ParsedMTime.IsZero() { 101 header.Modified = f.Info.ParsedMTime 102 } 103 if f.Info.Mode != 0 { 104 header.SetMode(f.Info.Mode) 105 } 106 w, err := a.z.CreateHeader(header) 107 if err != nil { 108 return err 109 } 110 if info.IsDir() { 111 return nil 112 } 113 if info.Mode()&os.ModeSymlink != 0 { 114 link, err := os.Readlink(f.Source) // #nosec 115 if err != nil { 116 return fmt.Errorf("%s: %w", f.Source, err) 117 } 118 _, err = io.WriteString(w, filepath.ToSlash(link)) 119 return err 120 } 121 file, err := os.Open(f.Source) // #nosec 122 if err != nil { 123 return err 124 } 125 defer file.Close() 126 _, err = io.Copy(w, file) 127 return err 128 } 129 130 // TODO: test fileinfo stuff