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 }