Simple YAML-based task runner written in Go.
- Not super slow
- Not super obscure
- No dependencies
- Variables
- Simple
From gobinaries.com:
$ curl -sf https://gobinaries.com/tj/robo | sh
From source:
$ go get github.com/tj/robo
Command-line usage.
Output tasks:
$ robo
aws – amazon web services cli
circle.open – open the repo in circle ci
events – send data to the "events" topic
push – push image from the current directory
Output task help:
$ robo help events
Usage:
events [project-id] [rate]
Description:
send data to the "events" topic
Examples:
Send 25 events a second to gy2d
$ robo events gy2d 25
Regardless of task type (shell, exec, script) any additional arguments given will be passed.
For example suppose you have the following task:
aws:
exec: ssh tools aws
You may then interact with the AWS cli as you would normally:
$ robo aws help
$ robo aws ec2 describe-instances
Task configuration.
The most basic task simply runs a shell command:
hello:
summary: some task
command: echo world
You may also define multi-line commands with YAML's |
:
hello:
summary: some task
command: |
echo hello
echo world
Commands are executed via sh -c
, thus you may use shell features and
positional variables, for example:
hello:
command: echo "Hello ${1:-there}"
Yields:
$ robo hello
Hello there
$ robo hello Tobi
Hello there Tobi
The exec alternative lets you replace the robo image without the fork & shell, however note that shell features are not available (pipes, redirection, etc).
hello:
summary: some task
exec: echo hello
Any arguments given are simply appended.
Shell scripts may be used instead of inline commands:
hello:
summary: some task
script: path/to/script.sh
If the script is executable, it is invoked directly, this allows you
to use #!
:
$ echo -e '#!/usr/bin/env ruby\nputs "yo"' > yo.rb
$ chmod +x yo.rb
$ cat > robo.yml
yo:
summary: yo from rb
script: yo.rb
^C
$ robo yo
yo
Script paths are relative to the config file, not the working directory.
Tasks may optionally specify usage parameters, which display upon help output:
events:
summary: send data to the "events" topic
exec: docker run -it events
usage: "[project-id] [rate]"
Tasks may optionally specify any number of example commands, which display upon help output:
events:
summary: send data to the "events" topic
exec: docker run -it events
usage: "[project-id] [rate]"
examples:
- description: Send 25 events a second to gy2d
command: robo events gy2d 25
Robo supports variables via the text/template package. All you have to do is define a map of variables
and use {{
}}
to refer to them.
Here's an example:
stage:
summary: Run commands against stage.
exec: ssh {{.hosts.stage}} -t robo
prod:
summary: Run commands against prod.
exec: ssh {{.hosts.prod}} -t robo
variables:
hosts:
prod: bastion-prod
stage: bastion-stage
The variables section does also interpolate itself with its own data via {{ .var }}
and allows shell like command
expressions via $(echo true)
to be executed first providing the output result as a variable. Note that variables are
interpolated first and then command expressions are evaluated. This will allow you to reduce repetitive variable definitions and declarations.
hash:
summary: echos the git {{ .branch }} branch's git hash
command: echo {{ .branch }} {{ .githash }}
variables:
branch: master
githash: $(git rev-parse --short {{ .branch }})
Along with your own custom variables, robo defines the following variables:
$ robo variables
robo.file: /Users/amir/dev/src/github.com/tj/robo/robo.yml
robo.path: /Users/amir/dev/src/github.com/tj/robo
user.home: /Users/amir
user.name: Amir Abushareb
user.username: amir
Tasks may define env
key with an array of environment variables, this allows you
to re-use robo configuration files, for example:
// aws.yml
dev:
summary: AWS commands in dev environment
exec: aws
env: ["AWS_PROFILE=eng-dev"]
stage:
summary: AWS commands in stage environment
exec: aws
env: ["AWS_PROFILE=eng-stage"]
prod:
summary: AWS commands in prod environment
exec: aws
env: ["AWS_PROFILE=eng-prod"]
You can also override environment variables:
$ cat > robo.yml
home:
summary: overrides $HOME
exec: echo $HOME
env: ["HOME=/tmp"]
^C
$ robo home // => /tmp
Variables can also be used to set env vars.
$ cat > robo.yml
aws-stage:
summary: AWS stage
exec: aws
env: ["AWS_PROFILE={{.aws.profile}}"]
variables:
aws:
profile: eng-stage
^C
$ robo aws-stage ...
Note that you cannot use shell featurs in the environment key.
Some tasks or even your entire robo configuration may require steps upfront for setup or afterwards for a cleanup. The keywords before
and after
can be embedded into a task or into the overall robo configuration. It has the same executable syntax as a task: script
, exec
and command
.
Defining it on a task level causes the steps to be executed before (respectively after) the task. Global before or after steps are invoked for every task in the configuration.
All steps get interpolated the same way tasks and variables are interpolated.
before:
- command: echo "global before {{ .foo }}"
after:
- script: /global/after-script.sh
foo:
before:
- command: echo "local before {{ .foo }}"
- exec: git pull -r
after:
- command: echo "local after"
- exec: git reset --hard HEAD
exec: git status
variables:
foo: bar
Task list
and help
output may be re-configured, for example if you
prefer to view usage information instead of the summary:
templates:
list: |
{{range .Tasks}} {{cyan .Name}} – {{.Usage}}
{{end}}
Or perhaps something more verbose:
templates:
list: |
{{range .Tasks}}
name: {{cyan .Name}}
summary: {{.Summary}}
usage: {{.Usage}}
{{end}}
By default ./robo.yml
is loaded, however if you want global tasks
you can simply alias to something like:
alias segment='robo --config ~/.robo.yml'
You can easily use Robo to chain Robo, which is useful for multi-environment setups. For example:
prod:
summary: production tasks
exec: robo --config production.yml
stage:
summary: stage tasks
exec: robo --config stage.yml
Or on remote boxes:
prod:
summary: production tasks
exec: ssh prod-tools -t robo --config production.yml
stage:
summary: stage tasks
exec: ssh stage-tools -t robo --config stage.yml
You can also use robo's builtin variables robo.path
, for example
if you put all robofiles in together:
├── dev.yml
├── prod.yml
├── root.yml
└── stage.yml
And you would like to call dev
, prod
and stage
from root
:
dev:
summary: Development commands
exec: robo --config {{ .robo.path }}/dev.yml
stage:
...
You can compose multiple commands into a single command
by utilizing robo's built-in robo.file
variable:
one:
summary: echo one
command: echo one
two:
summary: echo two
command: echo two
all:
summary: echo one two
command: |
robo -c {{ .robo.file }} one
robo -c {{ .robo.file }} two
$ robo all
one
two
We generally use Makefiles for project specific tasks, however the discoverability of global tasks within a large team is difficult unless there's good support for self-documentation, which Make is bad at.
I'm aware of the million other solutions (Sake, Thor, etc) but I just wanted something fast without dependencies.
MIT