Class: Hatchet::App
- Inherits:
-
Object
- Object
- Hatchet::App
- Defined in:
- lib/hatchet/app.rb
Defined Under Namespace
Classes: FailedDeploy
Constant Summary collapse
- HATCHET_BUILDPACK_BASE =
(ENV['HATCHET_BUILDPACK_BASE'] || "https://github.com/heroku/heroku-buildpack-ruby.git")
- HATCHET_BUILDPACK_BRANCH =
-> { ENV['HATCHET_BUILDPACK_BRANCH'] || Hatchet.git_branch }
- BUILDPACK_URL =
"https://github.com/heroku/heroku-buildpack-ruby.git"
Instance Attribute Summary collapse
-
#directory ⇒ Object
readonly
Returns the value of attribute directory.
-
#name ⇒ Object
readonly
Returns the value of attribute name.
-
#repo_name ⇒ Object
readonly
Returns the value of attribute repo_name.
-
#stack ⇒ Object
readonly
Returns the value of attribute stack.
Class Method Summary collapse
-
.config ⇒ Object
config is read only, should be threadsafe.
Instance Method Summary collapse
- #add_database(db_name = 'heroku-postgresql:dev', match_val = "HEROKU_POSTGRESQL_[A-Z]+_URL") ⇒ Object
- #allow_failure? ⇒ Boolean
- #api_key ⇒ Object
- #config ⇒ Object
- #create_app ⇒ Object
- #create_pipeline ⇒ Object
- #create_source ⇒ Object
-
#debug? ⇒ Boolean
(also: #debugging?)
set debug: true when creating app if you don’t want it to be automatically destroyed, useful for debugging…bad for app limits.
- #delete_pipeline(pipeline_id) ⇒ Object
-
#deploy(&block) ⇒ Object
creates a new app on heroku, “pushes” via anvil or git then yields to self so you can call self.run or self.deployed? Allow deploy failures on CI server by setting ENV.
- #deployed? ⇒ Boolean
- #get_config ⇒ Object
- #get_labs ⇒ Object
- #heroku ⇒ Object
- #in_directory(directory = self.directory) ⇒ Object
-
#initialize(repo_name, options = {}) ⇒ App
constructor
A new instance of App.
- #lab_is_installed?(lab) ⇒ Boolean
- #not_debugging? ⇒ Boolean (also: #no_debug?)
- #output ⇒ Object
- #pipeline_id ⇒ Object
- #platform_api ⇒ Object
- #push ⇒ Object (also: #push!, #push_with_retry)
- #push_without_retry! ⇒ Object
- #retry_error_message(error, attempt, max_retries) ⇒ Object
-
#run(cmd_type, command = nil, options = {}, &block) ⇒ Object
runs a command on heroku similar to ‘$ heroku run #foo` but programatically and with more control.
- #run_ci(timeout: 300, &block) ⇒ Object
- #set_config(options = {}) ⇒ Object
- #set_lab(lab) ⇒ Object
- #set_labs! ⇒ Object
-
#setup! ⇒ Object
(also: #setup)
creates a new heroku app via the API.
- #source_get_url ⇒ Object
- #teardown! ⇒ Object
Constructor Details
#initialize(repo_name, options = {}) ⇒ App
Returns a new instance of App.
22 23 24 25 26 27 28 29 30 31 32 |
# File 'lib/hatchet/app.rb', line 22 def initialize(repo_name, = {}) @repo_name = repo_name @directory = config.path_for_name(@repo_name) @name = [:name] || "hatchet-t-#{SecureRandom.hex(10)}" @stack = [:stack] @debug = [:debug] || [:debugging] @allow_failure = [:allow_failure] || false @labs = ([] << [:labs]).flatten.compact @buildpack = [:buildpack] || [:buildpack_url] || [HATCHET_BUILDPACK_BASE, HATCHET_BUILDPACK_BRANCH.call].join("#") @reaper = Reaper.new(heroku) end |
Instance Attribute Details
#directory ⇒ Object (readonly)
Returns the value of attribute directory.
10 11 12 |
# File 'lib/hatchet/app.rb', line 10 def directory @directory end |
#name ⇒ Object (readonly)
Returns the value of attribute name.
10 11 12 |
# File 'lib/hatchet/app.rb', line 10 def name @name end |
#repo_name ⇒ Object (readonly)
Returns the value of attribute repo_name.
10 11 12 |
# File 'lib/hatchet/app.rb', line 10 def repo_name @repo_name end |
#stack ⇒ Object (readonly)
Returns the value of attribute stack.
10 11 12 |
# File 'lib/hatchet/app.rb', line 10 def stack @stack end |
Class Method Details
Instance Method Details
#add_database(db_name = 'heroku-postgresql:dev', match_val = "HEROKU_POSTGRESQL_[A-Z]+_URL") ⇒ Object
73 74 75 76 77 78 79 |
# File 'lib/hatchet/app.rb', line 73 def add_database(db_name = 'heroku-postgresql:dev', match_val = "HEROKU_POSTGRESQL_[A-Z]+_URL") Hatchet::RETRIES.times.retry do heroku.post_addon(name, db_name) _, value = get_config.detect {|k, v| k.match(/#{match_val}/) } set_config('DATABASE_URL' => value) end end |
#allow_failure? ⇒ Boolean
34 35 36 |
# File 'lib/hatchet/app.rb', line 34 def allow_failure? @allow_failure end |
#api_key ⇒ Object
199 200 201 |
# File 'lib/hatchet/app.rb', line 199 def api_key @api_key ||= ENV['HEROKU_API_KEY'] || bundle_exec {`heroku auth:token`.chomp } end |
#config ⇒ Object
43 44 45 |
# File 'lib/hatchet/app.rb', line 43 def config self.class.config end |
#create_app ⇒ Object
113 114 115 116 117 118 119 120 121 122 |
# File 'lib/hatchet/app.rb', line 113 def create_app 3.times.retry do begin heroku.post_app({ name: name, stack: stack }.delete_if {|k,v| v.nil? }) rescue Heroku::API::Errors::RequestFailed => e @reaper.cycle if e..match(/app limit/) raise e end end end |
#create_pipeline ⇒ Object
233 234 235 |
# File 'lib/hatchet/app.rb', line 233 def create_pipeline platform_api.pipeline.create(name: @name) end |
#create_source ⇒ Object
242 243 244 245 246 247 248 249 |
# File 'lib/hatchet/app.rb', line 242 def create_source @create_source ||= begin result = platform_api.source.create @source_get_url = result["source_blob"]["get_url"] @source_put_url = result["source_blob"]["put_url"] @source_put_url end end |
#debug? ⇒ Boolean Also known as: debugging?
set debug: true when creating app if you don’t want it to be automatically destroyed, useful for debugging…bad for app limits. turn on global debug by setting HATCHET_DEBUG=true in the env
99 100 101 |
# File 'lib/hatchet/app.rb', line 99 def debug? @debug || ENV['HATCHET_DEBUG'] || false end |
#delete_pipeline(pipeline_id) ⇒ Object
251 252 253 |
# File 'lib/hatchet/app.rb', line 251 def delete_pipeline(pipeline_id) platform_api.pipeline.delete(pipeline_id) end |
#deploy(&block) ⇒ Object
creates a new app on heroku, “pushes” via anvil or git then yields to self so you can call self.run or self.deployed? Allow deploy failures on CI server by setting ENV
161 162 163 164 165 166 167 168 169 |
# File 'lib/hatchet/app.rb', line 161 def deploy(&block) in_directory do self.setup! self.push_with_retry! block.call(self, heroku, output) if block_given? end ensure self.teardown! end |
#deployed? ⇒ Boolean
109 110 111 |
# File 'lib/hatchet/app.rb', line 109 def deployed? !heroku.get_ps(name).body.detect {|ps| ps["process"].include?("web") }.nil? end |
#get_config ⇒ Object
53 54 55 |
# File 'lib/hatchet/app.rb', line 53 def get_config heroku.get_config_vars(name).body end |
#get_labs ⇒ Object
61 62 63 |
# File 'lib/hatchet/app.rb', line 61 def get_labs heroku.get_features(name).body end |
#heroku ⇒ Object
203 204 205 |
# File 'lib/hatchet/app.rb', line 203 def heroku @heroku ||= Heroku::API.new(api_key: api_key) end |
#in_directory(directory = self.directory) ⇒ Object
148 149 150 151 152 153 154 155 |
# File 'lib/hatchet/app.rb', line 148 def in_directory(directory = self.directory) Dir.mktmpdir do |tmpdir| FileUtils.cp_r("#{directory}/.", "#{tmpdir}/.") Dir.chdir(tmpdir) do yield directory end end end |
#lab_is_installed?(lab) ⇒ Boolean
57 58 59 |
# File 'lib/hatchet/app.rb', line 57 def lab_is_installed?(lab) get_labs.any? {|hash| hash["name"] == lab } end |
#not_debugging? ⇒ Boolean Also known as: no_debug?
104 105 106 |
# File 'lib/hatchet/app.rb', line 104 def not_debugging? !debug? end |
#output ⇒ Object
195 196 197 |
# File 'lib/hatchet/app.rb', line 195 def output @output end |
#pipeline_id ⇒ Object
229 230 231 |
# File 'lib/hatchet/app.rb', line 229 def pipeline_id @pipeline_id end |
#platform_api ⇒ Object
255 256 257 |
# File 'lib/hatchet/app.rb', line 255 def platform_api @platform_api ||= PlatformAPI.connect_oauth(api_key) end |
#push ⇒ Object Also known as: push!, push_with_retry
172 173 174 175 176 177 178 179 180 181 182 |
# File 'lib/hatchet/app.rb', line 172 def push max_retries = @allow_failure ? 1 : RETRIES max_retries.times.retry do |attempt| begin @output = self.push_without_retry! rescue StandardError => error puts (error, attempt, max_retries) raise error end end end |
#push_without_retry! ⇒ Object
135 136 137 |
# File 'lib/hatchet/app.rb', line 135 def push_without_retry! raise NotImplementedError end |
#retry_error_message(error, attempt, max_retries) ⇒ Object
188 189 190 191 192 193 |
# File 'lib/hatchet/app.rb', line 188 def (error, attempt, max_retries) attempt += 1 return "" if attempt == max_retries msg = "\nRetrying failed Attempt ##{attempt}/#{max_retries} to push for '#{name}' due to error: \n"<< "#{error.class} #{error.}\n #{error.backtrace.join("\n ")}" end |
#run(cmd_type, command = nil, options = {}, &block) ⇒ Object
runs a command on heroku similar to ‘$ heroku run #foo` but programatically and with more control
83 84 85 86 87 88 89 90 91 92 93 94 |
# File 'lib/hatchet/app.rb', line 83 def run(cmd_type, command = nil, = {}, &block) command = cmd_type.to_s if command.nil? = (.delete(:heroku) || {}).map {|k,v| "--#{k.to_s.shellescape}=#{v.to_s.shellescape}"}.join(" ") heroku_command = "heroku run #{command.to_s.shellescape} -a #{name} #{ }" bundle_exec do if block_given? ReplRunner.new(cmd_type, heroku_command, ).run(&block) else `#{heroku_command}` end end end |
#run_ci(timeout: 300, &block) ⇒ Object
207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 |
# File 'lib/hatchet/app.rb', line 207 def run_ci(timeout: 300, &block) Hatchet::RETRIES.times.retry do result = create_pipeline @pipeline_id = result["id"] end # create_app # platform_api.pipeline_coupling.create(app: name, pipeline: @pipeline_id, stage: "development") test_run = TestRun.new(token: api_key, buildpacks: @buildpack, timeout: timeout, app: self, pipeline: @pipeline_id) Hatchet::RETRIES.times.retry do test_run.create_test_run end test_run.wait!(&block) ensure delete_pipeline(@pipeline_id) if @pipeline_id end |
#set_config(options = {}) ⇒ Object
47 48 49 50 51 |
# File 'lib/hatchet/app.rb', line 47 def set_config( = {}) .each do |key, value| heroku.put_config_vars(name, key => value) end end |
#set_lab(lab) ⇒ Object
69 70 71 |
# File 'lib/hatchet/app.rb', line 69 def set_lab(lab) heroku.post_feature(lab, name) end |
#set_labs! ⇒ Object
65 66 67 |
# File 'lib/hatchet/app.rb', line 65 def set_labs! @labs.each {|lab| set_lab(lab) } end |
#setup! ⇒ Object Also known as: setup
creates a new heroku app via the API
125 126 127 128 129 130 131 132 |
# File 'lib/hatchet/app.rb', line 125 def setup! return self if @app_is_setup puts "Hatchet setup: #{name.inspect} for #{repo_name.inspect}" create_app set_labs! @app_is_setup = true self end |
#source_get_url ⇒ Object
237 238 239 240 |
# File 'lib/hatchet/app.rb', line 237 def source_get_url create_source @source_get_url end |
#teardown! ⇒ Object
139 140 141 142 143 144 145 146 |
# File 'lib/hatchet/app.rb', line 139 def teardown! return false unless @app_is_setup if debugging? puts "Debugging App:#{name}" return false end @reaper.cycle end |