Introduction

In this tutorial you will use CUE’s Go API to work with a CUE module dependency fetched from the Central Registry.

Along the way you will:

  • Create a CUE module that depends on an existing, well-known module
  • Use cue mod tidy to fetch and organise your module’s dependencies
  • Load your CUE using the Go API working in a modules-aware mode

Prerequisites

This tutorial uses the following version of CUE:

TERMINAL
$ cue version
cue version v0.13.1
...

Create a CUE module

1

Initialize a new main CUE module in an empty directory:

TERMINAL
$ cue mod init an.example/config@v0

You won’t publish this module, so the name you give it is unimportant.

2

Create the file main.cue, holding the code for the main module:

main.cue
package config

import "github.com/cue-labs/examples/frostyconfig@v0"

config: frostyconfig.#Config & {
	appName: "alpha"
	port:    80
	features: logging: true
}

Your main module defines some concrete values for a configuration, constrained by the frostyconfig.#Config schema.

3

Ensure the CUE module is tidy:

TERMINAL
$ cue mod tidy

This fetches the frostyconfig module (and any dependencies it might have) from the Central Registry.

If you see an error message mentioning “too many requests” (here, or at any point as you follow this tutorial), then login to the Central Registry and re-run the failing command. The Central Registry allows more requests from authenticated users.

4

Export the configuration from your CUE module:

TERMINAL
$ cue export
{
    "config": {
        "appName": "alpha",
        "port": 80,
        "features": {
            "logging": true
        }
    }
}

This export shows that your CUE is valid and you can successfully use a dependency from the Central Registry.

Create a Go module and program

5

Initialize a Go module for your program:

TERMINAL
$ go mod init an.example/config
...

You won’t publish this module, so the name you give it is unimportant.

6

Create the file main.go containing this Go program:

main.go
package main

import (
	"fmt"
	"log"

	"cuelang.org/go/cue"
	"cuelang.org/go/cue/cuecontext"
	"cuelang.org/go/cue/load"
	"cuelang.org/go/mod/modconfig"
)

func main() {
	ctx := cuecontext.New()

	// Create a registry client. Passing a nil config
	// will give us client that behaves like the cue command.
	reg, err := modconfig.NewRegistry(nil)
	if err != nil {
		log.Fatal(err)
	}

	// Load the package from the current directory.
	// We don't need to specify a Config in this example.
	insts := load.Instances([]string{"."}, &load.Config{
		Registry: reg,
	})

	// The current directory just has one file without any build tags,
	// and that file belongs to the example package, so we get a single
	// instance as a result.
	v := ctx.BuildInstance(insts[0])
	if err := v.Err(); err != nil {
		log.Fatal(err)
	}

	// Lookup the 'config' field and print it out
	config := v.LookupPath(cue.ParsePath("config"))
	fmt.Println(config)
}

This program loads the CUE package in the current directory, and then prints a message based on the config field.

7

Add a dependency on cuelang.org/go and ensure the Go module is tidy:

TERMINAL
$ go get cuelang.org/go@v0.13.1
...
$ go mod tidy
...

You can use @latest in place of the specific version mentioned here.

Run the Go program

8

Run the Go program:

TERMINAL
$ go run .
{
	appName: "alpha"
	port:    80
	features: {
		logging: true
	}
}

Summary

Well done - you’ve finished this tutorial! In completing it, you:

  • created a main module that depends on a well-known module from the Central Registry, and
  • used the Go API to load the main module, transparently using the module’s dependencies.