Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 91 additions & 2 deletions docs/core-concepts/1203-client.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,14 +92,103 @@ Sometimes you need something more advanced that only a local command can give yo

```

```shell title="Output"
➜ dagger do test
[✔] client.commands.arch
[✔] client.commands.os
[✔] actions.test
Field Value
os "Darwin"
arch "x86_64"
```

:::tip

There's a more portable way to find the OS and CPU architecture, just use the [client's platform](#platform).

:::

:::tip
You can also capture `stderr` for errors and provide `stdin` for input.

To learn more about controlling action outputs, see the [Handling action outputs](../guides/actions/1228-handling-outputs.md#controlling-the-output) guide.

:::

### Standard input

If your command needs to read from the standard input stream, you can use `stdin`:

```cue file=../tests/core-concepts/client/plans/cmd_stdin.cue
```

### Capturing errors

:::caution Attention

A failing exit code will fail the plan, so if you need to further debug the cause of a failed command, you can just try running it directly in your computer. Some commands print to `stderr` for messages that aren't fatal. This is for those cases.

:::

If you need the *stderr* output of a command in an action, you can capture it with `stderr`:

```cue file=../tests/core-concepts/client/plans/cmd_stderr.cue
```

```shell title="Output"
Field Value
error "cat: /foobar: No such file or directory"```
```

### Secrets

All input/output streams (`stdout`, `stderr` and `stdin`) accept a `dagger.#Secret` instead of a `string`. You can see a simple example using [SOPS](../core-concepts/1204-secrets.md#sops).

It may be useful to use a secret as an input to a command as well:

```cue file=../tests/core-concepts/client/plans/cmd_secret.cue
```

```shell title="Output"
Field Value
digest "aec070645fe53ee3b3763059376134f058cc337247c978add178b6ccdfb0019f"
```

Another use case is needing to provide a password from input to a command.

## Platform

If you need the current platform though, there’s a more portable way than running `uname` like in the previous example:
If you need the current platform, there’s a more portable way than running the `uname` command:

```cue file=../tests/core-concepts/client/plans/platform.cue
```

```shell title="dagger --log-format plain do test"
INFO actions.test._run._exec | #4 0.209 Platform: darwin / amd64
```

:::tip Remember

This is the platform where the `dagger` binary is being run (a.k.a *client*), which is different from the environment where the action is actually run (i.e., BuildKit, a.k.a *server*).

:::

:::tip

If `client: _` confuses you, see [Use top to match anything](../guidelines/1226-coding-style.md#use-top-to-match-anything).

:::

:::tip

You can see an example of this being used in our own [CI dagger plan](https://github.com/dagger/dagger/blob/main/ci.cue) in the build action, to specify the `os` and `arch` fields in [`go.#Build`](https://github.com/dagger/dagger/blob/main/pkg/universe.dagger.io/go/build.cue):

```cue
build: go.#Build & {
source: _source
os: client.platform.os
arch: client.platform.arch
...
}
```

:::
26 changes: 22 additions & 4 deletions docs/tests/core-concepts/client/plans/cmd.cue
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
package main

import (
"strings"
"dagger.io/dagger"
"dagger.io/dagger/core"
)

dagger.#Plan & {
client: commands: {
os: {
// notice: this command isn't available on Windows
name: "uname"
args: ["-s"]
}
Expand All @@ -10,9 +19,18 @@ dagger.#Plan & {
}
}

actions: build: go.#Build & {
os: client.commands.os.stdout
arch: client.commands.arch.stdout
// ...
actions: test: {
// using #Nop because we need an action for the outputs
_os: core.#Nop & {
// command outputs usually add a new line, you can trim it
input: strings.TrimSpace(client.commands.os.stdout)
}
_arch: core.#Nop & {
// we access the command's output via the `stdout` field
input: strings.TrimSpace(client.commands.arch.stdout)
}
// action outputs for debugging
os: _os.output
arch: _arch.output
}
}
32 changes: 32 additions & 0 deletions docs/tests/core-concepts/client/plans/cmd_secret.cue
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package main

import (
"strings"
"dagger.io/dagger"
"dagger.io/dagger/core"
)

dagger.#Plan & {
client: {
env: PRIVATE_KEY: string | *"/home/user/.ssh/id_rsa"
commands: {
pkey: {
name: "cat"
args: [env.PRIVATE_KEY]
stdout: dagger.#Secret
}
digest: {
name: "openssl"
args: ["dgst", "-sha256"]
stdin: pkey.stdout // a secret
}
}
}

actions: test: {
_op: core.#Nop & {
input: strings.TrimSpace(client.commands.digest.stdout)
}
digest: _op.output
}
}
25 changes: 25 additions & 0 deletions docs/tests/core-concepts/client/plans/cmd_stderr.cue
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package main

import (
"strings"
"dagger.io/dagger"
"dagger.io/dagger/core"
)

dagger.#Plan & {
client: commands: cat: {
name: "sh"
// simulate error output without failed exit status
flags: "-c": """
cat /foobar
echo ok
"""
}

actions: test: {
_op: core.#Nop & {
input: strings.TrimSpace(client.commands.cat.stderr)
}
error: _op.output
}
}
23 changes: 23 additions & 0 deletions docs/tests/core-concepts/client/plans/cmd_stdin.cue
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package main

import (
"strings"
"dagger.io/dagger"
"dagger.io/dagger/core"
)

dagger.#Plan & {
client: commands: rev: {
// Print stdin in reverse
// Same as `rev <(echo olleh)` or `echo olleh | rev`
name: "rev"
stdin: "olleh"
}

actions: test: {
_op: core.#Nop & {
input: strings.TrimSpace(client.commands.rev.stdout)
}
verify: _op.output & "hello"
}
}
14 changes: 10 additions & 4 deletions docs/tests/core-concepts/client/plans/platform.cue
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
package main

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

dagger.#Plan & {
client: _

actions: build: go.#Build & {
os: client.platform.os
arch: client.platform.arch
// ...
actions: test: python.#Run & {
script: contents: "print('Platform: \(client.platform.os) / \(client.platform.arch)')"
always: true
}
}