deploy_aws 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 116db5a7d2df916d2e15b6d152529785bc0d8e16
4
+ data.tar.gz: 44bc077b0a44f9ec8a17e86024ebe3af5ccb0595
5
+ SHA512:
6
+ metadata.gz: cbc4da23d9a26104dd9a8edba823931f5e977e24688f179ab112ceebe914613c266a60db52e1673963dc47c64ee69d9bd4f3eeddeef914861b5de80fc21ad2db
7
+ data.tar.gz: ff8d71993dfbdc5ac58adde1f76a4c660b1ab35409b99913cc42b744b83d700b59bf99652f8439798d60b98b0d353b968f9fd093e4d71ced343ed8ed46ca266e
@@ -0,0 +1,11 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ /config/deploy.yml
11
+ /vendor
@@ -0,0 +1 @@
1
+ 2.2.3
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.2.3
4
+ before_install: gem install bundler -v 1.10.6
@@ -0,0 +1,13 @@
1
+ # Contributor Code of Conduct
2
+
3
+ As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.
4
+
5
+ We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, or religion.
6
+
7
+ Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct.
8
+
9
+ Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team.
10
+
11
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers.
12
+
13
+ This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.0.0, available at [http://contributor-covenant.org/version/1/0/0/](http://contributor-covenant.org/version/1/0/0/)
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in deploy.gemspec
4
+ gemspec
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 Michael Smirnoff
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all 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,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,67 @@
1
+ # Deploy AWS
2
+
3
+ Deploy AWS is a gem for deploying your application code to AWS.
4
+ Currently, covered deployment usage scenarios are:
5
+ AWS Elastic Beanstalk applications (via AWS EB CLI)
6
+ AWS S3 hosted web sites (via npm bundle)
7
+
8
+ ## Installation
9
+
10
+ For multiple application usage, install the gem directly:
11
+
12
+ ```shell
13
+ gem install deploy_aws
14
+ ```
15
+
16
+ And then execute:
17
+
18
+ ```shell
19
+ deploy_aws
20
+ ```
21
+
22
+ ## Configuration
23
+
24
+ The gem assumes that your shell includes AWS access keys and region configured.
25
+ The gem makes certain assumptions about your S3 folder hierarchy, if S3 is the
26
+ deployment target./
27
+ Specifically, it expects to find a bucket containing folders that map to your deployable applications.
28
+ The application that is to be deployed is then expected to be found in the same named top level S3 bucket.
29
+
30
+ ElasticBeanstalk applications are deployed directly if detected, and this
31
+ configuration is not required.
32
+
33
+ Example: your S3 bucket named `configs-all` contains your application configurations.
34
+ The applications themselves are in S3 buckets.
35
+
36
+ Then, if you have a static hosted S3 site in the bucket `my-site`,
37
+ you'd expect to have a configuration collection in S3 like this:
38
+
39
+ `configs-all/my-site/config/1.0/somesetting.yml`
40
+
41
+ `configs-all/my-site/config/2.0/somesetting.yml`
42
+
43
+ Then, the deployment process will ask you to choose to deploy `my-site`, and the configuration version to be deployed. It will then attempt to find an S3 bucket with the name `my-site`.
44
+
45
+ ## Usage
46
+
47
+ After installing the gem, execute `deploy_aws` in your deployable project directory.
48
+ Follow the interactive prompts.
49
+ As per Configuration section, the deployment code makes certain assumptions.
50
+ Please consider if they apply to your use case.
51
+
52
+ ## Development
53
+
54
+ After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
55
+
56
+ Then, run `rake test` or `ruby -Ilib:test test/*` to run the tests. For benchmarked/coloured output, try `ruby -Ilib:test test/* -p`.
57
+
58
+ To install this gem onto your local machine, run `rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
59
+
60
+ ## Contributing
61
+
62
+ Bug reports and pull requests are welcome on GitHub at https://github.com/sealink/deploy. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](contributor-covenant.org) code of conduct.
63
+
64
+
65
+ ## License
66
+
67
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << "test"
6
+ t.libs << "lib"
7
+ t.test_files = FileList['test/**/*_test.rb']
8
+ end
9
+
10
+ task :default => :test
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "deploy"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ THIS_FILE = File.symlink?(__FILE__) ? File.readlink(__FILE__) : __FILE__
4
+ lib = File.expand_path(File.dirname(THIS_FILE) + '/../lib')
5
+ $LOAD_PATH.unshift(lib) if File.directory?(lib) && !$LOAD_PATH.include?(lib)
6
+
7
+ require "deploy"
8
+
9
+ tag = ARGV[0]
10
+ # Demand correct invocation, one argument only for tag, clean git, changelog
11
+ if tag.nil?
12
+ abort "USAGE:
13
+ #{File.basename($0)} [TAG]
14
+ TAG: tag to deploy or create on current commit
15
+ Incorrect number of arguments given."
16
+ end
17
+ deployment = Deploy::Runner.new(tag)
18
+ deployment.run
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
6
+
7
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,30 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+
5
+ require 'deploy/version'
6
+
7
+ Gem::Specification.new do |spec|
8
+ spec.name = "deploy_aws"
9
+ spec.version = Deploy::VERSION
10
+ spec.authors = ["Michael Smirnoff"]
11
+ spec.email = ["michael.smirnoff@sealink.com.au"]
12
+
13
+ spec.summary = %q{Deploys from current Git repo to AWS}
14
+ spec.description = %q{Deploys from current Git repo to AWS EB or S3 (via node)}
15
+ spec.homepage = "https://github.com/sealink/deploy_aws"
16
+ spec.license = "MIT"
17
+
18
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
19
+
20
+ spec.bindir = "bin"
21
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
22
+ spec.require_paths = ["lib"]
23
+
24
+ spec.add_dependency "aws-sdk", '~> 2' # For AWS API
25
+ spec.add_dependency "highline" # For user interaction
26
+
27
+ spec.add_development_dependency "bundler", "~> 1.10"
28
+ spec.add_development_dependency "rake", "~> 10.0"
29
+ spec.add_development_dependency "minitest"
30
+ end
@@ -0,0 +1,207 @@
1
+ require 'aws-sdk'
2
+ require 'highline'
3
+ require 'deploy/eb/application'
4
+ require 'deploy/eb/configuration'
5
+ require 'deploy/eb/platform'
6
+ require 'deploy/eb/state'
7
+ require 'deploy/iam/client'
8
+ require 'deploy/s3/configuration'
9
+ require 'deploy/s3/state'
10
+ require 'deploy/s3/platform'
11
+ require 'deploy/error_handler'
12
+ require 'deploy/repository'
13
+ require 'deploy/version'
14
+
15
+ module Deploy
16
+ class Runner
17
+ def initialize(tag)
18
+ @tag = tag
19
+ end
20
+
21
+ def run
22
+ trap_int
23
+ precheck!
24
+ validate!
25
+ perform!
26
+ end
27
+
28
+ private
29
+
30
+ def precheck!
31
+ check_for_unstaged_changes!
32
+ check_for_changelog!
33
+ check_for_aws_access!
34
+ end
35
+
36
+ def validate!
37
+ configure!
38
+ @name = deployment_target
39
+ @platform = verify_platform
40
+ end
41
+
42
+ def perform!
43
+ request_confirmation!
44
+ synchronize_repo!
45
+ deploy!
46
+ end
47
+
48
+ def log(msg)
49
+ # Currently no logging mechanism besides message to stdout
50
+ puts msg
51
+ end
52
+
53
+ def trap_int
54
+ Signal.trap('INT') {
55
+ abort "\nGot Ctrl-C, exiting.\n\
56
+ You will have to abort any in-progress deployments manually."
57
+ }
58
+ end
59
+
60
+ def check_for_unstaged_changes!
61
+ return unless repo.index_modified?
62
+ abort "You have staged changes! Please sort your life out mate, innit?"
63
+ end
64
+
65
+ def check_for_changelog!
66
+ changelog_updated =
67
+ cli.agree "Now hold on there for just a second, partner. "\
68
+ "Have you updated the changelog ?"
69
+ abort 'Better hop to it then ay?' unless changelog_updated
70
+ end
71
+
72
+ def check_for_aws_access!
73
+ # Verify up AWS params, i.e. that we have access key and region.
74
+ # Do so by connecting to IAM directly
75
+ # Why IAM? If your user doesn't exist, nothing else will work.
76
+ user = IAM::Client.connection
77
+ log "You are connected as #{user}."
78
+ end
79
+
80
+ def on_beanstalk?
81
+ @on_beanstalk ||= Eb::Platform.configured?
82
+ end
83
+
84
+ def repo
85
+ @repo ||= Repository.new
86
+ end
87
+
88
+ def synchronize_repo!
89
+ log 'Preparing the tagged release version for deployment.'
90
+ repo.prepare!(@tag)
91
+ end
92
+
93
+ def cli
94
+ @cli ||= HighLine.new
95
+ end
96
+
97
+ def configuration
98
+ @configuration ||= set_configuration
99
+ end
100
+
101
+ def set_configuration
102
+ if on_beanstalk?
103
+ Eb::Configuration.new
104
+ else
105
+ S3::Configuration.new(config_bucket_name)
106
+ end
107
+ end
108
+
109
+ def config_bucket_name
110
+ @config_bucket_name ||= set_config_bucket_name!
111
+ end
112
+
113
+ def set_config_bucket_name!
114
+ bucket_name = ENV['S3_CONFIG_BUCKET']
115
+ unless bucket_name
116
+ fail 'Please set your S3 config bucket name in '\
117
+ 'ENV[\'S3_CONFIG_BUCKET\']'
118
+ end
119
+ bucket_name
120
+ end
121
+
122
+ def configure!
123
+ return if on_beanstalk?
124
+ # Pull in and verify our deployment configurations
125
+ log "Checking available configurations... Please wait..."
126
+ configuration.verify!
127
+ unless configuration.created_folders.empty?
128
+ configuration.created_folders.each do |folder|
129
+ log "\tCreated #{folder}"
130
+ end
131
+ end
132
+ log "Check done."
133
+ end
134
+
135
+ def deployment_target
136
+ app = select_from_list(apps)
137
+ return app unless on_beanstalk?
138
+ select_from_list(eb_env_list(app))
139
+ end
140
+
141
+ def eb_env_list(app)
142
+ beanstalk_application(app).environments
143
+ end
144
+
145
+ def select_from_list(list)
146
+ # Have the user decide what to deploy
147
+ log "Configured applications are:"
148
+ name = cli.choose do |menu|
149
+ menu.prompt = "Choose application to deploy, by index or name."
150
+ menu.choices *list
151
+ end
152
+ log "Selected \"#{name}\"."
153
+ name
154
+ end
155
+
156
+ def app_bucket
157
+ configuration.config_bucket_for(@name)
158
+ end
159
+
160
+ def apps
161
+ configuration.apps
162
+ end
163
+
164
+ def eb
165
+ @eb ||= Eb::State.new(@name)
166
+ end
167
+
168
+ def s3
169
+ @s3 ||= S3::State.new(@name, app_bucket)
170
+ end
171
+
172
+ def beanstalk_application(app)
173
+ @beanstalk_application ||= Eb::Application.new(app)
174
+ end
175
+
176
+ def verify_platform
177
+ if eb.exists?
178
+ fail "EB app found but you did not \'eb init\'" unless on_beanstalk?
179
+ platform = Eb::Platform.new(eb: eb, tag: @tag)
180
+ log "Environment \'#{@name}\' found on EB."
181
+ elsif s3.exists?
182
+ platform = S3::Platform.new(s3: s3, tag: @tag)
183
+ log "Website \'#{@name}\' found on S3."
184
+ log "Config bucket version \"#{s3.version}\" selected."
185
+ end
186
+ unless platform
187
+ abort "Application given as \'#{@name}\'. "\
188
+ "EB environment \'#{@name}\' was not found. "\
189
+ "S3 bucket \'#{@name}\' was not found either. "\
190
+ "Please fix this before attempting to deploy."
191
+ end
192
+ platform
193
+ end
194
+
195
+ def request_confirmation!
196
+ confirm_launch = cli.agree "Deploy release \'#{@tag}\' to \'#{@name}\' ?"
197
+ abort 'Bailing out.' unless confirm_launch
198
+ end
199
+
200
+ def deploy!
201
+ log 'Deployment commencing.'
202
+ success = @platform.deploy!
203
+ abort "Deployment Failed or timed out. See system output." unless success
204
+ log 'All done.'
205
+ end
206
+ end
207
+ end
@@ -0,0 +1,22 @@
1
+ module Deploy
2
+ module Eb
3
+ class Application
4
+ def initialize(name)
5
+ @name = name
6
+ end
7
+
8
+ def environments
9
+ request = {application_name: @name}
10
+ response = elasticbeanstalk.describe_environments(request)
11
+ response.environments.map(&:environment_name)
12
+ end
13
+
14
+ private
15
+
16
+ def elasticbeanstalk
17
+ @elasticbeanstalk ||=
18
+ ErrorHandler.with_error_handling { Aws::ElasticBeanstalk::Client.new }
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,26 @@
1
+ module Deploy
2
+ module Eb
3
+ class Configuration
4
+ def apps
5
+ @apps ||= app_names
6
+ end
7
+
8
+ private
9
+
10
+ def client
11
+ @client ||=
12
+ ErrorHandler.with_error_handling { Aws::ElasticBeanstalk::Client.new }
13
+ end
14
+
15
+ def apps_list
16
+ ErrorHandler.with_error_handling do
17
+ client.describe_applications[0]
18
+ end
19
+ end
20
+
21
+ def app_names
22
+ apps_list.map(&:application_name)
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,41 @@
1
+ require 'open3'
2
+
3
+ module Deploy
4
+ module Eb
5
+ class Platform
6
+ def self.configured?
7
+ Open3.popen3("eb list") { |_input, _output, _error, thread|
8
+ thread.value
9
+ }.exitstatus == 0
10
+ end
11
+
12
+ def initialize(opts)
13
+ @eb = opts[:eb]
14
+ @tag = opts[:tag]
15
+ end
16
+
17
+ def deploy!
18
+ fail "Environment NOT READY!" unless @eb.ready?
19
+ fail "Environment switch failed." unless @eb.switch
20
+ eb_deploy!
21
+ end
22
+
23
+ private
24
+
25
+ def write_redeploy_notification
26
+ puts "Elastic Beanstalk application #{@eb.application_name}"\
27
+ " already has version #{@tag}"
28
+ puts "Assuming you do mean to redeploy, perhaps to a new target."
29
+ end
30
+
31
+ def eb_deploy!
32
+ if @eb.version_exists?(@tag)
33
+ write_redeploy_notification
34
+ system("eb deploy --version=#{@tag}")
35
+ else
36
+ system("eb deploy --label=#{@tag}")
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,50 @@
1
+ module Deploy
2
+ module Eb
3
+ class State
4
+ def initialize(env)
5
+ @env = env
6
+ end
7
+
8
+ def exists?
9
+ !environment_info.nil?
10
+ end
11
+
12
+ def ready?
13
+ environment_info.status.eql? 'Ready'
14
+ end
15
+
16
+ def switch
17
+ system("eb use #{@env}")
18
+ end
19
+
20
+ def environment_info
21
+ @environment_info ||= environment_description_message.environments[0]
22
+ end
23
+
24
+ def application_name
25
+ environment_info.application_name
26
+ end
27
+
28
+ def version_exists?(version)
29
+ request = {application_name: application_name, version_labels: [version]}
30
+ response = elasticbeanstalk.describe_application_versions(request)
31
+ ! response.application_versions.empty?
32
+ end
33
+
34
+ private
35
+
36
+ def elasticbeanstalk
37
+ @elasticbeanstalk ||=
38
+ ErrorHandler.with_error_handling { Aws::ElasticBeanstalk::Client.new }
39
+ end
40
+
41
+ def environment_description_message
42
+ ErrorHandler.with_error_handling do
43
+ elasticbeanstalk.describe_environments(
44
+ environment_names: [@env]
45
+ )
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,30 @@
1
+ require 'singleton'
2
+
3
+ module Deploy
4
+ class ErrorHandler
5
+ include Singleton
6
+
7
+ ERROR_MESSAGES = {
8
+ 'Aws::Errors::MissingCredentialsError' => 'Missing AWS credentials. Error thrown by AWS',
9
+ 'Aws::ElasticBeanstalk::Errors::ServiceError' => 'Error thrown by AWS EB',
10
+ 'Aws::S3::Errors::ServiceError' => 'Error thrown by AWS S3'
11
+ }
12
+
13
+ def self.with_error_handling(&block)
14
+ instance.with_error_handling(&block)
15
+ end
16
+
17
+ def with_error_handling
18
+ yield
19
+ rescue RuntimeError => error
20
+ fail message_for(error)
21
+ end
22
+
23
+ private
24
+
25
+ def message_for(error)
26
+ message = ERROR_MESSAGES[error.class.name] || 'Unknown error'
27
+ "#{message}: #{error}"
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,23 @@
1
+ module Deploy
2
+ module IAM
3
+ class Client
4
+
5
+ REGION_ERROR='Please configure your AWS region in ENV[\'AWS_REGION\'].'
6
+ CREDENTIALS_ERROR='Please set your AWS credentials.
7
+ Credentials are read from ~/.aws/credentials, '\
8
+ 'if possible.
9
+ Otherwise, credentials are loaded from '\
10
+ 'ENV[\'AWS_ACCESS_KEY_ID\'] and '\
11
+ 'ENV[\'AWS_SECRET_ACCESS_KEY\'].'
12
+
13
+ def self.connection
14
+ Aws::IAM::CurrentUser.new.arn
15
+ rescue Aws::Errors::MissingRegionError
16
+ fail ArgumentError, REGION_ERROR
17
+ rescue Aws::Errors::MissingCredentialsError
18
+ fail ArgumentError, CREDENTIALS_ERROR
19
+ end
20
+
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,69 @@
1
+ require 'fileutils'
2
+ require 'open3'
3
+
4
+ module Deploy
5
+ class Repository
6
+ def index_modified?
7
+ ! system('git diff-index --cached --quiet HEAD --ignore-submodules --')
8
+ end
9
+
10
+ def prepare!(tag)
11
+ fail "No tag given" unless tag
12
+ @tag = tag
13
+ sync! unless tag_exists?
14
+ end
15
+
16
+ private
17
+
18
+ def tag_exists?
19
+ Open3.popen3("git rev-parse #{@tag}") { |_input, _output, error, _thread|
20
+ error.read.chomp
21
+ }.empty?
22
+ end
23
+
24
+ def sync!
25
+ version!
26
+ commit!
27
+ tag!
28
+ push!
29
+ end
30
+
31
+ def version!
32
+ FileUtils.mkdir_p 'public'
33
+ File.open('public/version.txt', 'w') { |file| file.puts(@tag) }
34
+ end
35
+
36
+ def commit!
37
+ puts "Committing version.txt..."
38
+ unless system('git add public/version.txt') && system("git commit -m \"#{commit_message}\" ")
39
+ fail "Failed to commit."
40
+ end
41
+ end
42
+
43
+ def tag!
44
+ puts "Tagging #{@tag} as new version..."
45
+ unless system("git tag -a #{@tag} -m \"#{tag_message}\" ")
46
+ fail "Failed to tag"
47
+ end
48
+ end
49
+
50
+ def push!
51
+ puts "Pushing changes to origin..."
52
+ unless system('git push origin HEAD') && system('git push origin --tags')
53
+ fail "Failed to git push."
54
+ end
55
+ end
56
+
57
+ def tag_message
58
+ "Deployed #{@tag}"
59
+ end
60
+
61
+ def commit_message
62
+ @commit_message ||= "#{last_commit_message} - deploy"
63
+ end
64
+
65
+ def last_commit_message
66
+ @last_commit_message ||= `git log --pretty=%B -1`.chomp('')
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,103 @@
1
+ module Deploy
2
+ module S3
3
+ class Configuration
4
+
5
+ def initialize(config_bucket_name)
6
+ @config_bucket_name = config_bucket_name
7
+ end
8
+
9
+ def verify!
10
+ unless config_bucket.exists? && objects.count > 0
11
+ fail "Configuration bucket #{@config_bucket_name} not found or empty."
12
+ end
13
+ enforce_valid_app_paths!
14
+ @verified = true
15
+ end
16
+
17
+ def apps
18
+ fail "Asked for app list without verifying. It will be wrong." if !@verified
19
+ @apps ||= app_names
20
+ end
21
+
22
+ def created_folders
23
+ @created_folders ||= []
24
+ end
25
+
26
+ def config_bucket_for(name)
27
+ app_buckets.detect { |app| app.key == name + '/' }
28
+ end
29
+
30
+ private
31
+
32
+ def config_bucket
33
+ @config_bucket ||=
34
+ ErrorHandler.with_error_handling { Aws::S3::Bucket.new(@config_bucket_name) }
35
+ end
36
+
37
+ def objects
38
+ @objects ||= ErrorHandler.with_error_handling { config_bucket.objects }
39
+ end
40
+
41
+ def client
42
+ @client ||= ErrorHandler.with_error_handling { Aws::S3::Client.new }
43
+ end
44
+
45
+ def enforce_valid_app_paths!
46
+ # check folders in our config bucket, recreate any missing folders
47
+ object_names = objects.map(&:key)
48
+ file_names = object_names.select { |name| !name.end_with?('/') }
49
+ max_depth = object_names.map { |name| name.count('/') }.max
50
+ possible_object_names = (1..max_depth).reduce([]) { |list, depth|
51
+ list += object_names.map { |name| name.split('/').first(depth).join('/') }
52
+ }
53
+ folder_names = possible_object_names.uniq - file_names
54
+ path_names = folder_names.map { |folder| folder + '/' }
55
+
56
+ # Make the folder if needed
57
+ folders_to_create = path_names.
58
+ sort_by { |name| name.count('/') }.
59
+ select{|folder| ! folder_exists?(folder) }
60
+ folders_to_create.each do |folder|
61
+ create_folder!(folder)
62
+ end
63
+
64
+ @created_folders = folders_to_create
65
+ end
66
+
67
+ def create_folder!(folder)
68
+ ErrorHandler.with_error_handling do
69
+ client.put_object(
70
+ acl: 'private',
71
+ body: nil,
72
+ bucket: config_bucket.name,
73
+ key: folder
74
+ )
75
+ end
76
+ end
77
+
78
+ def folder_exists?(folder)
79
+ ErrorHandler.with_error_handling do
80
+ config_bucket.object(folder).exists?
81
+ end
82
+ end
83
+
84
+ def app_buckets
85
+ @app_buckets ||= app_buckets_list
86
+ end
87
+
88
+ def app_buckets_list
89
+ ErrorHandler.with_error_handling do
90
+ objects.select do |o|
91
+ !o.key.empty? &&
92
+ o.key.end_with?('/') &&
93
+ o.key.count('/') == 1
94
+ end
95
+ end
96
+ end
97
+
98
+ def app_names
99
+ app_buckets.map{|o| o.key.sub('/', '') }
100
+ end
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,25 @@
1
+ module Deploy
2
+ module S3
3
+ class Platform
4
+ def initialize(opts)
5
+ @s3 = opts[:s3]
6
+ @tag = opts[:tag]
7
+ end
8
+
9
+ def deploy!
10
+ # TODO: Add option to re-use existing deployment if we can
11
+ s3_deploy!
12
+ end
13
+
14
+ private
15
+
16
+ def s3_deploy!
17
+ system(
18
+ "bucket=#{@s3.target}"\
19
+ " s3_config_version=#{@s3.version}"\
20
+ " npm run publish"
21
+ )
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,59 @@
1
+ module Deploy
2
+ module S3
3
+ class State
4
+ def initialize(name, app_configs)
5
+ @name = name
6
+ @app_configs = app_configs
7
+ end
8
+
9
+ def exists?
10
+ target_bucket.exists?
11
+ end
12
+
13
+ def version
14
+ @version ||= version_select
15
+ end
16
+
17
+ def target
18
+ target_bucket.name
19
+ end
20
+
21
+ private
22
+
23
+ def target_bucket
24
+ ErrorHandler.with_error_handling do
25
+ Aws::S3::Bucket.new(name: @name)
26
+ end
27
+ end
28
+
29
+ def version_select
30
+ # Provide the configuration versions and let user choose
31
+ versions = version_folders.map{|obj| obj.key.split('/').last }
32
+
33
+ puts "Found configuration versions:"
34
+ cli = HighLine.new
35
+ cli.choose do |menu|
36
+ menu.prompt = "Select index of the configuration to use:"
37
+ menu.choices *versions
38
+ end
39
+ end
40
+
41
+ def version_folders
42
+ @version_folders ||= read_version_folders
43
+ end
44
+
45
+ # The assumed structure of a config folder in S3 is:
46
+ # sealink-config/<app>/config/<version>
47
+ def read_version_folders
48
+ ErrorHandler.with_error_handling do
49
+ @app_configs.bucket.objects.select do |o|
50
+ !o.key.empty? &&
51
+ o.key.start_with?("#{@name}/config/") &&
52
+ o.key.end_with?('/') &&
53
+ o.key.count('/') == 3
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,3 @@
1
+ module Deploy
2
+ VERSION = "0.1.0"
3
+ end
metadata ADDED
@@ -0,0 +1,141 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: deploy_aws
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Michael Smirnoff
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-02-03 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: aws-sdk
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2'
27
+ - !ruby/object:Gem::Dependency
28
+ name: highline
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.10'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.10'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '10.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '10.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: minitest
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ description: Deploys from current Git repo to AWS EB or S3 (via node)
84
+ email:
85
+ - michael.smirnoff@sealink.com.au
86
+ executables:
87
+ - console
88
+ - deploy
89
+ - setup
90
+ extensions: []
91
+ extra_rdoc_files: []
92
+ files:
93
+ - ".gitignore"
94
+ - ".ruby-version"
95
+ - ".travis.yml"
96
+ - CODE_OF_CONDUCT.md
97
+ - Gemfile
98
+ - LICENSE.txt
99
+ - README.md
100
+ - Rakefile
101
+ - bin/console
102
+ - bin/deploy
103
+ - bin/setup
104
+ - deploy_aws.gemspec
105
+ - lib/deploy.rb
106
+ - lib/deploy/eb/application.rb
107
+ - lib/deploy/eb/configuration.rb
108
+ - lib/deploy/eb/platform.rb
109
+ - lib/deploy/eb/state.rb
110
+ - lib/deploy/error_handler.rb
111
+ - lib/deploy/iam/client.rb
112
+ - lib/deploy/repository.rb
113
+ - lib/deploy/s3/configuration.rb
114
+ - lib/deploy/s3/platform.rb
115
+ - lib/deploy/s3/state.rb
116
+ - lib/deploy/version.rb
117
+ homepage: https://github.com/sealink/deploy_aws
118
+ licenses:
119
+ - MIT
120
+ metadata: {}
121
+ post_install_message:
122
+ rdoc_options: []
123
+ require_paths:
124
+ - lib
125
+ required_ruby_version: !ruby/object:Gem::Requirement
126
+ requirements:
127
+ - - ">="
128
+ - !ruby/object:Gem::Version
129
+ version: '0'
130
+ required_rubygems_version: !ruby/object:Gem::Requirement
131
+ requirements:
132
+ - - ">="
133
+ - !ruby/object:Gem::Version
134
+ version: '0'
135
+ requirements: []
136
+ rubyforge_project:
137
+ rubygems_version: 2.4.5.1
138
+ signing_key:
139
+ specification_version: 4
140
+ summary: Deploys from current Git repo to AWS
141
+ test_files: []