Build Plans

Each Job has a single build plan. When a build of a job is created, the plan determines what happens.

A build plan is a sequence of steps to execute. These steps may fetch down or update Resources, or execute Tasks.

A new build of the job is scheduled whenever get steps with trigger: true have new versions available.

To visualize the job in the pipeline, resources that appear as get steps are drawn as inputs, and resources that appear in put steps appear as outputs.

A simple unit test job may look something like:

name: banana-unit
- get: banana
  trigger: true
- task: unit
  file: banana/task.yml

This job says: get the banana resource, and run a task step called unit, using the configuration from the task.yml file fetched from the banana step.

When new versions of banana are detected, a new build of banana-unit will be scheduled, because we've set trigger: true.

Jobs can depend on resources that are produced by or pass through upstream jobs, by configuring passed: [job-a, job-b] on the get step.

Putting these pieces together, if we were to propagate banana from the above example into an integration suite with another apple component (pretending we also defined its apple-unit job), the configuration for the integration job may look something like:

name: fruit-basket-integration
- aggregate:
  - get: banana
    trigger: true
    passed: [banana-unit]
  - get: apple
    trigger: true
    passed: [apple-unit]
  - get: integration-suite
    trigger: true
- task: integration
  file: integration-suite/task.yml

Note the use of the aggregate step to collect multiple inputs at once.

With this example we've configured a tiny pipeline that will automatically run unit tests for two components, and continuously run integration tests against whichever versions pass both unit tests.

This can be further chained into later "stages" of your pipeline; for example, you may want to continuously deliver an artifact built from whichever components pass fruit-basket-integration.

To push artifacts, you would use a put step that targets the destination resource. For example:

name: deliver-food
- aggregate:
  - get: banana
    trigger: true
    passed: [fruit-basket-integration]
  - get: apple
    trigger: true
    passed: [fruit-basket-integration]
  - get: baggy
    trigger: true
- task: shrink-wrap
  file: baggy/shrink-wrap.yml
- put: bagged-food
    bag: shrink-wrap/bagged.tgz

This presumes that there's a bagged-food resource defined, which understands that the bag parameter points to a file to ship up to the resource's location.

Note that both banana and apple list the same job as an upstream dependency. This guarantees that deliver-food will only trigger when a version of both of these dependencies pass through the same build of the integration job (and transitively, their individual unit jobs). This prevents bad apples or bruised bananas from being delivered. (I'm sorry.)

For a reference on each type of step, read on.

  1. get: fetch a resource
  2. put: update a resource
  3. task: execute a task
  4. aggregate: run steps in parallel
  5. do: run steps in series
  6. on_success: perform a step when another succeeds
  7. on_failure: perform a step when another fails
  8. ensure: guarantee execution of a step
  9. tags: perform a step on certain workers
  10. try: permit failure of a step
  11. timeout: enforce a time limit on a step
  12. attempts: retry a step