docs/versioned_docs/version-0.17.2/ci/quickstart/env.mdx
import Tabs from "@theme/Tabs"; import TabItem from "@theme/TabItem";
You're now ready to dive into Dagger and see how Dagger Functions work!
In the example pipeline you just ran, the publish stage depends on the test and build stages, which in turn depend on the build-env stage. Dagger represents these stages and dependencies in a Directed Acyclic Graph (DAG), which it then runs concurrently to maximize pipeline speed and accuracy.
Let's start with the build-env stage and look at its Dagger Function first.
This Dagger Function requires a source argument of type Directory. This argument tells the Dagger Function where to find the application's source code, and may refer to either a local directory or a remote Git repository.
It's possible to specify a default path for this argument. This Dagger Function (and all the others in this quickstart) set / as their default path, which means that if no source argument is provided on the command line, Dagger will default to the root of the Git repository as the location of the application's source code.
:::tip DEFAULT PATHS
Default paths are only available for Directory and File arguments. Dagger's default path resolution differs depending on whether the paths are (a) absolute or relative; and (b) in Git or non-Git contexts.
:::
The Dagger Function begins by calling the dag client, which is pre-initialized in every Dagger Function. This client contains all the core types (like Container, Directory, etc.), as well as bindings to any dependencies your module has declared.
It uses the dag client to initialize a base container from the node:21-slim image as a Container object. This Container object comes with useful methods of its own, which are then called in a chain to add source code to the container, configure a cache volume and run npm install to install dependencies (refer to the code comments for details).
:::tip FUNCTION CHAINING
If you've worked with Dockerfiles, this Dagger Function should look very familiar to you. Similar to the instructions in a Dockerfile, it starts with a base container image, then calls various functions to revise the base image. The base image is represented as a Container object, which is one of Dagger's core types. The Dagger API lets you manipulate the container by calling the object's functions, which can then return another object, and so on. This is called "chaining", and is a core feature of Dagger.
:::
Now that you know how the Dagger Function works, call it from Dagger Shell:
<Tabs groupId="shell"> <TabItem value="System shell"> ```shell dagger -c build-env ``` </TabItem> <TabItem value="Dagger Shell"> ```shell title="First type 'dagger' for interactive mode." build-env ``` </TabItem> </Tabs>Here's what you should see:
This output means that the build succeeded, and a Container type representing the built container image was returned. This is a "just-in-time container" - a transient artifact produced as the result of a Dagger Function.
:::tip FUNCTION NAMES
When calling Dagger Functions, all names (functions, arguments, fields, etc.) are converted into a shell-friendly "kebab-case" style. This is why Dagger Functions named FooBar in Go, foo_bar in Python and fooBar in TypeScript/PHP/Java are called with foo-bar ... (in interactive mode) or dagger shell -c foo-bar ... (in non-interactive mode).
:::
Just-in-time containers come with useful built-in functions, which you can chain together with the pipe (|) operator. This feature makes it easy to build powerful pipelines on the fly. One of the most interesting built-in functions is terminal, which can be used to open an interactive terminal session with the running container.
To see this in action, chain an additional function call to terminal on the returned Container:
This command builds the container image and then drops you into an interactive terminal running the bash shell. You can now directly execute commands in the running container, as shown below:
:::tip INTERACTIVE CONTAINER DEBUGGING
The terminal function is very useful for debugging and experimenting, since it allows you to interact directly with containers and inspect their state, at any stage of your Dagger Function execution.
:::