Skip to content

Commit c8072c2

Browse files
committed
Output json file version manifest
1 parent e46aae3 commit c8072c2

File tree

5 files changed

+192
-159
lines changed

5 files changed

+192
-159
lines changed

β€ŽREADME.mdβ€Ž

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,32 @@
11
# buffer-static-upload
22

3-
A straightforward static asset uploader
3+
A straightforward static asset uploader which generates a json file for your
4+
application to read the uploaded file locations from.
5+
6+
## Usage
7+
8+
Ensure your AWS credentials environment variables are set (`AWS_ACCESS_KEY_ID`,
9+
`AWS_SECRET_ACCESS_KEY`). The cli has the following argument options:
10+
11+
```
12+
$ buffer-static-upload -h
13+
Usage of buffer-static-upload:
14+
-bucket string
15+
the s3 bucket to upload to (default "static.buffer.com")
16+
-dir string
17+
required, the directory to upload files to in the bucket
18+
-files string
19+
the path to the files you'd like to upload, ex. "public/**/.*js,public/style.css"
20+
-o string
21+
the json file you'd like your generate (default "staticAssets.json")
22+
```
23+
24+
For example, you can use glob patterns to match multiple sets of files:
25+
26+
```
27+
buffer-static-upload -files "public/js/**/*.js,public/css/*.css"
28+
```
29+
30+
*Note* - The default bucket is used by multiple teams, so if you use that you
31+
must specify a directory to use for your project as not to create unnecessary
32+
collisions.

β€Žmain.goβ€Ž

Lines changed: 162 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,181 @@
11
package main
22

33
import (
4+
"crypto/md5"
5+
"encoding/hex"
6+
"encoding/json"
47
"flag"
58
"fmt"
9+
"io"
10+
"io/ioutil"
11+
"mime"
12+
"os"
13+
"path"
14+
"path/filepath"
15+
"strings"
16+
"time"
617

7-
"github.com/bufferapp/buffer-static-upload/utils"
18+
"github.com/aws/aws-sdk-go/aws"
19+
"github.com/aws/aws-sdk-go/aws/credentials"
20+
"github.com/aws/aws-sdk-go/aws/endpoints"
21+
"github.com/aws/aws-sdk-go/aws/session"
22+
"github.com/aws/aws-sdk-go/service/s3/s3manager"
823
)
924

10-
func main() {
11-
s3Bucket := "buffer-dan-test"
25+
var defaultS3Bucket string = "static.buffer.com"
26+
27+
// GetFileMd5 returns a checksum for a given file
28+
func GetFileMd5(file *os.File) (string, error) {
29+
var fileHash string
30+
hash := md5.New()
31+
if _, err := io.Copy(hash, file); err != nil {
32+
return fileHash, err
33+
}
34+
hashInBytes := hash.Sum(nil)[:16]
35+
fileHash = hex.EncodeToString(hashInBytes)
36+
return fileHash, nil
37+
}
38+
39+
// GetVersionedFilename returns a new filename with the version before the extension
40+
func GetVersionedFilename(filename string, version string) string {
41+
ext := filepath.Ext(filename)
42+
versionedExt := "." + version + ext
43+
versionedFilename := strings.Replace(filename, ext, versionedExt, 1)
44+
return versionedFilename
45+
}
46+
47+
// GetFileMimeType returns the mime type of a file using it's extension
48+
func GetFileMimeType(filename string) string {
49+
ext := filepath.Ext(filename)
50+
return mime.TypeByExtension(ext)
51+
}
52+
53+
// GetFilesFromGlobsList returns a list of files that match a list of
54+
// comma-deliniated file path globs
55+
func GetFilesFromGlobsList(globList string) ([]string, error) {
56+
var files []string
57+
globs := strings.Split(globList, ",")
58+
59+
for _, glob := range globs {
60+
fileList, err := filepath.Glob(glob)
61+
if err != nil {
62+
return files, err
63+
}
64+
files = append(files, fileList...)
65+
}
66+
return files, nil
67+
}
68+
69+
// GetS3Uploader returns a configured Uploader
70+
func GetS3Uploader() (*s3manager.Uploader, error) {
71+
var uploader *s3manager.Uploader
72+
73+
awsAccessKeyID := os.Getenv("AWS_ACCESS_KEY_ID")
74+
awsSecretAccessKey := os.Getenv("AWS_SECRET_ACCESS_KEY")
75+
76+
creds := credentials.NewStaticCredentials(awsAccessKeyID, awsSecretAccessKey, "")
77+
78+
sess := session.Must(session.NewSession(&aws.Config{
79+
Credentials: creds,
80+
Region: aws.String(endpoints.UsEast1RegionID),
81+
}))
82+
83+
_, err := creds.Get()
84+
if err != nil {
85+
fmt.Printf("failed to load AWS credentials %s", err)
86+
return uploader, err
87+
}
88+
89+
uploader = s3manager.NewUploader(sess)
90+
return uploader, nil
91+
}
1292

13-
filesArg := flag.String("files", "", "the path to the files you'd like to upload")
93+
// VersionAndUploadFiles will verion files and upload them to s3 and return
94+
// a map of filenames and their version hashes
95+
func VersionAndUploadFiles(
96+
bucket string,
97+
directory string,
98+
filenames []string,
99+
) (map[string]string, error) {
100+
fileVersions := map[string]string{}
101+
102+
uploader, err := GetS3Uploader()
103+
if err != nil {
104+
return fileVersions, err
105+
}
106+
107+
for _, filename := range filenames {
108+
file, err := os.Open(filename)
109+
if err != nil {
110+
return fileVersions, err
111+
}
112+
defer file.Close()
113+
114+
checksum, err := GetFileMd5(file)
115+
if err != nil {
116+
return fileVersions, err
117+
}
118+
119+
versionedFilename := GetVersionedFilename(filename, checksum)
120+
bucketFilename := path.Join(directory, versionedFilename)
121+
mimeType := GetFileMimeType(filename)
122+
123+
result, err := uploader.Upload(&s3manager.UploadInput{
124+
Bucket: aws.String(bucket),
125+
Key: aws.String(bucketFilename),
126+
ContentType: aws.String(mimeType),
127+
CacheControl: aws.String("public, max-age=31520626"),
128+
Expires: aws.Time(time.Now().AddDate(10, 0, 0)),
129+
Body: file,
130+
})
131+
if err != nil {
132+
return fileVersions, err
133+
}
134+
135+
// the static.buffer.com bucket has a domain alias
136+
if bucket == defaultS3Bucket {
137+
fileVersions[filename] = "https://" + path.Join(bucket, bucketFilename)
138+
} else {
139+
fileVersions[filename] = result.Location
140+
}
141+
fmt.Printf("Uploaded %s\n", fileVersions[filename])
142+
}
143+
144+
return fileVersions, nil
145+
}
146+
147+
func main() {
148+
s3Bucket := flag.String("bucket", defaultS3Bucket, "the s3 bucket to upload to")
149+
directory := flag.String("dir", "", "required, the directory to upload files to in the bucket")
150+
filesArg := flag.String("files", "", "the path to the files you'd like to upload, ex. \"public/**/.*js,public/style.css\"")
151+
outputFilename := flag.String("o", "staticAssets.json", "the json file you'd like your generate")
14152
flag.Parse()
15153

16-
files, err := utils.GetFilesFromGlobsList(*filesArg)
154+
if *directory == "" && *s3Bucket == defaultS3Bucket {
155+
fmt.Println("To use the default bucket you need to specify an upload directory (-dir)")
156+
os.Exit(1)
157+
}
158+
159+
files, err := GetFilesFromGlobsList(*filesArg)
17160
if err != nil {
18-
fmt.Printf("err %s", err)
161+
fmt.Printf("failed to get files %s", err)
19162
}
20-
fmt.Printf("Found %d files to upload and version\n", len(files))
163+
fmt.Printf("Found %d files to upload and version:\n", len(files))
21164

22-
fileVersions, err := utils.VersionAndUploadFiles(s3Bucket, files)
165+
fileVersions, err := VersionAndUploadFiles(*s3Bucket, *directory, files)
23166
if err != nil {
24-
fmt.Printf("Failed to upload files: %s", err)
167+
fmt.Printf("failed to upload files %s", err)
25168
}
26169

27-
for file, version := range fileVersions {
28-
fmt.Printf("%s = %s\n", file, version)
170+
output, err := json.MarshalIndent(fileVersions, "", " ")
171+
if err != nil {
172+
fmt.Printf("failed to generate versions json file %s", err)
29173
}
174+
175+
err = ioutil.WriteFile(*outputFilename, output, 0644)
176+
if err != nil {
177+
fmt.Printf("failed to write versions json file %s", err)
178+
}
179+
180+
fmt.Printf("\nSuccessfully uploaded static assets and generated %s\n", *outputFilename)
30181
}

β€Žtests/hash-test.txtβ€Ž

Lines changed: 0 additions & 1 deletion
This file was deleted.

β€Žtests/utils_test.goβ€Ž

Lines changed: 0 additions & 17 deletions
This file was deleted.

β€Žutils/utils.goβ€Ž

Lines changed: 0 additions & 129 deletions
This file was deleted.

0 commit comments

Comments
Β (0)