class Hatchet::HerokuRun

Used for running Heroku commands

Example:

run_obj = HerokuRun.new("ruby -v", app: app).call
puts run_obj.output #=> "ruby 2.7.1p83 (2020-03-31 revision a0c7c23c9c) [x86_64-linux]"
puts run_obj.status.success? #=> true

There’s a bug in specs sometimes where App#run will return an empty value. When that’s detected then the command will be re-run. This can be optionally disabled by setting ‘retry_on_empty: false` if you’re expecting the command to be empty.

Attributes

command[R]

Public Class Methods

new( command, app: , heroku: {}, retry_on_empty: !ENV["HATCHET_DISABLE_EMPTY_RUN_RETRY"], raw: false, stderr: $stderr) click to toggle source
# File lib/hatchet/heroku_run.rb, line 53
def initialize(
  command,
  app: ,
  heroku: {},
  retry_on_empty: !ENV["HATCHET_DISABLE_EMPTY_RUN_RETRY"],
  raw: false,
  stderr: $stderr)

  @raw = raw
  @app = app
  @command = build_heroku_command(command, heroku || {})
  @retry_on_empty = retry_on_empty
  @stderr = stderr
  @result = nil
  @empty_fail_count = 0
end

Public Instance Methods

call() click to toggle source
# File lib/hatchet/heroku_run.rb, line 84
def call
  loop do
    execute!

    break unless output.empty?
    break unless @retry_on_empty

    @empty_fail_count += 1

    break if @empty_fail_count >= 3

    message = String.new("Empty output from command #{@command}, retrying the command.")
    message << "\nTo disable pass in `retry_on_empty: false` or set HATCHET_DISABLE_EMPTY_RUN_RETRY=1 globally"
    message << "\nfailed_count: #{@empty_fail_count}"
    message << "\nreleases: #{@app.releases}"
    message << "\n#{caller.join("\n")}"
    @stderr.puts message
  end

  self
end
output() click to toggle source
# File lib/hatchet/heroku_run.rb, line 75
def output
  result.stdout
end
result() click to toggle source
# File lib/hatchet/heroku_run.rb, line 70
def result
  raise "You must run `call` on this object first" unless @result
  @result
end
status() click to toggle source
# File lib/hatchet/heroku_run.rb, line 79
def status
  result
  @status
end

Private Instance Methods

build_heroku_command(command, options = {}) click to toggle source
# File lib/hatchet/heroku_run.rb, line 119
        def build_heroku_command(command, options = {})
  command = command.shellescape unless @raw

  default_options = { "app" => @app.name, "exit-code" => nil }
  heroku_options_array = (default_options.merge(options)).map do |k,v|
    # This was a bad interface decision
    next if v == Hatchet::App::SkipDefaultOption # for forcefully removing e.g. --exit-code, a user can pass this

    arg = "--#{k.to_s.shellescape}"
    arg << "=#{v.to_s.shellescape}" unless v.nil? # nil means we include the option without an argument
    arg
  end

  "heroku run #{heroku_options_array.compact.join(' ')} -- #{command}"
end
execute!() click to toggle source
# File lib/hatchet/heroku_run.rb, line 106
        def execute!
  ShellThrottle.new(platform_api: @app.platform_api).call do |throttle|
    run_shell!
    throw(:throttle) if @result.stderr.match?(/reached the API rate limit/)
  end
end
run_shell!() click to toggle source
# File lib/hatchet/heroku_run.rb, line 113
        def run_shell!
  stdout, stderr, status = Open3.capture3(@command)
  @result = BashResult.new(stdout: stdout, stderr: stderr, status: status, set_global_status: true)
  @status = $?
end