Skip to content

Instantly share code, notes, and snippets.

@sagikazarmark
Last active May 3, 2024 18:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sagikazarmark/53a77bf72ad494154f691c0ccd968e35 to your computer and use it in GitHub Desktop.
Save sagikazarmark/53a77bf72ad494154f691c0ccd968e35 to your computer and use it in GitHub Desktop.
Dagger module best practices

Constructor

Reusable modules generally operate on a single tool in a container under the hood. Allow specifying the tool version, so silent upgrades won't break your pipelines.

It's also a good practice to allow customizing the container itself (eg. install additional dependencies).

func New(
	// Version (image tag) to use from the official image repository as a base container.
	//
	// +optional
	version string,

	// Custom container to use as a base container.
	//
	// +optional
	container *Container,

	// ...
) *Go {
	if container == nil {
		if version == "" {
			version = defaultVersion
		}

		container = dag.Container().From(fmt.Sprintf("%s:%s", defaultImageRepository, version))
	}

	return &Go{container}
}

Note

A previous version of this recommended a third, image parameter, but it was removed after @shykes made this comment.

Do not mount cache volumes by default

I used to write modules that by default mounts cache volumes with some arbitrary default name (for example go-mod for Go module cache).

Here is an example I used to do:

func New(
	// ...

	// Disable mounting cache volumes.
	// +optional
	disableCache bool,
  
  	// Go module cache volume.
	// +optional
	goModCache *CacheVolume,
) *Go {
	var ctr *Container

	// ...

	m := &Go{ctr}

	if !disableCache {
		if goModCache == nil {
			goModCache = dag.CacheVolume("go-mod")
		}
		m = m.
			WithModuleCache(goModCache, nil, "")
	}

	return m
}

As you can see, there is also an option to disable mounting the cache.

The problem with this approach is you may end up sharing cache volumes across different projects (depending on how you run the engine and/or whether you use Dagger Cloud or not) resulting in a cache that grows endlessly.

As a result, you probably want to explicitly set cache volumes whenever you want to use any cache anyway, so disabling cache mounts by default makes more sense.

That isn't to say you can't have some sort of a default cache that you can enable explicitly if you don't care about the consequences (eg. you don't save the cache anyway).

Alternative approaches:

  • enableDefaultCache option in the constructor
  • WithDefaultCache in your module API

But the differences may be negligible:

m := dag.Go().WithDefaultCache()

// vs

m := dag.Go().WithModuleCache(dag.CacheVolume("my-go-mod")).WithBuildCache(dag.CacheVolume("my-go-build"))
@shykes
Copy link

shykes commented May 1, 2024

You can take a CacheVolume as an optional argument :)

@shykes
Copy link

shykes commented May 1, 2024

I recommend merging container and base in a single argument . They seem redundant to me. Remember you can pass a registry ref to a Container argument in the CLI, so the string is not needed.

@sagikazarmark
Copy link
Author

sagikazarmark commented May 2, 2024

@shykes

You can take a CacheVolume as an optional argument :)

Well, now you can. :) But yeah, that needs updating.

I recommend merging container and base in a single argument . They seem redundant to me. Remember you can pass a registry ref to a Container argument in the CLI, so the string is not needed.

I guess you mean container and image. Yeah, that's a great point, thanks!

BTW I have some more to add to this. Maybe we should convert it to a living document somewhere? Like a wiki.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment