From 580847f24203b48abbbd032fcf33dff71f9c15c6 Mon Sep 17 00:00:00 2001 From: bloodearnest Date: Wed, 14 Jan 2026 16:18:15 +0000 Subject: [PATCH 1/7] Add more linting Add shellcheck and actionlint linting, and fix related errors --- .github/workflows/update-ubuntu-sha.yaml | 2 +- Justfile | 3 ++- docker-apt-install.sh | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/update-ubuntu-sha.yaml b/.github/workflows/update-ubuntu-sha.yaml index e25706d..0f41a2b 100644 --- a/.github/workflows/update-ubuntu-sha.yaml +++ b/.github/workflows/update-ubuntu-sha.yaml @@ -22,7 +22,7 @@ jobs: - name: Commit file run: | git status - git add *.sha + git add "*.sha" if git diff-index --quiet HEAD; then exit fi diff --git a/Justfile b/Justfile index 89db248..18fd356 100644 --- a/Justfile +++ b/Justfile @@ -15,8 +15,9 @@ clean-build: (build "--no-cache") # hadolint the Dockerfile lint: - @docker pull hadolint/hadolint @docker run --rm -i hadolint/hadolint < Dockerfile + @ls *.sh | xargs docker run --rm -v "$PWD:/mnt:ro" koalaman/shellcheck:v0.11.0 + @docker run --rm -v "$PWD:/repo:ro" --workdir /repo rhysd/actionlint:1.7.10 -color # build and test all images test: build diff --git a/docker-apt-install.sh b/docker-apt-install.sh index 8bc23cc..5d897f1 100755 --- a/docker-apt-install.sh +++ b/docker-apt-install.sh @@ -11,7 +11,7 @@ test "${UPGRADE:-}" = "yes" && apt-get upgrade --yes PACKAGES= for arg in "$@"; do - if test -f $arg; then + if test -f "$arg"; then # argument is a file # strip any comments and install every package listed in the file sed 's/^#.*//' "$arg" | xargs apt-get install --yes --no-install-recommends From 28729752f1b148099106e0a98112e96c18c70405 Mon Sep 17 00:00:00 2001 From: bloodearnest Date: Wed, 14 Jan 2026 16:23:40 +0000 Subject: [PATCH 2/7] Update readme to reflect multiple versions --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 69322a2..858116b 100644 --- a/README.md +++ b/README.md @@ -3,12 +3,13 @@ Base docker images for the OpenSAFELY framework. These provide a common, up to date base image to build on top of. -This repo produces two image: `base-docker`, and `base-action`. +This repo produces two image flavours: `base-docker`, and `base-action`. It +produces a version of these flavours for 20.04, 22.04, and 24.04, e.g. `base-docker:22.04` ## base-docker -This image is up-to-date Ubuntu 20.04 base along with common debugging tools. -(e.g. `strace`). +This image is up-to-date Ubuntu image along with common debugging tools. (e.g. +`strace`). It includes a helpful script for installing apt packages in the most docker friendly space-efficient manner. Adding this and using it in this and dependent @@ -16,12 +17,12 @@ images saves over 100MB, typically. It is rebuilt and publish weekly, so there's always a fresh base to build from. - ## base-action This is built from `base-docker` but also include a base action entrypoint, which supports the actions are used in OpenSAFELY's [project.yaml](https://docs.opensafely.org/actions-pipelines/) + This entrypoint supports invoking actions with both an explicit custom CMD or an implicit one. i.e. @@ -32,4 +33,3 @@ or Images built from base-action can define `ACTION_EXEC` env var to customise the default implicit executable used to execute. - From f6f28c363c326bc63fe2100ce0d383549e37e18d Mon Sep 17 00:00:00 2001 From: bloodearnest Date: Wed, 14 Jan 2026 16:24:47 +0000 Subject: [PATCH 3/7] Add UBUNTU_PRO_TOKEN support This ensures that an UBUNTU_PRO_TOKEN env var is set, and writes it to a file in .secrets, which is exposed to the builder via docker compose secret support. We need use a file rather than an env var to keep the token a secret, as docker build env vars get stored in the layer metadata. --- .gitignore | 2 ++ Justfile | 18 +++++++++++++++++- docker-compose.yaml | 6 ++++++ 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 49c1224..4d000f9 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ bash.link +.secrets/ +.env diff --git a/Justfile b/Justfile index 18fd356..7490aae 100644 --- a/Justfile +++ b/Justfile @@ -1,11 +1,27 @@ +set dotenv-load := true + export ACTION_IMAGE_NAME := env_var_or_default('ACTION_IMAGE_NAME', "base-action") +export UBUNTU_PRO_TOKEN_FILE := env_var_or_default('UBUNTU_PRO_TOKEN_FILE', justfile_directory() + "/.secrets/ubuntu_pro_token") _default: @just --list +ensure-pro-token: + #!/bin/bash + set -euo pipefail + token_file="{{ UBUNTU_PRO_TOKEN_FILE }}" + if test -z "${UBUNTU_PRO_TOKEN:-}"; then + echo "UBUNTU_PRO_TOKEN is required to create $token_file" >&2 + exit 1 + fi + mkdir -p "$(dirname "$token_file")" + umask 077 + printf '%s' "$UBUNTU_PRO_TOKEN" > "$token_file" + # build all images -build *args: +build *args: ensure-pro-token #!/bin/bash + set -euo pipefail export DOCKER_BUILDKIT=1 export BASE_CREATED=$(date --utc +'%Y-%m-%dT%H:%M:%S+00:00') export BASE_GITREF=$(git rev-parse --short HEAD) diff --git a/docker-compose.yaml b/docker-compose.yaml index 9d9228f..dad7657 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -5,6 +5,8 @@ services: target: base-docker cache_from: # should speed up the build in CI, where we have a cold cache - ghcr.io/opensafely-core/base-docker + secrets: + - ubuntu_pro_token args: # this makes the image work for later cache_from: usage - BUILDKIT_INLINE_CACHE=1 @@ -57,3 +59,7 @@ services: args: - UBUNTU_VERSION=ubuntu:24.04 target: base-action + +secrets: + ubuntu_pro_token: + file: ${UBUNTU_PRO_TOKEN_FILE:-.secrets/ubuntu_pro_token} From d5a10c01930a215c751e1d9cd5a61659f610b46d Mon Sep 17 00:00:00 2001 From: bloodearnest Date: Wed, 14 Jan 2026 16:28:00 +0000 Subject: [PATCH 4/7] Add support for Ubuntu pro in docker-app-install.sh script It will now optionally enable the Ubuntu Pro ESM package archives, if a) the secret is present and b) we are on 20.04, and then clean up after packages are installed. Embedding it in this help script servers two purposes: a) it ensures that it setups and tears down ubuntu pro within one step, ensuring the pro secret is removed. b) it can be reused by downstream docker images to enable installing from ESM repos for other 20.04 images (e.g. python:v1, r:v1) We also expose this secret in the Dockerfile when installing pacakges This enables ESM archives for 20.04 images. --- Dockerfile | 4 +++- docker-apt-install.sh | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 08925d6..91ec2a1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -25,7 +25,9 @@ COPY docker-apt-install.sh /root/docker-apt-install.sh # install some base tools we want in all images # Caching from docs: https://docs.docker.com/reference/dockerfile/#example-cache-apt-packages # Enable full caching of apt packages and metadata, undoing the debian defaults. -RUN --mount=type=cache,target=/var/cache/apt,sharing=locked --mount=type=cache,target=/var/lib/apt,sharing=locked < /etc/apt/apt.conf.d/keep-cache UPGRADE=yes /root/docker-apt-install.sh ca-certificates sysstat lsof net-tools tcpdump vim-tiny strace file diff --git a/docker-apt-install.sh b/docker-apt-install.sh index 5d897f1..e412411 100755 --- a/docker-apt-install.sh +++ b/docker-apt-install.sh @@ -3,9 +3,38 @@ # It does so in the lowest footprint way possible, in a single RUN command. set -euo pipefail +pro_attached=0 +pro_token_file=/run/secrets/ubuntu_pro_token + # ensure apt lists are populated apt-get update +if grep -q 'VERSION_ID="20.04"' /etc/os-release; then + # enable ubuntu pro, based on the example in the Canonical docs: + # https://documentation.ubuntu.com/pro-client/en/docs/howtoguides/enable_in_dockerfile/ + # A file is used rather than an env var which would leak into the docker metadata. + # + # We do it in this helper script, rather than in the Dockerfile, so that + # downstream docker images can also re-use this logic and enable esm + # installations. + # + # We enable two additional archives: + # - esm-infra: core infra packages + # - esm-apps: applications and server packages + if test -s "$pro_token_file"; then + apt-get install --no-install-recommends -y ubuntu-pro-client ca-certificates + cat > /tmp/pro-attach-config.yaml < Date: Wed, 14 Jan 2026 16:52:02 +0000 Subject: [PATCH 5/7] Add tests for Ubuntu Pro enablement for 20.04 images. Check we have packages from the ESM archives, and that we not left the token in the image. --- tests.sh | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/tests.sh b/tests.sh index f9110bc..188deba 100755 --- a/tests.sh +++ b/tests.sh @@ -61,6 +61,25 @@ test_executable strace # test install works w/o UPDATE=yes /root/docker-apt-install.sh make +# test ubuntu pro +if grep -q 'VERSION_ID="20.04"' /etc/os-release; then + + # check we are using packages from ESM archives + dpkg-query -W -f='${Package}\t${Version}\n' libssl1.1 > "$output" + assert_output "$output" "esm" + + # check we have not left any files with the token on the image + if test ! -e /tmp/pro-attach-config.yaml && + test ! -d /etc/ubuntu-advantage && + test ! -d /var/lib/ubuntu-advantage && + test ! -e /var/log/ubuntu-advantage.log && + ! dpkg -l | grep -q ubuntu-pro-client; then + success "no Ubuntu Pro files found" + else + failure "Found Ubuntu Pro file that should not be in the image" + fi +fi + # test script that just echos its arguments for testing script=$(mktemp /tmp/action_exec.XXXX.sh) chmod +x "$script" @@ -109,9 +128,4 @@ chmod +x "$non_exec_script" test_entrypoint "$non_exec_script" assert_output "$output" "SUCCESS" - - - - exit $failed - From c63b1721b4282c4e95bc509275113b8436f83033 Mon Sep 17 00:00:00 2001 From: bloodearnest Date: Wed, 14 Jan 2026 17:15:35 +0000 Subject: [PATCH 6/7] Add UBUNTO_PRO_TOKEN to github workflow --- .github/workflows/build_and_publish.yaml | 1 + .github/workflows/tests.yaml | 1 + 2 files changed, 2 insertions(+) diff --git a/.github/workflows/build_and_publish.yaml b/.github/workflows/build_and_publish.yaml index 0767e23..00ffc02 100644 --- a/.github/workflows/build_and_publish.yaml +++ b/.github/workflows/build_and_publish.yaml @@ -10,6 +10,7 @@ on: env: BASE_IMAGE_NAME: base-docker ACTION_IMAGE_NAME: base-action + UBUNTU_PRO_TOKEN: ${{ secrets.UBUNTU_PRO_TOKEN }} jobs: build: runs-on: ubuntu-latest diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 65f71ab..f261f27 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -3,6 +3,7 @@ on: pull_request: env: IMAGE_NAME: base-docker + UBUNTU_PRO_TOKEN: ${{ secrets.UBUNTU_PRO_TOKEN }} jobs: build: runs-on: ubuntu-latest From 956b5cfe60c8e0ead5c688821dd5c5e9d4c8258d Mon Sep 17 00:00:00 2001 From: bloodearnest Date: Fri, 16 Jan 2026 16:01:05 +0000 Subject: [PATCH 7/7] Document Ubuntu Pro/ESM in README --- README.md | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 858116b..d229719 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,9 @@ This image is up-to-date Ubuntu image along with common debugging tools. (e.g. It includes a helpful script for installing apt packages in the most docker friendly space-efficient manner. Adding this and using it in this and dependent -images saves over 100MB, typically. +images saves over 100MB, typically. This scrips also provides some +conveniences, allowing use of text file with comments to list, and also +supports enabling ESM repositories. It is rebuilt and publish weekly, so there's always a fresh base to build from. @@ -33,3 +35,17 @@ or Images built from base-action can define `ACTION_EXEC` env var to customise the default implicit executable used to execute. + + +## ESM Support for 20.04 + +Our python:v1 and r:v1 action images are based on 20.04, which reached EOL in +Oct 2025. Because of our current backwards compatibility policy, we need to +support these images unchanged, and cannot switch them to 22.04 or later, and +so we need to continue to support the base 20.04 images. + +So we enable ESM repos via Ubuntu Pro, to provide security fixes for system +packages for 20.04 until 2030. This requires a valid Ubuntu Pro token to be +able to build the 20.04 images. In CI, this is provided via Github Actions +secret. Locally, you should add `UBUNTU_PRO_TOKEN=` in a .env file to be +able to build the images.