Sources
A "source" in Dalec is an abstraction for fetching dependencies of a build. Usually this is source code but technically it could be anything. The source abstraction enables fetching build sources over various protocols.
Some sources are considered as inherently file-based sources, like HTTP URLs or local directories. Other sources are considered as inherently directory-based sources, like git repositories. Depending on the source type, the behavior of certain things may be different, depending on the target implementation (e.g. mariner2, jammy, windows, etc). Sources are injected into the root path of the build environment using the name of the source.
Ideally the content of a source is platform agnostic (e.g. no platform specific binaries). These sources are used to create source packages for the target platform, such as an srpm or a debian dsc. However, some source types may allow you to mount another source type in or are wrappers for other source types, like docker images or build sources respectively. Only the output of a top-level source is included in the build environment. These wrapper types (docker image, build) are useful for more advanced use-cases where you need to generate content or utilize other tooling in order to create the source.
Top-level source configuration
For all source types, you can specify the following top-level configuration:
path
: The path to extract from the source typeincludes
: A list of glob patterns to include from the sourceexcludes
: A list of glob patterns to exclude from the sourcegenerate
: See Generators
The below example uses a context
source type.
The root of the source is the path/in/source
directory.
The source will include all .txt
files within path/in/source
except for secret.txt
.
sources:
someSource:
path: path/in/source
includes:
- "*.txt"
excludes:
- "secret.txt"
context: {}
Source Types
Git
Git sources fetch a git repository at a specific commit. You can use either an SSH style git URL or an HTTPS style git URL.
For SSH style git URLs, if the client (such as the docker CLI) has provided access to an SSH agent, that agent will be used to authenticate with the git server.
sources:
someSource1:
git:
# This uses an SSH style git URL.
url: git@github.com:myOrg/myRepo.git
commit: 1234567890abcdef
someSource2:
git:
# This uses an HTTPS style git URL.
url: https://github.com/myOrg/myRepo.git
commit: 1234567890abcdef
keepGitDir: true # [Optional] Keep the .git directory when fetching the git source. Default: false
By default, Dalec will discard the .git
directory when fetching a git source.
You can override this behavior by setting keepGitDir: true
in the git configuration.
Git repositories are considered to be "directory" sources.
Authentication will be handled using some default secret names which are fetched from the client:
- Default SSH agent
- Providing a build secret called
GIT_AUTH_HEADER
for header based auth - Providing a build secret called
GIT_AUTH_TOKEN
for token based auth
You can customize each of these by setting the appropriate field in the git auth section (shown below with default values):
someSource1:
git:
# This uses an SSH style git URL.
url: git@github.com:myOrg/myRepo.git
commit: 1234567890abcdef
auth:
header: GIT_AUTH_HEADER # Default header secret used
token: GIT_AUTH_TOKEN # Default token secret used
ssh: default # Default SSH secret used
Note: These are secret names which are used to reference the secrets provided by the client, not the actual secret values.
HTTP
HTTP sources fetch a file from an HTTP URL. The HTTP source type is considered to be a "file" source.
The fetched file can be verified against a digest
if one is supplied. There is also a permissions
field that can set the octal permissions
of the fetched file.
sources:
someSource1:
http:
url: https://example.com/someFile.txt
# optional digest field
digest: sha256:1234567890abcdef
# optional permissions field
permissions: 0644
Build context
Clients provide a build context to Dalec. As an example, here is how the Docker client provides a build context to Dalec:
$ docker build <some args> .
In this case the .
, or current directory, is the build context.
Dalec is able to use the build context as a source:
sources:
someSource:
context: {}
Note the empty brackets.
This is an unfortunate syntax requirement to not have context
considered as a nil value.
This is the equivalent of the following:
sources:
someSource:
context:
name: "context"
Where name: "context"
, not to be confused with the source type context
, is named by convention by the docker CLI.
Additionally contexts can be passed in from the docker cli: docker build --build-context <name>=<path>
.
The <name>
would be the name to use in your yaml to access it.
This could also be written as below, since the name: context
is the default and is the main build context passed in by the client:
sources:
someSource:
context: {}
The context
source type is considered to be a "directory" source.
Inline
Inline sources are sources that are defined inline in the Dalec configuration.
You can only specify one of file
or dir
in an inline source.
Directories cannot be nested in inline sources.
Filenames must not contain a path separator (/
).
sources:
someInlineFile:
inline:
# This is the content of the source.
file:
uid: 0
gid: 0
permissions: 0644
contents: |
some content
some more content
someInlineDir:
inline:
dir:
uid: 0
gid: 0
permissions: 0755
files:
# This is the content of the source.
file1:
contents: |
some content
some more content
permissions: 0644
uid: 0
gid: 0
file2:
contents: |
some content
some more content
permissions: 0644
uid: 0
gid: 0
Inline sources with file
are considered to be "file" sources.
Inline sources with dir
are considered to be "directory" sources.
Docker Image
Docker image sources fetch a docker image from a registry. The output of this source is a directory containing the contents of the image.
sources:
someDockerImage:
image:
ref: docker.io/library/alpine:3.14
You can also run commands in the image before fetching the contents. This is especially useful for generating content.
sources:
someDockerImage:
image:
ref: docker.io/library/alpine:3.14
cmd:
dir: / # Default path that command steps are executed in
cache_dirs: null # Map of cache mounts. Default value: `null`
/foo: {
mode: shared # The other options are `locked` or `private`
key: myCacheKey
include_distro_key: false # Add the target key from the target being built into the cache key
include_arch_key: false # add the architecture of the image to run the command in into the cache key
}
steps:
- command: echo ${FOO} ${BAR}
env: # Environment variables to set for the step
FOO: foo
BAR: bar
You can mount any other source type into the image as well. Here's an example mounting an inline source, modifying it, and extracting the result:
sources:
someDockerImage:
path: /bar # Extract `/bar` from he result of running the command in the docker image below
image:
ref: docker.io/library/alpine:3.14
cmd:
mounts: # Mount other sources into each command step
- dest: /foo
spec:
inline:
file:
uid: 0
gid: 0
permissions: 0644
contents: |
some content
some more content
steps:
- command: echo add some extra stuff >> /foo; mkdir /bar; cp /foo /bar
You can use the docker image source to produce any kind of content for your build.
Docker image sources are considered to be "directory" sources.
Build
Build sources allow you to build a dockerfile and use the resulting image as a source.
It takes as input another source which must include the dockerfile to build.
The default dockerfile path is Dockerfile
just like a normal docker build.
sources:
someBuild:
build:
source: # Specify another source to use as the build context of this build operation
git:
url: https://github.com/Azure/dalec.git
commit: v0.1.0
The above example will fetch the git repo and build the dockerfile at the root of the repo.
Here's another example using an inline source as the build source:
someBuild:
path: /hello.txt
build:
dockerfile_path: other.Dockerfile # [Optional] Change dockerfile path. Default value: "Dockerfile"
source:
inline:
dir:
uid: 0
gid: 0
permissions: 0755
files:
Dockerfile:
contents: |
FROM alpine:3.14 AS base
RUN echo "hello world" > /hello.txt
FROM scratch
COPY --from=base /hello.txt /hello.txt
You can also specify a target
which is the name of the build stage to execute.
Build args can be specified as well as args
which is a map of key value pairs.
Build sources are considered to be "directory" sources.
Generators
Generators are used to generate a source from another source.
Currently the only generator supported is gomod
.
Gomod
The gomod
generator manages a single go module cache for all sources that
specify it in the spec. It is expected that the build dependencies include a
go toolchain suitable for fetching go module dependencies.
Adding a gomod generator to 1 or more sources causes the following to occur automatically:
- Fetch all go module dependencies for all sources in the spec that specify the generator
- Keeps a single go module cache directory for all go module deps.
- Adds the go module cache directory a source which gets included in source packages like a normal source.
- Adds the
GOMODCACHE
environment variable to the build environment.
sources:
md2man:
git:
url: https://github.com/cpuguy83/go-md2man.git
commit: v2.1.0
generate:
subpath: "" # path inside the source to use as the root for the generator
gomod: {} # Generates a go module cache to cache dependencies
The gomod
generator also supports generating multiple modules in a single source. The paths
field is a list of paths where the generator should fetch the dependencies. Assuming src
looks like this:
.
├── module1
│ ├── go.mod
│ ├── go.sum
│ └── main.go
└── module2
├── go.mod
├── go.sum
└── main.go
The dalec spec will look like this:
sources:
src:
path: ./
context: {}
generate:
- gomod:
paths:
- module1
- module2
Patches
Dalec supports applying patches to sources. Patches must be specified in the sources section just like any other type of source. To apply a source as a patch to another source there is a patches section that is a mapping of the source name you want to apply a patch to, to an ordered list of sources that are the patch to apply.
sources:
md2man:
git:
url: https://github.com/cpuguy83/go-md2man.git
commit: v2.0.3
generate:
gomod: {} # Generates a go module cache to cache dependencies
md2man-patch:
http:
url: https://github.com/cpuguy83/go-md2man/commit/fd6bc094ed445b6954a67df55e75d7db95fa8879.patch
patches:
m2dman: # Name of the source we want to patch.
# Each entry is a patch spec and points to a source listed in the `sources` section
- source: md2man-patch # The name of the source that contains the patch
path: "" # Path inside the patch source where the patch file is located.
# Add more patches to the list (After adding them to the sources section) if needed
Each patch in the list of patch sources MUST be pointing to a file. When the
patch source is a file-based source, such as http
, the path
parameter in the
patch spec must not be set. When the patch is a directory-based source, such as
context
, the path
parameter in the patch spec must be set AND referencing a file
in the source.
See the source type definitions for if a source type is directory or file based.
Here is another example using a directory-backed source for patches. In the example we'll also sow using multiple patch files from the same source.
sources:
md2man:
git:
url: https://github.com/cpuguy83/go-md2man.git
commit: v2.0.3
localPatches:
context: {}
patches:
md2man:
- source: localPatches
path: patches/some0.patch
- source: localPatches
path: patches/some2.patch
Note: If you want to optimize the above example you can use the includes
feature on the context source so that only the needed files are fetched during
the build.
sources:
md2man:
git:
url: https://github.com/cpuguy83/go-md2man.git
commit: v2.0.3
localPatches:
context: {}
includes:
- patches/some0.patch
- patches/some1.patch
patches:
md2man:
- source: localPatches
path: patches/some0.patch
- source: localPatches
path: patches/some2.patch
Here is another example where we have a directory-based source with a subpath defined on the source. Note that even if the subpath is pointing to a file, it is still considered a directory-based source and still requires specifying a path in the patch spec.
sources:
md2man:
git:
url: https://github.com/cpuguy83/go-md2man.git
commit: v2.0.3
localPatches:
context: {}
path: patches/some0.patch
patches:
md2man:
- source: localPatches
path: some0.patch
Advanced Source Configurations
You can see more advanced configurations in our test fixtures. These are in here to test lots of different edge cases and are only mentioned to provide examples of what might be possible when these simple configurations are not enough. The examples in that directory are not exhaustive and are not guaranteed to work in all cases or with all inputs and are there strictly for testing purposes.