Run a Local Rails Script on Heroku

Heroku provides a convenient command line interface for executing snippets of Ruby code remotely. One-liners can easily be piped into the heroku run console command. But what about much longer scripts that you write locally and want to execute in a remote Heroku environment? In this post, I’ll show you how to execute a long Ruby/Rails script in a remote Heroku environment.

One-Liner

The run console command will read input from stdin and pass it to the remote console session. Knowing this, you can run a simple Rails query:


echo "puts User.count" | heroku run console --app=my-heroku-app

The result will be displayed in the terminal, but you’ll still be in the Heroku console session. One way to avoid that would be to force the session to end by tacking on an exit to your one-liner.


echo "puts User.count; exit" | heroku run console --app=my-heroku-app

Now the result is displayed, and the console session exits immediately.

Long Scripts

I wanted to run a much longer script (several thousand lines) using this same technique, but whenever I’d try, it would always hang part-way through the script, never completing.

My teammate Brian Vanderwal pointed out the --no-tty argument, and that did the trick! By including that argument, I can pipe (or redirect) an arbitrarily long Ruby script into a Heroku console. And as an added bonus, when you specify the --no-tty argument, you no longer need the exit at the end.


echo "puts User.count" | heroku run console --app=my-heroku-app --no-tty

or


cat some_script.rb | heroku run console --app=my-heroku-app --no-tty

Conclusion

For years, I’ve known about the ability to run scripts that are checked into the repository using the heroku command line tool.


heroku run bundle exec rails runner ./scripts/script.rb -a my-heroku-app

But there are some cases where you want to execute code in the production environment without doing a full production deploy first. In those cases, the --no-tty argument to the heroku run console command is exactly what you need.

Conversation
  • Schneemsn says:

    Another great tip that few people know about is you can access a shell by running

    “`
    $ heroku run bash –app=
    “`

    Thanks for the post!

  • Schneemsn says:

    And the formatting was messed up there. The gist is:

    $ heroku run bash

    • Patrick Bacon Patrick Bacon says:

      Thanks for mentioning that!

      I hadn’t heard of the “bash” command until I was writing this post. Could be very useful.

  • James says:

    Extremely relevant. I am going to need to do this today! Thanks.

  • Obromios says:

    The –no-tty trick is very useful, but, I would first want to try the script with the rails console in sandbox mode to see what it does to the database. However rails console –sandbox –no-tty will probably exit immediately, so how do I run the script without exiting?

    • Patrick Bacon Patrick Bacon says:

      Unfortunately, I don’t think the –sandbox and –no-tty options are going to work all that well together. The point of the sandbox is to execute commands and explore the results interactively without actually updating the database… but the –no-tty argument means you aren’t going to have an interactive session.

      Perhaps you could add some statements to the end of the script that print out info you’d be interested in to verify the script did what you expected. It wouldn’t be interactive, but you’d be able to run the script in a sandbox and at least do some minimal verification.

      • Obromios says:

        That makes sense, you could even add some tests at the end of the script.

  • Matt says:

    Are you aware if the script you use in this command can run rake tasks?:

    heroku run bundle exec rails runner ./scripts/script.rb -a my-heroku-app

    If so, what’s the syntax?

    • Patrick Bacon Patrick Bacon says:

      Matt,

      I don’t know if you can run a rake task from inside a script run through the rails runner or not. But I do know you can remotely execute a rake task through heroku, and since a rake task can either depend on other tasks, or just invoke other tasks directly, I think that would be good enough:

      heroku run rake my_rake_task -a my-heroku-app

  • Thank you so much!! I never knew about the –no-tty command.

  • Comments are closed.