Simple, lightweight task runner for Bash.
Inspired by Gulp version 4.
Script is still early and incomplete. If you find any bugs, let me know by creating an issue.
Runner depends on:
bash >=4.0
coreutils >=8.0
These are very likely to be already installed on your system.
Note for Mac OS X users:
Use Homebrew to install the dependencies:
brew install bash coreutils
There are many different ways to use runner
in your project.
Using npm
:
## To install to your project folder
npm install --save-dev bash-task-runner
## To install globally
npm install -g bash-task-runner
Alternatively, you can simply download the runner.sh
file (from src
folder)
and place it inside your project folder.
Create an empty bash script (runnerfile.sh
), which is an entry point for the
task runner.
Optional: If you want the runnerfile.sh
to be a task runner itself, add
this to the beginning of the script:
#!/bin/bash
cd `dirname ${0}`
source <path_to>/runner.sh
Create some tasks:
task_foo() {
## Do something...
}
task_bar() {
## Do something...
}
Then you can run tasks with the runner
tool:
$ runner foo bar
[23:43:37.754] Starting 'foo'
[23:43:37.755] Finished 'foo' after 1 ms
[23:43:37.756] Starting 'bar'
[23:43:37.757] Finished 'bar' after 1 ms
Or in case your script sources the task runner:
$ bash runnerfile.sh foo bar
You can define a default task. It will run if no arguments were provided to the task runner:
task_default() {
## Do something...
}
You can change which task is default:
runner_set_default_task foo
Tasks can launch other tasks in two ways: sequentially and in parallel. This way you can optimize the task flow for maximum concurrency.
To run tasks sequentially, use:
task_default() {
runner_sequence foo bar
## [23:50:33.194] Starting 'foo'
## [23:50:33.195] Finished 'foo' after 1 ms
## [23:50:33.196] Starting 'bar'
## [23:50:33.198] Finished 'bar' after 2 ms
}
To run tasks in parallel, use:
task_default() {
runner_parallel foo bar
## [23:50:33.194] Starting 'foo'
## [23:50:33.194] Starting 'bar'
## [23:50:33.196] Finished 'foo' after 2 ms
## [23:50:33.196] Finished 'bar' after 2 ms
}
Sometimes you need to stop the whole task if some of the commands fails. You can achieve this with a simple conditional return:
task_foo() {
...
php composer.phar install || return
...
}
If a failed task was a part of a sequence, the whole sequence fails. Same applies to the tasks running in parallel.
The difference in runner_parallel
is if an error occurs in one of the tasks,
other tasks continue to run. After all tasks finish, it returns 0
if
none have failed, 41
if some have failed and 42
if all have failed.
All flags you pass to the script are passed to your tasks.
$ runner foo --production
task_foo() {
echo ${@} # --production
}
This example is a real world script, which automates the initial setup of Laravel project environment:
#!/bin/bash
cd `dirname ${0}`
source runner.sh
NPM_GLOBAL_PACKAGES="gulp bower node-gyp"
task_php() {
if [[ ! -e "composer.phar" ]]; then
php -r "readfile('https://getcomposer.org/installer');" | php || return
fi
php composer.phar install || return
if [[ ! -e ".env" ]]; then
cp .env.example .env
php artisan key:generate
fi
}
task_node() {
if [ -e "${HOME}/.nvm/nvm.sh" ]; then
if ! runner_is_defined ${NPM_GLOBAL_PACKAGES}; then
npm install -g ${NPM_GLOBAL_PACKAGES} || return
fi
fi
runner_parallel node_{npm,bower}
}
task_node_npm() {
if [[ "${1}" == "--virtualbox" ]]; then
local npm_options="--no-bin-links"
runner_log_warning "Using npm options: ${npm_options}"
fi
npm install ${npm_options}
}
task_node_bower() {
bower install
}
task_default() {
runner_parallel php node || return
if ! runner_is_defined ${NPM_GLOBAL_PACKAGES}; then
runner_log_warning "Please install these packages manually:"
runner_log "'npm install -g ${NPM_GLOBAL_PACKAGES}'"
fi
}
Q: Isn't Bash itself fundamentally a task runner?
Bash is a scripting language, not a task runner by design. My library is a very
thin layer around bash
for declaring tasks, which can be run concurrently and
individually, from CLI. It doesn't restrict you in any way, you write a script
as you always do.
Q: Why should i use it?
- This library is very lightweight and easy to bundle within your project.
- When you got nothing else, but
bash
. - If you already know
bash
, there is no new syntax to learn! - You can run tasks in parallel. Even single core systems may benefit from this, especially if tasks are more IO oriented.
Q: Why not to use make
/ ant
/ ...?
ant
has a benefit of being cross-platform (it has built-in coreutils-like features and some others), and many implementations exist; but if you need something outside the box, you end up tying everything to existing OS infrastructure anyway; it also has the drawback of using XML - you write a lot just to do something simple.make
is awesome, because it uses a very compact syntax and does a lot under the hood, but it's very opinionated about it, so if you need to run a generic script, you call it frommake
. If all you do is run scripts, then whymake
?- Yet another dependency. If you're already using many build tools in your project, another one will just add more confusion.
I would also like to mention Manuel, which is a similar task runner for
bash
. Feel free to check it out, too.
Please provide pull requests in a separate branch (other than master
), this
way it's more manageable for me to review and pull.
Before writing code, open an issue to get initial feedback and resolve potential problems. Write all feature related comments there, not into pull-request.
This software is covered by LGPL-3 license.
Style Mistake <[email protected]>