Created
February 8, 2017 15:14
-
-
Save Taytay/8a66bf77aeb9eec82a31364381b8a6a0 to your computer and use it in GitHub Desktop.
A more robust version of `heroku run --exit-code`
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env expect | |
# This script requires `expect` to be installed on your system. | |
# This script runs a remote command on Heroku in detached mode, | |
# and exits with the same exit code that the remote command does. | |
# Note that this is superior to `heroku run --exit-code`, since that has a bug that | |
# often ends the command prematurely, and doesn't always return the correct exit code. | |
# This also has the advantage of running in detached mode, which will log the output of the command | |
# to Heroku's logplex. An attached `heroku run` command does not have its output logged. | |
# | |
# Usage: heroku_run.sh <APP> "<COMMAND_STRING>" | |
# | |
set herokuApp [lindex $argv 0]; | |
set commandToExecute [lindex $argv 1]; | |
if {$commandToExecute == "" || $herokuApp == ""} { | |
puts "Usage: heroku_run.sh <APP> '<COMMAND_STRING>'" | |
exit 1 | |
} | |
puts "Command: $commandToExecute" | |
puts "Heroku App: $herokuApp" | |
# Each of these commands will be allowed 60s with no matching activity | |
set timeout 60 | |
spawn heroku run:detached --app $herokuApp $commandToExecute | |
# Output will look something like: | |
# Running <COMMAND> on ⬢ <APP>... done, run.5742 (Hobby) | |
# Run heroku logs --app <APP> --dyno run.5742 to view the output. | |
expect { | |
# Wait for the dyno number to be echoed. | |
-re {dyno run.(\d+)} { | |
set dynoNumber $expect_out(1,string) | |
} | |
} | |
if {[info exists dynoNumber]} { | |
# `heroku logs --tail` is a bit finicky. | |
# Sometimes the connection will get severed, and expect will freak out because we didn't get the exit code we wanted | |
# So we need to keep retrying this logs command until we get what we want! | |
set attempt 1 | |
# Let's try this 10 times - why 10? why not? | |
set max_attempts 10 | |
while {$attempt <= $max_attempts} { | |
# We want to make sure we haven't missed too much of the logs, so we tell it to give us the last 1k lines of the log | |
spawn heroku logs --num 1000 --tail --app $herokuApp --dyno "run.$dynoNumber" | |
# 2017-01-11T20:48:08.482974+00:00 heroku[run.5245]: Starting process with command `echo 'create_test_accounts(2, "[email protected]", true)' | rails c` | |
# 2017-01-11T20:48:09.073859+00:00 heroku[run.5245]: State changed from starting to up | |
# 2017-01-11T20:48:14.569735+00:00 heroku[run.5245]: source=run.5245 dyno=heroku.26025121.2f854147-bcb7-4f79-bf99-4e5ba21bd906 sample#memory_total=85.91MB sample#memory_rss=85.91MB sample#memory_cache=0.00MB sample#memory_swap=0.00MB sample#memory_pgpgin=23370pages sample#memory_pgpgout=1376pages sample#memory_quota=512.00MB | |
# 2017-01-11T20:48:15.527630+00:00 app[run.5245]: Loading production environment (Rails 4.2.7.1) | |
# We're waiting for text like this: | |
# "Process exited with status 0" | |
expect { | |
-re {Process exited with status (\d+)} { | |
set exitCode $expect_out(1,string) | |
exit $exitCode | |
} | |
# All lines of output are prefixed with this: heroku[run.5245] or this: app[run.5245] | |
# As long as we see this occasionally, we know that we're progressing, so we do this so that we don't time out | |
"run.$dynoNumber" { | |
exp_continue | |
} | |
timeout { | |
puts "Timeout when waiting for heroku logs. More than $timeout seconds elapsed with no output."; | |
# In this case, let's just abort and retry the command | |
close | |
} | |
} | |
puts "Something went wrong when tailing the logs."; | |
incr attempt | |
if { $attempt <= $max_attempts } { | |
puts "We'll try again..." | |
} | |
} | |
# If we made it here, we weren't able to parse and use the exit code | |
puts "Couldn't find exit code of Heroku process after $max_attempts tries. Giving up." | |
exit 1 | |
} else { | |
puts "Couldn't find dyno number for Heroku run" | |
exit 1 | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment