Dagger Actions
This documentation is for an older version of Dagger, which is no longer actively maintained.
We encourage you to upgrade and refer to the documentation for the most current version.
If you cannot upgrade to the latest version, please contact us in the help forum on Discord. When contacting us, please let us know why you cannot move to the latest version. From there, our team will work with you on your use case.
Actions are the basic building block of the Dagger platform. An action encapsulates an arbitrarily complex automation into a simple software component that can be safely shared, and repeatably executed by any Dagger engine.
Actions can be executed directly with dagger do
, or integrated as a component of a more complex action.
There are two types of actions: core actions and composite actions.
Core Actions
Core Actions are primitives implemented by the Dagger Engine itself. They can be combined into higher-level composite actions. Their definitions can be imported in the dagger.io/dagger/core
package.
To learn more about core actions, see the core action reference.
Composite Actions
Composite Actions are actions made of other actions. Dagger supports arbitrary nesting of actions, so a composite action can be assembled from any combination of core and composite actions.
One consequence of arbitrary nesting is that Dagger doesn't need to distinguish between "pipelines" and "steps": everything is an action. Some actions are just more complex and powerful than others. This is a defining feature of Dagger.
Universe packages
Universe packages are ready-to-use composite actions with embedded domain logic.
They are abstractions on top of other actions, aiming to:
- Promote code reusability
- Enforce good practices
- Abstract complexity
For example, the netlify package is based on the docker and the bash actions. It is a composite action that we rely on internally whenever we need to deploy code on this platform.
Lifecycle of an Action
A composite action's lifecycle has 4 stages:
- Definition
- Integration
- Discovery
- Execution
Definition
A new action is defined in a declarative template called a CUE definition. This definition describes the action's inputs, outputs and its domain logic.
Here is an example of a simple action definition:
package main
import (
"dagger.io/dagger"
"dagger.io/dagger/core"
)
// Write a greeting to a file, and add it to a directory
#AddHello: {
// The input directory
dir: dagger.#FS
// The name of the person to greet
name: string | *"world"
write: core.#WriteFile & {
input: dir
path: "hello-\(name).txt"
contents: "hello, \(name)!"
}
// The directory with greeting message added
result: write.output
}
Note the free-form structure: an action definition is not structured by a rigid schema. It is simply a CUE struct with fields of various types.
- "inputs" are simply fields which are not complete, and therefore can receive an external value at integration. For example,
dir
andname
are inputs. - "outputs" are simply fields which produce a value that can be referenced externally at integration. For example,
result
is an output. - The "domain logic" is implemented via the wiring of any number of sub-actions. Sub-actions are pre-existing core actions, universe packages or any composite actions containing parts of the domain logic.
For example, this composite action includes one sub-action: core.#WriteFile
, at the heart of the domain logic of #AddHello
, and referenced by the write
field.
There are no constraints to an action's field names or types.
Integration
Action definitions cannot be executed directly: they must be integrated into a plan.
A plan is an execution context for actions. It specifies:
- What actions to present to the end user
- Dependencies between those tasks, if any
- Interactions between the tasks and the client system, if any
Actions are integrated into a plan by merging their CUE definition into the plan's CUE definition.
Here is an example of a plan:
package main
import (
"dagger.io/dagger"
)
dagger.#Plan & {
// Say hello by writing to a file
actions: hello: #AddHello & {
dir: client.filesystem.".".read.contents
}
client: filesystem: ".": {
read: contents: dagger.#FS
write: contents: actions.hello.result
}
}
Note that #AddHello
was integrated directly into the plan, whereas core.#WriteFile
was integrated indirectly, by virtue of being a sub-action of #AddHello
.
To learn more about the structure of a plan, see it all begins with a plan.
Discovery
Once integrated into a plan, actions can be discovered by end users, by using the familiar convention of usage messages:
$ dagger do --help
Execute a dagger action.
Available Actions:
hello Say hello by writing to a file
Usage:
dagger do [OPTIONS] ACTION [SUBACTION...] [flags]
Flags:
[...]
Execution
Once the end user has discovered the action that they need, they can execute it with dagger do
. For example:
dagger do hello
Example
Given this representative CUE plan:
dagger.#Plan & {
actions: {
build: {...}
deploy: {
local: {...}
cloud: {...}
}
}
}
Running:
dagger do build
will run thebuild
actiondagger do deploy
will run both thelocal
andcloud
actionsdagger do deploy local
will run thelocal
sub-actiondagger do deploy cloud
will run thecloud
sub-action
If you specify the key path to an action regrouping several sub-actions, all of the sub-actions will run. When you specify the key path to a single action/sub-action, only one will run.
There is no depth limit to the key path you specify: it can be useful for debugging purposes.