Project file organization
When your Dagger configuration grows, you may feel the need to better organize your project by splitting it into multiple files.
A simple way to accomplish this is to create and import packages within your project's module.
What is a CUE module?
A module in CUE is any directory including a cue.mod
folder. It makes for the prefix/root of importable packages. For example, universe.dagger.io
is a module which includes several packages.
When you ran dagger project init
, dagger created this directory for you.
Learn more about CUE modules in Modules, Packages, and Instances — How files are organized in CUE.
Anonymous module (default)
When the module name is an empty string, it's known as an anonymous module:
module: ""
Anonymous modules are used when we don't need to import other packages from within the same module.
By default, this is how Dagger projects start: a single CUE file (e.g. dagger.cue
), which imports everything from third-party packages, such as universe.dagger.io/docker
.
When to name a module
If you want to create packages inside your project, and import from each other, you need to give a name to your module. This is required so you have a prefix/root before your imports.
The module path becomes the import path prefix for packages in the module. This might be the name of a domain you own or another name you control (such as your company name), even your email, followed optionally by a descriptive path (e.g., project name). The address doesn't have to exist, it's only used for namespacing.
You import packages by prefixing the module name they're a part of, plus the path to them, relative to the cue.mod
directory.
root // <- this is a module because it includes a cue.mod dir
|-- cue.mod
| |-- module.cue // module: "example.com/myproject"
|-- schemas
| |-- compose // <- this is a package because it includes files with a package directive
| | |-- spec.cue // package compose
...
|-- dagger.cue // import "example.com/myproject/schemas/compose"
Summary
Consider the module as the URL to access the root of your project. Any subfolder inside this module needs to have CUE files with a package name equivalent to the directory name. File names inside each directory are not important, the package name is.
Initializing the module
Project non initialized
The module name can be set during project initialization: dagger project init --name <NAME>
.
Let's use an email address for convenience purposes:
dagger project init --name "info@example.com"
Project already initialized
Manually edit the desired name in module.cue
:
module: "info@example.com"
Creating packages in subdirectories
Practice
Let's put everything above into practice:
- Initialize the workdir environment
mkdir daggerTest && cd daggerTest
- Initialize the project
dagger project init --name "info@example.com"
- Install
dagger.io
anduniverse.dagger.io
dependencies
dagger project update
- Create 2 subfolders:
foo
andbar
:
mkdir foo bar
- Inside the
bar
folder, create a CUE file with any name and whose package isbar
(same as parent directory):
package bar
#Test: "world"
- Inside the
foo
folder, create a CUE file with any name and whose package isfoo
(same as parent directory):
package foo
import (
"universe.dagger.io/bash"
"info@example.com/bar"
)
#Foo: {
script: string
_run: bash.#RunSimple & {
// reference the #Test definition from the bar directory
env: TEST: bar.#Test
// store the output of the command
"script": contents: "\(script) > /output.txt"
export: files: "/output.txt": _
// don't cache
always: true
}
result: _run.export.files."/output.txt"
}
- At the root of the project, create your
main.cue
file (file and package names are arbitrary):
package main
import (
"dagger.io/dagger"
"info@example.com/foo"
)
dagger.#Plan & {
actions: {
hello: foo.#Foo & {
script: "echo -n Hello, inlined $TEST!"
}
}
}
Recap
We now have the directory structure shown below:
$ tree -L 2 .
.
├── bar
│ └── anything.cue
├── cue.mod
│ ├── module.cue
│ ├── pkg
├── foo
│ └── main.cue
└── main.cue
4 directories, 4 files
And the expected output is:
$ dagger do hello
[✔] actions.hello
Field Value
result "Hello, inlined world!"