At LifeChurch.tv, we’ve spent some time over the last year improving our toolset. Good tools allow us to spend less time thinking about the random details of our processes and focus more on creating great products for church’s around the world.
Before we get into the meat, let’s first talk about setup.
We use Heroku for our hosting. Each app has a staging and production environment.
The first shortcut we found was to stop referring to them using the --app APP_NAME
flag.
Instead, each is added as a distinct git remote:
$ git remote add staging OUR_STAGING_GIT_REMOTE
$ git remote add production OUR_PRODUCTION_GIT_REMOTE
That alone simplifies many of our commands. So now instead of heroku run console --app APP_NAME
, each app can now be referenced as the remote,
like this: heroku run console -r production
. This is especially nice if your Heroku app names have strange (or verbose) naming conventions.
We’ve been using 37Signals “sub” for awhile to extract and generalize some common CLI commands we use every day.
We named our sub “c2c” (our team name is “Church to Church”), so each of our commands is c2c <command>
.
Because we use Heroku for hosting, it’s easy to run the same commands across each of our apps.
For example, we can simplify heroku run console -r production
across each app with this little script:
1
2
3
4
5
6
7
8
9
10
11
#!/usr/bin/env ruby
# Usage: c2c console [<environment>]
# Summary: Launches rails console
# default to staging
def env
ARGV[0] == "production" ? "production" : "staging"
end
puts "Getting #{env.upcase} console..."
system("heroku run console -r #{env}")
Now, we can run c2c console production
inside any of our 4 apps. While this saves a few keystrokes, it’s not a huge gain.
But when you can simplify longer workflows, that’s where you begin to see some of the savings.
Another common process is running migrations. We don’t run anything complicated like read-only modes, etc. We turn on maintenance, migrate, restart, and turn maintenance off. So we can simplify that:
1
2
3
4
5
6
7
8
9
if production?
system("heroku maintenance:on -r production && heroku ps:scale worker=0 -r production")
end
system("heroku run rake db:migrate -r #{env} && heroku restart -r #{env}")
if production?
system("heroku maintenance:off -r production && heroku ps:scale worker=2 -r production")
end
For deployments, we use STDIN to get a brief overview of what’s being deployed. This gets posted to a free Redis instance with info about who deployed and when. Our Dashing dashboard pulls from Redis to display the latest two deployments for the rest of the team.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#!/usr/bin/env ruby
# Usage: c2c deploy [<environment>]
# Summary: Deploys the application you're in
require 'redis'
require 'json'
def deploy_production(product)
puts "===> What are you deploying? "
message = $stdin.gets().chomp
success = system("git push production master")
# post details to dashboard
if success
user_email = `git config --global user.email`.chomp()
puts "NOTIFYING DASHBOARD OF #{message}"
data = { message: message, at: Time.now.to_i, who: user_email }.to_json
redis = Redis.new()
redis.sadd("#{product}_deploys", data)
end
end
##
# The business
##
product = dir.gsub(/[-.]/, "")
puts "Deploying #{product} to #{env}..."
if production?
deploy_production(product)
else
system("git push staging master")
end
The next version of the script I’m working on today will include the c2c migrate
task. If the deployment needs to be done with migrations, we can
easily ask the user and shell out to run this other task and come back to posting to the dashboard. We can also turn on/off preboot on Heroku
to make sure we have a hard restart because of the database changes.
Overall, sub is a great tool for standardizing and simplifying common tasks across your team. Enjoy!
If you see any ways to further simplify any of the above scripts or if you have any questions, let me know!
Writer. Musician. Adventurer. Nerd.
Purveyor of GIFs and dad jokes.