deployme 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +12 -0
- data/.rspec +3 -0
- data/.travis.yml +5 -0
- data/Gemfile +6 -0
- data/README.md +35 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/deployme.gemspec +29 -0
- data/exe/deployme +6 -0
- data/lib/core_ext/aws_ecs_client.rb +10 -0
- data/lib/deployme/cli.rb +37 -0
- data/lib/deployme/config.rb +44 -0
- data/lib/deployme/deployment.rb +74 -0
- data/lib/deployme/notification.rb +30 -0
- data/lib/deployme/notifications/envfile.rb +19 -0
- data/lib/deployme/notifications/github.rb +84 -0
- data/lib/deployme/notifications/jira.rb +59 -0
- data/lib/deployme/option_parser.rb +40 -0
- data/lib/deployme/options.rb +41 -0
- data/lib/deployme/provider.rb +43 -0
- data/lib/deployme/providers/ecs.rb +57 -0
- data/lib/deployme/settings.rb +29 -0
- data/lib/deployme/util.rb +31 -0
- data/lib/deployme/version.rb +3 -0
- data/lib/deployme.rb +4 -0
- metadata +140 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 8fbea79290474ae00a0744d8fc9fa484190a0b258b6a4b3a693c7010545ee83a
|
4
|
+
data.tar.gz: 63a4a0850f20007995df87c74191bec38764f8a62bcf54179427bad83075048b
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: c20d1a5b635bbd900e6383d64895d8bc5de55e0e262547129ef3f74c67389eb46584610a4541c55516219cfcdee3988dd6065526a705a83e3dc2fb6251b0902c
|
7
|
+
data.tar.gz: 4bb384e6aab9b5d788bb61f06a38c2546721187bf71579792fed46a76cd49ab3deb4ce9e8b55f6bddaefc13eb1d5face25b01cc3300b08e2ebcb0e9b74342a59
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
# Deployme
|
2
|
+
|
3
|
+
Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/deployme`. To experiment with that code, run `bin/console` for an interactive prompt.
|
4
|
+
|
5
|
+
TODO: Delete this and the text above, and describe your gem
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
Add this line to your application's Gemfile:
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
gem 'deployme'
|
13
|
+
```
|
14
|
+
|
15
|
+
And then execute:
|
16
|
+
|
17
|
+
$ bundle
|
18
|
+
|
19
|
+
Or install it yourself as:
|
20
|
+
|
21
|
+
$ gem install deployme
|
22
|
+
|
23
|
+
## Usage
|
24
|
+
|
25
|
+
TODO: Write usage instructions here
|
26
|
+
|
27
|
+
## Development
|
28
|
+
|
29
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
30
|
+
|
31
|
+
To install this gem onto your local machine, run `bundle exec 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).
|
32
|
+
|
33
|
+
## Contributing
|
34
|
+
|
35
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/deployme.
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "deployme"
|
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(__FILE__)
|
data/bin/setup
ADDED
data/deployme.gemspec
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
|
2
|
+
lib = File.expand_path('lib', __dir__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'deployme/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'deployme'
|
8
|
+
spec.version = Deployme::VERSION
|
9
|
+
spec.authors = ['Mikko Kokkonen']
|
10
|
+
spec.email = ['mikko.kokkonen@appearhere.co.uk']
|
11
|
+
|
12
|
+
spec.summary = 'Deploys current directory to anywhere'
|
13
|
+
spec.description = 'Simple tool to deploy current directory to anywhere'
|
14
|
+
spec.homepage = 'https://github.com/mikian/deployme'
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
17
|
+
f.match(%r{^(test|spec|features)/})
|
18
|
+
end
|
19
|
+
spec.bindir = 'exe'
|
20
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
21
|
+
spec.require_paths = ['lib']
|
22
|
+
|
23
|
+
spec.add_dependency 'aws-sdk-ecs', '~> 1.13.0'
|
24
|
+
spec.add_dependency 'jira-ruby'
|
25
|
+
|
26
|
+
spec.add_development_dependency 'bundler', '~> 1.16'
|
27
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
28
|
+
spec.add_development_dependency 'rspec', '~> 3.0'
|
29
|
+
end
|
data/exe/deployme
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
require 'aws-sdk-ecs'
|
2
|
+
|
3
|
+
class Aws::ECS::Client # rubocop:disable Style/ClassAndModuleChildren
|
4
|
+
def upsert_service(options)
|
5
|
+
send(:update_service, options)
|
6
|
+
rescue Aws::ECS::Errors::ServiceNotFoundException, Aws::ECS::Errors::ServiceNotActiveException
|
7
|
+
options[:service_name] = options.delete(:service)
|
8
|
+
send(:create_service, options)
|
9
|
+
end
|
10
|
+
end
|
data/lib/deployme/cli.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'deployme/deployment'
|
2
|
+
require 'deployme/option_parser'
|
3
|
+
require 'deployme/options'
|
4
|
+
require 'deployme/provider'
|
5
|
+
require 'deployme/version'
|
6
|
+
|
7
|
+
module Deployme
|
8
|
+
class CLI
|
9
|
+
def self.run(args)
|
10
|
+
runtime = OptionParser.run(args) do |parser|
|
11
|
+
(Provider.all + Notification.all).each do |mod|
|
12
|
+
parser.separator ''
|
13
|
+
parser.separator "#{mod.to_s.split('::').last.upcase} Options:"
|
14
|
+
mod.options(parser)
|
15
|
+
end
|
16
|
+
|
17
|
+
Deployment.options(parser)
|
18
|
+
|
19
|
+
parser.separator ''
|
20
|
+
parser.separator 'Common options:'
|
21
|
+
|
22
|
+
parser.on_tail('-h', '--help', 'Show this message') do
|
23
|
+
puts parser
|
24
|
+
exit
|
25
|
+
end
|
26
|
+
|
27
|
+
parser.on('--debug', 'Increase verbosity') { |options| options.debug = true }
|
28
|
+
parser.on_tail('--version', 'Show version') do
|
29
|
+
puts ::Deployme::VERSION
|
30
|
+
exit
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
Deployment.new(options: Options.new(runtime)).run
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'erb'
|
2
|
+
require 'yaml'
|
3
|
+
|
4
|
+
module Deployme
|
5
|
+
class Config
|
6
|
+
def initialize(options:)
|
7
|
+
@options = options
|
8
|
+
end
|
9
|
+
|
10
|
+
def load
|
11
|
+
file = File.join(options.directory, "#{options.environment}.yml")
|
12
|
+
|
13
|
+
raise "Cannot find environment #{file}" unless File.exist?(file)
|
14
|
+
|
15
|
+
Util.deep_symbolize_keys(YAML.load(ERB.new(File.read(file)).result(Context.with(options))))
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
attr_reader :options
|
21
|
+
|
22
|
+
class Context
|
23
|
+
def self.with(obj)
|
24
|
+
new(obj).context
|
25
|
+
end
|
26
|
+
|
27
|
+
def initialize(options)
|
28
|
+
@options = options
|
29
|
+
end
|
30
|
+
|
31
|
+
def method_missing(meth, *args, &blk)
|
32
|
+
@options.send(meth, *args, &blk) || super
|
33
|
+
end
|
34
|
+
|
35
|
+
def respond_to_missing?(method_name, include_private = false)
|
36
|
+
@options.respond_to?(method_name, include_private) || super
|
37
|
+
end
|
38
|
+
|
39
|
+
def context
|
40
|
+
binding
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'deployme/config'
|
2
|
+
require 'deployme/notification'
|
3
|
+
require 'deployme/provider'
|
4
|
+
require 'deployme/util'
|
5
|
+
require 'logger'
|
6
|
+
|
7
|
+
module Deployme
|
8
|
+
class Deployment
|
9
|
+
def self.options(parser)
|
10
|
+
parser.separator ''
|
11
|
+
parser.separator 'Deployment Options:'
|
12
|
+
|
13
|
+
parser.on('--url=URL', String, 'URL for deployment environment') { |options, value| options.deploy_url = value }
|
14
|
+
parser.on('--domain=domain', String, 'URL for deployment environment') { |options, value| options.deploy_domain = value }
|
15
|
+
parser.on('-nNAME', '--name=NAME', String, 'Deployment name') { |options, value| options.name = value }
|
16
|
+
parser.on('-eENVIRONMENT', '--environment=ENVIRONMENT', String, 'Environment to deploy to') { |options, value| options.environment = value }
|
17
|
+
parser.on('-dDIRECTORY', '--directory=DIRECTORY', String, 'Directory where to find deployment instructions') { |options, value| options.directory = File.expand_path(value) }
|
18
|
+
|
19
|
+
parser.on('--commit=COMMIT', String, 'Git Commit to deploy') { |options, value| options.git_commit = value }
|
20
|
+
parser.on('--branch=BRANCH', String, 'Git Branch to deploy') { |options, value| options.git_branch = value }
|
21
|
+
parser.on('--change-id=CHANGE_ID', String, 'Change ID (Pull Request) to deploy') { |options, value| options.change_id = value }
|
22
|
+
end
|
23
|
+
|
24
|
+
attr_reader :config, :options
|
25
|
+
|
26
|
+
def initialize(options:)
|
27
|
+
@options = options
|
28
|
+
@config ||= Config.new(options: options).load
|
29
|
+
end
|
30
|
+
|
31
|
+
def run
|
32
|
+
providers.each(&:deploy)
|
33
|
+
end
|
34
|
+
|
35
|
+
def notify(stage, *args)
|
36
|
+
notifications.each do |notification|
|
37
|
+
notification.public_send("notify_#{stage}", *args)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# Attributes
|
42
|
+
def logger
|
43
|
+
@logger ||= Logger.new(STDOUT).tap do |logger|
|
44
|
+
logger.formatter = proc do |severity, _datetime, _progname, msg|
|
45
|
+
status = case severity
|
46
|
+
when 'INFO' then "\e[34m==>\e[0m"
|
47
|
+
when 'WARNING' then "\e[33m==>\e[0m"
|
48
|
+
when 'ERROR' then "\e[31m==>\e[0m"
|
49
|
+
end
|
50
|
+
|
51
|
+
format("%<status>s %<msg>s\n", status: status, msg: msg)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def environment
|
57
|
+
options.environment
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
def providers
|
63
|
+
@providers ||= config[:providers].map do |type, options|
|
64
|
+
Providers.const_get(Util.camelize(type)).new(deployment: self, config: options || {})
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def notifications
|
69
|
+
@notifications ||= config[:notifications].map do |type, options|
|
70
|
+
Notifications.const_get(Util.camelize(type)).new(deployment: self, config: options || {})
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
require 'deployme/settings'
|
3
|
+
|
4
|
+
module Deployme
|
5
|
+
class Notification
|
6
|
+
extend Forwardable
|
7
|
+
|
8
|
+
def self.defaults
|
9
|
+
{}
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.all
|
13
|
+
Notifications.constants.map { |c| Notifications.const_get(c) }
|
14
|
+
end
|
15
|
+
|
16
|
+
def initialize(deployment:, config:)
|
17
|
+
@deployment = deployment
|
18
|
+
@settings = Settings.new(self.class, config, deployment.options)
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
attr_reader :deployment, :settings
|
24
|
+
delegate logger: :deployment
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
require 'deployme/notifications/envfile'
|
29
|
+
require 'deployme/notifications/github'
|
30
|
+
require 'deployme/notifications/jira'
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Deployme
|
2
|
+
module Notifications
|
3
|
+
class Envfile < Notification
|
4
|
+
def self.options(parser)
|
5
|
+
parser.on('--envfile-path=NAME', String, 'Environment File to Write out') { |options, value| options.envfile_path = value }
|
6
|
+
end
|
7
|
+
|
8
|
+
def notify_start; end
|
9
|
+
|
10
|
+
def notify_error(error = nil); end
|
11
|
+
|
12
|
+
def notify_finish
|
13
|
+
File.open(settings.path, 'w') do |f|
|
14
|
+
f.puts "DEPLOYMENT_URL=#{deployment.options.deploy_url}"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
module Deployme
|
5
|
+
module Notifications
|
6
|
+
class Github < Notification
|
7
|
+
def self.options(parser)
|
8
|
+
parser.on('--github-token=NAME', String, 'GitHub Token to send Deployments') { |options, value| options.github_token = value }
|
9
|
+
end
|
10
|
+
|
11
|
+
def notify_start
|
12
|
+
logger.info 'Create GitHub Deployment...'
|
13
|
+
@github_deployment = request(
|
14
|
+
:post,
|
15
|
+
'https://api.github.com/repos/appearhere/web/deployments',
|
16
|
+
ref: deployment.options.git_commit,
|
17
|
+
environment: deployment.environment,
|
18
|
+
auto_merge: false,
|
19
|
+
transient_environment: deployment.environment != 'production',
|
20
|
+
production_environment: deployment.environment == 'production',
|
21
|
+
required_contexts: []
|
22
|
+
)
|
23
|
+
return if @github_deployment['id']
|
24
|
+
|
25
|
+
logger.error 'Failed to create GitHub Deployment'
|
26
|
+
logger.error @github_deployment['message']
|
27
|
+
raise
|
28
|
+
end
|
29
|
+
|
30
|
+
def notify_error(error = nil)
|
31
|
+
return unless @github_deployment
|
32
|
+
|
33
|
+
logger.error 'Reporting Error to GitHub'
|
34
|
+
response = request(
|
35
|
+
:post,
|
36
|
+
"https://api.github.com/repos/appearhere/web/deployments/#{@github_deployment['id']}/statuses",
|
37
|
+
state: 'error',
|
38
|
+
description: error.to_s[0...140]
|
39
|
+
)
|
40
|
+
return if response['id']
|
41
|
+
logger.error response['message']
|
42
|
+
end
|
43
|
+
|
44
|
+
def notify_finish
|
45
|
+
logger.info 'Successful GitHub Deployment...'
|
46
|
+
request(
|
47
|
+
:post,
|
48
|
+
"https://api.github.com/repos/appearhere/web/deployments/#{@github_deployment['id']}/statuses",
|
49
|
+
state: 'success',
|
50
|
+
environment_url: deployment.options.deploy_url,
|
51
|
+
description: 'Review app created successfully.'
|
52
|
+
)
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def header
|
58
|
+
{
|
59
|
+
'Accept' => 'application/vnd.github.ant-man-preview+json',
|
60
|
+
'Content-Type' => 'text/json',
|
61
|
+
'Authorization' => "token #{settings.token}"
|
62
|
+
}
|
63
|
+
end
|
64
|
+
|
65
|
+
def request(method, url, data = nil)
|
66
|
+
uri = URI.parse(url)
|
67
|
+
|
68
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
69
|
+
http.use_ssl = true
|
70
|
+
request = case method
|
71
|
+
when :get
|
72
|
+
Net::HTTP::Get.new(uri.request_uri, header)
|
73
|
+
when :post
|
74
|
+
Net::HTTP::Post.new(uri.request_uri, header)
|
75
|
+
end
|
76
|
+
request.body = data.to_json if data
|
77
|
+
|
78
|
+
# Send the request
|
79
|
+
response = http.request(request)
|
80
|
+
JSON.parse(response.body)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'jira-ruby'
|
2
|
+
|
3
|
+
module Deployme
|
4
|
+
module Notifications
|
5
|
+
class Jira < Notification
|
6
|
+
def self.defaults
|
7
|
+
{ issues: [] }
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.options(parser)
|
11
|
+
parser.on('--jira-issues=TICKET,TICKET', Array, 'JIRA Tickets to Update') { |options, value| options.jira_issues = value }
|
12
|
+
parser.on('--jira-url=URL', String, 'JIRA Instance URL') { |options, value| options.jira_url = value }
|
13
|
+
parser.on('--jira-username=USERNAME', String, 'JIRA Instance Username') { |options, value| options.jira_username = value }
|
14
|
+
parser.on('--jira-token=PASSWORD', String, 'JIRA Instance Password') { |options, value| options.jira_token = value }
|
15
|
+
end
|
16
|
+
|
17
|
+
def notify_start
|
18
|
+
logger.info 'Adding Website link to JIRA'
|
19
|
+
require 'pry'; binding.pry
|
20
|
+
settings.issues.each do |issue_key|
|
21
|
+
logger.info issue_key
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def notify_error(error = nil); end
|
26
|
+
|
27
|
+
def notify_finish
|
28
|
+
logger.info 'Adding Website link to JIRA'
|
29
|
+
settings.issues.each do |issue_key|
|
30
|
+
logger.info "Finding Issue: #{issue_key}"
|
31
|
+
issue = client.Issue.find(issue_key)
|
32
|
+
next if issue.remotelink.find { |link| link.attrs['object']['url'] == deployment.options.deploy_url }
|
33
|
+
|
34
|
+
logger.info "Adding link to #{deployment.options.deploy_url}"
|
35
|
+
link = issue.remotelink.build
|
36
|
+
link.save(
|
37
|
+
object: {
|
38
|
+
url: deployment.options.deploy_url,
|
39
|
+
title: deployment.options.deploy_url
|
40
|
+
}
|
41
|
+
)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def client
|
48
|
+
@client ||= JIRA::Client.new(
|
49
|
+
username: settings.username,
|
50
|
+
password: settings.token,
|
51
|
+
site: settings.url,
|
52
|
+
context_path: '',
|
53
|
+
auth_type: :basic,
|
54
|
+
http_debug: true
|
55
|
+
)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
require 'ostruct'
|
3
|
+
|
4
|
+
module Deployme
|
5
|
+
class OptionParser < ::OptionParser
|
6
|
+
def self.run(args, &block)
|
7
|
+
new(&block).run(args)
|
8
|
+
end
|
9
|
+
|
10
|
+
def initialize(&block)
|
11
|
+
@options = OpenStruct.new
|
12
|
+
super
|
13
|
+
end
|
14
|
+
|
15
|
+
def on(*args)
|
16
|
+
super(*args) do |value|
|
17
|
+
yield(@options, value)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def on_tail(*args)
|
22
|
+
super(*args) do
|
23
|
+
yield(@options)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def run(args)
|
28
|
+
begin
|
29
|
+
parse!(args)
|
30
|
+
rescue OptionParser::InvalidOption, OptionParser::MissingArgument
|
31
|
+
puts $ERROR_INFO.to_s
|
32
|
+
puts
|
33
|
+
puts self
|
34
|
+
exit 1
|
35
|
+
end
|
36
|
+
|
37
|
+
@options
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
|
3
|
+
module Deployme
|
4
|
+
class Options
|
5
|
+
def initialize(hash)
|
6
|
+
@data = OpenStruct.new(hash)
|
7
|
+
|
8
|
+
# Set defaults
|
9
|
+
@data.name ||= File.basename(File.expand_path('.'))
|
10
|
+
@data.directory ||= File.expand_path('.deploy')
|
11
|
+
end
|
12
|
+
|
13
|
+
def git_commit
|
14
|
+
@data.git_commit ||= ENV.fetch('GIT_COMMIT') { `git rev-parse HEAD`.strip }
|
15
|
+
end
|
16
|
+
|
17
|
+
def git_branch
|
18
|
+
@data.git_branch ||= ENV.fetch('GIT_BRANCH') { `git rev-parse HEAD | git branch -a --contains | sed -n 2p | cut -d'/' -f 3-`.strip }
|
19
|
+
end
|
20
|
+
|
21
|
+
def change_id
|
22
|
+
@data.change_id ||= ENV.fetch('CHANGE_ID', nil)
|
23
|
+
end
|
24
|
+
|
25
|
+
def deploy_name
|
26
|
+
"#{@data.name}-pr-#{change_id}"
|
27
|
+
end
|
28
|
+
|
29
|
+
def deploy_url
|
30
|
+
@data.deploy_url ||= "https://#{deploy_name}.#{@data.deploy_domain}"
|
31
|
+
end
|
32
|
+
|
33
|
+
def method_missing(meth, *args, &blk)
|
34
|
+
@data.send(meth, *args, &blk)
|
35
|
+
end
|
36
|
+
|
37
|
+
def respond_to_missing?(method_name, include_private = false)
|
38
|
+
@data.respond_to?(method_name, include_private) || super
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
require 'deployme/settings'
|
3
|
+
|
4
|
+
module Deployme
|
5
|
+
class Provider
|
6
|
+
extend Forwardable
|
7
|
+
|
8
|
+
def self.defaults
|
9
|
+
{}
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.all
|
13
|
+
Providers.constants.map { |c| Providers.const_get(c) }
|
14
|
+
end
|
15
|
+
|
16
|
+
def initialize(deployment:, config:)
|
17
|
+
@deployment = deployment
|
18
|
+
@settings = Settings.new(self.class, config, deployment.options)
|
19
|
+
end
|
20
|
+
|
21
|
+
def deploy
|
22
|
+
deployment.notify(:start)
|
23
|
+
|
24
|
+
execute
|
25
|
+
|
26
|
+
deployment.notify(:finish)
|
27
|
+
rescue StandardError => e
|
28
|
+
deployment.notify(:error, e)
|
29
|
+
throw e
|
30
|
+
end
|
31
|
+
|
32
|
+
def execute
|
33
|
+
raise NotImplementedError
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
attr_reader :deployment, :settings
|
39
|
+
delegate logger: :deployment
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
require 'deployme/providers/ecs'
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'core_ext/aws_ecs_client'
|
2
|
+
|
3
|
+
module Deployme
|
4
|
+
module Providers
|
5
|
+
class Ecs < Provider
|
6
|
+
def self.options(parser)
|
7
|
+
parser.on('--ecs-image=IMAGE_TAG', String, 'Image tag to use for deployment') { |options, value| options.ecs_image = value }
|
8
|
+
parser.on('--ecs-cluster=NAME', String, 'ECS Cluster name to deploy to') { |options, value| options.ecs_cluster = value }
|
9
|
+
end
|
10
|
+
|
11
|
+
def execute
|
12
|
+
register_tasks
|
13
|
+
register_services
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def register_tasks
|
19
|
+
config[:task_definitions].each do |family, task_definition|
|
20
|
+
logger.info "Registering task: #{family}"
|
21
|
+
definition = Util.deep_dup(task_definition)
|
22
|
+
definition[:family] = family
|
23
|
+
definition[:container_definitions].each { |container| container[:image] ||= settings.ecs_image }
|
24
|
+
response = client.register_task_definition(definition)
|
25
|
+
definition[:arn] = response.task_definition.task_definition_arn
|
26
|
+
logger.info "New task definition: #{definition[:arn]}"
|
27
|
+
task_definitions[family] = definition
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def register_services
|
32
|
+
config[:services].each do |service|
|
33
|
+
logger.info "Registering Service #{service[:name]}"
|
34
|
+
task_definition = task_definitions[service[:task_family].to_sym]
|
35
|
+
|
36
|
+
response = client.upsert_service(
|
37
|
+
cluster: settings.ecs_cluster,
|
38
|
+
service: service[:name],
|
39
|
+
desired_count: service[:desired_count],
|
40
|
+
task_definition: task_definition[:arn],
|
41
|
+
deployment_configuration: service[:deployment_configuration]
|
42
|
+
)
|
43
|
+
|
44
|
+
logger.info "Success: #{response.service.status}"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def task_definitions
|
49
|
+
@task_definitions ||= {}
|
50
|
+
end
|
51
|
+
|
52
|
+
def client
|
53
|
+
@client ||= Aws::ECS::Client.new
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
|
3
|
+
module Deployme
|
4
|
+
class Settings
|
5
|
+
def initialize(klass, config, options)
|
6
|
+
@prefix = klass.to_s.split('::').last.downcase
|
7
|
+
@config = OpenStruct.new(klass.defaults.merge(config))
|
8
|
+
@options = options
|
9
|
+
end
|
10
|
+
|
11
|
+
def method_missing(name, *args, &blk)
|
12
|
+
fetch_param(name)
|
13
|
+
end
|
14
|
+
|
15
|
+
def respond_to_missing?(method_name, include_private = false)
|
16
|
+
detect_param(method_name) || super
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def fetch_param(name)
|
22
|
+
@config[name] ||= ENV["#{@prefix}_#{name}".upcase] || @options.send("#{@prefix}_#{name}")
|
23
|
+
end
|
24
|
+
|
25
|
+
def detect_param(name)
|
26
|
+
@config.respond_to?(name) || ENV.key?("#{@prefix}_#{name}".upcase) || @options.respond_to?("#{@prefix}_#{name}")
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Deployme
|
2
|
+
module Util
|
3
|
+
# From activesupport/lib/active_support/inflector/methods.rb, line 67
|
4
|
+
def self.camelize(term)
|
5
|
+
string = term.to_s
|
6
|
+
string = string.sub(/^[a-z\d]*/) { $&.capitalize }
|
7
|
+
string.gsub!(%r{(?:_|(/))([a-z\d]*)}) { "#{Regexp.last_match(1)}#{inflections.acronyms[Regexp.last_match(2)] || Regexp.last_match(2).capitalize}" }
|
8
|
+
string.gsub!(%r{/}, '::')
|
9
|
+
string
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.deep_dup(obj)
|
13
|
+
Marshal.load(Marshal.dump(obj))
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.deep_symbolize_keys(hash)
|
17
|
+
case hash
|
18
|
+
when Hash
|
19
|
+
Hash[
|
20
|
+
hash.map do |k, v|
|
21
|
+
[k.respond_to?(:to_sym) ? k.to_sym : k, deep_symbolize_keys(v)]
|
22
|
+
end
|
23
|
+
]
|
24
|
+
when Enumerable
|
25
|
+
hash.map { |v| deep_symbolize_keys(v) }
|
26
|
+
else
|
27
|
+
hash
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
data/lib/deployme.rb
ADDED
metadata
ADDED
@@ -0,0 +1,140 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: deployme
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Mikko Kokkonen
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2018-06-06 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: aws-sdk-ecs
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 1.13.0
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 1.13.0
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: jira-ruby
|
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.16'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.16'
|
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: rspec
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '3.0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '3.0'
|
83
|
+
description: Simple tool to deploy current directory to anywhere
|
84
|
+
email:
|
85
|
+
- mikko.kokkonen@appearhere.co.uk
|
86
|
+
executables:
|
87
|
+
- deployme
|
88
|
+
extensions: []
|
89
|
+
extra_rdoc_files: []
|
90
|
+
files:
|
91
|
+
- ".gitignore"
|
92
|
+
- ".rspec"
|
93
|
+
- ".travis.yml"
|
94
|
+
- Gemfile
|
95
|
+
- README.md
|
96
|
+
- Rakefile
|
97
|
+
- bin/console
|
98
|
+
- bin/setup
|
99
|
+
- deployme.gemspec
|
100
|
+
- exe/deployme
|
101
|
+
- lib/core_ext/aws_ecs_client.rb
|
102
|
+
- lib/deployme.rb
|
103
|
+
- lib/deployme/cli.rb
|
104
|
+
- lib/deployme/config.rb
|
105
|
+
- lib/deployme/deployment.rb
|
106
|
+
- lib/deployme/notification.rb
|
107
|
+
- lib/deployme/notifications/envfile.rb
|
108
|
+
- lib/deployme/notifications/github.rb
|
109
|
+
- lib/deployme/notifications/jira.rb
|
110
|
+
- lib/deployme/option_parser.rb
|
111
|
+
- lib/deployme/options.rb
|
112
|
+
- lib/deployme/provider.rb
|
113
|
+
- lib/deployme/providers/ecs.rb
|
114
|
+
- lib/deployme/settings.rb
|
115
|
+
- lib/deployme/util.rb
|
116
|
+
- lib/deployme/version.rb
|
117
|
+
homepage: https://github.com/mikian/deployme
|
118
|
+
licenses: []
|
119
|
+
metadata: {}
|
120
|
+
post_install_message:
|
121
|
+
rdoc_options: []
|
122
|
+
require_paths:
|
123
|
+
- lib
|
124
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
125
|
+
requirements:
|
126
|
+
- - ">="
|
127
|
+
- !ruby/object:Gem::Version
|
128
|
+
version: '0'
|
129
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
130
|
+
requirements:
|
131
|
+
- - ">="
|
132
|
+
- !ruby/object:Gem::Version
|
133
|
+
version: '0'
|
134
|
+
requirements: []
|
135
|
+
rubyforge_project:
|
136
|
+
rubygems_version: 2.7.3
|
137
|
+
signing_key:
|
138
|
+
specification_version: 4
|
139
|
+
summary: Deploys current directory to anywhere
|
140
|
+
test_files: []
|