On macOS, you can install with:
brew install rosszurowski/tap/tandem
If you have Go installed, you can install from the source with:
go install github.com/rosszurowski/tandem@latest
If you're using tandem from a Makefile, this snippet shows how to download a locally cached copy.
Use tandem by passing a set of commands to run in parallel. Wrap each command in quotes, like so:
tandem 'command1 "arg"' 'command2 "arg"' 'command3 "arg"'
Working on a Next.js app, you might want to run your front-end dev server alongside a backend API server with live updating changes through nodemon:
$ tandem 'next dev' 'nodemon --quiet ./server.js'
next ready - started server on 0.0.0.0:3000, url: http://localhost:3000
next event - compiled client and server successfully in 15 ms (25 modules)
nodemon starting server...
nodemon listening on http://localhost:3001
If your scripts are defined in package.json
, you can reference them by using npm:
as a prefix:
{
"scripts": {
"dev:php": "...",
"dev:js": "...",
"dev:css": "..."
}
}
$ tandem 'npm:dev:php' 'npm:dev:js' 'npm:dev:css'
Wildcard rules like npm:dev:*
are also supported as a shortcut. This line is equivalent to the above:
$ tandem 'npm:dev:*'
In a Makefile, use this snippet to fetch a local copy for your project. Change the .cache
path as needed, and add it to your .gitignore
.
dev: node_modules .cache/tandem
@.cache/tandem 'command1' 'command2'
.PHONY: dev
.cache/tandem:
@mkdir -p $$(dirname $@)
@curl -fsSL https://raw.githubusercontent.com/rosszurowski/tandem/main/install.sh | bash -s -- --dest="$$(dirname $@)"
Running make dev
will download tandem once, and then use it for every run from there on out.
I regularly use Makefiles to automate project commands and tools. Makefiles are mostly great! But their biggest failing (also a failing of shells generally) is that it's shockingly hard to coordinate multiple commands as one group:
make -jN <a> <b> <c>
doesn't end all tasks when another one fails. For running local dev servers, this means you can lose your CSS or JS watcher and not realize.command1 & command2 & wait
often leaves commands hanging around in the background, which is annoying when it eats up a port you want to use.tandem makes running concurrent servers easy. It takes inspiration from concurrently or npm-run-all, but improves performance and works as a static binary.
tandem owes a big thanks to hivemind, from which much of the source is drawn. tandem
can be thought of as a fork of hivemind, but rather than defining commands in a Procfile, defining them from a list of arguments.
tandem's illustration was drawn by Hannah Lee.