Quickstart
Publish the container image
The end stage of the example pipeline is the publish
stage, which publishes the container image to a registry.
Inspect the Dagger Function
- Go
- Python
- TypeScript
package main
import (
"context"
"fmt"
"math"
"math/rand"
"dagger/hello-dagger/internal/dagger"
)
type HelloDagger struct{}
// Publish the application container after building and testing it on-the-fly
func (m *HelloDagger) Publish(ctx context.Context, source *dagger.Directory) (string, error) {
// call Dagger Function to run unit tests
_, err := m.Test(ctx, source)
if err != nil {
return "", err
}
// call Dagger Function to build the application image
// publish the image to ttl.sh
address, err := m.Build(source).
Publish(ctx, fmt.Sprintf("ttl.sh/hello-dagger-%.0f", math.Floor(rand.Float64()*10000000))) //#nosec
if err != nil {
return "", err
}
return address, nil
}
import random
import dagger
from dagger import function, object_type
@object_type
class HelloDagger:
@function
async def publish(self, source: dagger.Directory) -> str:
"""Publish the application container after building and testing it on-the-fly"""
# call Dagger Function to run unit tests
await self.test(source)
# call Dagger Function to build the application image
# publish the image to ttl.sh
return await self.build(source).publish(
f"ttl.sh/myapp-{random.randrange(10 ** 8)}"
)
import { dag, Container, Directory, object, func } from "@dagger.io/dagger"
@object()
class HelloDagger {
/**
* Publish the application container after building and testing it on-the-fly
*/
@func()
async publish(source: Directory): Promise<string> {
// call Dagger Function to run unit tests
await this.test(source)
// call Dagger Function to build the application image
// publish the image to ttl.sh
return await this.build(source).publish(
"ttl.sh/myapp-" + Math.floor(Math.random() * 10000000),
)
}
}
Just as you can call Dagger Functions individually from the CLI, you can also call (and combine) them using a programming language. This Dagger Function is an example. It is a higher-level function which orchestrates the Dagger Functions in previous sections and the core Dagger API to:
- run the application's tests and return the results;
- build and return a container image of the final application;
- publish the container image to a registry and return the image identifier.
There are various reasons why you might want to write Dagger Functions that call other Dagger Functions. A common reason is that when developing locally, it's quicker and easier to trigger your pipeline using a single command (dagger call publish ...
) instead of multiple commands (dagger call test ... && dagger call build ... && ...
).
Call the Dagger Function
Call the Dagger Function:
dagger call publish --source=.
You should see the application being tested, built, and published to the ttl.sh container registry:
You can test the published container image by pulling and running it with docker run
:
Function chaining works the same way, whether you're writing Dagger Function code using a Dagger SDK or invoking a Dagger Function using the Dagger CLI. The following are equivalent:
- Go
- Python
- TypeScript
- Dagger CLI
package main
import (
"context"
"dagger/hello-dagger/internal/dagger"
)
type HelloDagger struct{}
// Returns a base container
func (m *HelloDagger) Base() *dagger.Container {
return dag.Container().From("cgr.dev/chainguard/wolfi-base")
}
// Builds on top of base container and returns a new container
func (m *HelloDagger) Build() *dagger.Container {
return m.Base().WithExec([]string{"apk", "add", "bash", "git"})
}
// Builds and publishes a container
func (m *HelloDagger) BuildAndPublish(ctx context.Context) (string, error) {
return m.Build().Publish(ctx, "ttl.sh/bar")
}
import dagger
from dagger import dag, function, object_type
@object_type
class HelloDagger:
@function
def base(self) -> dagger.Container:
"""Returns a base container"""
return dag.container().from_("cgr.dev/chainguard/wolfi-base")
@function
def build(self) -> dagger.Container:
"""Builds on top of base container and returns a new container"""
return self.base().with_exec(["apk", "add", "bash", "git"])
@function
async def build_and_publish(self) -> str:
"""Builds and publishes a container"""
return await self.build().publish("ttl.sh/bar")
import { dag, Container, object, func } from "@dagger.io/dagger"
@object()
class HelloDagger {
/**
* Returns a base container
*/
@func()
base(): Container {
return dag.container().from("cgr.dev/chainguard/wolfi-base")
}
/**
* Builds on top of base container and returns a new container
*/
@func()
build(): Container {
return this.base().withExec(["apk", "add", "bash", "git"])
}
/**
* Builds and publishes a container
*/
@func()
async buildAndPublish(): Promise<string> {
return await this.build().publish("ttl.sh/bar")
}
}
# all equivalent
dagger call base with-exec --args apk,add,bash,git publish --address="ttl.sh/bar"
dagger call build publish --address="ttl.sh/bar"
dagger call build-and-publish