DEV Community

Cover image for Dagger and CUE, the new kids in the block
Lorenzo (Mec-iS)
Lorenzo (Mec-iS)

Posted on

Dagger and CUE, the new kids in the block

Intro

Some of the nice guys that gave Docker to the world have moved on into a new project for automating CI/CD called dagger.io: this new product starts from nice assumptions and all the experience from the previous project, absolutely interesting indeed. What we are going to peak here is the language at the base of Dagger: CUE, to which I have taken a peak recently.

What is CUE

From the CUE language homepage:

CUE is an open source language, with a rich set of APIs and tooling, for defining, generating, and validating all kinds of data: configuration, APIs, database schemas, code, … you name it.

CUE is a superset of JSON specializing in "Validate, define, and use dynamic and text-based data". This means that all the serialization fans can find this useful: JSON follower, YAML prayers, ProtoBuf believers can give it a try to the language and see what is can do to make more solid their configuration files.

Install GO and CUE

CUE is a CLI tool written in Golang and can be installed as a library for your Golang installation as stated in the minimal documentation:

# install as a library
$ go install cuelang.org/go/cmd/cue@latest
# install as CLI
$ git clone git@github.com:cue-lang/cue.git && cd cue && go get cuelang.org/go/cue
Enter fullscreen mode Exit fullscreen mode

(ensure that your $HOME/go/bin directory is in your path).

Try your installation:

$ cue --help
Enter fullscreen mode Exit fullscreen mode

The declarative power

The main point of CUE is to provide powerful schema definition and serialization to different format via a declarative language, let's see an example with mixed data (string, numbers, collections):

// save this content to a namefile.cue file

// you can declare a generic structure to define your map 

nameyourmap: data: "point.json":{ x: 4.5, y: 2.34 }
nameyourmap: yaml_string: """
one: this_is_yaml
two: this_is_also_yaml
"""
nameyourmap:this_is_list: [1, 2, 3.5]
Enter fullscreen mode Exit fullscreen mode

This is a generic CUE file that can be serialized into text formats:

# generate text file from code
$ cue export namefile.cue
{
    "nameyourmap": {
        "data": {
            "point.json": {
                "x": 4.5,
                "y": 2.34
            }
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Or:

$ cue export namefile.cue
{
    "nameyourmap": {
        "data": {
            "point.json": {
                "x": 4.5,
                "y": 2.34
            }
        },
        "yaml_string": "one: this_is_yaml\ntwo: this_is_also_yaml",
        "list": [
            1,
            2,
            3.5
        ]
    }
}
Enter fullscreen mode Exit fullscreen mode

This is already great as we can easily write scripts to create large configuration files, inject values or even entire files in the position where it is needed.
We can also see three of the basic characteristics of CUE syntax:

  • inline, easy to read
  • multiple declarations add new fields
  • fields cannot be accidentally over-written (try to add nameyourmap:this_is_list: [1, 2, 3.5, 4.5] to the end of the file and you will see an error)

Marshal/Unmarshal formats

In the previous example we had two critical points, at lines 4 and 5 we defined two structure for the fields point.json and yaml_string. How is it possible to have two different formats in the same declaration? That is one of the best feature up to now, as strings can be imported from different formats leveraging the right functions:

// save this content to a test1.cue file

// you can declare a generic structure to define your map 

nameyourmap: data: "point.json":{ x: 4.5, y: 2.34 }
nameyourmap: yaml_structure: yaml.Unmarshal("""
one: this_is_yaml
two: this_is_also_yaml
""")
nameyourmap:this_is_list: [1, 2, 3.5]
Enter fullscreen mode Exit fullscreen mode

Let's run the code:

$ cue export test1.cue
{
    "nameyourmap": {
        "data": {
            "point.json": {
                "x": 4.5,
                "y": 2.34
            }
        },
        "yaml_structure": {   <<<
            "one": "this_is_yaml",
            "two": "this_is_also_yaml"
        },
        "this_is_list": [
            1,
            2,
            3.5
        ]
    }
}
Enter fullscreen mode Exit fullscreen mode

As you can see now the YAML field is not a string anymore but a map defined from the YAML string.

That is all for now. CUE has other superpowers:

  • Data Validation
  • Schema Definition
  • Code Generation and Extraction
  • Querying
  • Scripting

You can find some explanations in the documentation.

Dagger is built on CUE to provide building CI/CD pipelines quickly and run them anywhere.

If you liked this post please ❤️ and I will consider going on with this walk-through.

Discussion (0)