Skip to content

rcwbr/dockerfile-partials

Repository files navigation

dockerfile-partials

Dockerfile partials and devcontainer bake files for re-use across multiple applications.

GitHub cache bake file

The github-cache-bake.hcl bake config file provides a basic bake configuration for use in repos that produce container images using GitHub Actions and targeted to the GitHub container registry.

The file configures registry outputs and registry cache-to and cache-from, all directed to the specified registry. It infers naming and tagging config for the image based on the context (local vs. CI, protected vs. development versions), such that while each context can push both image and cache content, development and local contexts do not collide with each other or with releases.

GitHub cache bake file usage

The configuration of the bake file requires the use of a Docker builder with the docker-container driver. To set one up, use the following command:

docker builder create --use --bootstrap --driver docker-container

The bake file requires at least IMAGE_NAME variable to be set, and REGISTRY should nearly always be overridden (see GitHub cache bake file inputs). Export them:

REGISTRY=ghcr.io/[your account]
IMAGE_NAME=[image name]

By default, the github-cache-bake.hcl file expects a Dockerfile in the bake command execution directory (via cwd://Dockerfile), which is used for the build. It sets the default context to BAKE_CMD_CONTEXT such that the filesystem local to the bake execution are available to the image build.

To build your image using the GitHub cache bake file via remote bake definition, run this command:

REGISTRY=ghcr.io/[your account] IMAGE_NAME=[image name] docker buildx bake --file github-cache-bake.hcl https://github.com/rcwbr/dockerfile-partials.git#0.13.0

GitHub cache bake file inputs

The GitHub cache bake file can be configured using the following inputs:

Variable Required Default Effect
IMAGE_NAME N/A The name of the image, specifically not fully-qualified. This is the reference loaded to the host Docker daemon.
GITHUB_REF_NAME "local-${HOST}" The human-friendly ref name for the GitHub context. Used to inform the image tag.
GITHUB_REF_PROTECTED "false" Indicates if the CI context is for a protected (vs. development) ref. Indicates whether to append a unique version ID to the tag
GITHUB_SHA "local" The commit SHA of the CI context. Appended to tags after VERSION unless GITHUB_REF_PROTECTED is true.
HOST $HOSTNAME The name of the host device. Used to qualify image and cache names and avoid collisions between users.
HOSTNAME N/A The name of the host device, for some OSs.
REGISTRY "ghcr.io/" The registry to which to push remote content. Generally, "ghcr.io/[user/org]/".
IMAGE_REF "${REGISTRY}${IMAGE_NAME}" The fully-qualified image name used as the base for version and cache references.
VERSION ${GITHUB_REF_NAME}, sanitized This is the default tag for the image.

Devcontainer bake files

Each Dockerfile partial is accompanied by a devcontainer-bake.hcl bake config file, and a common bake file is defined at the repository root. These are intended to make composition of devcontainer image contents trivial. They are designed to work with the devcontainer-cache-build initialize script.

Using these bake files remotely requires setting the primary build context to this repo by remote definition. Because of this limitation of remote bake build contexts

We don't currently support reading a remote Dockerfile with a local context when doing a remote invocation because we automatically derive the dockerfile from the context atm

this means that any build contexts required by the Dockerfile partial must be provided via a local directory contexts context rather than the primary build context. For example, the pre-commit Dockerfile reads from the local_context context set to BAKE_CMD_CONTEXT. This allows it to consume contents of the downstream build context.

Devcontainer bake files devcontainer-cache-build usage

In a devcontainer.json leveraging the devcontainer-cache-build initialize script, several fields must be configured to fully integrate. These include:

  1. The initializeCommand (see Devcontainer bake files devcontainer-cache-build initializeCommand config)
  2. For some images, such as pre-commit and Zsh, map onCreateCommand to "/opt/devcontainers/on_create_command"
  3. For some images, map postCreateCommand to "/opt/devcontainers/post_create_command"

Devcontainer bake files devcontainer-cache-build initializeCommand config

Add the following configuration to the script called by initializeCommand before the curl to the initialize script:

# .devcontainer/initialize
export DEVCONTAINER_DEFINITION_TYPE=bake
export DEVCONTAINER_DEFINITION_FILES="devcontainer-bake.hcl [path to each desired partial bake file] cwd://.devcontainer/devcontainer-bake.hcl"
export DEVCONTAINER_BUILD_ADDITIONAL_ARGS=https://github.com/rcwbr/dockerfile-partials.git#0.13.0
curl https://raw.githubusercontent.com/rcwbr/devcontainer-cache-build/0.13.0/devcontainer-cache-build-initialize | bash

DEVCONTAINER_DEFINITION_FILES must begin with devcontainer-bake.hcl and end with cwd://.devcontainer/devcontainer-bake.hcl (see Devcontainer bake files devcontainer-cache-build .devcontainer/devcontainer-bake.hcl config), and with each desired partial bake file in between (ordering is important for override priority). For example:

export DEVCONTAINER_DEFINITION_FILES="devcontainer-bake.hcl useradd/devcontainer-bake.hcl pre-commit/devcontainer-bake.hcl cwd://.devcontainer/devcontainer-bake.hcl"

Devcontainer bake files devcontainer-cache-build .devcontainer/devcontainer-bake.hcl config

To join the devcontainer partial bake files, you must define a bake file local to your project that configures targets from each partial you select. It must define at least the devcontainer_layers variable as a list with the names of each selected partial, and override the base_context for at least the first partial target. For example:

variable "devcontainer_layers" {
  default = [
    "useradd",
    "pre-commit"
  ]
}

target "useradd" {
  contexts = {
    base_context = "docker-image://python:3.12.4"
  }
}

Optionally, targets may be configured for each layer. Values provided to these will override those defined in the partials devcontainer-bake.hcl.

Devcontainer bake files direct usage

The partial bake files may be used manually through a command like this:

docker buildx bake --file devcontainer-bake.hcl [--file arg for each desired partial bake file] --file cwd://.devcontainer/devcontainer-bake.hcl https://github.com/rcwbr/dockerfile-partials.git#0.13.0

Devcontainer script integration

Dockerfile partials

docker-client

The docker-client Dockerfile defines steps to install the Docker CLI client in a Docker image. It copies the CLI executable from the Docker docker image.

docker-client Dockerfile usage

The recommended usage is via the Devcontainer bake files. It is also possible to use the Dockerfile partial directly.

Use a Bake config file, and set the base_context context as the image to which to apply the docker-client installation. For example:

target "base" {
  dockerfile = "Dockerfile"
}

target "default" {
  context = "https://github.com/rcwbr/dockerfile_partials.git#0.13.0"
  dockerfile = "docker-client/Dockerfile"
  contexts = {
    base_context = "target:base"
  }
}

The args accepted by the Dockerfile include:

Variable Required Default Effect
DOCKER_GID 800 Group ID of the docker user group
USER "root" Username to grant access to the Docker daemon

docker-client bake file usage

The docker-client partial contains a devcontainer bake config file. See Devcontainer bake files for general usage. The docker-client bake config file accepts the following inputs:

Variable Required Default Effect
DOCKER_GID 800 See docker-client Dockerfile
USER "root" See docker-client Dockerfile

docker-client devcontainer usage

The docker-client partial installs only the client CLI by default. To leverage the container host's Docker daemon, the relevant socket must be mounted at runtime. In a devcontainer.json, the following content must be included:

{
  "image": "[image including docker-client layers]",
  "mounts": [
    { "source": "/var/run/docker.sock", "target": "/var/run/docker.sock", "type": "bind" }
  ]
}

pre-commit

The pre-commit layer defines steps to install pre-commit and install the hooks required by a repo configuration.

This layer produces an accompanying tool image that contains pre-commit executions, while the main image presents that tool image as the pre-commit executable:

graph TD;

pl[_prior layers..._] --> tool[pre-commit-tool-image]
pl --> pre-commit[pre-commit]
tool -.-> pre-commit
pre-commit --> nl[_next layers..._]
Loading

⚠️ Use of the pre-commit layer requires that the variable DEVCONTAINER_HOST_WORKSPACE_MOUNT be provided and set to the path of the workspace on the host filesystem. This informs the workspace mount for the pre-commit-tool-image container.

pre-commit Dockerfile usage

The recommended usage is via the Devcontainer bake files. It is also possible to use the Dockerfile partial directly.

Use a Bake config file, and set the base_context context as the image to which to apply the pre-commit installation, and the local_context to the directory from which the .pre-commit-config.yaml can be loaded (generally BAKE_CMD_CONTEXT). Additionally, provide appropriate values for the USER build arg. For example:

target "base" {
  dockerfile = "Dockerfile"
}

target "default" {
  context = "https://github.com/rcwbr/dockerfile_partials.git#0.13.0"
  dockerfile = "pre-commit/Dockerfile"
  contexts = {
    base_context = "target:base"
    local_context = BAKE_CMD_CONTEXT
  }
  args = {
    USER = "myuser"
  }
}

The args accepted by the Dockerfile include:

Variable Required Default Effect
USER "root" Username to assume for hook pre-loading

pre-commit bake file usage

The pre-commit partial contains a devcontainer bake config file. See Devcontainer bake files for general usage. The pre-commit bake config file accepts the following inputs:

Variable Required Default Effect
USER "root" See pre-commit Dockerfile
DEVCONTAINER_REGISTRY "" The registry to which the pre-commit-tool-image belongs
DEVCONTAINER_IMAGE "" The base devcontainer image name to which naming for the pre-commit image will be appended
GIT_BRANCH_SANITIZED "" The context Git branch name, sanitized for use as a Docker image tag
PRE_COMMIT_TOOL_IMAGE_NAME "${DEVCONTAINER_REGISTRY}/${DEVCONTAINER_IMAGE}-pre-commit:${GIT_BRANCH_SANITIZED}" The name of the pre-commit-tool-image

The devcontainer-bake.hcl config devcontainer_layers variable must list each of the pre-commit targets; pre-commit-base, pre-commit-tool-image, and pre-commit.

For example:

variable "devcontainer_layers" {
  default = [
    "docker-client",
    "useradd",
    "pre-commit-base",
    "pre-commit-tool-image",
    "pre-commit"
  ]
}

pre-commit Codespaces usage

For use in Codespaces devcontainers, the build args must be set to the following values:

  • USER: codespace

These values may be hard-coded in the Bake config file, or may be exposed as variables for compatibility with local environments.

variable "USER" {
  default = "root"
}
...
  args = {
    USER = "${USER}"
  }
...

If exposed as variables, the appropriate values for Codespaces use must be set as secrets so as to be available during Codespace provisioning.

In a devcontainer.json for a Codespace, the DEVCONTAINER_HOST_WORKSPACE_MOUNT var should be set to /var/lib/docker/codespacemount/workspace/[repo name], e.g.:

{
  "containerEnv": {
    "DEVCONTAINER_HOST_WORKSPACE_MOUNT": "/var/lib/docker/codespacemount/workspace/dockerfile-partials"
  },
}

pyenv

The pyenv layer defines steps to install pyenv, leverage it to install a specified version of Python, and prepare a virtualenv based on that Python installation.

pyenv Dockerfile usage

The recommended usage is via the Devcontainer bake files. It is also possible to use the Dockerfile partial directly.

Use a Bake config file, and set the base_context context as the image to which to apply the pyenv installation. For example:

target "base" {
  dockerfile = "Dockerfile"
}

target "default" {
  context = "https://github.com/rcwbr/dockerfile_partials.git#0.13.0"
  dockerfile = "pyenv/Dockerfile"
  contexts = {
    base_context = "target:base"
  }
}

The args accepted by the Dockerfile include:

Variable Required Default Effect
PYTHON_VERSION 3.12.4 The version of Python to install
VIRTUALENV_NAME venv The name of the virtualenv to create
PYENV_ROOT /opt/devcontainers/pyenv The path in which to install Pyenv

pyenv bake file usage

The pyenv partial contains a devcontainer bake config file. See Devcontainer bake files for general usage.

tmux

The tmux Dockerfile defines steps to install tmux in a Docker image.

tmux Dockerfile usage

The recommended usage is via the Devcontainer bake files. It is also possible to use the Dockerfile partial directly.

Use a Bake config file, and set the base_context context as the image to which to apply the tmux installation. For example:

target "base" {
  dockerfile = "Dockerfile"
}

target "default" {
  context = "https://github.com/rcwbr/dockerfile_partials.git#0.13.0"
  dockerfile = "tmux/Dockerfile"
  contexts = {
    base_context = "target:base"
  }
}

tmux bake file usage

The tmux partial contains a devcontainer bake config file. See Devcontainer bake files for general usage. The tmux bake config file accepts no inputs.

useradd

The useradd Dockerfile defines steps to add a user to the image, with configurable user name, id, and group id.

useradd Dockerfile usage

The recommended usage is via the Devcontainer bake files. It is also possible to use the Dockerfile partial directly.

Use a Bake config file, and set the base_context context as the image to which to apply the user addition. Additionally, provide appropriate values for the USER, USER_UID, and USER_GID build args. For example:

target "base" {
  dockerfile = "Dockerfile"
}

target "default" {
  context = "https://github.com/rcwbr/dockerfile_partials.git#0.13.0"
  dockerfile = "useradd/Dockerfile"
  contexts = {
    base_context = "target:base"
  }
  args = {
    USER = "myuser"
    USER_UID = 1000
    USER_GID = 1000
  }
}

The args accepted by the Dockerfile include:

Variable Required Default Effect
USER N/A Username of the user to create
EXTRA_GID_ARGS "" Extra --gid [id] args to apply to the useradd command
USER_UID 1000 User UID for the user to create
USER_GID $USER_UID User GID for the user to create

useradd bake file usage

The useradd partial contains a devcontainer bake config file. See Devcontainer bake files for general usage. The useradd bake config file accepts the following inputs:

Variable Required Default Effect
USER "root" See useradd Dockerfile
EXTRA_GID_ARGS "" or DOCKER_CLIENT_EXTRA_GID_ARGS if defined See useradd Dockerfile
UID 0 Maps to USER_UID. See useradd Dockerfile
GID ${UID} Maps to USER_GID.See useradd Dockerfile

useradd Codespaces usage

For use in Codespaces devcontainers, the build args must be set to the following values:

  • USER: codespace
  • UID: 1000

These values may be hard-coded in the Bake config file, or may be exposed as variables for compatibility with local environments.

variable "USER" {
  default = "root"
}
variable "UID" {
  default = 0
}
variable "GID" {
  // Use the user id as group id unless set
  default = "${UID}"
}

...
  args = {
    USER = "${USER}"
    USER_UID = "${UID}"
    USER_GID = "${GID}"
  }
...

If exposed as variables, the appropriate values for Codespaces use must be set as secrets so as to be available during Codespace provisioning.

uv-project

The uv-project Dockerfile defines steps to prepare an environment to work with a Python project, using the uv tool.

uv-project Dockerfile usage

The recommended usage is via the Devcontainer bake files. It is also possible to use the Dockerfile partial directly.

Use a Bake config file, and set the base_context context as the image to which to apply the uv-project installation, the local_context to the directory in which the Python project is defined, and the UV_PACKAGE_NAME build arg to the name of the package. For example:

target "base" {
  dockerfile = "Dockerfile"
}

target "default" {
  context = "https://github.com/rcwbr/dockerfile_partials.git#0.13.0"
  dockerfile = "uv-project/Dockerfile"
  contexts = {
    base_context = "target:base"
    local_context = BAKE_CMD_CONTEXT
  }
  args = {
    UV_PACKAGE_NAME = "<your package name>"
  }
}

uv-project devcontainer bake file usage

The uv-project partial contains a devcontainer bake config file. See Devcontainer bake files for general usage. The uv-project bake config file accepts no inputs.

uv-project bake file usage

The uv-project partial contains a bake config file for providing. The uv-project bake config file accepts no inputs directly, but the UV_PACKAGE_NAME build arg should be specified (see uv-project Dockerfile usage), similarly to this:

// docker-bake.hcl
target "uv-project" {
  args = {
    UV_PACKAGE_NAME = "<your package name>"
  }
}

Then the local bake config may be used alongside the partial by executing this command:

docker buildx bake \
	-f uv-project/docker-bake.hcl \
	-f cwd://docker-bake.hcl \
	https://github.com/rcwbr/dockerfile_partials.git#0.13.0

Zsh

The Zsh Dockerfile defines steps to install Zsh, Oh My Zsh, fzf, and thefuck to the image.

Zsh Dockerfile usage

The recommended usage is via the Devcontainer bake files. It is also possible to use the Dockerfile partial directly.

Use a Bake config file, and set the base_context context as the image to which to apply the Zsh installation. For example:

target "base" {
  dockerfile = "Dockerfile"
}

target "zsh-thefuck-pyenv" {
  dockerfile = "pyenv/Dockerfile"
  contexts = {
    base_context = "target:base"
  }
  args = {
    PYTHON_VERSION = "3.8.20"
    VIRTUALENV_NAME = "thefuck"
  }
}

target "default" {
  context = "https://github.com/rcwbr/dockerfile_partials.git#0.13.0"
  dockerfile = "zsh/Dockerfile"
  contexts = {
    base_context = "target:base"
    pyenv_context = "target:zsh-thefuck-pyenv"
  }
}

Zsh bake file usage

The Zsh partial contains a devcontainer bake config file. See Devcontainer bake files for general usage. The Zsh bake config file accepts no inputs.

The devcontainer-bake.hcl config devcontainer_layers variable must list each of the Zsh targets; zsh-base, zsh-thefuck-pyenv, and zsh.

For example:

variable "devcontainer_layers" {
  default = [
    "docker-client",
    "zsh-base",
    "zsh-thefuck-pyenv",
    "zsh",
  ]
}

pre-commit reusable workflow

The .github/workflows/pre-commit.yaml reusable workflow defines steps to execute pre-commit CI checks by leveraging the pre-commit-tool-image layer.

pre-commit reusable workflow usage

Basic usage of the workflow consists of including it in a GitHub workflow:

on:
  pull_request:
jobs:
  pre-commit:
    if: github.event_name == 'pull_request'
    uses: rcwbr/dockerfile-partials/.github/workflows/pre-commit.yaml@0.13.0

ℹ️ Often, pre-commit configurations require the commit context of a pull request as input. To apply the pre-commit workflow to other contexts, omit or modify the if: github.event_name == 'pull_request' condition.

If using the pre-commit workflow alongside a devcontainer-cache-build workflow, the pre-commit image generated by that workflow should be provided as input to the pre-commit workflow. For example:

...
jobs:
  pre-commit:
    if: github.event_name == 'pull_request'
    uses: rcwbr/dockerfile-partials/.github/workflows/pre-commit.yaml@0.13.0
    needs: devcontainer-cache-build
    with:
      pre-commit-image: ${{ fromJSON(needs.devcontainer-cache-build.outputs.devcontainer-cache-image-all_configs).target.pre-commit.args.DEVCONTAINER_PRE_COMMIT_IMAGE }}

pre-commit reusable workflow inputs

Variable Required Default Effect
pre-commit-image pre-commit The image used for pre-commit execution in the workflow.

Contributing

devcontainer

This repo contains a devcontainer definition in the .devcontainer folder. It leverages the devcontainer cache build tool and the Dockerfile partials defined in this repo.

devcontainer basic usage

The devcontainer cache build tool requires authentication to the GitHub package registry, as a token stored as DOCKERFILE_PARTIALS_DEVCONTAINER_INITIALIZE (see instructions).

devcontainer Codespaces usage

For use with Codespaces, the DOCKERFILE_PARTIALS_DEVCONTAINER_INITIALIZE token (see devcontainer basic usage) must be stored as a Codespaces secret (see instructions), as must values for USER, and UID (see useradd Codespaces usage).

devcontainer pre-commit usage

By default, the devcontainer configures pre-commit hooks in the repository to ensure commits pass basic testing. This includes enforcing conventional commit messages as the standard for this repository, via commitlint.

CI/CD

This repo uses the release-it-gh-workflow, with the conventional-changelog image defined at any given ref, as its automation. It leverages the devcontainer-cache-build workflow to pre-generate devcontainer images, which are also used for the pre-commit workflow. Finally, a Dive Docker image efficiency analysis job first builds an image with all partials layers included, then analyses the storage efficiency of the resulting image.

Settings

The GitHub repo settings for this repo are defined as code using the Probot settings GitHub App. Settings values are defined in the .github/settings.yml file. Enabling automation of settings via this file requires installing the app.

The settings applied are as recommended in the release-it-gh-workflow usage, including tag and branch protections, GitHub App and environment authentication, and required checks.

About

Dockerfile partials for re-use across multiple applications

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors