Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
.git
.github
tests
LICENSE
README.md
16 changes: 15 additions & 1 deletion .github/workflows/anchore-analysis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,34 @@ on:
- cron: '24 5 * * 4'
workflow_dispatch:

permissions:
contents: read
security-events: write

jobs:
Anchore-Build-Scan:
runs-on: ubuntu-latest
steps:
- name: Checkout the code
uses: actions/checkout@v7
- name: Lint shell scripts
run: |
shellcheck -x bin/bootstrap.sh lib/common.sh tests/smoke.sh
shellcheck -s bash -S error bashrc
- name: Lint Dockerfile
run: docker run --rm -i hadolint/hadolint < Dockerfile
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4
- name: build local container
uses: docker/build-push-action@v5
uses: docker/build-push-action@v7
with:
tags: localbuild/testimage:latest
push: false
load: true
# The Dependabot auto-merge workflow waits on this workflow, so a failing
# smoke test also blocks automatic merges.
- name: Smoke test the image
run: docker run --rm -v "$PWD/tests:/tests:ro" localbuild/testimage:latest bash /tests/smoke.sh
- name: Scan image
id: scan
uses: anchore/scan-action@v7
Expand Down
29 changes: 24 additions & 5 deletions .github/workflows/docker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,11 @@ on:
workflow_dispatch:

env:
# TODO: Change variable to your image's name.
IMAGE_NAME: kjake/base
PLATFORMS: linux/386,linux/amd64,linux/arm64,linux/arm/v7,linux/ppc64le,linux/s390x,linux/riscv64

permissions:
contents: read

jobs:
push:
Expand All @@ -23,14 +26,30 @@ jobs:
uses: docker/setup-qemu-action@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4
# Build and test the native image before anything is published, so a
# broken rebuild never replaces the image that child containers use.
- name: Build test image
uses: docker/build-push-action@v7
with:
tags: localbuild/testimage:latest
push: false
load: true
- name: Smoke test the image
run: docker run --rm -v "$PWD/tests:/tests:ro" localbuild/testimage:latest bash /tests/smoke.sh
- name: Compute date tag
id: date
run: echo "tag=$(date -u +%Y%m%d)" >> "$GITHUB_OUTPUT"
- name: Login to Docker Hub
uses: docker/login-action@v4.6.0
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Build and push
uses: docker/build-push-action@v5
uses: docker/build-push-action@v7
with:
platforms: linux/386,linux/amd64,linux/arm64,linux/arm/v7,linux/ppc64le,linux/s390x,linux/riscv64
tags: ${{ env.IMAGE_NAME }}:latest
push: true
platforms: ${{ env.PLATFORMS }}
# A dated tag lets child images pin a known-good snapshot of testing.
tags: |
${{ env.IMAGE_NAME }}:latest
${{ env.IMAGE_NAME }}:${{ steps.date.outputs.tag }}
push: true
21 changes: 13 additions & 8 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,21 +1,26 @@
FROM debian:testing-slim

LABEL maintainer="kjake"
LABEL maintainer="kjake" \
org.opencontainers.image.title="kjake/base" \
org.opencontainers.image.description="Debian testing base image with contrib and non-free enabled" \
org.opencontainers.image.source="https://github.com/kjake/docker-base" \
org.opencontainers.image.licenses="GPL-3.0"

ENV HOME=/root
ENV DEBIAN_FRONTEND=noninteractive

# Configure Apt
ADD sources.list /etc/apt/sources.list
# Configure Apt. This replaces the base image's deb822 source list; see the
# comments in debian.sources for why it starts out on http.
COPY debian.sources /etc/apt/sources.list.d/debian.sources

# Prepare environment
ADD lib/common.sh /app/lib/common.sh
ADD bin/bootstrap.sh /app/bin/bootstrap.sh
RUN chmod 0755 /app/bin/bootstrap.sh
# Prepare environment. /app/lib/common.sh is kept for downstream images;
# bootstrap.sh removes itself once it has run.
COPY lib/common.sh /app/lib/common.sh
COPY --chmod=0755 bin/bootstrap.sh /app/bin/bootstrap.sh
RUN /app/bin/bootstrap.sh

# Install Chambana.net bashrc
ADD bashrc /etc/bash.bashrc
COPY bashrc /etc/bash.bashrc

ENV LC_ALL=C.UTF-8
ENV TERM=xterm
47 changes: 47 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,49 @@
# docker-base

My base docker image, forked from chambana-net/docker-base, based on debian.

The image is built from `debian:testing-slim` so that containers built on top of
it get newer community-maintained package releases than Debian stable offers.
It is published to Docker Hub as `kjake/base` for 386, amd64, arm64, arm/v7,
ppc64le, s390x and riscv64.

## Tags

| Tag | Meaning |
|--------------|-----------------------------------------------------------|
| `latest` | Most recent weekly rebuild of Debian testing. |
| `YYYYMMDD` | The rebuild from that day; pin this for reproducible builds. |

## What the image provides

- Apt sources for `testing`, `testing-updates` and `testing-security`, over
https, with the `main`, `contrib`, `non-free` and `non-free-firmware`
components enabled. See `debian.sources`.
- A fully upgraded package set plus `ca-certificates`, `locales`, `less`,
`patch`, `diffutils`, `debconf-utils` and `vim-tiny` linked as `vim`.
- `DEBIAN_FRONTEND=noninteractive`, `LC_ALL=C.UTF-8` and `TERM=xterm`.
- `/app/lib/common.sh`, a small helper library for child images with `MSG`,
`ERR`, `CHECK_BIN` and `CHECK_VAR`.
- An interactive `/etc/bash.bashrc` with the Chambana prompt.

## Building and testing

```sh
docker build -t kjake/base .
docker run --rm -v "$PWD/tests:/tests:ro" kjake/base bash /tests/smoke.sh
```

The build fails if any apt repository cannot be fetched. `tests/smoke.sh`
checks the apt configuration, installed tooling, environment and helper
library; set `SMOKE_OFFLINE=1` to skip the checks that need network access.

## CI

- **Docker**: weekly and on every push to `master`, builds and smoke tests
the image, then builds all platforms and pushes `latest` and a dated tag.
- **Anchore Container Scan**: on pull requests, weekly and on push; lints the
scripts and Dockerfile, builds and smoke tests the image, then scans it with
Grype and uploads the results to code scanning.
- **Dependabot auto-merge**: merges Dependabot pull requests once the scan
workflow succeeds.
- **Keepalive**: keeps scheduled workflows from being disabled by inactivity.
19 changes: 10 additions & 9 deletions bashrc
Original file line number Diff line number Diff line change
Expand Up @@ -186,17 +186,18 @@ if [ -f /etc/bash_completion ] && ! shopt -oq posix; then
fi

# if the command-not-found package is installed, use it
if [ -x /usr/lib/command-not-found -o -x /usr/share/command-not-found ]; then
if [ -x /usr/lib/command-not-found ] || [ -x /usr/share/command-not-found/command-not-found ]; then
function command_not_found_handle {
# check because c-n-f could've been removed in the meantime
if [ -x /usr/lib/command-not-found ]; then
/usr/bin/python /usr/lib/command-not-found -- $1
return $?
elif [ -x /usr/share/command-not-found ]; then
/usr/bin/python /usr/share/command-not-found -- $1
return $?
# check because c-n-f could've been removed in the meantime
if [ -x /usr/lib/command-not-found ]; then
/usr/lib/command-not-found -- "$1"
return $?
elif [ -x /usr/share/command-not-found/command-not-found ]; then
/usr/share/command-not-found/command-not-found -- "$1"
return $?
else
return 127
printf "%s: command not found\n" "$1" >&2
return 127
fi
}
fi
Expand Down
19 changes: 17 additions & 2 deletions bin/bootstrap.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,22 @@
# Preparation script based on https://github.com/olberger/baseimage-docker
#

set -o errexit -o pipefail

# shellcheck source=lib/common.sh disable=SC1091
. /app/lib/common.sh

APT_INSTALL='apt-get install -y --no-install-recommends'
SOURCES=/etc/apt/sources.list.d/debian.sources

## Fail the build if any configured repository cannot be fetched, instead of
## silently continuing with missing or stale package lists.
apt_update() {
apt-get -qq update --error-on=any
}

MSG "Updating apt repositories..."
apt-get -qq update
apt_update

## Temporarily disable dpkg fsync to make building faster.
echo force-unsafe-io > /etc/dpkg/dpkg.cfg.d/02apt-speedup
Expand All @@ -29,7 +39,12 @@ ln -sf /bin/true /usr/bin/ischroot

MSG "Installing packages..."
$APT_INSTALL apt-utils
$APT_INSTALL ca-certificates apt-transport-https diffutils patch locales debconf-utils vim-tiny less
$APT_INSTALL ca-certificates diffutils patch locales debconf-utils vim-tiny less

## Now that ca-certificates is present, fetch packages over https.
MSG "Switching apt repositories to https..."
sed -i 's|http://deb.debian.org/|https://deb.debian.org/|' "$SOURCES"
apt_update

## Link vim -> vim.tiny
ln -sf /usr/bin/vim.tiny /usr/bin/vim
Expand Down
20 changes: 20 additions & 0 deletions debian.sources
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Debian testing ("rolling") with every archive component enabled, so images
# built on top of this one get newer upstream releases than stable offers and
# can install contrib and non-free packages.
#
# Plain http is only used for the very first `apt-get update`, because the
# slim base image ships without ca-certificates. bin/bootstrap.sh installs
# ca-certificates and then switches these URIs to https. Packages are verified
# with the archive signing key either way.

Types: deb
URIs: http://deb.debian.org/debian
Suites: testing testing-updates
Components: main contrib non-free non-free-firmware
Signed-By: /usr/share/keyrings/debian-archive-keyring.pgp

Types: deb
URIs: http://deb.debian.org/debian-security
Suites: testing-security
Components: main contrib non-free non-free-firmware
Signed-By: /usr/share/keyrings/debian-archive-keyring.pgp
4 changes: 0 additions & 4 deletions sources.list

This file was deleted.

88 changes: 88 additions & 0 deletions tests/smoke.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
#!/bin/bash
# Several checks deliberately pass single-quoted scripts to an inner bash.
# shellcheck disable=SC2016
#
# Smoke tests for the kjake/base image. Run inside a container built from it:
#
# docker run --rm -v "$PWD/tests:/tests:ro" kjake/base bash /tests/smoke.sh
#
# Set SMOKE_OFFLINE=1 to skip the checks that need to reach the Debian mirrors.

set -o nounset -o pipefail

failures=0

pass() { printf 'ok - %s\n' "$1"; }
fail() { printf 'FAIL - %s\n' "$1"; failures=$((failures + 1)); }

# check "description" command [args...]
check() {
local desc=$1
shift
if "$@" >/tmp/smoke.out 2>&1; then
pass "$desc"
else
fail "$desc"
sed 's/^/ /' /tmp/smoke.out
fi
}

SOURCES=/etc/apt/sources.list.d/debian.sources

## Apt configuration
check "deb822 source list is present" test -f "$SOURCES"
check "no legacy /etc/apt/sources.list" test ! -e /etc/apt/sources.list
check "sources use https" grep -q '^URIs: https://deb.debian.org/debian$' "$SOURCES"
check "sources track testing" grep -q '^Suites: testing testing-updates$' "$SOURCES"
check "sources include testing-security" grep -q '^Suites: testing-security$' "$SOURCES"
check "no plain http sources remain" bash -c "! grep -q 'http://' '$SOURCES'"
check "contrib and non-free components enabled" \
bash -c "[ \"\$(grep -c '^Components: main contrib non-free non-free-firmware$' '$SOURCES')\" -eq 2 ]"
check "apt package lists were cleaned" bash -c '[ -z "$(ls -A /var/lib/apt/lists | grep -v -e ^lock$ -e ^partial$ -e ^auxfiles$)" ]'
check "build-time dpkg speedup removed" test ! -e /etc/dpkg/dpkg.cfg.d/02apt-speedup

if [ "${SMOKE_OFFLINE:-0}" != 1 ]; then
check "apt-get update succeeds for every repository" apt-get -qq update --error-on=any
for component in main contrib non-free non-free-firmware; do
check "testing/$component index is available" bash -c "apt-cache policy | grep -q ' testing/$component '"
done
# testing-security usually carries no packages, so it publishes no indexes;
# check that its signed release file was fetched instead.
check "testing-security release file was fetched" \
bash -c 'ls /var/lib/apt/lists/*debian-security_dists_testing-security_InRelease'
rm -rf /var/lib/apt/lists/*
fi

## Installed tooling
for pkg in apt-utils ca-certificates diffutils patch locales debconf-utils vim-tiny less; do
check "package $pkg installed" bash -c "dpkg-query -W -f='\${Status}' $pkg | grep -q 'install ok installed'"
done
check "vim points at vim.tiny" test "$(readlink /usr/bin/vim)" = /usr/bin/vim.tiny
check "ischroot is diverted to true" /usr/bin/ischroot
check "INITRD disabled for container" test "$(cat /etc/container_environment/INITRD)" = no

## Environment
check "DEBIAN_FRONTEND is noninteractive" test "${DEBIAN_FRONTEND:-}" = noninteractive
check "LC_ALL is C.UTF-8" test "${LC_ALL:-}" = C.UTF-8
check "C.UTF-8 locale is usable" bash -c 'locale 2>&1 | grep -vq "Cannot set"'

## /app layout
check "bootstrap removed itself" test ! -e /app/bin/bootstrap.sh
check "common.sh available to child images" test -r /app/lib/common.sh
check "common.sh MSG prints" bash -c '. /app/lib/common.sh; MSG hello | grep -q hello'
check "common.sh ERR prints to stderr" bash -c '. /app/lib/common.sh; ERR oops 2>&1 >/dev/null | grep -q oops'
check "common.sh CHECK_BIN accepts existing program" bash -c '. /app/lib/common.sh; CHECK_BIN bash'
check "common.sh CHECK_BIN rejects missing program" bash -c '! (. /app/lib/common.sh; CHECK_BIN no-such-program) 2>/dev/null'
check "common.sh CHECK_VAR accepts defined variable" bash -c '. /app/lib/common.sh; FOO=1; CHECK_VAR FOO'
check "common.sh CHECK_VAR rejects undefined variable" bash -c '! (. /app/lib/common.sh; CHECK_VAR NOT_DEFINED) 2>/dev/null'

## Interactive shell
check "bash.bashrc loads cleanly in an interactive shell" \
bash -c 'out=$(bash -i -c "echo loaded; declare -p PROMPT_COMMAND" 2>&1 </dev/null); echo "$out"; [ "$(echo "$out" | grep -cv -e ^loaded -e PROMPT_COMMAND= -e "cannot set terminal process group" -e "no job control")" -eq 0 ]'

echo
if [ "$failures" -ne 0 ]; then
echo "$failures check(s) failed"
exit 1
fi
echo "all checks passed"
Loading