Dagger 0.8: Big Summer Clean Up

August 3, 2023

Aug 3, 2023

Share
Share
Share
Share

We're continuously improving and adding features to Dagger and occasionally, those improvements require us to make breaking changes to the Dagger API. Dagger 0.8 introduces several such breaking changes and this blog post explains the most important ones.

Be sure to consult the Dagger 0.8 release notes for the complete list of changes, breaking and otherwise, and to the update instructions for information on how to update to Dagger 0.8.

Container.exitCode API field removed

Dagger 0.8 removes the Container.exitCode field. As a result, command execution status must now be retrieved with ExecError in the SDKs:

Additional information

Using the exit code of a command to check if it failed or not never worked. Consider the following Go example code:

exitCode, err := ctr.ExitCode(ctx) if err != nil { return err } if exitCode != 0 { // ❌ Never called!! 😕 fmt.Println("FAILED") } else { fmt.Println("PASSED") } return nil

When a command fails, the API returns an error and a null result (example). So, in the previous example, .ExitCode(ctx) would always return 0 (zero) on success, but nil on failure. However, the actual exit code value of the failed command is in the error itself.

The recommended approach is to check dagger.ExecError to specifically handle errors from an exec operation. The ExecError struct gives easy access to various properties related to the failure.

Here's a revised Go example that demonstrates this:

_, err := ctr.Sync(ctx) if err != nil { var e *dagger.ExecError if errors.As(err, &e) { fmt.Println("FAILED") return nil } return err } fmt.Println("PASSED") return nil

Note: If your container doesn't define any WithExec() operation, Sync() won’t execute the container’s entrypoint and/or default arguments like Stdout and Stderr. You need to add an empty WithExec(nil) for that. However, it’s equally useful if that’s actually what you want - for example, building from a Dockerfile without executing the defined ENTRYPOINT or CMD.

In Node.js and Python exceptions are used instead. For example, here's how it looks in Node.js:

try { await ctr.sync() } catch (e) { if (e instanceof ExecError) { console.log("FAILED") return } throw e } console.log("PASSED")

And in Python:

try: await ctr.sync() except dagger.ExecError: print("FAILED") else: print("PASSED")

Refer to the Dagger Cookbook for more examples of error handling with Dagger.

For more information on this breaking change, refer to the following GitHub issue:

Host.envVariable API field removed

Reading environment variables from the host with Host.envVariable is no longer supported. Use the programming language's built-in features for this instead:

  • Go: Replace Host().EnvVariable() with os.Getenv

  • Python: Replace Host().env_variable() with os.getenv

  • Node.js: Replace Host().envVariable() with process.env

Additional information

Host.envVariable was previously required to turn an environment variable into a secret (with HostVariable.secret, also removed). This is no longer necessary and to avoid confusion, it has been removed in favor of using the language's native ability to read environment variables.

For more information on this breaking change, refer to the following GitHub discussion:

Other deprecated API fields removed

Dagger 0.8 removes the following API fields, deprecated since Dagger 0.3.6:

Dagger 0.8 also removes the following secret related API fields, deprecated since Dagger 0.5.0:

For more information on these breaking changes, refer to the following GitHub issue:

Optional keyword arguments enforced in the Python SDK

Dagger 0.8 now requires the use of keyword arguments when they’re optional.

Additional information

All API field arguments in Python are grouped, with required arguments first and optional arguments later. Until now, you could choose to use either a positional or keyword argument. With Dagger 0.8, you need to use only keyword arguments when they’re optional. For example:

# ❌ this will now fail ctr.with_exec(["foobar"], None, None, "/out/stderrr") # ✅ use this instead ctr.with_exec(["foobar"], redirect_stderr="/out/stderr")

This improves readability and is incidentally more consistent with the Node.js and Go SDKs, since they both use a separate data structure for the optional arguments.

For more information on this breaking change, refer to the following GitHub issue:

Synchronous client removed from the Python SDK

Dagger 0.8 removes SyncClient and supporting code.

Follow the instructions on how to migrate to the asynchronous client, and refer to the resources section to learn more.

Additional information

The Python SDK used to have two clients: Client (asynchronous, supporting coroutines) and SyncClient (synchronous, supporting only regular functions). This led to confusion, fragmentation and a higher maintenance burden.

Most importantly, non-blocking I/O works best with Dagger, so the synchronous client had a big performance bottleneck and we didn’t see any usage or demand to justify keeping it.

For more information on this breaking change, refer to the following GitHub issue:

Client export modified in the Node.js SDK

Dagger 0.8 makes Client a named export (instead of default) in TypeScript.

Additional information

Use the example below as a reference to update your Dagger pipelines written in TypeScript:

// ❌ this will now fail import Client, { connect } from "@dagger.io/dagger" // ✅ use this instead import { connect, Client } from "@dagger.io/dagger" connect(async (client: Client) => { // ... })

For more information on this breaking change, refer to the following GitHub issue:

Other changes

There are various other changes, bug fixes and improvements in this release. For a complete list, refer to the respective release notes:

As you are reading this, there is likely to be a 0.8 patch release. To benefit from all bug fixes, please remember to upgrade to the latest 0.8 patch release.

It's also worth pointing out that Dagger 0.8 brings all SDKs together under the same version number, matching the Engine and CLI. This bump will make it easier to know which versions are compatible to update together. Learn more about updating Dagger.

If you get stuck or have additional questions about this release, please reach out to us through this 🐙 GitHub Discussion

We look forward to releasing more features soon! In the meanwhile, if you have general feedback or would like to suggest new features or documentation, let us know on Discord or create a GitHub issue.

We're continuously improving and adding features to Dagger and occasionally, those improvements require us to make breaking changes to the Dagger API. Dagger 0.8 introduces several such breaking changes and this blog post explains the most important ones.

Be sure to consult the Dagger 0.8 release notes for the complete list of changes, breaking and otherwise, and to the update instructions for information on how to update to Dagger 0.8.

Container.exitCode API field removed

Dagger 0.8 removes the Container.exitCode field. As a result, command execution status must now be retrieved with ExecError in the SDKs:

Additional information

Using the exit code of a command to check if it failed or not never worked. Consider the following Go example code:

exitCode, err := ctr.ExitCode(ctx) if err != nil { return err } if exitCode != 0 { // ❌ Never called!! 😕 fmt.Println("FAILED") } else { fmt.Println("PASSED") } return nil

When a command fails, the API returns an error and a null result (example). So, in the previous example, .ExitCode(ctx) would always return 0 (zero) on success, but nil on failure. However, the actual exit code value of the failed command is in the error itself.

The recommended approach is to check dagger.ExecError to specifically handle errors from an exec operation. The ExecError struct gives easy access to various properties related to the failure.

Here's a revised Go example that demonstrates this:

_, err := ctr.Sync(ctx) if err != nil { var e *dagger.ExecError if errors.As(err, &e) { fmt.Println("FAILED") return nil } return err } fmt.Println("PASSED") return nil

Note: If your container doesn't define any WithExec() operation, Sync() won’t execute the container’s entrypoint and/or default arguments like Stdout and Stderr. You need to add an empty WithExec(nil) for that. However, it’s equally useful if that’s actually what you want - for example, building from a Dockerfile without executing the defined ENTRYPOINT or CMD.

In Node.js and Python exceptions are used instead. For example, here's how it looks in Node.js:

try { await ctr.sync() } catch (e) { if (e instanceof ExecError) { console.log("FAILED") return } throw e } console.log("PASSED")

And in Python:

try: await ctr.sync() except dagger.ExecError: print("FAILED") else: print("PASSED")

Refer to the Dagger Cookbook for more examples of error handling with Dagger.

For more information on this breaking change, refer to the following GitHub issue:

Host.envVariable API field removed

Reading environment variables from the host with Host.envVariable is no longer supported. Use the programming language's built-in features for this instead:

  • Go: Replace Host().EnvVariable() with os.Getenv

  • Python: Replace Host().env_variable() with os.getenv

  • Node.js: Replace Host().envVariable() with process.env

Additional information

Host.envVariable was previously required to turn an environment variable into a secret (with HostVariable.secret, also removed). This is no longer necessary and to avoid confusion, it has been removed in favor of using the language's native ability to read environment variables.

For more information on this breaking change, refer to the following GitHub discussion:

Other deprecated API fields removed

Dagger 0.8 removes the following API fields, deprecated since Dagger 0.3.6:

Dagger 0.8 also removes the following secret related API fields, deprecated since Dagger 0.5.0:

For more information on these breaking changes, refer to the following GitHub issue:

Optional keyword arguments enforced in the Python SDK

Dagger 0.8 now requires the use of keyword arguments when they’re optional.

Additional information

All API field arguments in Python are grouped, with required arguments first and optional arguments later. Until now, you could choose to use either a positional or keyword argument. With Dagger 0.8, you need to use only keyword arguments when they’re optional. For example:

# ❌ this will now fail ctr.with_exec(["foobar"], None, None, "/out/stderrr") # ✅ use this instead ctr.with_exec(["foobar"], redirect_stderr="/out/stderr")

This improves readability and is incidentally more consistent with the Node.js and Go SDKs, since they both use a separate data structure for the optional arguments.

For more information on this breaking change, refer to the following GitHub issue:

Synchronous client removed from the Python SDK

Dagger 0.8 removes SyncClient and supporting code.

Follow the instructions on how to migrate to the asynchronous client, and refer to the resources section to learn more.

Additional information

The Python SDK used to have two clients: Client (asynchronous, supporting coroutines) and SyncClient (synchronous, supporting only regular functions). This led to confusion, fragmentation and a higher maintenance burden.

Most importantly, non-blocking I/O works best with Dagger, so the synchronous client had a big performance bottleneck and we didn’t see any usage or demand to justify keeping it.

For more information on this breaking change, refer to the following GitHub issue:

Client export modified in the Node.js SDK

Dagger 0.8 makes Client a named export (instead of default) in TypeScript.

Additional information

Use the example below as a reference to update your Dagger pipelines written in TypeScript:

// ❌ this will now fail import Client, { connect } from "@dagger.io/dagger" // ✅ use this instead import { connect, Client } from "@dagger.io/dagger" connect(async (client: Client) => { // ... })

For more information on this breaking change, refer to the following GitHub issue:

Other changes

There are various other changes, bug fixes and improvements in this release. For a complete list, refer to the respective release notes:

As you are reading this, there is likely to be a 0.8 patch release. To benefit from all bug fixes, please remember to upgrade to the latest 0.8 patch release.

It's also worth pointing out that Dagger 0.8 brings all SDKs together under the same version number, matching the Engine and CLI. This bump will make it easier to know which versions are compatible to update together. Learn more about updating Dagger.

If you get stuck or have additional questions about this release, please reach out to us through this 🐙 GitHub Discussion

We look forward to releasing more features soon! In the meanwhile, if you have general feedback or would like to suggest new features or documentation, let us know on Discord or create a GitHub issue.

We're continuously improving and adding features to Dagger and occasionally, those improvements require us to make breaking changes to the Dagger API. Dagger 0.8 introduces several such breaking changes and this blog post explains the most important ones.

Be sure to consult the Dagger 0.8 release notes for the complete list of changes, breaking and otherwise, and to the update instructions for information on how to update to Dagger 0.8.

Container.exitCode API field removed

Dagger 0.8 removes the Container.exitCode field. As a result, command execution status must now be retrieved with ExecError in the SDKs:

Additional information

Using the exit code of a command to check if it failed or not never worked. Consider the following Go example code:

exitCode, err := ctr.ExitCode(ctx) if err != nil { return err } if exitCode != 0 { // ❌ Never called!! 😕 fmt.Println("FAILED") } else { fmt.Println("PASSED") } return nil

When a command fails, the API returns an error and a null result (example). So, in the previous example, .ExitCode(ctx) would always return 0 (zero) on success, but nil on failure. However, the actual exit code value of the failed command is in the error itself.

The recommended approach is to check dagger.ExecError to specifically handle errors from an exec operation. The ExecError struct gives easy access to various properties related to the failure.

Here's a revised Go example that demonstrates this:

_, err := ctr.Sync(ctx) if err != nil { var e *dagger.ExecError if errors.As(err, &e) { fmt.Println("FAILED") return nil } return err } fmt.Println("PASSED") return nil

Note: If your container doesn't define any WithExec() operation, Sync() won’t execute the container’s entrypoint and/or default arguments like Stdout and Stderr. You need to add an empty WithExec(nil) for that. However, it’s equally useful if that’s actually what you want - for example, building from a Dockerfile without executing the defined ENTRYPOINT or CMD.

In Node.js and Python exceptions are used instead. For example, here's how it looks in Node.js:

try { await ctr.sync() } catch (e) { if (e instanceof ExecError) { console.log("FAILED") return } throw e } console.log("PASSED")

And in Python:

try: await ctr.sync() except dagger.ExecError: print("FAILED") else: print("PASSED")

Refer to the Dagger Cookbook for more examples of error handling with Dagger.

For more information on this breaking change, refer to the following GitHub issue:

Host.envVariable API field removed

Reading environment variables from the host with Host.envVariable is no longer supported. Use the programming language's built-in features for this instead:

  • Go: Replace Host().EnvVariable() with os.Getenv

  • Python: Replace Host().env_variable() with os.getenv

  • Node.js: Replace Host().envVariable() with process.env

Additional information

Host.envVariable was previously required to turn an environment variable into a secret (with HostVariable.secret, also removed). This is no longer necessary and to avoid confusion, it has been removed in favor of using the language's native ability to read environment variables.

For more information on this breaking change, refer to the following GitHub discussion:

Other deprecated API fields removed

Dagger 0.8 removes the following API fields, deprecated since Dagger 0.3.6:

Dagger 0.8 also removes the following secret related API fields, deprecated since Dagger 0.5.0:

For more information on these breaking changes, refer to the following GitHub issue:

Optional keyword arguments enforced in the Python SDK

Dagger 0.8 now requires the use of keyword arguments when they’re optional.

Additional information

All API field arguments in Python are grouped, with required arguments first and optional arguments later. Until now, you could choose to use either a positional or keyword argument. With Dagger 0.8, you need to use only keyword arguments when they’re optional. For example:

# ❌ this will now fail ctr.with_exec(["foobar"], None, None, "/out/stderrr") # ✅ use this instead ctr.with_exec(["foobar"], redirect_stderr="/out/stderr")

This improves readability and is incidentally more consistent with the Node.js and Go SDKs, since they both use a separate data structure for the optional arguments.

For more information on this breaking change, refer to the following GitHub issue:

Synchronous client removed from the Python SDK

Dagger 0.8 removes SyncClient and supporting code.

Follow the instructions on how to migrate to the asynchronous client, and refer to the resources section to learn more.

Additional information

The Python SDK used to have two clients: Client (asynchronous, supporting coroutines) and SyncClient (synchronous, supporting only regular functions). This led to confusion, fragmentation and a higher maintenance burden.

Most importantly, non-blocking I/O works best with Dagger, so the synchronous client had a big performance bottleneck and we didn’t see any usage or demand to justify keeping it.

For more information on this breaking change, refer to the following GitHub issue:

Client export modified in the Node.js SDK

Dagger 0.8 makes Client a named export (instead of default) in TypeScript.

Additional information

Use the example below as a reference to update your Dagger pipelines written in TypeScript:

// ❌ this will now fail import Client, { connect } from "@dagger.io/dagger" // ✅ use this instead import { connect, Client } from "@dagger.io/dagger" connect(async (client: Client) => { // ... })

For more information on this breaking change, refer to the following GitHub issue:

Other changes

There are various other changes, bug fixes and improvements in this release. For a complete list, refer to the respective release notes:

As you are reading this, there is likely to be a 0.8 patch release. To benefit from all bug fixes, please remember to upgrade to the latest 0.8 patch release.

It's also worth pointing out that Dagger 0.8 brings all SDKs together under the same version number, matching the Engine and CLI. This bump will make it easier to know which versions are compatible to update together. Learn more about updating Dagger.

If you get stuck or have additional questions about this release, please reach out to us through this 🐙 GitHub Discussion

We look forward to releasing more features soon! In the meanwhile, if you have general feedback or would like to suggest new features or documentation, let us know on Discord or create a GitHub issue.

Get Involved With the community

Discover what our community is doing, and join the conversation on Discord & GitHub to help shape the evolution of Dagger.

Subscribe to our newsletter

Get Involved With the community

Discover what our community is doing, and join the conversation on Discord & GitHub to help shape the evolution of Dagger.

Subscribe to our newsletter

Get Involved With the community

Discover what our community is doing, and join the conversation on Discord & GitHub to help shape the evolution of Dagger.

Subscribe to our newsletter