docs: Link to markdown files
4.3 KiB
slug | displayed_sidebar |
---|---|
/1221/action | europa |
Dagger Actions
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.
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, sub-actions, and the wiring between them.
Here is an example of a simple action definition:
package hello
import (
"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 that this action includes one sub-action: core.#WriteFile
. An action can incorporate any number of sub-actions.
Also 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. - "sub-actions" are simply fields which contain another action definition. For example,
write
is a sub-action.
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