github.com/tickoalcantara12/micro/v3@v3.0.0-20221007104245-9d75b9bcbab9/service/build/util/tar/tar.go (about)

     1  // Package tar is a wrapper around archive/tar, it's used for archiving and unarchiving source from
     2  // and into folders on the host. Because runtime has many cases where it streams source via RPC,
     3  // this package encapsulates the shared logic.
     4  package tar
     5  
     6  import (
     7  	"archive/tar"
     8  	"bytes"
     9  	"fmt"
    10  	"io"
    11  	"io/ioutil"
    12  	"os"
    13  	"path/filepath"
    14  	"strings"
    15  )
    16  
    17  // Unarchive decodes the source in a tar and writes it to a directory
    18  func Unarchive(src io.Reader, dir string) error {
    19  	tr := tar.NewReader(src)
    20  	for {
    21  		hdr, err := tr.Next()
    22  		if err == io.EOF {
    23  			break
    24  		} else if err != nil {
    25  			return err
    26  		}
    27  
    28  		path := filepath.Join(dir, hdr.Name)
    29  		bytes, err := ioutil.ReadAll(tr)
    30  		if err != nil {
    31  			return err
    32  		}
    33  
    34  		switch hdr.Typeflag {
    35  		case tar.TypeDir:
    36  			if _, verr := os.Stat(path); os.IsNotExist(verr) {
    37  				err = os.Mkdir(path, os.ModePerm)
    38  			}
    39  		case tar.TypeReg:
    40  			err = ioutil.WriteFile(path, bytes, os.ModePerm)
    41  		default:
    42  			err = fmt.Errorf("Unknown tar header type flag: %v", string(hdr.Typeflag))
    43  		}
    44  
    45  		if err != nil {
    46  			return err
    47  		}
    48  	}
    49  	return nil
    50  }
    51  
    52  // Archive a local directory into a tar gzip
    53  func Archive(dir string) (io.Reader, error) {
    54  	// Create a tar writer and a buffer to store the archive
    55  	tf := bytes.NewBuffer(nil)
    56  	tw := tar.NewWriter(tf)
    57  	defer tw.Close()
    58  
    59  	// walkFn archives each file in the directory
    60  	walkFn := func(path string, info os.FileInfo, err error) error {
    61  		if err != nil {
    62  			return err
    63  		}
    64  
    65  		// get the relative path, e.g. cmd/main.go
    66  		relpath, err := filepath.Rel(dir, path)
    67  		if err != nil {
    68  			return err
    69  		}
    70  
    71  		// skip git files
    72  		if filepath.HasPrefix(relpath, ".git") {
    73  			return nil
    74  		}
    75  
    76  		// skip irrelevent files
    77  		if !info.IsDir() && !shouldArchive(relpath) {
    78  			return nil
    79  		}
    80  
    81  		// generate and write tar header
    82  		header, err := tar.FileInfoHeader(info, relpath)
    83  		if err != nil {
    84  			return err
    85  		}
    86  
    87  		// Since os.FileInfo's Name method only returns the base name of the file it describes, it is
    88  		// necessary to modify Header.Name to provide the full path name of the file. See:
    89  		// https://golang.org/src/archive/tar/common.go?s=22088:22153#L626
    90  		header.Name = relpath
    91  
    92  		// write the header to the archive
    93  		if err := tw.WriteHeader(header); err != nil {
    94  			return err
    95  		}
    96  
    97  		// there is no body if it's a directory
    98  		if info.IsDir() {
    99  			return nil
   100  		}
   101  
   102  		// read the contents of the file
   103  		bytes, err := ioutil.ReadFile(path)
   104  		if err != nil {
   105  			return err
   106  		}
   107  
   108  		// write the contents of the file to the tar
   109  		_, err = tw.Write([]byte(bytes))
   110  		return err
   111  	}
   112  
   113  	// Add the files to the archive
   114  	if err := filepath.Walk(dir, walkFn); err != nil {
   115  		return nil, err
   116  	}
   117  
   118  	// Return the archive
   119  	return tf, nil
   120  }
   121  
   122  // shouldArchive is a helper func which indicates if a file should be archived. TODO: implement a
   123  // smarter check which just excludes executables
   124  func shouldArchive(file string) bool {
   125  	if filepath.HasPrefix(file, "vendor") {
   126  		return true
   127  	}
   128  	if strings.HasSuffix(file, ".go") {
   129  		return true
   130  	}
   131  	if strings.HasSuffix(file, "go.sum") {
   132  		return true
   133  	}
   134  	if strings.HasSuffix(file, "go.mod") {
   135  		return true
   136  	}
   137  	if strings.HasSuffix(file, ".txt") {
   138  		return true
   139  	}
   140  	return false
   141  }