heroku_feature_deployments 0.3.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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 726c017f8a0d5433b0f2bff6311af07bdcc0ddb1
4
+ data.tar.gz: acf19bb4607366068844dc662443d85650bd6283
5
+ SHA512:
6
+ metadata.gz: 5754a4ca7175d8ec084528310b24a126ee68cc4b59eaf00c044b14e07340a3147a735ee15323a2c3e68079fef577d1051651030f921a6acd47bad38596648b59
7
+ data.tar.gz: 713ef76e3c3064f8ee5da1e8ce80450c443c18fda5a9c033d7d2ffda091a541ad47ee4e85677f619f3ea0d7540a25db65dc7e9dcb82b74fb767a3adfdf6ec572
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in heroku_feature_deployments.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Matt Beedle
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,29 @@
1
+ # HerokuFeatureDeployments
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'heroku_feature_deployments'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install heroku_feature_deployments
18
+
19
+ ## Usage
20
+
21
+ TODO: Write usage instructions here
22
+
23
+ ## Contributing
24
+
25
+ 1. Fork it
26
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
27
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
28
+ 4. Push to the branch (`git push origin my-new-feature`)
29
+ 5. Create new Pull Request
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,26 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'heroku_feature_deployments/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "heroku_feature_deployments"
8
+ gem.version = HerokuFeatureDeployments::VERSION
9
+ gem.authors = ["Matt Beedle"]
10
+ gem.email = ["mattbeedle@googlemail.com"]
11
+ gem.description = %q{Gem to deploy the current git branch to a new app on heroku}
12
+ gem.summary = %q{Gem to deploy the current git branch to a new app on heroku}
13
+ gem.homepage = ""
14
+
15
+ gem.files = `git ls-files`.split($/)
16
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
+ gem.require_paths = ["lib"]
19
+
20
+ gem.add_runtime_dependency('heroku-api')
21
+ gem.add_runtime_dependency('dnsimple-ruby')
22
+ gem.add_runtime_dependency('github_api')
23
+
24
+ gem.add_development_dependency('rspec')
25
+ gem.add_development_dependency('vcr')
26
+ end
@@ -0,0 +1,21 @@
1
+ require 'rubygems'
2
+ require 'heroku-api'
3
+ require 'dnsimple'
4
+ require 'github_api'
5
+ require 'heroku_feature_deployments/configuration'
6
+ require 'heroku_feature_deployments/deployer'
7
+ require 'heroku_feature_deployments/pull_request_creator'
8
+ require 'heroku_feature_deployments/railtie' if defined?(Rails)
9
+ require 'heroku_feature_deployments/version'
10
+
11
+ module HerokuFeatureDeployments
12
+ class << self
13
+ attr_accessor :configuration
14
+ end
15
+
16
+ def self.configure
17
+ self.configuration ||= HerokuFeatureDeployments::Configuration.new
18
+ yield(configuration)
19
+ return self.configuration
20
+ end
21
+ end
@@ -0,0 +1,16 @@
1
+ module HerokuFeatureDeployments
2
+ class Configuration
3
+ attr_accessor :heroku_api_key, :dnsimple_username, :dnsimple_api_key,
4
+ :addons, :env_vars, :logger, :pivotal_tracker_api_key, :namespace,
5
+ :github_token, :github_repo, :domain, :pivotal_tracker_project_id,
6
+ :heroku_account_name, :collaborators
7
+
8
+ def logger
9
+ @logger ||= Logger.new(STDOUT)
10
+ end
11
+
12
+ def collaborators
13
+ @collaborators || []
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,211 @@
1
+ module HerokuFeatureDeployments
2
+ class Deployer
3
+
4
+ def initialize(options = {})
5
+ @branch_name = options[:branch_name] || get_branch_name
6
+ @remote_name = options[:remote_name] || @branch_name.underscore
7
+ @app_name = options[:app_name] || @branch_name.parameterize.
8
+ gsub(/_/, '-')
9
+ @full_app_name = "#{config.namespace}-#{@app_name}"
10
+ @pivotal_tracker_project_id = options[:pivotal_tracker_project_id]
11
+
12
+ DNSimple::Client.username = config.dnsimple_username
13
+ DNSimple::Client.api_token = config.dnsimple_api_key
14
+
15
+ # PivotalTracker::Client.token = config.pivotal_tracker_api_key
16
+ end
17
+
18
+ def deploy(pivotal_ticket_id)
19
+ @pivotal_ticket_id = pivotal_ticket_id
20
+ if app_exists?
21
+ add_environment_variables
22
+ add_collaborators
23
+ push_code
24
+ migrate_db
25
+ else
26
+ create_app
27
+ add_collaborators
28
+ add_addons
29
+ add_to_dnsimple
30
+ add_custom_domain
31
+ add_environment_variables
32
+ push_code
33
+ create_db
34
+ wait_for_process_to_finish 'rake db:create'
35
+ migrate_db
36
+ wait_for_process_to_finish 'rake db:migrate'
37
+ seed_db
38
+ wait_for_process_to_finish 'rake db:seed'
39
+ # add_pivotal_comment if @pivotal_ticket_id
40
+ create_pull_request
41
+ end
42
+
43
+ if config.domain
44
+ run_command "open http://#{@app_name}.#{config.domain}"
45
+ else
46
+ run_command "open http://#{@full_app_name}.herokuapp.com"
47
+ end
48
+ end
49
+
50
+ def undeploy
51
+ delete_app
52
+ remove_from_dnsimple
53
+ remove_git_remote
54
+ end
55
+
56
+ private
57
+
58
+ def wait_for_process_to_finish(command)
59
+ config.logger.info "waiting for #{command} to finish"
60
+
61
+ while true
62
+ break unless running_process_names.
63
+ any? { |n| n.match(/#{Regexp.escape(command)}/i) }
64
+ print '.'
65
+ sleep 5
66
+ end
67
+ puts
68
+ end
69
+
70
+ def running_process_names
71
+ heroku.get_ps(@full_app_name).body.map { |i| i['command'] }
72
+ end
73
+
74
+ def add_collaborators
75
+ config.logger.info 'Adding collaborators'
76
+ config.collaborators.each do |collaborator|
77
+ heroku.post_collaborator(@full_app_name, collaborator)
78
+ end
79
+ end
80
+
81
+ def add_custom_domain
82
+ config.logger.info 'Adding custom domain'
83
+ heroku.post_domain(@full_app_name, "#{@app_name}.#{config.domain}")
84
+ heroku.post_domain(@full_app_name, "*.#{@app_name}.#{config.domain}")
85
+ end
86
+
87
+ def create_pull_request
88
+ config.logger.info "Creating Pull Request"
89
+ run_command "git push origin #{@branch_name}"
90
+ PullRequestCreator.new(@full_app_name, @pivotal_ticket_id, @branch_name).
91
+ create
92
+ rescue Github::Error::UnprocessableEntity
93
+ config.logger.info 'Pull request already exists.'
94
+ end
95
+
96
+ # def add_pivotal_comment
97
+ # PivotalTracker::Project.all
98
+ # project = PivotalTracker::Project.find(config.pivotal_tracker_project_id)
99
+ # project.stories.find(@pivotal_tracker_id).tap do |story|
100
+ # story.notes.create(
101
+ # text: "location: http://#{@app_name}.#{config.domain}"
102
+ # )
103
+ # end
104
+ # end
105
+
106
+ def get_branch_name
107
+ `git branch`.split("\n").select {|s| s =~ /\*/ }.first.gsub(/\*/, '').
108
+ strip
109
+ end
110
+
111
+ def create_db
112
+ config.logger.info "Creating database"
113
+ heroku.post_ps(@full_app_name, 'rake db:create')
114
+ end
115
+
116
+ def migrate_db
117
+ config.logger.info "Migrating database"
118
+ heroku.post_ps(@full_app_name, 'rake db:migrate')
119
+ end
120
+
121
+ def seed_db
122
+ config.logger.info "Seeding database"
123
+ heroku.post_ps(@full_app_name, 'rake db:seed')
124
+ end
125
+
126
+ def push_code
127
+ run_command "git push #{@remote_name} #{@branch_name}:master"
128
+ end
129
+
130
+ def app_exists?
131
+ @app_exists ||= heroku.get_apps.body.any? do |a|
132
+ a['name'] == @full_app_name
133
+ end
134
+ end
135
+
136
+ def add_environment_variables
137
+ config.logger.info "Adding environment variables"
138
+ config.env_vars.merge!('SUBDOMAIN' => @app_name)
139
+ heroku.put_config_vars(@full_app_name, config.env_vars)
140
+ end
141
+
142
+ def add_to_dnsimple
143
+ config.logger.info "Adding records to DNSimple"
144
+ domain = DNSimple::Domain.find(config.domain)
145
+ DNSimple::Record.create(
146
+ domain, @app_name, 'CNAME', 'proxy.herokuapp.com'
147
+ )
148
+ DNSimple::Record.create(
149
+ domain, "*.#{@app_name}", 'CNAME', 'proxy.herokuapp.com'
150
+ )
151
+ end
152
+
153
+ def remove_from_dnsimple
154
+ config.logger.info "Removing records from DNSimple"
155
+ domain = DNSimple::Domain.find(config.domain)
156
+ DNSimple::Record.all(domain).each do |record|
157
+ record.destroy if %W(#{@app_name} *.#{@app_name}).include?(record.name)
158
+ end
159
+ end
160
+
161
+ def migrate_database
162
+ heroku.post_ps(@full_app_name, 'rake db:migrate --trace')
163
+ end
164
+
165
+ def create_app
166
+ config.logger.info "Creating App #{@full_app_name}"
167
+ heroku.post_app(name: @full_app_name).tap do |response|
168
+ run_command add_git_remote(response.body['git_url'])
169
+ end
170
+ end
171
+
172
+ def add_git_remote(git_url)
173
+ ['git remote add', @remote_name].tap do |command|
174
+ if config.heroku_account_name
175
+ command << git_url.gsub(/\.com/, ".#{config.heroku_account_name}")
176
+ end
177
+ end.join(' ')
178
+ end
179
+
180
+ def remove_git_remote
181
+ ['git remote rm', @remote_name].join(' ')
182
+ end
183
+
184
+ def delete_app
185
+ config.logger.info "Deleting App #{@full_app_name}"
186
+ heroku.delete_app(@full_app_name)
187
+ run_command "git remote rm #{@remote_name}"
188
+ end
189
+
190
+ def add_addons
191
+ config.logger.info "Adding addons"
192
+ config.addons.each do |addon|
193
+ config.logger.info "Adding #{addon}"
194
+ heroku.post_addon(@full_app_name, addon)
195
+ end
196
+ end
197
+
198
+ def heroku
199
+ @heroku ||= Heroku::API.new(api_key: config.heroku_api_key)
200
+ end
201
+
202
+ def config
203
+ @config ||= HerokuFeatureDeployments.configuration
204
+ end
205
+
206
+ def run_command(command)
207
+ config.logger.info "Running command: #{command}"
208
+ system command
209
+ end
210
+ end
211
+ end
@@ -0,0 +1,30 @@
1
+ module HerokuFeatureDeployments
2
+ class PullRequestCreator
3
+
4
+ def initialize(app_name, ticket_id, branch_name)
5
+ @app_name = app_name
6
+ @ticket_id = ticket_id
7
+ @branch_name = branch_name
8
+ end
9
+
10
+ def create
11
+ github = Github.new(
12
+ oauth_token: HerokuFeatureDeployments.configuration.github_token
13
+ )
14
+
15
+ github.pull_requests.create(
16
+ 'geeland',
17
+ HerokuFeatureDeployments.configuration.github_repo,
18
+ title: title, body: body, head: @branch_name, base: 'master'
19
+ )
20
+ end
21
+
22
+ def title
23
+ @app_name
24
+ end
25
+
26
+ def body
27
+ @ticket_id
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,9 @@
1
+ module HerokuFeatureDeployments
2
+ class Railtie < Rails::Railtie
3
+ railtie_name :heroku_feature_deployments
4
+
5
+ rake_tasks do
6
+ load 'tasks/heroku_feature_deployments.rake'
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,3 @@
1
+ module HerokuFeatureDeployments
2
+ VERSION = '0.3.0'
3
+ end
@@ -0,0 +1,13 @@
1
+ namespace :hfd do
2
+
3
+ desc 'Deploy the current branch'
4
+ task deploy: :environment do
5
+ HerokuFeatureDeployments::Deployer.new.
6
+ deploy(ENV['PIVOTAL_TICKET_ID'])
7
+ end
8
+
9
+ desc 'Undeploy the current branch'
10
+ task undeploy: :environment do
11
+ HerokuFeatureDeployments::Deployer.new.undeploy
12
+ end
13
+ end
metadata ADDED
@@ -0,0 +1,126 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: heroku_feature_deployments
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.3.0
5
+ platform: ruby
6
+ authors:
7
+ - Matt Beedle
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-07-08 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: heroku-api
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '>='
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '>='
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: dnsimple-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: github_api
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: vcr
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: Gem to deploy the current git branch to a new app on heroku
84
+ email:
85
+ - mattbeedle@googlemail.com
86
+ executables: []
87
+ extensions: []
88
+ extra_rdoc_files: []
89
+ files:
90
+ - .gitignore
91
+ - Gemfile
92
+ - LICENSE.txt
93
+ - README.md
94
+ - Rakefile
95
+ - heroku_feature_deployments.gemspec
96
+ - lib/heroku_feature_deployments.rb
97
+ - lib/heroku_feature_deployments/configuration.rb
98
+ - lib/heroku_feature_deployments/deployer.rb
99
+ - lib/heroku_feature_deployments/pull_request_creator.rb
100
+ - lib/heroku_feature_deployments/railtie.rb
101
+ - lib/heroku_feature_deployments/version.rb
102
+ - lib/tasks/heroku_feature_deployments.rake
103
+ homepage: ''
104
+ licenses: []
105
+ metadata: {}
106
+ post_install_message:
107
+ rdoc_options: []
108
+ require_paths:
109
+ - lib
110
+ required_ruby_version: !ruby/object:Gem::Requirement
111
+ requirements:
112
+ - - '>='
113
+ - !ruby/object:Gem::Version
114
+ version: '0'
115
+ required_rubygems_version: !ruby/object:Gem::Requirement
116
+ requirements:
117
+ - - '>='
118
+ - !ruby/object:Gem::Version
119
+ version: '0'
120
+ requirements: []
121
+ rubyforge_project:
122
+ rubygems_version: 2.0.3
123
+ signing_key:
124
+ specification_version: 4
125
+ summary: Gem to deploy the current git branch to a new app on heroku
126
+ test_files: []