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.
- .default_buildpack ⇒ Object
Instance Method Summary collapse
- #add_database(plan_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
- #update_stack(stack_name) ⇒ Object
Constructor Details
#initialize(repo_name, options = {}) ⇒ App
Returns a new instance of App.
24 25 26 27 28 29 30 31 32 33 34 35 |
# File 'lib/hatchet/app.rb', line 24 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 @buildpacks = [:buildpack] || [:buildpacks] || [:buildpack_url] || self.class.default_buildpack @buildpacks = Array(@buildpacks) @reaper = Reaper.new(platform_api: platform_api) end |
Instance Attribute Details
#directory ⇒ Object (readonly)
Returns the value of attribute directory.
12 13 14 |
# File 'lib/hatchet/app.rb', line 12 def directory @directory end |
#name ⇒ Object (readonly)
Returns the value of attribute name.
12 13 14 |
# File 'lib/hatchet/app.rb', line 12 def name @name end |
#repo_name ⇒ Object (readonly)
Returns the value of attribute repo_name.
12 13 14 |
# File 'lib/hatchet/app.rb', line 12 def repo_name @repo_name end |
#stack ⇒ Object (readonly)
Returns the value of attribute stack.
12 13 14 |
# File 'lib/hatchet/app.rb', line 12 def stack @stack end |
Class Method Details
.config ⇒ Object
config is read only, should be threadsafe
46 47 48 |
# File 'lib/hatchet/app.rb', line 46 def self.config @config ||= Config.new end |
.default_buildpack ⇒ Object
37 38 39 |
# File 'lib/hatchet/app.rb', line 37 def self.default_buildpack [HATCHET_BUILDPACK_BASE, HATCHET_BUILDPACK_BRANCH.call].join("#") end |
Instance Method Details
#add_database(plan_name = 'heroku-postgresql:dev', match_val = "HEROKU_POSTGRESQL_[A-Z]+_URL") ⇒ Object
84 85 86 87 88 89 90 91 |
# File 'lib/hatchet/app.rb', line 84 def add_database(plan_name = 'heroku-postgresql:dev', match_val = "HEROKU_POSTGRESQL_[A-Z]+_URL") Hatchet::RETRIES.times.retry do # heroku.post_addon(name, plan_name) platform_api.addon.create(name, plan: plan_name ) _, value = get_config.detect {|k, v| k.match(/#{match_val}/) } set_config('DATABASE_URL' => value) end end |
#allow_failure? ⇒ Boolean
41 42 43 |
# File 'lib/hatchet/app.rb', line 41 def allow_failure? @allow_failure end |
#api_key ⇒ Object
223 224 225 |
# File 'lib/hatchet/app.rb', line 223 def api_key @api_key ||= ENV['HEROKU_API_KEY'] || bundle_exec {`heroku auth:token`.chomp } end |
#config ⇒ Object
50 51 52 |
# File 'lib/hatchet/app.rb', line 50 def config self.class.config end |
#create_app ⇒ Object
126 127 128 129 130 131 132 133 134 135 136 137 138 |
# File 'lib/hatchet/app.rb', line 126 def create_app 3.times.retry do begin # heroku.post_app({ name: name, stack: stack }.delete_if {|k,v| v.nil? }) hash = { name: name, stack: stack } hash.delete_if { |k,v| v.nil? } platform_api.app.create(hash) rescue @reaper.cycle raise e end end end |
#create_pipeline ⇒ Object
257 258 259 |
# File 'lib/hatchet/app.rb', line 257 def create_pipeline platform_api.pipeline.create(name: @name) end |
#create_source ⇒ Object
266 267 268 269 270 271 272 273 |
# File 'lib/hatchet/app.rb', line 266 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
111 112 113 |
# File 'lib/hatchet/app.rb', line 111 def debug? @debug || ENV['HATCHET_DEBUG'] || false end |
#delete_pipeline(pipeline_id) ⇒ Object
275 276 277 |
# File 'lib/hatchet/app.rb', line 275 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
185 186 187 188 189 190 191 192 193 |
# File 'lib/hatchet/app.rb', line 185 def deploy(&block) in_directory do self.setup! self.push_with_retry! block.call(self, platform_api, output) if block_given? end ensure self.teardown! end |
#deployed? ⇒ Boolean
121 122 123 124 |
# File 'lib/hatchet/app.rb', line 121 def deployed? # !heroku.get_ps(name).body.detect {|ps| ps["process"].include?("web") }.nil? platform_api.formation.list(name).detect {|ps| ps["type"] == "web"} end |
#get_config ⇒ Object
61 62 63 64 |
# File 'lib/hatchet/app.rb', line 61 def get_config # heroku.get_config_vars(name).body platform_api.config_var.info_for_app(name) end |
#get_labs ⇒ Object
70 71 72 73 |
# File 'lib/hatchet/app.rb', line 70 def get_labs # heroku.get_features(name).body platform_api.app_feature.list(name) end |
#heroku ⇒ Object
227 228 229 |
# File 'lib/hatchet/app.rb', line 227 def heroku raise "Not supported, use `platform_api` instead." end |
#in_directory(directory = self.directory) ⇒ Object
172 173 174 175 176 177 178 179 |
# File 'lib/hatchet/app.rb', line 172 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
66 67 68 |
# File 'lib/hatchet/app.rb', line 66 def lab_is_installed?(lab) get_labs.any? {|hash| hash["name"] == lab } end |
#not_debugging? ⇒ Boolean Also known as: no_debug?
116 117 118 |
# File 'lib/hatchet/app.rb', line 116 def not_debugging? !debug? end |
#output ⇒ Object
219 220 221 |
# File 'lib/hatchet/app.rb', line 219 def output @output end |
#pipeline_id ⇒ Object
253 254 255 |
# File 'lib/hatchet/app.rb', line 253 def pipeline_id @pipeline_id end |
#platform_api ⇒ Object
279 280 281 282 |
# File 'lib/hatchet/app.rb', line 279 def platform_api # We have to not use a cache due to https://github.com/heroku/platform-api/issues/73 @platform_api ||= PlatformAPI.connect_oauth(api_key, cache: Moneta.new(:Null)) end |
#push ⇒ Object Also known as: push!, push_with_retry
196 197 198 199 200 201 202 203 204 205 206 |
# File 'lib/hatchet/app.rb', line 196 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
159 160 161 |
# File 'lib/hatchet/app.rb', line 159 def push_without_retry! raise NotImplementedError end |
#retry_error_message(error, attempt, max_retries) ⇒ Object
212 213 214 215 216 217 |
# File 'lib/hatchet/app.rb', line 212 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
95 96 97 98 99 100 101 102 103 104 105 106 |
# File 'lib/hatchet/app.rb', line 95 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
231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 |
# File 'lib/hatchet/app.rb', line 231 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: @buildpacks, 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
54 55 56 57 58 59 |
# File 'lib/hatchet/app.rb', line 54 def set_config( = {}) .each do |key, value| # heroku.put_config_vars(name, key => value) platform_api.config_var.update(name, key => value) end end |
#set_lab(lab) ⇒ Object
79 80 81 82 |
# File 'lib/hatchet/app.rb', line 79 def set_lab(lab) # heroku.post_feature(lab, name) platform_api.app_feature.update(name, lab, enabled: true) end |
#set_labs! ⇒ Object
75 76 77 |
# File 'lib/hatchet/app.rb', line 75 def set_labs! @labs.each {|lab| set_lab(lab) } end |
#setup! ⇒ Object Also known as: setup
creates a new heroku app via the API
146 147 148 149 150 151 152 153 154 155 156 |
# File 'lib/hatchet/app.rb', line 146 def setup! return self if @app_is_setup puts "Hatchet setup: #{name.inspect} for #{repo_name.inspect}" create_app set_labs! # heroku.put_config_vars(name, 'BUILDPACK_URL' => @buildpack) buildpack_list = @buildpacks.map {|pack| { buildpack: pack }} platform_api.buildpack_installation.update(name, updates: buildpack_list) @app_is_setup = true self end |
#source_get_url ⇒ Object
261 262 263 264 |
# File 'lib/hatchet/app.rb', line 261 def source_get_url create_source @source_get_url end |
#teardown! ⇒ Object
163 164 165 166 167 168 169 170 |
# File 'lib/hatchet/app.rb', line 163 def teardown! return false unless @app_is_setup if debugging? puts "Debugging App:#{name}" return false end @reaper.cycle end |
#update_stack(stack_name) ⇒ Object
140 141 142 143 |
# File 'lib/hatchet/app.rb', line 140 def update_stack(stack_name) @stack = stack_name platform_api.app.update(name, build_stack: @stack) end |