Skip to main content

Go on Docker Hub

Dagger stands as a powerful CI/CD tool that works on any environment.

For instance, you can use the Dagger Go package to control the whole CI/CD process, from testing to pushing into a remote registry.

tip

The following examples can be used as a template for any standalone Go project.

Retrieve Go project

The first step is to make your Go project accessible to the Dagger plan.

You can indeed choose which files to include. Since it's a Golang project it should contain the module and all Go source files:

package main

import (
"dagger.io/dagger"
)

dagger.#Plan & {
// Retrieve go source code
client: filesystem: ".": read: {
contents: dagger.#FS
include: ["go.mod", "go.sum", "**/*.go"]
}

actions: {
// Alias to code directory
_code: client.filesystem.".".read.contents
}
}
tip

To make it more accessible in actions, you can set a private field that will act as an alias.

Build a Go base image

The universe.dagger.io/go package provides a base image to build your pipeline, but your project may use CGO or any external dependencies.

You can customize the base image to install required dependencies:

import (
"dagger.io/dagger"
"universe.dagger.io/go"
)

dagger.#Plan & {
actions: {
// Improve go base image with useful tool
// Enable cgo by installing build-base
_base: go.#Image & {
packages: "build-base": version: _
}
}
}

Run unit tests

Before delivering your application, you certainly want to run unit tests.

Use the #Test definition:

// Run go unit tests
unitTest: go.#Test & {
source: _code
package: "./..."
input: _base.output
}
tip

You can also use Dagger to write integration tests.

Build Go binary

To put your Go project on Docker Hub, you first need to compile a binary.

Use the #Build definition to do that:

// Build go project
build: go.#Build & {
source: _code
}
tip

You can control the binary platform with os and arch fields.

Prepare docker image

To make it usable for other users, you must put your binary in an image and set an entrypoint.

For optimization purposes, you can use alpine as the base image to contain your binary:

import (
"universe.dagger.io/alpine"
"universe.dagger.io/docker"
)

// Build docker image (depends on build)
image: docker.#Build & {
steps: [
alpine.#Build,
docker.#Copy & {
input: _base.output
contents: build.output
dest: "/usr/bin"
},
docker.#Set & {
config: cmd: ["</path/to/binary>"]
},
]
}

Push to Docker Hub

To push an image to Docker Hub, you will need your private credentials.

To not hard code your docker password in the plan, you can retrieve it from your environment:

dagger.#Plan & {
client: {
// ...

env: DOCKER_PASSWORD: dagger.#Secret
}
}

You can now push your image:

// Push image to remote registry (depends on image)
push: {
// Docker username
_dockerUsername: "<my_username>"

docker.#Push & {
"image": image.output
dest: "\(_dockerUsername)/<my_repository>"
auth: {
username: _dockerUsername
secret: client.env.DOCKER_PASSWORD
}
}
}

Complete CI/CD

After merging all examples, you will have a complete CI/CD to deliver a Go binary on Docker Hub.

package main

import (
"dagger.io/dagger"
"universe.dagger.io/go"
"universe.dagger.io/docker"
"universe.dagger.io/alpine"
)

dagger.#Plan & {
client: {
filesystem: ".": read: {
contents: dagger.#FS
include: ["go.mod", "go.sum", "**/*.go"]
}

env: DOCKER_PASSWORD: dagger.#Secret
}

actions: {
// Alias to code
_code: client.filesystem.".".read.contents

// Improve go base image with useful tool
// Enable cgo by installing build-base
_base: go.#Image & {
packages: {
"build-base": version: _
bash: version: _
}
}

// Run go unit tests
unitTest: go.#Test & {
source: _code
packages: ["./..."]
input: _base.output
}

// Build go project
build: go.#Build & {
source: _code
}

// Build docker image (depends on build)
image: docker.#Build & {
steps: [
alpine.#Build,
docker.#Copy & {
contents: build.output
dest: "/usr/bin"
},
docker.#Set & {
config: cmd: ["<path/to/binary>"]
},
]
}

// Push image to remote registry (depends on image)
push: {
// Docker username
_dockerUsername: "<my_username>"

docker.#Push & {
"image": image.output
dest: "\(_dockerUsername)/<my_repository>"
auth: {
username: _dockerUsername
secret: client.env.DOCKER_PASSWORD
}
}
}
}
}

You can then use dagger do to select which action you want to run.

Push multi-platform

Coming soon...