Skip to main content

It all starts with a plan

A CI/CD pipeline declared in Dagger starts with a plan, specifically dagger.#Plan

This plan is the entrypoint for everything that runs within a pipeline. The simplest plan will usually:

  • interact with the client filesystem to read (e.g. source code) or write files (e.g. build output)
  • read environment variables
  • declare a few actions, e.g. deps, test & build

This is our Getting Started example app plan structure:

dagger.#Plan & {
client: {
filesystem: {
// ...
}
env: {
// ...
}
}
actions: {
deps: docker.#Build & {
// ...
}
test: bash.#Run & {
// ...
}
build: {
run: bash.#Run & {
// ...
}
contents: dagger.#Subdir & {
// ...
}
}
}
}

When the above plan gets executed via dagger do build, it produces the following output:

[] client.filesystem.".".read                                    0.0s
[] actions.deps 1.1s
[] actions.test.script 0.0s
[] actions.test 0.0s
[] actions.build.run.script 0.0s
[] actions.build.run 0.0s
[] actions.build.contents 0.0s
[] client.filesystem.build.write 0.1s

Since these actions have run before, they are cached and take less than 1 second to complete.

While the names used for the actions above - deps, test & build - are short & descriptive, any other names would have worked. Put differently, action naming does not affect plan execution.

In the example above, the deps action is an instance of the docker package build definition.

This is written as deps: docker.#Build

Default definition configuration - docker.#Build in this case - can be modified via curly brackets, e.g.

    deps: docker.#Build & {
// ...
}

We can build complex pipelines efficiently by referencing any definition, from any package in our actions. This is one of the fundamental concepts that makes Dagger a powerful language for CI/CD. Before we can use a package in a plan, we need to declare it at the top of the pipeline configuration, like this:

import (
"universe.dagger.io/docker"
)

Since we are using the plan definition from the dagger package - dagger.#Plan - we also need to declare it at the top of the pipeline configuration:

import (
"dagger.io/dagger"
"universe.dagger.io/docker"
)
tip

Now that we understand the basics of a Dagger plan, we are ready to learn more about how to interact with the client environment. This will enable us to configure plans just-in-time, save build artefacts, and perform other interactions with the environment within which Dagger runs.