-
Notifications
You must be signed in to change notification settings - Fork 16
/
main.go
136 lines (118 loc) · 3.49 KB
/
main.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
package main
import (
"crypto/sha1"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"path/filepath"
"strings"
getopt "github.com/pborman/getopt"
)
var exclude = getopt.ListLong("exclude", 'x', "", "glob patterns to exclude")
var help = getopt.BoolLong("help", 'h', "", "print this help")
func main() {
getopt.SetParameters("<root dir> <bucket name>")
getopt.Parse()
if *help {
getopt.PrintUsage(os.Stdout)
return
}
args := getopt.Args()
if len(args) != 2 {
getopt.PrintUsage(os.Stderr)
os.Exit(1)
}
rootDir := args[0]
bucketName := args[1]
resourcesMap := map[string]interface{}{}
result := map[string]interface{}{
"resource": map[string]interface{}{
"aws_s3_bucket_object": resourcesMap,
},
}
filepath.Walk(rootDir, func(path string, info os.FileInfo, err error) error {
if err != nil {
fmt.Fprintf(os.Stderr, "Error reading %s: %s\n", path, err)
// Skip stuff we can't read.
return nil
}
relPath, err := filepath.Rel(rootDir, path)
if err != nil {
fmt.Fprintf(os.Stderr, "Failed make %s relative: %s\n", path, err)
return nil
}
path, err = filepath.EvalSymlinks(path)
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to resolve symlink %s: %s\n", path, err)
return nil
}
if info.IsDir() {
// Don't need to create directories since they are implied
// by the files within.
return nil
}
for _, pattern := range *exclude {
var toMatch []string
if strings.ContainsRune(pattern, filepath.Separator) {
toMatch = append(toMatch, relPath)
} else {
// If the pattern does not include a path separator
// then we apply it to all segments of the path
// individually.
toMatch = strings.Split(relPath, string(filepath.Separator))
}
for _, matchPath := range toMatch {
matched, _ := filepath.Match(pattern, matchPath)
if matched {
return nil
}
}
}
// We use the initial bytes of the file to infer a MIME type
file, err := os.Open(path)
if err != nil {
fmt.Fprintf(os.Stderr, "Error opening %s: %s\n", path, err)
return nil
}
hasher := sha1.New()
fileBytes := make([]byte, 1024*1024)
contentType := ""
_, err = file.Read(fileBytes)
// If we got back and error and it isn't the end of file then
// skip it. This does "something" with 0 length files. It is
// likely we should really be categorizing those based on file
// extension.
if err != nil && err != io.EOF {
fmt.Fprintf(os.Stderr, "Error reading %s: %s\n", path, err)
return nil
}
if strings.HasSuffix(relPath, ".svg") {
// If we start to need a set of overrides for DetectContentType
// then we need to find a different way to do this.
contentType = "image/svg+xml"
} else if strings.HasSuffix(relPath, ".css") {
// If we start to need a set of overrides for DetectContentType
// then we need to find a different way to do this.
contentType = "text/css"
} else {
contentType = http.DetectContentType(fileBytes)
}
// Resource name is a hash of the path, so it should stay consistent
// for a given file path as long as the relative path to the target
// directory is always the same across runs.
hasher.Write([]byte(relPath))
resourceName := fmt.Sprintf("%x", hasher.Sum(nil))
resourcesMap[resourceName] = map[string]interface{}{
"bucket": bucketName,
"key": relPath,
"source": path,
"etag": fmt.Sprintf("${md5(file(%q))}", path),
"content_type": contentType,
}
return nil
})
encoder := json.NewEncoder(os.Stdout)
encoder.Encode(result)
}