The search for the perfect setup script
We’ve all been there. You open up a project for the first time, or a project you haven’t used in a while:
Wait, how do I run this again?
If you’re lucky, the project will have a README, or your company will keep a wiki. But what if the instructions are out of date?
Enter the setup script. The approach is simple - a script that automates setting up the project on your machine for development. It could install packages, create docker containers, start development servers, etc:
$ ./bin/dev.sh
Installing packages...
Starting docker containers...
Starting development server...
Ready! Go to http://localhost:8000 to get started.
An easy setup like this can make a huge difference to your team’s productivity, especially if your project is complicated or you switch projects frequently.
Like anything in software, this process can always be optimised for maximum benefit. Here are some tips and tricks I’ve learned over the years.
Use a common pattern for all projects
There are many ways to write a setup script, and many ways to run it, for example:
./bin/dev.sh
./bin/setup
(made popular by thoughtbot)npm run dev
,composer run-script dev
(language-specific)
It doesn’t really matter, but what does matter is having a common interface for your developers. If, like us, your team works on many different projects, having a single command to run for every project keeps things easy.
We always turn to Makefiles for this.
Create a dev
make target that wraps whatever setup procedure you need:
.PHONY: dev
dev:
./bin/dev.sh
.PHONY: dev
dev:
npm install
run dev
It doesn’t matter how your setup script works, the only thing developers need to know is make dev
.
Break the script into discrete sections
A single script that sets up everything is great, until it breaks! Splitting the script into sections makes the whole setup resilient to failure, even if a part of it is broken.
.PHONY: dev
dev: packages docker-start server
.PHONY: packages
packages:
pip install -r requirements.txt
npm install
.PHONY: docker-start
docker-start:
docker-compose up -d
.PHONY: docker-stop
docker-stop:
docker-compose stop
.PHONY: server
server:
./bin/server.py
It also makes the workflow more flexible for developers. With these new targets, the developer can choose to just reset the database while keeping the server running:
make dev
# Oh, I need to reset the database!
make docker-stop docker-start
Make targets are also discoverable.
make [TAB]
will often work out of the box on MacOS and Linux.
For bonus points, add a help
target at the top of the file:
.PHONY: help
help:
@echo 'Usage: make [target]'
@echo 'Available targets:'
@echo
@grep -Eo '^[-a-z]+' Makefile | sort
This will list all the targets in the Makefile when a developer types make
.
Make sure the regex used for grep -E
matches your target names - here we’re using kebab-case
.
Guide them through the manual steps
Sometimes you can’t automate absolutely everything - there may be manual steps involved, like adding keys to an external system, grabbing OAuth credentials, etc.
Dan Slimmon suggested an approach in Do-nothing scripting that elegantly solves the problem - simply print the manual instructions to the terminal and wait for the user to confirm.
Dan uses Python for his setup script. Here’s a simple bash implementation:
#!/bin/bash
set -eu
wait() {
echo
echo $1
read -p "Press [enter] when ready. "
}
make packages docker-start
wait "Make sure you have an account at https://example.com."
wait "Go to https://example.com/oauth and create a new set of client credentials, then add them to '.env'."
make server
echo
echo "Ready! Go to http://localhost:8000 to get started."
echo
The beauty of this approach is the ability to combine automated and manual steps in one process. Over time, these manual steps can be automated, but the overall workflow will never change.
Compared to a README or wiki, a guided setup script has the advantage of always being up to date. When used every day, a developer will immediately notice if a manual setup instruction is wrong or outdated.
Making it friendly
Thoughtbot have some excellent tips in Shell Script Suggestions for Speedy Setups to make your setup script as friendly as possible. Top tips include:
- Highlight things with colours, line breaks, and emojis 🎉
- Have the script check that dependencies are installed, such as
python
ordocker
. Follow the rule of silence. Use
@
to apply this to your make targets:.PHONY: packages packages: # Use '@' before a command to hide it from the output @pip install -r requirements.txt @npm install
Do you have any setup tips to share? Let us know on twitter!