Quickstart
Publish the application
At this point, your Dagger pipeline has tested, built and delivered the application to your local host. But why not also publish a container image of the application to a registry?
Dagger SDKs have built-in support to publish container images. So, let's update the pipeline to copy the built React application into an NGINX web server container and deliver the result to a public registry. Depending on the SDK, you need either the publish()
method (for Node.js and Python) or the Publish()
method (for Go).
package main
import (
"context"
"fmt"
"math"
"math/rand"
"os"
"dagger.io/dagger"
)
func main() {
ctx := context.Background()
// initialize Dagger client
client, err := dagger.Connect(ctx, dagger.WithLogOutput(os.Stderr))
if err != nil {
panic(err)
}
defer client.Close()
source := client.Container().
From("node:16").
WithDirectory("/src", client.Host().Directory(".", dagger.HostDirectoryOpts{
Exclude: []string{"node_modules/", "ci/", "build/"},
}))
runner := source.WithWorkdir("/src").
WithExec([]string{"npm", "install"})
test := runner.WithExec([]string{"npm", "test", "--", "--watchAll=false"})
_, err = test.WithExec([]string{"npm", "run", "build"}).
Directory("./build").
Export(ctx, "./build")
if err != nil {
panic(err)
}
ref, err := client.Container().
From("nginx:1.23-alpine").
WithDirectory("/usr/share/nginx/html", client.Host().Directory("./build")).
Publish(ctx, fmt.Sprintf("ttl.sh/hello-dagger-%.0f", math.Floor(rand.Float64()*10000000))) //#nosec
if err != nil {
panic(err)
}
fmt.Printf("Published image to: %s\n", ref)
}
Run the pipeline by executing the command below from the application directory:
dagger run go run ci/main.go
import { connect } from "@dagger.io/dagger"
connect(
async (client) => {
// use a node:16-slim container
// mount the source code directory on the host
// at /src in the container
const source = client
.container()
.from("node:16-slim")
.withDirectory(
"/src",
client
.host()
.directory(".", { exclude: ["node_modules/", "ci/", "build/"] }),
)
// set the working directory in the container
// install application dependencies
const runner = source.withWorkdir("/src").withExec(["npm", "install"])
// run application tests
const test = runner.withExec(["npm", "test", "--", "--watchAll=false"])
// build application
// write the build output to the host
await test
.withExec(["npm", "run", "build"])
.directory("./build")
.export("./build")
// use an nginx:alpine container
// copy the build/ directory into the container filesystem
// at the nginx server root
// publish the resulting container to a registry
const imageRef = await client
.container()
.from("nginx:1.23-alpine")
.withDirectory(
"/usr/share/nginx/html",
client.host().directory("./build"),
)
.publish("ttl.sh/hello-dagger-" + Math.floor(Math.random() * 10000000))
console.log(`Published image to: ${imageRef}`)
},
{ LogOutput: process.stderr },
)
Run the pipeline by executing the command below from the application directory:
dagger run node ci/index.mjs
import random
import sys
import anyio
import dagger
async def main():
config = dagger.Config(log_output=sys.stdout)
async with dagger.Connection(config) as client:
# use a node:16-slim container
# mount the source code directory on the host
# at /src in the container
source = (
client.container()
.from_("node:16-slim")
.with_directory(
"/src",
client.host().directory(
".", exclude=["node_modules/", "ci/", "build/"]
),
)
)
# set the working directory in the container
# install application dependencies
runner = source.with_workdir("/src").with_exec(["npm", "install"])
# run application tests
test = runner.with_exec(["npm", "test", "--", "--watchAll=false"])
# build application
# write the build output to the host
await (
test.with_exec(["npm", "run", "build"])
.directory("./build")
.export("./build")
)
# use an nginx:alpine container
# copy the build/ directory into the container filesystem
# at the nginx server root
# publish the resulting container to a registry
image_ref = await (
client.container()
.from_("nginx:1.23-alpine")
.with_directory("/usr/share/nginx/html", client.host().directory("./build"))
.publish(f"ttl.sh/hello-dagger-{random.randrange(10 ** 8)}")
)
print(f"Published image to: {image_ref}")
anyio.run(main)
Run the pipeline by executing the command below from the application directory:
dagger run python ci/main.py
This revised pipeline does everything described in the previous step, and then publishes the container to ttl.sh, an ephemeral Docker registry.
To prevent name collisions, the container image name is suffixed with a random number, and printed to the console, as in the example output below:
Published image to: ttl.sh/hello-dagger-8724562@sha256:16b9344023b9bf1313fd111395a585e03bea13eff8a171616f202c0e9dbb219a
After Dagger resolves the pipeline, the newly-built container image will be available in the ttl.sh registry. Download and test it using the command below (update the container image name as per the output of the pipeline):
docker run -p 8080:80 ttl.sh/hello-dagger-8724562@sha256:16b9344023b9bf1313fd111395a585e03bea13eff8a171616f202c0e9dbb219a
Browse to host port 8080. Confirm that you see the React application's welcome page.
You may have noticed that the pipeline above is able to publish to the registry without requiring the user to enter any authentication credentials. This is only possible because the ttl.sh registry allows anonymous access. In reality, however, most popular registries require authentication before accepting images for publication.
Dagger SDKs rely on your existing Docker credentials for registry authentication. This means that you must either execute docker login
on the Dagger host, or invoke the Container.withRegistryAuth()
API method in your Dagger pipeline, before attempting to publish an image to that registry.