├── static ├── text.png ├── default.png ├── folder.png └── style.css ├── circle.yml ├── .gitignore ├── Makefile ├── Dockerfile ├── LICENSE ├── README.md ├── s3_utils.go ├── static.go └── main.go /static/text.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker-archive/docker-bb/HEAD/static/text.png -------------------------------------------------------------------------------- /static/default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker-archive/docker-bb/HEAD/static/default.png -------------------------------------------------------------------------------- /static/folder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker-archive/docker-bb/HEAD/static/folder.png -------------------------------------------------------------------------------- /circle.yml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | post: 3 | # install golint 4 | - go get github.com/golang/lint/golint 5 | 6 | test: 7 | pre: 8 | - go vet ./... 9 | - test -z "$(golint ./... | tee /dev/stderr)" 10 | - test -z "$(gofmt -s -l . | tee /dev/stderr)" 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ###Go### 2 | 3 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 4 | *.o 5 | *.a 6 | *.so 7 | *.swo 8 | *.swp 9 | 10 | # Folders 11 | _obj 12 | _test 13 | 14 | # Architecture specific extensions/prefixes 15 | *.[568vq] 16 | [568vq].out 17 | 18 | *.cgo1.go 19 | *.cgo2.c 20 | _cgo_defun.c 21 | _cgo_gotypes.go 22 | _cgo_export.* 23 | 24 | _testmain.go 25 | 26 | *.exe 27 | *.test 28 | 29 | 30 | ###OSX### 31 | 32 | .DS_Store 33 | .AppleDouble 34 | .LSOverride 35 | 36 | # Icon must ends with two \r. 37 | Icon 38 | 39 | 40 | # Thumbnails 41 | ._* 42 | 43 | # Files that might appear on external disk 44 | .Spotlight-V100 45 | .Trashes 46 | 47 | docker-bb 48 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Set an output prefix, which is the local directory if not specified 2 | PREFIX?=$(shell pwd) 3 | BUILDTAGS= 4 | 5 | .PHONY: clean all fmt vet lint build test install static 6 | .DEFAULT: default 7 | 8 | all: clean build static fmt lint test vet 9 | 10 | build: 11 | @echo "+ $@" 12 | @go build -tags "$(BUILDTAGS) cgo" . 13 | 14 | static: 15 | @echo "+ $@" 16 | CGO_ENABLED=1 go build -tags "$(BUILDTAGS) cgo static_build" -ldflags "-w -extldflags -static" -o docker-bb . 17 | 18 | fmt: 19 | @echo "+ $@" 20 | @gofmt -s -l . 21 | 22 | lint: 23 | @echo "+ $@" 24 | @golint ./... 25 | 26 | test: fmt lint vet 27 | @echo "+ $@" 28 | @go test -v -tags "$(BUILDTAGS) cgo" ./... 29 | 30 | vet: 31 | @echo "+ $@" 32 | @go vet ./... 33 | 34 | clean: 35 | @echo "+ $@" 36 | @rm -rf docker-bb 37 | 38 | install: 39 | @echo "+ $@" 40 | @go install -v . 41 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine 2 | MAINTAINER Jessica Frazelle 3 | 4 | ENV PATH /go/bin:/usr/local/go/bin:$PATH 5 | ENV GOPATH /go 6 | 7 | RUN apk update && apk add \ 8 | ca-certificates \ 9 | git \ 10 | && rm -rf /var/cache/apk/* 11 | 12 | # make git happy 13 | RUN git config --global user.name docker-bb \ 14 | && git config --global user.email dockerbb@dockerproject.com \ 15 | && ln -s /.dockerinit /usr/bin/docker 16 | 17 | COPY . /go/src/github.com/docker/docker-bb 18 | 19 | RUN buildDeps=' \ 20 | go \ 21 | gcc \ 22 | libc-dev \ 23 | libgcc \ 24 | ' \ 25 | set -x \ 26 | && apk update \ 27 | && apk add $buildDeps \ 28 | && cd /go/src/github.com/docker/docker-bb \ 29 | && go get -d -v github.com/docker/docker-bb \ 30 | && go build -o /usr/bin/docker-bb . \ 31 | && apk del $buildDeps \ 32 | && rm -rf /var/cache/apk/* \ 33 | && rm -rf /go \ 34 | && echo "Build complete." 35 | 36 | 37 | ENTRYPOINT ["docker-bb"] 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Jessie Frazelle 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Docker Binary Builder 2 | 3 | [![Circle CI](https://circleci.com/gh/docker/docker-bb.svg?style=svg)](https://circleci.com/gh/docker/docker-bb) 4 | 5 | Docker binary builder, triggered from nsq messages. 6 | 7 | ```console 8 | $ docker-bb -h 9 | Usage of docker-bb: 10 | -channel="binaries": nsq channel 11 | -d=false: run in debug mode 12 | -lookupd-addr="nsqlookupd:4161": nsq lookupd address 13 | -s3bucket="s3://test.docker.com/master/binaries/": s3 bucket to push binaries 14 | -s3region="us-east-1": s3 region where bucket lives 15 | -topic="hooks-docker": nsq topic 16 | -v=false: print version and exit (shorthand) 17 | -version=false: print version and exit 18 | ``` 19 | 20 | Example docker run command: 21 | 22 | ```bash 23 | $ docker run -d --restart always \ 24 | --link nsqlookupd1:nsqlookupd \ 25 | -e AWS_ACCESS_KEY_ID -e AWS_SECRET_ACCESS_KEY \ 26 | -v /var/run/docker.sock:/var/run/docker.sock \ 27 | -v /usr/local/bin/docker:/usr/local/bin/docker \ 28 | -v /tmp:/tmp \ 29 | -e DOCKER_HOST="unix:///var/run/docker.sock" \ 30 | --privileged \ 31 | --name binary-builder \ 32 | dockercore/docker-bb -d -s3bucket="s3://jesss/test/docker/master/" \ 33 | -s3region="us-west-1" \ 34 | -topic hooks-docker -channel binaries \ 35 | -lookupd-addr nsqlookupd:4161 36 | ``` 37 | -------------------------------------------------------------------------------- /static/style.css: -------------------------------------------------------------------------------- 1 | @import url('//fonts.googleapis.com/css?family=Source+Code+Pro:400,500,600'); 2 | /* Have to use @import for the font, as you can only specify a single stylesheet */ 3 | * { 4 | margin:0; 5 | padding:0; 6 | -webkit-box-sizing:border-box; 7 | -moz-box-sizing:border-box; 8 | box-sizing: border-box; 9 | } 10 | 11 | html { 12 | min-height:100%; 13 | border-top:10px solid #ECEEF1; 14 | border-bottom:10px solid #ECEEF1; 15 | color:#61666c; 16 | font-weight:400; 17 | font-size:1em; 18 | font-family:'Source Code Pro', sans-serif; 19 | line-height:2em; 20 | } 21 | body { 22 | padding:20px; 23 | -webkit-backface-visibility:hidden; 24 | } 25 | code { 26 | font-family:Inconsolata,monospace; 27 | } 28 | a { 29 | color:#61666c; 30 | text-decoration:none; 31 | } 32 | a:hover { 33 | color:#2a2a2a; 34 | } 35 | /*------------------------------------*\ 36 | Wrapper 37 | \*------------------------------------*/ 38 | .wrapper { 39 | margin:0 auto; 40 | padding-top:20px; 41 | max-width:800px; 42 | } 43 | /*------------------------------------*\ 44 | Demo block 45 | \*------------------------------------*/ 46 | .block, p { 47 | font-size:.875em; 48 | margin:20px 0; 49 | padding:20px; 50 | color:#9099A3; 51 | } 52 | 53 | h1 { 54 | font-weight:200; 55 | text-align:center; 56 | font-size:1.4em; 57 | line-height:3em; 58 | } 59 | /*------------------------------------*\ 60 | Table (directory listing) 61 | \*------------------------------------*/ 62 | table { 63 | border-collapse:collapse; 64 | font-size:.875em; 65 | max-width:100%; 66 | margin:20px auto 0px auto; 67 | white-space:nowrap; 68 | } 69 | tr { 70 | outline:0; 71 | border:0; 72 | } 73 | tr:hover td { 74 | background:#f6f6f6; 75 | } 76 | th { 77 | text-align:left; 78 | font-size:.80em; 79 | padding-right:20px; 80 | } 81 | /* 2nd Column: Filename */ 82 | th + th { 83 | width:65%; 84 | } 85 | /* 3rd Column: Last Modified */ 86 | th + th + th { 87 | } 88 | /* 4th Column: Size */ 89 | th + th + th + th { 90 | width:5%; 91 | } 92 | tr td:first-of-type { 93 | padding-left:10px; 94 | padding-right:10px; 95 | } 96 | td { 97 | padding:5px 0; 98 | outline:0; 99 | border:0; 100 | border-bottom:1px solid #edf1f5; 101 | vertical-align:middle; 102 | text-align:left; 103 | -webkit-transition:background 300ms ease-in; 104 | -moz-transition:background 300ms ease-in; 105 | -ms-transition:background 300ms ease-in; 106 | -o-transition:background 300ms ease-in; 107 | transition:background 300ms ease-in; 108 | } 109 | td:last-child, th:last-child { 110 | text-align:right; 111 | padding-right:0px; 112 | } 113 | td a{ 114 | display: block; 115 | } 116 | tr.parent a { 117 | color:#9099A3; 118 | } 119 | .parent a:hover { 120 | color:#2a2a2a; 121 | } 122 | /*------------------------------------*\ 123 | Footer 124 | \*------------------------------------*/ 125 | .footer { 126 | text-align:center; 127 | font-size:.75em; 128 | margin-top:50px; 129 | } 130 | img { 131 | outline:none; 132 | border:none; 133 | max-height:16px; 134 | } 135 | -------------------------------------------------------------------------------- /s3_utils.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | "os" 7 | "path" 8 | "path/filepath" 9 | "strings" 10 | 11 | "github.com/Sirupsen/logrus" 12 | "github.com/crowdmob/goamz/s3" 13 | ) 14 | 15 | func pushToS3(bucket *s3.Bucket, bucketpath, bundlesPath string) error { 16 | if _, err := os.Stat(bundlesPath); os.IsNotExist(err) { 17 | return fmt.Errorf("This is awkward, the bundles path DNE: %s", bundlesPath) 18 | } 19 | 20 | walkFn := func(fpath string, info os.FileInfo, err error) error { 21 | stat, err := os.Stat(fpath) 22 | if err != nil { 23 | return err 24 | } 25 | 26 | relFilePath, err := filepath.Rel(bundlesPath, fpath) 27 | if err != nil || (fpath == bundlesPath && stat.IsDir()) { 28 | // Error getting relative path OR we are looking 29 | // at the root path. Skip in both situations. 30 | return nil 31 | } 32 | 33 | if stat.IsDir() { 34 | return nil 35 | } 36 | 37 | if err = uploadFileToS3(bucket, fpath, path.Join(bucketpath, relFilePath), ""); err != nil { 38 | return fmt.Errorf("Uploading %s to s3 failed: %v", fpath, err) 39 | } 40 | 41 | return nil 42 | } 43 | 44 | // walk the filepath 45 | if err := filepath.Walk(bundlesPath, walkFn); err != nil { 46 | return err 47 | } 48 | 49 | return nil 50 | } 51 | 52 | func uploadFileToS3(bucket *s3.Bucket, fpath, s3path, contentType string) error { 53 | contents, err := ioutil.ReadFile(fpath) 54 | if err != nil { 55 | return fmt.Errorf("Reading %q failed: %v", fpath, err) 56 | } 57 | 58 | // push the file to s3 59 | logrus.Debugf("Pushing %s to s3", s3path) 60 | if err := bucket.Put(s3path, contents, contentType, "public-read", s3.Options{CacheControl: "no-cache"}); err != nil { 61 | return err 62 | } 63 | logrus.Infof("Sucessfully pushed %s to s3", s3path) 64 | return nil 65 | } 66 | 67 | // parse for the parts of the bucket name 68 | func bucketParts(bucket string) (bucketname, path string) { 69 | s3Prefix := "s3://" 70 | if strings.HasPrefix(bucket, s3Prefix) { 71 | bucket = strings.Replace(bucket, s3Prefix, "", 1) 72 | } 73 | parts := strings.SplitN(bucket, "/", 2) 74 | 75 | if len(parts) <= 1 { 76 | path = "" 77 | } else { 78 | path = parts[1] 79 | } 80 | return parts[0], path 81 | } 82 | 83 | // listFiles lists the files in a specific s3 bucket. 84 | func listFiles(prefix, delimiter, marker string, maxKeys int, b *s3.Bucket) (files []s3.Key, err error) { 85 | resp, err := b.List(prefix, delimiter, marker, maxKeys) 86 | if err != nil { 87 | return nil, err 88 | } 89 | 90 | // append to files 91 | for _, fl := range resp.Contents { 92 | if strings.Contains(fl.Key, "index.html") || strings.Contains(fl.Key, "static") || strings.Contains(fl.Key, "logs") { 93 | continue 94 | } 95 | 96 | files = append(files, fl) 97 | } 98 | 99 | // recursion for the recursion god 100 | if resp.IsTruncated && resp.NextMarker != "" { 101 | f, err := listFiles(resp.Prefix, resp.Delimiter, resp.NextMarker, resp.MaxKeys, b) 102 | if err != nil { 103 | return nil, err 104 | } 105 | 106 | // append to files 107 | files = append(files, f...) 108 | } 109 | 110 | return files, nil 111 | } 112 | -------------------------------------------------------------------------------- /static.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | "os" 7 | "path" 8 | "strings" 9 | "text/template" 10 | 11 | "github.com/crowdmob/goamz/s3" 12 | units "github.com/docker/go-units" 13 | ) 14 | 15 | const ( 16 | index string = ` 17 | 18 | 19 | 20 | 21 | Docker Master Binaries 22 | 23 | 24 | 25 |

Docker Master Binaries

26 | 27 |
28 | 29 |

These binaries are built and updated with each commit to the master branch of Docker. Want to use that cool new feature that was just merged? Download your system's binary and check out the master docs at docs.master.dockerproject.com.

30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | {{ range $key, $value := . }} 42 | 43 | 44 | 45 | 46 | 47 | 48 | {{ end }} 49 | 50 |
[ICO]NameSizeUploaded Date
[ICO]{{ $value.Key }}{{ $value.Size | size }}{{ $value.LastModified }}
51 |
52 | 53 | ` 54 | ) 55 | 56 | // create the index.html file 57 | func createIndexFile(bucket *s3.Bucket, bucketpath string) error { 58 | // list all the files 59 | files, err := listFiles(bucketpath, bucketpath, "", 2000, bucket) 60 | if err != nil { 61 | return fmt.Errorf("Listing all files in bucket failed: %v", err) 62 | } 63 | 64 | // create a temp file for the index 65 | tmp, err := ioutil.TempFile("", "index.html") 66 | if err != nil { 67 | return fmt.Errorf("Creating temp file failed: %v", err) 68 | } 69 | defer os.RemoveAll(tmp.Name()) 70 | 71 | // set up custom functions 72 | funcMap := template.FuncMap{ 73 | "ext": func(name string) string { 74 | if strings.HasSuffix(name, ".sha256") || strings.HasSuffix(name, ".md5") { 75 | return "text" 76 | } 77 | return "default" 78 | }, 79 | "size": func(s int64) string { 80 | return units.HumanSize(float64(s)) 81 | }, 82 | } 83 | 84 | // parse & execute the template 85 | tmpl, err := template.New("").Funcs(funcMap).Parse(index) 86 | if err != nil { 87 | return fmt.Errorf("Parsing template failed: %v", err) 88 | } 89 | 90 | if err := tmpl.Execute(tmp, files); err != nil { 91 | return fmt.Errorf("Execute template failed: %v", err) 92 | } 93 | 94 | // push the file to s3 95 | if err = uploadFileToS3(bucket, tmp.Name(), path.Join(bucketpath, "index.html"), "text/html"); err != nil { 96 | return fmt.Errorf("Uploading %s to s3 failed: %v", tmp.Name(), err) 97 | } 98 | 99 | return nil 100 | } 101 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "io/ioutil" 7 | "os" 8 | "os/signal" 9 | "path" 10 | "syscall" 11 | "time" 12 | 13 | "github.com/Sirupsen/logrus" 14 | "github.com/bitly/go-nsq" 15 | "github.com/crowdmob/goamz/aws" 16 | "github.com/crowdmob/goamz/s3" 17 | "github.com/drone/go-github/github" 18 | ) 19 | 20 | const ( 21 | // VERSION is the binary version. 22 | VERSION = "v0.1.0" 23 | ) 24 | 25 | var ( 26 | lookupd string 27 | topic string 28 | channel string 29 | bucket string 30 | region string 31 | debug bool 32 | version bool 33 | ) 34 | 35 | func init() { 36 | // parse flags 37 | flag.BoolVar(&version, "version", false, "print version and exit") 38 | flag.BoolVar(&version, "v", false, "print version and exit (shorthand)") 39 | flag.BoolVar(&debug, "d", false, "run in debug mode") 40 | flag.StringVar(&lookupd, "lookupd-addr", "nsqlookupd:4161", "nsq lookupd address") 41 | flag.StringVar(&topic, "topic", "hooks-docker", "nsq topic") 42 | flag.StringVar(&channel, "channel", "binaries", "nsq channel") 43 | flag.StringVar(&bucket, "s3bucket", "s3://master.dockerproject.com/", "s3 bucket to push binaries") 44 | flag.StringVar(®ion, "s3region", "us-east-1", "s3 region where bucket lives") 45 | flag.Parse() 46 | } 47 | 48 | func main() { 49 | // set log level 50 | if debug { 51 | logrus.SetLevel(logrus.DebugLevel) 52 | } 53 | 54 | if version { 55 | fmt.Println(VERSION) 56 | return 57 | } 58 | 59 | bb := &Handler{} 60 | if err := ProcessQueue(bb, QueueOptsFromContext(topic, channel, lookupd)); err != nil { 61 | logrus.Fatal(err) 62 | } 63 | } 64 | 65 | // Handler is the message processing interface for the consumer to nsq. 66 | type Handler struct{} 67 | 68 | // QueueOpts are the options for the nsq queue. 69 | type QueueOpts struct { 70 | LookupdAddr string 71 | Topic string 72 | Channel string 73 | Concurrent int 74 | Signals []os.Signal 75 | } 76 | 77 | // QueueOptsFromContext returns a QueueOpts object from the given settings. 78 | func QueueOptsFromContext(topic, channel, lookupd string) QueueOpts { 79 | return QueueOpts{ 80 | Signals: []os.Signal{syscall.SIGTERM, syscall.SIGINT}, 81 | LookupdAddr: lookupd, 82 | Topic: topic, 83 | Channel: channel, 84 | Concurrent: 1, 85 | } 86 | } 87 | 88 | // ProcessQueue sets up the handler to process the nsq queue with the given options. 89 | func ProcessQueue(handler nsq.Handler, opts QueueOpts) error { 90 | if opts.Concurrent == 0 { 91 | opts.Concurrent = 1 92 | } 93 | s := make(chan os.Signal, 64) 94 | signal.Notify(s, opts.Signals...) 95 | 96 | consumer, err := nsq.NewConsumer(opts.Topic, opts.Channel, nsq.NewConfig()) 97 | if err != nil { 98 | return err 99 | } 100 | consumer.AddConcurrentHandlers(handler, opts.Concurrent) 101 | if err := consumer.ConnectToNSQLookupd(opts.LookupdAddr); err != nil { 102 | return err 103 | } 104 | 105 | for { 106 | select { 107 | case <-consumer.StopChan: 108 | return nil 109 | case sig := <-s: 110 | logrus.WithField("signal", sig).Debug("received signal") 111 | consumer.Stop() 112 | } 113 | } 114 | } 115 | 116 | // HandleMessage reads the nsq message body and parses it as a github webhook, 117 | // checks out the source for the repository & builds/uploads the binaries. 118 | func (h *Handler) HandleMessage(m *nsq.Message) error { 119 | hook, err := github.ParseHook(m.Body) 120 | if err != nil { 121 | // Errors will most likely occur because not all GH 122 | // hooks are the same format 123 | // we care about those that are pushes to master 124 | logrus.Debugf("Error parsing hook: %v", err) 125 | return nil 126 | } 127 | 128 | shortSha := hook.After[0:7] 129 | // checkout the code in a temp dir 130 | temp, err := ioutil.TempDir("", fmt.Sprintf("commit-%s", shortSha)) 131 | if err != nil { 132 | return err 133 | } 134 | defer os.RemoveAll(temp) 135 | 136 | if err := checkout(temp, hook.Repo.Url, hook.After); err != nil { 137 | logrus.Warn(err) 138 | return err 139 | } 140 | logrus.Debugf("Checked out %s for %s", hook.After, hook.Repo.Url) 141 | 142 | var ( 143 | image = fmt.Sprintf("docker:commit-%s", shortSha) 144 | container = fmt.Sprintf("build-%s", shortSha) 145 | ) 146 | logrus.Infof("image=%s container=%s\n", image, container) 147 | 148 | // build the image 149 | if err := build(temp, image); err != nil { 150 | logrus.Warn(err) 151 | return err 152 | } 153 | logrus.Debugf("Successfully built image %s", image) 154 | 155 | // make the binary 156 | defer removeContainer(container) 157 | if err = makeBinary(temp, image, container, 20*time.Minute); err != nil { 158 | logrus.Warn(err) 159 | return err 160 | } 161 | logrus.Debugf("Successfully built binaries for %s", hook.After) 162 | 163 | // read the version 164 | version, err := getBinaryVersion(temp) 165 | if err != nil { 166 | logrus.Warnf("Getting binary version failed: %v", err) 167 | return err 168 | } 169 | 170 | bundlesPath := path.Join(temp, "bundles", version, "cross") 171 | 172 | // create commit file 173 | if err := ioutil.WriteFile(path.Join(bundlesPath, "commit"), []byte(hook.After), 0755); err != nil { 174 | return err 175 | } 176 | 177 | // create version file 178 | if err := ioutil.WriteFile(path.Join(bundlesPath, "version"), []byte(version), 0755); err != nil { 179 | return err 180 | } 181 | 182 | // use env variables to connect to s3 183 | auth, err := aws.EnvAuth() 184 | if err != nil { 185 | return fmt.Errorf("AWS Auth failed: %v", err) 186 | } 187 | 188 | // connect to s3 bucket 189 | s := s3.New(auth, aws.GetRegion(region)) 190 | bucketname, bucketpath := bucketParts(bucket) 191 | bucket := s.Bucket(bucketname) 192 | 193 | // push to s3 194 | if err = pushToS3(bucket, bucketpath, bundlesPath); err != nil { 195 | logrus.Warn(err) 196 | return err 197 | } 198 | 199 | // push tars to s3 200 | bundlesPath = path.Join(temp, "bundles", version, "tgz") 201 | 202 | if err = pushToS3(bucket, bucketpath, bundlesPath); err != nil { 203 | logrus.Warn(err) 204 | return err 205 | } 206 | 207 | // add html to template 208 | if err := createIndexFile(bucket, bucketpath); err != nil { 209 | logrus.Warn(err) 210 | return err 211 | } 212 | 213 | return nil 214 | } 215 | --------------------------------------------------------------------------------