Defining and executing quick commmands

When creating Docker files, writing blog posts, or working on my home automation server there are a number of repetitve tasks that you find yourself executing. For testing dockerfiles I run the docker build -t <tagname> . over and over again. Another example is blog regeneration. To rebuild my blog I run a custom shell script as shown below:

cd ~/repos/blog
git pull
cp ./assets/profile.jpg _site/profile.jpg
touch _site/index.html
echo "<html><h1>Site is being rebuilt</h1><p>Woah, you really caught me at the wrong time... my blog is currently being regenerated because I made some updates. Check back in 1 or 2 minutes. </p></html>" > _site/index.html
docker start jekyll-build
docker exec jekyll-build jekyll build -t JEKYLL_ENV=production
docker stop jekyll-build

It is easy enough to use the Up arrow key and hit enter to run a command again, however a few days down the track I may not have access to that command anymore. To solve this developer issue, I started saving repetitive commands (like the docker build and blog rebuild commands) in shell script files, which means a simple bash build.sh can rebuild a Docker image for me.

The upshot of this method are commands are version controlled and stored in repository (useful for other people when repo is cloned). Still, the method is not quite what I am looking for because it creates a many different bash scripts in the root of the repository, each containing only a few statements. Since these are all somehow build related, I was wondering if there is a simple build tool that would allow me to specify simple tasks in the same file and allow me to run them easily.

NPM itself includes such a tool where you specify npm scripts in the package.json file. This does require the installation of npm though which is why I looked for a pure Bash solution.

Introducing stylemistake/runner a bash script that presents a solution to my problems:

  • ability to define simple tasks or sequences of commands using shell commands
  • stores all tasks in a single runnerfile
  • can be executed easily by calling runner <taskname> or bash runnerfile.sh <taskname>
  • will save you a ton of time (and headaches) running repetitive tasks and looking for those perfect commands you figured out weeks ago but that got hopelessly lost in your ``.bash_history` file… never to be found again.

Runner ships as a single bash file. The documentation shows different ways of installing the script, which include using a git module (automatic updates) or including the runner.sh directly in your repository. For the RF Gateway Docker images I went with the git module approach, combined with the standalone runnerfile.sh script. This packages the runnerfile.sh script as a standalone script that you can run by passing in the task name.

You can rename the script to whatever you want. I got tired of typing bash runnerfile.sh hello real quick and renamed all my Runnerfiles to run.sh.

Example Runnerfile


#!/usr/bin/env bash
cd "$(dirname "$0")" || exit
source ./runner/src/runner.sh


task_hello() {
    echo "Hello"
}

We can execute the hello task by running bash runnderfile.sh hello, which prints the following:

[16:46:31.161] Starting 'hello'...
Hello
[16:46:31.208] Finished 'hello' after 12 ms

Neat.