One task per job

I care for the ability to keep my build and deploy config under version control (just like application source code and config). I also care for the ability to run my builds and deployments outside of Go if needed. Turns out this is pretty straightforward to achieve with Go because Go simply execs its tasks, even in case of first-class support (except for fetch-artifact). As an aside, this is actually one reason why even task-plugins should not be more than exec - get too close and you lose independence.

One useful step along the way is to make sure I don’t inline my build and deployment commands within Go. So each task should just be a script invocation to an externally version controlled script. For example if we use a tool like ant, maven, nant or rake, pretty much every task can be of the form: tool <target> that runs off a version controlled build file.

In general, a job with N tasks can be refactored to a single-task-job that invokes a version controlled script containing N
lines. There is one small hitch though. In Go, a job is the unit of work on the agent. A job can have many tasks, they will be executed in sequence on the same agent. If any task exits with non-zero, the job is failed and subsequent tasks are not executed (except for cancel and fail handlers). This is useful fail-fast behaviour.

However, we lose this behaviour when we move from N tasks to a single-script-
task because by default, a bash script or windows batch script will not stop execution if any of its lines returns non-zero. This can be easily fixed in bash with the set -e command (thanks Arvind). Combine this with the -x option to get even better logging on the Go Job Console!

set -e -x
# rest of the script

There are ways to do this on Windows, please see this discussion on stackoverflow.