github.com/sberex/go-sberex@v1.8.2-0.20181113200658-ed96ac38f7d7/internal/build/archive.go (about) 1 // This file is part of the go-sberex library. The go-sberex library is 2 // free software: you can redistribute it and/or modify it under the terms 3 // of the GNU Lesser General Public License as published by the Free 4 // Software Foundation, either version 3 of the License, or (at your option) 5 // any later version. 6 // 7 // The go-sberex library is distributed in the hope that it will be useful, 8 // but WITHOUT ANY WARRANTY; without even the implied warranty of 9 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 10 // General Public License <http://www.gnu.org/licenses/> for more details. 11 12 package build 13 14 import ( 15 "archive/tar" 16 "archive/zip" 17 "compress/gzip" 18 "fmt" 19 "io" 20 "os" 21 "path/filepath" 22 "strings" 23 ) 24 25 type Archive interface { 26 // Directory adds a new directory entry to the archive and sets the 27 // directory for subsequent calls to Header. 28 Directory(name string) error 29 30 // Header adds a new file to the archive. The file is added to the directory 31 // set by Directory. The content of the file must be written to the returned 32 // writer. 33 Header(os.FileInfo) (io.Writer, error) 34 35 // Close flushes the archive and closes the underlying file. 36 Close() error 37 } 38 39 func NewArchive(file *os.File) (Archive, string) { 40 switch { 41 case strings.HasSuffix(file.Name(), ".zip"): 42 return NewZipArchive(file), strings.TrimSuffix(file.Name(), ".zip") 43 case strings.HasSuffix(file.Name(), ".tar.gz"): 44 return NewTarballArchive(file), strings.TrimSuffix(file.Name(), ".tar.gz") 45 default: 46 return nil, "" 47 } 48 } 49 50 // AddFile appends an existing file to an archive. 51 func AddFile(a Archive, file string) error { 52 fd, err := os.Open(file) 53 if err != nil { 54 return err 55 } 56 defer fd.Close() 57 fi, err := fd.Stat() 58 if err != nil { 59 return err 60 } 61 w, err := a.Header(fi) 62 if err != nil { 63 return err 64 } 65 if _, err := io.Copy(w, fd); err != nil { 66 return err 67 } 68 return nil 69 } 70 71 // WriteArchive creates an archive containing the given files. 72 func WriteArchive(name string, files []string) (err error) { 73 archfd, err := os.Create(name) 74 if err != nil { 75 return err 76 } 77 78 defer func() { 79 archfd.Close() 80 // Remove the half-written archive on failure. 81 if err != nil { 82 os.Remove(name) 83 } 84 }() 85 archive, basename := NewArchive(archfd) 86 if archive == nil { 87 return fmt.Errorf("unknown archive extension") 88 } 89 fmt.Println(name) 90 if err := archive.Directory(basename); err != nil { 91 return err 92 } 93 for _, file := range files { 94 fmt.Println(" +", filepath.Base(file)) 95 if err := AddFile(archive, file); err != nil { 96 return err 97 } 98 } 99 return archive.Close() 100 } 101 102 type ZipArchive struct { 103 dir string 104 zipw *zip.Writer 105 file io.Closer 106 } 107 108 func NewZipArchive(w io.WriteCloser) Archive { 109 return &ZipArchive{"", zip.NewWriter(w), w} 110 } 111 112 func (a *ZipArchive) Directory(name string) error { 113 a.dir = name + "/" 114 return nil 115 } 116 117 func (a *ZipArchive) Header(fi os.FileInfo) (io.Writer, error) { 118 head, err := zip.FileInfoHeader(fi) 119 if err != nil { 120 return nil, fmt.Errorf("can't make zip header: %v", err) 121 } 122 head.Name = a.dir + head.Name 123 head.Method = zip.Deflate 124 w, err := a.zipw.CreateHeader(head) 125 if err != nil { 126 return nil, fmt.Errorf("can't add zip header: %v", err) 127 } 128 return w, nil 129 } 130 131 func (a *ZipArchive) Close() error { 132 if err := a.zipw.Close(); err != nil { 133 return err 134 } 135 return a.file.Close() 136 } 137 138 type TarballArchive struct { 139 dir string 140 tarw *tar.Writer 141 gzw *gzip.Writer 142 file io.Closer 143 } 144 145 func NewTarballArchive(w io.WriteCloser) Archive { 146 gzw := gzip.NewWriter(w) 147 tarw := tar.NewWriter(gzw) 148 return &TarballArchive{"", tarw, gzw, w} 149 } 150 151 func (a *TarballArchive) Directory(name string) error { 152 a.dir = name + "/" 153 return a.tarw.WriteHeader(&tar.Header{ 154 Name: a.dir, 155 Mode: 0755, 156 Typeflag: tar.TypeDir, 157 }) 158 } 159 160 func (a *TarballArchive) Header(fi os.FileInfo) (io.Writer, error) { 161 head, err := tar.FileInfoHeader(fi, "") 162 if err != nil { 163 return nil, fmt.Errorf("can't make tar header: %v", err) 164 } 165 head.Name = a.dir + head.Name 166 if err := a.tarw.WriteHeader(head); err != nil { 167 return nil, fmt.Errorf("can't add tar header: %v", err) 168 } 169 return a.tarw, nil 170 } 171 172 func (a *TarballArchive) Close() error { 173 if err := a.tarw.Close(); err != nil { 174 return err 175 } 176 if err := a.gzw.Close(); err != nil { 177 return err 178 } 179 return a.file.Close() 180 }