Launching Dev Dependencies with tmux

When working on CollBox, we have a handful of external services the app depends on which we need to have running at development time. I used to run these via Foreman, but somewhere along the way my Ruby installation seems to have gotten borked (thanks, Catalina?) and since then I’ve been running the services by hand. I always work in a tmux session anyway, so I decided it was time to see what it would look like to launch my window full of dependencies in tmux from a script.

As it turns out, tmux is fantastically scriptable. If you simply run tmux <command> from a shell inside of a tmux session, that command will be run by the encapsulating tmux instance, and these are the same set of commands that can be bound to your tmux keyboard shortcuts. You can also simply send a stream of keystrokes to panes inside of tmux. All of this means running my four dependencies in a single window (named “deps”) under their own panes is as simple as:

#!/bin/sh
# deps.sh

tmux new-window -n "deps"

tmux send-keys "redis-server redis.conf" Enter
tmux split-window
tmux send-keys "datomic/bin/transactor transactor.properties" Enter
tmux split-window
tmux send-keys "lein shadow-watch" Enter
tmux split-window
tmux send-keys "npx gulp" Enter

tmux select-layout tiled

When I’m ready to work on the project I simply run ./deps.sh at my shell and I end up with something like this:

+---------------------------------+---------------------------------+
| $ redis-server redis.conf       | $ datomic/bin/transactor transa |
| ...                             | ...                             |
|                                 |                                 |
|                                 |                                 |
|                                 |                                 |
|                                 |                                 |
|                                 |                                 |
|---------------------------------+---------------------------------|
| $ lein shadow-watch             | $ npx gulp                      |
| ...                             | ...                             |
|                                 |                                 |
|                                 |                                 |
|                                 |                                 |
|                                 |                                 |
|                                 |                                 |
+---------------------------------+---------------------------------+
| 1:deps* | 2:zsh |                                                 |
+-------------------------------------------------------------------+

fin.


A few thoughts on other tools that can be used to address this same problem:

  • Foreman: Great project; I’ve used it happily for years. I love the aggregated, colorized logs, which keep things compact yet comprehensible. The one downside is that it’s not convenient to restart a single service, should you need to. But my main reason for switching was just to see if I could avoid the additional dependencies to my development stack given that I already use tmux.
  • tmuxinator: Looks like a nice project to encapsulate essentially what I did above into a simple data-driven format, but I don’t see that the gains are big enough (at least for my simple case) to bother adding the additional dependencies.
  • Docker: As much as I like the idea of cleanly locking down development dependencies in the Docker style, in practice I’ve found it exceptionally costly, both in terms of the performance overhead (I literally had to buy a new laptop at my last job once we switched to Docker), and the increased complexity of accessing application state / debugging.