Using docker-compose
in dev, but a more complex orchestrator in production?
This script (and GitHub
action) uses the (Docker) compose file to capture building
information for your images, but can help out when pushing to several registries
or tagging them with a release number (from a GitHub workflow?), for example.
The script supplements and/or replaces docker(-)compose build
with direct
calls to docker build
in order to:
- Push the resulting image(s) to their registries.
- Replace the registry settings from the compose file with another one, if necessary.
- Re-tag, or add tags to the images that will be built and/or pushed.
- Perform some (automated) initialisation actions prior to building/pushing.
- Perform some (automated) cleanup actions once all images have been built and/or pushed. This can be used to trigger actions at the orchestration layer, for example.
This script has sane defaults. If docker-compose
is installed, its default
behaviour is to build all or some of the services that are pointed at by the
(Docker) compose file in the current directory. When operations such as
pushing, retagging or changing the destination registry are requested, the
script will actively read the content of the compose file and pick the
necessary information from there. At the time of writing, this will work best
when the services
section is last in the file.
The result of this script is a list of the Docker images that were built (or
pushed, depending) on stdout
, one per
line. This is to facilitate automation. All other output (logging, output of
docker
or docker-compose
, etc.) is redirected to stderr
.
The project even implements a replacement for
docker-compose
build
(and push
), with a similar UX at the CLI (i.e. same
set of options and flags).
To use this script in your projects, you can either make this project a submodule or subtree of your main project. The script will print a warning when a new version is available. This script itself makes use of a reg-tags as a subtree to interact with remote Docker registries without downloading the images.
The script is designed to work against compose files according to the following skeleton:
version: "2.2"
services:
myservice:
build:
context: .
dockerfile: Dockerfile
image: myregistry.mydomain.com/myimage
When docker-compose
is installed, the following command would simply relay
calling the build
sub-command of docker-compose
. When docker-compose
is
not installed (but docker
is), the script will automatically switch to
building with the docker
CLI client. As the default is to read information
from the docker-compose.yml
file, the resulting image will be as if it had
been created by running docker-compose build
.
./build.sh
When run with the following command, the script will build using the docker
CLI client. It will automatically pick buildx
or old-style build
, in that
order, and depending on your local installation. The behaviour is the same as
when running with docker-compose
: only images that have a build
context will
be built.
./build.sh -b auto
When run with the following command, the script will automatically add the tag
test
to the image that is being built. Adding a tag will automatically depart
from the default docker-compose
-based build and use the installed docker
instead.
./build.sh -t test
The script accepts a number of command-line options and flags. Its behaviour can
also be changed through a number of environment variables, all starting with the
BUILD_
prefix. Command-line options and flags always have precedence over
variables. All further arguments to the script will be blindly passed to
docker-compose
or docker
when building. To ensure correctness, you can
separate the command-line options and flags, from the arguments using a
double-dash, i.e. --
.
Provide quick usage summary at the terminal and exit.
When the flag is given or the variable set to 1
, the script will describe the
various steps that it performs more verbosedly on the stderr
.
When the flag is given or the variable set to 1
, the script will only describe
the various steps that it would perform on the stderr
, but not actually do
anything. No image name will be printed out on the stdout
, as no image was
built or pushed.
Specifies the location of the compose file to use. The default is to look for the following files, in that order, first in the current directory, then in the same directory as the script is located at:
compose.yaml
compose.yml
docker-compose.yaml
docker-compose.yml
When the flag is given or the variable set to 1
, the images specified by the
compose file will also be pushed to their respective registries, once building
has finished. Your local docker
client must have enough credentials to access
the remote registries. Old images will automatically be
skipped.
Specifies the builder to use, can be one of:
compose
(the default): will try hard to use compose, but will revert to theauto
builder (see below) whendocker-compose
is not installed.auto
: will pick the best of the newbuildx
or old-styledocker build
, depending on which is available, and in that order, i.e.buildx
preferred.buildx
: will use the newbuildx
for building. This requires thebuildx
Docker plugin to be available and properly installed.docker
orbuild
: will use the old-styledocker build
command.- An empty string: will not build anything, only push in case
-p
was specified. Out-of-script initialisation will still happen. This facilitates running the script in two distinct phases, i.e. first build, then push, as in the following example:
./build.sh; # Build, do not push
./build.sh -b "" -p; # Do not build, but push
Specifies the space separated list of services to build. These services need to exist in the compose file. When empty, the default, the script will default to building/pushing all the services specified in the compose file.
Specifies the space separated list of tags to give to the images that will be built/pushed. When building, the image with the first tag in the list will be built, while images with the other tags will be tagged with the first image as the source. The default is to not specified any tag, in which case the tag from the compose file will be picked up and used, if any.
Specifies an alternative registry to use instead of the one specified as part of the compose file. When no registry is given, the default, the registry will be the one from the compose file.
Specifies the maximum age of the image since creation to decide whether it
should be pushed or not. This is a safety measure to avoid pushing "junk" images
that would have been built at a prior run of the script, but without the
-p
flag. The default of 1200
seconds
should work in most cases, but any negative value will turn this check off,
meaning that all relevant images will be pushed, disregarding their age.
The implementation will always check if the destination image (and tag) exists at the remote registry. When it does not, and disregarding the specified age, the image will be pushed to ensure its existence.
Specifies a list of directory paths, separated by the colon :
sign wherefrom
to find and execute initialisation actions. All exectuable (scripts or programs)
in these directories will automatically be executed once the script
initialisation has ended and before build and push operations are about to
start. Initialisation happens in the order of the directories in the path, and
in the alphabetical order of the executable files, within each directory. All
variables starting with BUILD_
are exported prior to running these
out-of-script initialisation actions, the variables will reflect the state of
the script after its initialisation has performed. In other words, variables
such as BUILD_BUILDER
will reflect
the builder that is about to be used.
The default for this variable is the special character -
. It will
automatically be resolved to the directory named
build-init.d
in the same directory as the compose
file, and the directory containing the script.
Specifies a list of directory paths, separated by the colon :
sign wherefrom
to find and execute cleanup actions. All exectuable (scripts or programs) in
these directories will automatically be executed once images have been built and
pushed. Cleanup happens in the order of the directories in the path, and in the
alphabetical order of the executable files, within each directory. All variables
starting with BUILD_
are exported prior to running these out-of-script cleanup
actions, similarily to the -i
option. In addition, cleanup actions can know about built and/or pushed images
through the BUILD_IMAGES
variable.
The default for this variable is the special character -
. It will
automatically be resolved to the directory named
build-cleanup.d
in the same directory as the
compose file, and the directory containing the
script.
Specifies a glob pattern. Executable files from the init and cleanup directories, which basename match the pattern will not be executed. The default is to ignore Markdown files as these would otherwise be deemed executables when running with WSL.
Specifies how to run docker-compose
, which also is the default value.
Specifies how to run docker
, which also is the default value. This makes it,
in theory, possible to use alternatives such as podman or nerdctl.
Specifies the root directory for the script, and is automatically initialised to
the root directory of the script. The root directory is used in the default
value for a number of other variables, e.g.
BUILD_INIT_DIR
or
BUILD_COMPOSE
. BUILD_ROOTDIR
will
be passed further to out-of-script
initialisations to help them locating
the triggering script, if necessary.
Specifies the command used to download release information from GitHub. When run
the command specified in this variable will be given an additional argument, the
URL to download and it should dump the content of the URL to stdout
as a
result. When empty, the default, one of curl
or wget
, if present, will be
used. When a dash -
, version checks will be skipped.
This variable is computed by the script as it progresses, it cannot be set in any way. It is passed further to cleanup programs at the end. The variable contains the space-separated list of images that were built, or the list of images that were pushed. When images were requested to be built and pushed, only the list of pushed images will be present.
The script doubles as a GitHub Action, use it in a workflow as exemplified
below, provided you have access to docker
. For a complete list of inputs and
their usage, consult the action.yml
file. The action results
in an output called images
. images
contains the list of images that were
built/pushed, one per line.
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Build and Push
uses: Mitigram/docker-compose-build@main
with:
compose: <path-to-compose.yml>
In addition, this project contains a shim that is able to
entirely replace docker-compose
build
(and push
). The shim can be
installed and called docker-compose
at the OS level and used to build Docker
images directly with the docker
client, bypassing docker-compose
entirely.
This can be usefull to use alternative projects for building Docker images, as
long as they implement a docker
-compatible CLI interface, e.g. img or
nerdctl. The shim is capable of translating the standard set of CLI options to
the build
command of docker-compose
, to the specific set of options
supported by the build
command of the docker
, img
and nerdctl
apps.
To install the shim as docker-compose
, you can make a copy of this project
under $HOME/.local/share/docker-compose-build
and create a symbolic link to
the compose.sh
script from $HOME/.local/bin/docker-compose
(and arrange for
$HOME/.local/bin
to be first in your $PATH
).
As build has become an optional part of the compose specification, this
script could provide a replacement for docker compose build
, if ever this
functionality was removed (?!). In its current shape, the tool supports the most
common build options, i.e. context and dockerfile, but also build arguments
and labels.