Skip to content

Commit

Permalink
feat: useradd and pre-commit partials; root bake
Browse files Browse the repository at this point in the history
  • Loading branch information
rcwbr committed Sep 22, 2024
1 parent 32b521c commit 87b03a6
Show file tree
Hide file tree
Showing 6 changed files with 346 additions and 2 deletions.
213 changes: 211 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,211 @@
# dockerfile_partials
Dockerfile partials for re-use across multiple applications
# dockerfile-partials

Dockerfile partials and devcontainer [bake](https://docs.docker.com/build/bake/introduction/) files for re-use across multiple applications.

## Devcontainer bake files

Each Dockerfile partial is accompanied by a `devcontainer-bake.hcl` [bake](https://docs.docker.com/build/bake/introduction/) 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](https://github.com/rcwbr/devcontainer-cache-build/tree/main?tab=readme-ov-file#initialize-script).

Using these bake files remotely requires setting the primary build context to this repo by [remote definition](https://docs.docker.com/build/bake/remote-definition/). Because of [this limitation of remote bake build contexts](https://github.com/docker/buildx/blob/056cf8a7ca083d91eccf9200e7e2c99ff170bbaf/bake/bake.go#L1213C6-L1213C73)

> ```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](https://docs.docker.com/build/bake/reference/#targetcontexts) rather than the primary build context. For example, the `pre-commit` Dockerfile [reads from the `local_context` context](https://github.com/rcwbr/dockerfile-partials/blob/086902fb92beb8ab2bf887ff6a6a141804762eb9/pre-commit/Dockerfile#L10C26-L10C39) [set to `BAKE_CMD_CONTEXT`](https://github.com/rcwbr/dockerfile-partials/blob/086902fb92beb8ab2bf887ff6a6a141804762eb9/pre-commit/devcontainer-bake.hcl#L11). 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](https://github.com/rcwbr/devcontainer-cache-build/tree/main?tab=readme-ov-file#initialize-script), add the following configuration to the `initializeCommand` before the `curl` to the initialize script:

#### Devcontainer bake files devcontainer-cache-build initializeCommand config

```json5
...
"initializeCommand": [
...
"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.1.0",
...
]
...
```

`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](#devcontainer-bake-files-devcontainer-cache-build-devcontainerdevcontainer-bakehcl-config), and with each desired partial bake file in between (ordering is important for override priority). For example:

```bash
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:
```hcl
variable "devcontainer_layers" {
default = [
"useradd",
"pre-commit"
]
}
target "useradd" {
contexts = {
base_context = "docker-image://python:3.12.4"
}
}
```
Optionally, `target`s 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:
```bash
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.1.0
```
## Dockerfile partials
### pre-commit
The pre-commit Dockerfile defines steps to install [pre-commit](https://pre-commit.com/) and install the hooks required by a repo configuration.
#### pre-commit Dockerfile usage
The recommended usage is via the [Devcontainer bake files](#devcontainer-bake-files). It is also possible to use the Dockerfile partial directly.
Use a [Bake](https://docs.docker.com/reference/cli/docker/buildx/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`](https://docs.docker.com/build/bake/reference/#built-in-variables)). Additionally, provide appropriate values for the `USER` build arg. For example:
```hcl
target "base" {
dockerfile = "Dockerfile"
}
target "default" {
context = "https://github.com/rcwbr/dockerfile_partials.git#0.1.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](#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](#pre-commit-dockerfile-usage) |
#### Codespaces usage
For use in [Codespaces](https://github.com/features/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.
```hcl
variable "USER" {
default = "root"
}
...
args = {
USERNAME = "${USER}"
}
...
```
If exposed as variables, the appropriate values for Codespaces use must be [set as secrets](https://docs.github.com/en/codespaces/managing-your-codespaces/managing-your-account-specific-secrets-for-github-codespaces#adding-a-secret) so as to be available during Codespace provisioning.
### 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](#devcontainer-bake-files). It is also possible to use the Dockerfile partial directly.
Use a [Bake](https://docs.docker.com/reference/cli/docker/buildx/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 `USERNAME`, `USER_UID`, and `USER_GID` build args. For example:
```hcl
target "base" {
dockerfile = "Dockerfile"
}
target "default" {
context = "https://github.com/rcwbr/dockerfile_partials.git#0.1.0"
dockerfile = "useradd/Dockerfile"
contexts = {
base_context = "target:base"
}
args = {
USERNAME = "myuser"
USER_UID = 1000
USER_GID = 1000
}
}
```
The args accepted by the Dockerfile include:
| Variable | Required | Default | Effect |
| --- | --- | --- | --- |
| `USERNAME` | ✓ | N/A | Username of the user to create |
| `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](#devcontainer-bake-files) for general usage. The useradd bake config file accepts the following inputs:
| Variable | Required | Default | Effect |
| --- | --- | --- | --- |
| `USER` | ✗ | `"root"` | See [useradd Dockerfile](#useradd-dockerfile-usage) |
| `UID` | ✗ | `0` | See [useradd Dockerfile](#useradd-dockerfile-usage) |
| `GID` | ✗ | `${UID}` | See [useradd Dockerfile](#useradd-dockerfile-usage) |
#### useradd Codespaces usage
For use in [Codespaces](https://github.com/features/codespaces) devcontainers, the build args must be set to the following values:
- `USERNAME`: `codespace`
- `USER_UID`: `1000`
- `USER_GID`: `1000`
These values may be hard-coded in the Bake config file, or may be exposed as variables for compatibility with local environments.
```hcl
variable "USER" {
default = "root"
}
variable "UID" {
default = 0
}
variable "GID" {
// Use the user id as group id unless set
default = "${UID}"
}
...
args = {
USERNAME = "${USER}"
USER_UID = "${UID}"
USER_GID = "${GID}"
}
...
```
If exposed as variables, the appropriate values for Codespaces use must be [set as secrets](https://docs.github.com/en/codespaces/managing-your-codespaces/managing-your-account-specific-secrets-for-github-codespaces#adding-a-secret) so as to be available during Codespace provisioning.
57 changes: 57 additions & 0 deletions devcontainer-bake.hcl
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// This file prepares only basic target configurations, handling the default base_contexts and output.
// All other config for a target must be overridden by accompanying bake files.
// It also configures a default group with each target listed.

variable "DEVCONTAINER_CACHE_FROMS" {
default = ""
}
variable "DEVCONTAINER_CACHE_TOS" {
default = ""
}
variable "DEVCONTAINER_OUTPUTS" {
default = ""
}

target "layer" {
name = "${layer.name}"
matrix = {
layer = [
for index, input_layer in devcontainer_layers: {
name = input_layer,
index = index
}
]
}

// Apply the contexts from options and context
contexts = { // Establish default base_context from layer order
base_context = (
// A default base_context can be inferred only for the second layer and later
layer.index >= 1
? "target:${element(devcontainer_layers, layer.index - 1)}"
: "no_base_context_provided"
)
}

// Apply cache args from the devcontainer context
cache-from = [
for cache_from in split(" ", trimspace("${DEVCONTAINER_CACHE_FROMS}")):
"${cache_from}-${layer.name}"
]
cache-to = [
for cache_to in split(" ", trimspace("${DEVCONTAINER_CACHE_TOS}")):
"${cache_to}-${layer.name}"
]

// Apply the default output to the final layer
output = (
layer.index == length(devcontainer_layers) - 1
? split(" ", trimspace("${DEVCONTAINER_OUTPUTS}"))
: null
)
}

group "default" {
// Target the last layer as default
targets = [devcontainer_layers[length(devcontainer_layers) - 1]]
}
25 changes: 25 additions & 0 deletions pre-commit/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Set base_context in the downstream Bake file
FROM base_context
ARG USER=root
USER root
# Install git to spoof pre-commit environment
RUN DEBIAN_FRONTEND=noninteractive \
apt-get update \
&& apt-get install --allow-unauthenticated --no-install-recommends -y \
git \
&& rm -rf /var/lib/apt/lists/*
# Assume root role for installs
USER ${USER}
SHELL [ "/bin/bash", "-c" ]
# Create a temp repo and install the hooks from the config
# Load the config file from the downstream context
RUN \
--mount=type=bind,from=local_context,source=.pre-commit-config.yaml,target=/tmp/.pre-commit-config.yaml \
python -m venv $HOME/pre-commit-venv \
&& source $HOME/pre-commit-venv/bin/activate \
&& mkdir $HOME/tmp_repo \
&& cd $HOME/tmp_repo \
&& cp /tmp/.pre-commit-config.yaml . \
&& git init . \
&& pip install pre-commit \
&& pre-commit install-hooks
13 changes: 13 additions & 0 deletions pre-commit/devcontainer-bake.hcl
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
variable "USER" {
default = "root"
}

target "pre-commit" {
dockerfile = "pre-commit/Dockerfile"
contexts = {
local_context = BAKE_CMD_CONTEXT
}
args = {
USER = "${USER}"
}
}
21 changes: 21 additions & 0 deletions useradd/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Set base_context in the downstream Bake file
FROM base_context AS base

# Sourced from https://code.visualstudio.com/remote/advancedcontainers/add-nonroot-user
ARG USERNAME=
ARG USER_UID=1000
ARG USER_GID=$USER_UID

# Create the user
RUN groupadd --gid $USER_GID $USERNAME \
&& useradd --uid $USER_UID --gid $USER_GID -m $USERNAME \
#
# [Optional] Add sudo support. Omit if you don't need to install software after connecting.
&& apt-get update \
&& apt-get install -y sudo \
&& echo $USERNAME ALL=\(root\) NOPASSWD:ALL > /etc/sudoers.d/$USERNAME \
&& chmod 0440 /etc/sudoers.d/$USERNAME

# Make use of the USER directive optional via target
FROM base AS with_user
USER $USERNAME
19 changes: 19 additions & 0 deletions useradd/devcontainer-bake.hcl
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
variable "USER" {
default = "root"
}
variable "UID" {
default = 0
}
variable "GID" {
// Use the user id as group id unless set
default = "${UID}"
}

target "useradd" {
dockerfile = "useradd/Dockerfile"
args = {
USERNAME = "${USER}"
USER_UID = "${UID}"
USER_GID = "${GID}"
}
}

0 comments on commit 87b03a6

Please sign in to comment.