Simpler Dev Environments with Procfiles
Throughout the time, I've tried a number of different ways to manage my development environment. From Make files, to PM2 and Tmux.
Tmux, is well.., tmux. Tmuxinator makes it manageable, but the "layout" definition is far from readable, and copying errors from history to your clipboard, is a true pain in the ass.
I still recommend taking a look at PM2 if you need more than "get things running". It comes with a lot of options, but that also means that your config script comes with some verbosity.
This article explains how I cover my simple use cases. Those that don't require additional file watchers (see PM2), or split panes to separate logs while sticking to a single terminal (see Tmux). For those, I use Procfiles.
No fear, Procfile is here!
Procfile? Yeah, the first time I heard it I was "not another make file, right?!". And luckily, it's nothing like that. Let's get started.
So a Procfile is a simple key: command
format. And much like a Dockerfile, it's by convention named after the format. I recommend creating file named Procfile
in the root of your project, and commit it to your repo. If not for yourself, than for every new contributor that one day joins your team.
For MagicBell, our procfile looks as follows:
server: bin/rails s -p 3000 -e development
worker: bin/bundle exec sidekiq
webpack: bin/webpacker-dev-server
That's it. That's all the config you need to get multiple services up and running through a single command.
Obviously, we still need to install a runner to handle this procfile. Meet foreman, or one of it's forks. Foreman is a Ruby script, so for that you'll need to have Ruby installed. There are many forks though. Such as shoreman, which is a dependency free shell script, or node-foreman, which is a javascript fork. I go with node-foreman, for the simple reason that I'm a node guy and I like that I can npm install
it to the dependencies of my node projects.
So, go ahead, and install node-foreman
. For the sake of this howto, let's go global. Drop the -g
if you have a concrete project at hand.
npm i -g node-foreman
Node-foreman installs a binary named nf
. With this installed, it's as simple as running nf start
in the same directory as your Procfile
. Try it, and see all your services spin up and log to the same terminal. When you have an .env
file in the same directory, node-foreman
automatically loads all environment variables from it. If your env file is named differently, say .env.development
, you can specify it with the --env
flag. Use the --procfile
flag if you need to specify a Procfile.
A bit more… tabs
The above might be all you need. But sometimes, I like to have my startup process a bit personalised. For that, I use scripts that I store under my home directory.
For MagicBell, my config exists of two files. One custom procfile, saved as ~/scripts/magicbell.proc
:
server: bin/rails s -p 3000 -e development
worker: bin/bundle exec sidekiq
webpack: bin/webpacker-dev-server
codegen: yarn codegen -w
And the "executable" script that I run, saved as ~/scripts/start-magicbell
(don't forget to chmod +x
)
#!/usr/bin/env bash
cd ~/dev/magicbell/backend
ttab docker-compose up
bundle install
yarn install
yarn db:migrate
nf start -j ~/scripts/magicbell.proc -e .env.development
When read that script, you'll notice ttab
. ttab is a small utility that allows us to start commands in a new terminal tab. That way, I can start postgres & redis in one terminal instance (tab) while running everything else in another. Ttab is optional, but I highly recommend using it for simplicity.
npm i -g ttab
As I've added ~/scripts
to my PATH
, I can get the MagicBell environment up and running using a single start-magicbell
command. Let me walk you trough wat it does:
- first, it navigates to the git repo on my drive, at
~/dev/magicbell/backend
- runs
docker-compose up
in a new tab, detached from the current - installs all ruby gems, while the db is spinning up
- installs all node modules
- runs our database migration script
- starts the four services defined in the procfile
With this single command, I've started 6 services and ran some install/migrate commands. It happened too often that I manually had to install new gems or modules after pulling changes. Those commands are fast enough to run as part of my startup, yet take enough time for the database to be online before the server starts.
That's it. Both node-foreman and ttab come with a bunch of options. So be sure to check out their readmes (especially from node-foreman!)