Modules and Packages



Cue has a module and package system like any proper language. With modules and packages, you also have imports. This is one of its most significant advantages over current configuration languages like Yaml and JSON. Cue’s module system is very similar to Go’s, but has it’s differences.

  • A module name has a particular format
    • domain.com/name is the minimal
    • github.com/owner/repo is common
    • imports without a domain are assumed to be builtins from the standard library
    • You don’t actually need a domain or repository, it’s just a path naming requirement
  • You must create a file (cue.mod/module.cue) to signify a module
  • Modules are comprised of packages, Cue supports multiple packages in a directory
  • Only absolute imports are allowed, no relative imports (for security reasons)
  • You can rename imported packages at the time of import (like the k8s.io imports previously seen)
  • Definitions and Values in the same package can be accessed across files without imports

While, Cue does not yet have a dependency management system, it will import and process them. See below for details.

Defining a module

To define a module, we simply need to add a cue.mod/module.cue file. Before you do, you will not be able to import files from other directories or modules.

file listing

├── root.cue
├── a
│   └── a.cue
└── cue.mod
    ├── module.cue
    └── pkg
        └── github.com
            └── foo
                └── bar
                    ├── bar.cue
                    ├── b
                    │   └── b.cue
                    └── multi
                        ├── hello.cue
                        └── world.cue

cue.mod/module.cue

module: "github.com/hofstadter-io/cuetorials"

root.cue

package root

import (
	// imports from a dependency
	"github.com/foo/bar"
	// import from a dependency subdir, rewrite name
	B "github.com/foo/bar/b"
	// import from a nested dir with package name
	"github.com/foo/bar/multi:hello"
	"github.com/foo/bar/multi:world"

	// import from this module
	"github.com/hofstadter-io/cuetorials/a"
)

root: "root"

vals: {
	ex1: bar.value
	ex2: B.b
	ex3: hello.msg
	ex4: world.msg
	ex5: a.a
}

a/a.cue

package a

a: "a"

cue.mod/pkg/github.com/foo/bar/bar.cue

package bar

value: "BAR"

cue.mod/pkg/github.com/foo/bar/b/b.cue

package b

b: "b"

cue.mod/pkg/github.com/foo/bar/multi/hello.cue

package hello

msg: "hello"

cue.mod/pkg/github.com/foo/bar/multi/world.cue

package world

msg: "world"

When we run this, we can see the output by combining the modules and packages

cue eval root.cue

root: "root"
vals: {
	ex1: "BAR"
	ex2: "b"
	ex3: "hello"
	ex4: "world"
	ex5: "a"
}

Dependency management

Cue dependencies are located in the cue.mod/pkg/... directory. From here, dependent modules are nested under directories mirroring their import path.

cue.mod
├── module.cue
└── pkg
    └── github.com
        ├── foo
        │   └── bar
        └── hofstadter-io
            ├── hof
            ├── hofmod-cli
            └── hofmod-server

Cue does not yet have a dependency management system. You have to get them in place another way. (Open issue)

Fortunately, Hofstadter has built a generalized dependency management system as part of our hof tool, based on go mod and Minimum Version Selection (MVS). While it does not have all of the automation of a go mod solution, but should help you out unitl Cue has its own.

First, you will need to initialize a module

hof mod init cue github.com/hofstadter-io/cuetorials will setup both the cue.mods file and the cue.mod/module.cue file.

The format for cue.mods looks like Go’s.

cue.mods

// this should match the cue.mod/modules.cue value
module "github.com/hofstadter-io/cuetorials"

cue = master // or another version, not really used but a required field

require (
  domain.com/owner/repo v0.1.2    // select a version
  domain.com/other/repo v0.0.0    // latest version
)

replace github.com/other/repo => github.com/myorg/repo  // replace with a different remote repository
replace github.com/hof/studios => ../../studios         // local replace

hof will not inspect your code to determine dependencies, thus you have to always manually add them.

When you add or change dependencies, or change the code in a local replace, run the following command to fetch them to your modules “vendor” directory (cue.mod/pkg)

hof mod vendor cue

hof will fetch, cache, and update your dependencies using the MVS algorithm. You will now be able to import from both the dependencies and local directories to your module. If you only want local imports, you don’t need

You can learn more about Hofstadter and our hof tool at https://docs.hofstadter.io

We also have several modules available on GitHub (https://github.com/hofstadter-io) using the hofmod- prefix. These are designed for our code generation framework, discussed in the final section of “first-steps”.

We'll never share your email with anyone else.
2023 Hofstadter, Inc