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  }