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