Dagger 0.6: New Terminal UI
Read more

How we release Dagger

March 21st, 2023By Gerhard Lazu
Photo credits:https://unsplash.com/photos/GDdRP7U5ct0?utm_source=unsplash&utm_medium=referral&utm_content=creditShareLink

At Dagger, CI/CD is something that we think about a lot.

While you can read our new guides, watch community call videos, or even look at how our various repositories use Dagger, this post aims to go beyond that.

We want to share how we think about our own releases, what goes into a release & how they are produced. We will wrap-up this post with the release improvements that we are currently working on.

This is a high-level sequence diagram of how all the pieces fit together:

release diagram

How do we think of Dagger releases?

Our aim is to release as often as necessary, always in small batches - think one or two weeks worth of changes at most. Fixes and small improvements should be available to our users within a handful of days. Few things show that we care about our users' experience more than addressing their issues within days.

We tend to release larger features behind an experimental environment variable flag, e.g. _EXPERIMENTAL_DAGGER_SERVICES_DNS. This enables us to get real-world feedback, work on the feature together with our users, and reduce the risk of breaking things for the unsuspecting. As soon as we have sufficient user feedback, we make it available to everyone else by default, without any experimental opt-in. The biggest advantage is that there is no last-minute integration effort: the feature has been available for many weeks already (just not the default option).

The last significant feature that we have released using this approach is service containers.

What goes into a Dagger release?

We currently release five different artifacts:

  1. Dagger Engine
  2. Dagger CLI
  3. Go SDK
  4. Python SDK
  5. Node.js SDK

The source of truth for all these releases lives in github.com/dagger/dagger. We like this monorepo approach because it keeps things simple.

πŸš™ Dagger Engine & πŸš— CLI

The Dagger Engine & CLI are always produced together, as a single release. This is what the v0.3.13 release looks like:

release view

Under Assets, there is a Dagger CLI for each platform that we support. The Engine is not listed here. It is published as a container image & available via registry.dagger.io. Users don't download this image directly. They set up their preferred SDK as a package dependency in their language of choice, which internally references the dependent CLI version. The Dagger CLI has all the platform-specific code necessary to download and set up the required Engine container image. This is transparent to the end-user.

It is worth mentioning that every commit merged into the main branch publishes a development version of the Engine & CLI. The CLI is available via dl.dagger.io/dagger/main/<GIT_SHA>/dagger_<GIT_SHA>_<OS>_<PLATFORM>.tar.gz. This is useful to anyone that needs to test a fix or feature before we publish a new version with that change.

🐹 Dagger Go SDK

All GitHub releases that start with the sdk/go tag are for our Go SDK. There are no assets, since Go packages are referenced via a URL. These releases are the source of truth for our Go SDK.

Our users are encouraged to use the dagger.io/dagger package which resolves to the dagger/dagger-go-sdk read-only repository replica - a limitation of how GitHub serves Go packages.

Dagger Go SDK is also published to pkg.go.dev/dagger.io/dagger. This is the best place to check the reference documentation. For all other Go SDK documentation, check out docs.dagger.io/sdk/go.

🐍 Dagger Python SDK

Python SDK releases on GitHub start with the sdk/python tag. This has the added benefit of making all release notes available as a single list.

We publish the Dagger Python SDK to pypi.org, as https://pypi.org/project/dagger-io/.Β 

The reference documentation is published to dagger-io.readthedocs.org. As with all our SDKs, we release the main documentation to docs.dagger.io/sdk/python.

β¬’ Dagger Node.js SDK

Node.js SDK releases are prefixed with the sdk/nodejs tag. See the changelog for all releases.

The Dagger Node.js SDK is published to npmjs.com/@dagger.io/dagger.

All documentation - including the reference - is available via docs.dagger.io/sdk/nodejs.

πŸ“’ Docs

Documentation is a cross-cutting artifact, meaning that it is closely coupled to all the other artifacts mentioned above. Documentation has a different release cadence. Every merge into the main branch of github.com/dagger/dagger automatically triggers a build on Netlify for two separate websites:

  1. https://devel.docs.dagger.io/
  2. https://docs.dagger.io/

The important difference between the two websites is that the first one gets automatically published, while the second one - our live docs - requires a human decision. We manually check that all docs are there and everything looks good before we publish as https://docs.dagger.io

How are Dagger releases produced?

We use Dagger to release Dagger. The runtime context is GitHub Actions, since our code is already on GitHub. All workflows used for releasing start with publish-, e.g. publish-sdk-go.yml, publish.yml, etc. These workflows are triggered by specific git tags, e.g. sdk/go/**, v**, etc.

For example, if we look at .github/workflows/publish-sdk-go.yml, we notice surprisingly little YAML - just 17 lines. The most important line is ./hack/make sdk:go:publish ${{ github.ref_name }}. This kicks off the Dagger Go SDK pipeline, which publishes the Dagger Go SDK. That's right: we use the Go SDK to publish the Go SDK. First class dogfooding 🐢 

If you want to see the rest of the code, check out the Publish function in dagger/internal/mage/sdk/go.go. That Publish function represents the entire Dagger pipeline for publishing the Go SDK.

You can check the code that publishes the other SDKs by looking at the files next to go.go, i.e. python.go & nodejs.go. If it helps, the Go interface for all SDK pipelines is declared in the all.go file.

The Dagger Engine & CLI GitHub Actions publish workflow is slightly more involved: 292 lines of YAML. Almost all of this YAML is necessary since we are replicating what will happen on hosts that run Dagger for the first time. Running this inside Dagger would defeat the purpose: we are testing the correctness of the code that provisions Dagger. There is a specific line of interest: ./hack/make dagger:publish ${{ github.ref_name }}. The Go code for this starts in dagger/internal/mage/dagger.go. From this file, follow the CLI & Engine references to see the rest. Noteworthy facts:

The other interesting fact is that a new Dagger Engine & CLI release will open a new PR that sets the Engine version across all SDKs to the one that was just released. Once this PR gets merged, new SDK versions are ready to be released.

If you are curious to find out more about how Dagger releases are produced, check out github.com/dagger/dagger/RELEASING.md

To summarize all the above, this sequence diagram captures it all:

Upcoming improvements to the Dagger release process

Now that we understand how all the pieces fit together, and what goes into producing a Dagger release, we can talk about what aspects we are thinking of improving next.

New SDK releases are already announced in Discord, in the #releases channel. We are yet to configure our release bot to announce Engine & CLI releases.

Publishing https://docs.dagger.io currently requires a manual step. In order to make releases flow smoother, new documentation should be automatically published when a new SDK release goes out. If you are interested, we started discussing this aspect in this public GitHub issue.

The last & most significant improvement that we are thinking about are our release notes. Currently, these are generated manually, with the gh CLI. This is a simple first step, but we think that friendlier release notes are going to increase the chances of our users reading them. Pull request titles can only take us so far.

One idea is to define a simple pull request interface. For starters, each pull request should be clearly labeled in terms of the area that it impacts, e.g. sdk/python, area/documentation, area/engine etc. The other label is the type of change, e.g. Feature, Fix, Docs, etc. Also, when a pull request implements changes that warrant a minor version bump, it should be labeled with release/minor. Lastly, each pull request should include a RELEASE NOTES summary in the top comment that will be used for the final release notes, e.g.

RELEASE NOTESΒ  Sevices now has an Opt-in option. Follow the instructions in the PR to enable it.

The other idea is for each pull request to add release notes in a CHANGELOG file. This is based on the Keep a Changelog idea and is known to work well with https://changie.dev. We want to thank our community member @sheldonhull for mentioning this back in November 2022, in the context of dsv-github-action.

Before we wrap up this blog post, what would you like us to improve about Dagger releases? Let us know via this GitHub discussion.