From 0e1cf095bd2f600b0cce67d9e72832e54bf06e78 Mon Sep 17 00:00:00 2001 From: Abrar Shivani Date: Mon, 3 Aug 2026 20:18:12 -0700 Subject: [PATCH] Install packages without docs to reduce image size The UBI 8/9/10 base images ship almost no documentation of their own, but they do not set tsflags in /etc/dnf/dnf.conf, so every dnf install and dnf update we run installs docs and man pages into the layers we ship. They grow the image for no runtime benefit and get picked up by CI scanners; #772 hit this when the perl docs pulled in by git were flagged for containing sample secrets. Pass --nodocs to the transactional dnf calls in the RHEL driver and vGPU Manager images, the same way #772 did for git and wget. dnf module enable, versionlock, config-manager, remove and autoremove are left alone. Because this relies on the flag being present at each call, add tests/check-nodocs.sh and run it in CI. It joins continuation lines before matching, so a command split across lines is still seen as one command, and splits each command on &&, ||, ; and | so a chain only passes if every transaction in it carries the flag. The Ubuntu images need no change: the official ubuntu base images ship /etc/dpkg/dpkg.cfg.d/excludes, which already excludes /usr/share/doc and /usr/share/man, so dpkg never writes them. Signed-off-by: Abrar Shivani --- .github/workflows/image.yaml | 8 +++ rhel10/Dockerfile | 4 +- rhel10/install.sh | 30 ++++----- rhel8/Dockerfile | 4 +- rhel8/install.sh | 20 +++--- rhel9/Dockerfile | 4 +- rhel9/install.sh | 22 +++---- tests/check-nodocs.sh | 112 ++++++++++++++++++++++++++++++++++ vgpu-manager/rhel8/Dockerfile | 4 +- vgpu-manager/rhel9/Dockerfile | 4 +- 10 files changed, 166 insertions(+), 46 deletions(-) create mode 100755 tests/check-nodocs.sh diff --git a/.github/workflows/image.yaml b/.github/workflows/image.yaml index 444387b36..0965a0b8b 100644 --- a/.github/workflows/image.yaml +++ b/.github/workflows/image.yaml @@ -23,6 +23,14 @@ on: - release-* jobs: + check-nodocs: + runs-on: linux-amd64-cpu4 + steps: + - uses: actions/checkout@v7 + name: Check out code + - name: Check dnf transactions pass --nodocs + run: ./tests/check-nodocs.sh + image: runs-on: linux-amd64-cpu4 strategy: diff --git a/rhel10/Dockerfile b/rhel10/Dockerfile index f1277c3a1..831528755 100644 --- a/rhel10/Dockerfile +++ b/rhel10/Dockerfile @@ -35,7 +35,7 @@ ENV TARGETARCH=$TARGETARCH SHELL ["/bin/bash", "-c"] -RUN dnf update -y && dnf clean all +RUN dnf update -y --nodocs && dnf clean all #ARG BASE_URL=http://us.download.nvidia.com/XFree86/Linux-x86_64 ARG BASE_URL=https://us.download.nvidia.com/tesla @@ -108,7 +108,7 @@ LABEL description="See summary" # Install / upgrade packages here that are required to resolve CVEs ARG CVE_UPDATES RUN if [ -n "${CVE_UPDATES}" ]; then \ - dnf update -y ${CVE_UPDATES} && \ + dnf update -y --nodocs ${CVE_UPDATES} && \ dnf clean all; \ fi diff --git a/rhel10/install.sh b/rhel10/install.sh index 36a70b1ab..c5be0e45f 100755 --- a/rhel10/install.sh +++ b/rhel10/install.sh @@ -8,7 +8,7 @@ echo "DRIVER_ARCH is $DRIVER_ARCH" dep_installer () { if [ "$DRIVER_ARCH" = "x86_64" ]; then - dnf install -y \ + dnf install -y --nodocs \ libglvnd-glx \ ca-certificates \ curl-minimal \ @@ -19,7 +19,7 @@ dep_installer () { file \ kmod elif [ "$DRIVER_ARCH" = "ppc64le" ]; then - dnf install -y \ + dnf install -y --nodocs \ libglvnd-glx \ ca-certificates \ curl-minimal \ @@ -30,7 +30,7 @@ dep_installer () { file \ kmod elif [ "$DRIVER_ARCH" = "aarch64" ]; then - dnf install -y \ + dnf install -y --nodocs \ libglvnd-glx \ ca-certificates \ curl-minimal \ @@ -42,8 +42,8 @@ dep_installer () { kmod fi - if ! dnf install -y 'dnf-command(config-manager)'; then - dnf install -y dnf5-plugins + if ! dnf install -y --nodocs 'dnf-command(config-manager)'; then + dnf install -y --nodocs dnf5-plugins fi OS_RELEASE_ID=$(grep -oP '(?<=^ID=).+' /etc/os-release | tr -d '"') @@ -57,15 +57,15 @@ dep_installer () { # unzboot is only available on the EPEL RPM repo if [ "$DRIVER_ARCH" = "aarch64" ]; then rpm --import https://dl.fedoraproject.org/pub/epel/RPM-GPG-KEY-EPEL-10 - dnf install -y https://dl.fedoraproject.org/pub/epel/epel-release-latest-10.noarch.rpm + dnf install -y --nodocs https://dl.fedoraproject.org/pub/epel/epel-release-latest-10.noarch.rpm dnf config-manager --enable epel # Try to install unzboot from EPEL. If it is not available yet, build it # from source because RHEL/Rocky 10 arm64 kernel images require it. - if ! dnf install -y unzboot; then + if ! dnf install -y --nodocs unzboot; then echo "unzboot package not available in current EPEL version; building from source." # Install meson build dependencies - if dnf install -y git gcc meson ninja-build glib2-devel zlib-devel libzstd-devel; then + if dnf install -y --nodocs git gcc meson ninja-build glib2-devel zlib-devel libzstd-devel; then if command -v meson >/dev/null 2>&1 && command -v ninja >/dev/null 2>&1; then if git clone https://github.com/eballetbo/unzboot.git /tmp/unzboot-src 2>/dev/null; then if meson setup /tmp/unzboot-src/build /tmp/unzboot-src && meson compile -C /tmp/unzboot-src/build; then @@ -73,7 +73,7 @@ dep_installer () { chmod +x /usr/bin/unzboot runtime_pkgs=$(ldd /usr/bin/unzboot | awk '/=> \// { print $3 } /^\// { print $1 }' | xargs -r rpm -q --whatprovides | sort -u) if [ -n "$runtime_pkgs" ]; then - dnf install -y $runtime_pkgs + dnf install -y --nodocs $runtime_pkgs fi echo "Built and installed unzboot from source" else @@ -152,35 +152,35 @@ nvidia_installer () { } fabricmanager_install() { - dnf install -y nvidia-fabricmanager-${DRIVER_VERSION} nvidia-fabric-manager-devel-${DRIVER_VERSION} + dnf install -y --nodocs nvidia-fabricmanager-${DRIVER_VERSION} nvidia-fabric-manager-devel-${DRIVER_VERSION} dnf versionlock add nvidia-fabricmanager nvidia-fabric-manager-devel } nscq_install() { - dnf install -y libnvidia-nscq-${DRIVER_VERSION} + dnf install -y --nodocs libnvidia-nscq-${DRIVER_VERSION} dnf versionlock add libnvidia-nscq } # libnvsdm packages are not available for arm64 nvsdm_install() { if [ "$TARGETARCH" = "amd64" ]; then - dnf install -y libnvsdm-${DRIVER_VERSION} + dnf install -y --nodocs libnvsdm-${DRIVER_VERSION} dnf versionlock add libnvsdm fi } nvlink5_pkgs_install() { - dnf install -y infiniband-diags nvlsm + dnf install -y --nodocs infiniband-diags nvlsm } imex_install() { - dnf install -y nvidia-imex-${DRIVER_VERSION} + dnf install -y --nodocs nvidia-imex-${DRIVER_VERSION} dnf versionlock add nvidia-imex } extra_pkgs_install() { if [ "$DRIVER_TYPE" != "vgpu" ]; then - dnf install -y 'dnf-command(versionlock)' + dnf install -y --nodocs 'dnf-command(versionlock)' fabricmanager_install nscq_install diff --git a/rhel8/Dockerfile b/rhel8/Dockerfile index efe12cb57..cff206fe3 100644 --- a/rhel8/Dockerfile +++ b/rhel8/Dockerfile @@ -34,7 +34,7 @@ ENV TARGETARCH=$TARGETARCH SHELL ["/bin/bash", "-c"] -RUN dnf update -y && dnf clean all +RUN dnf update -y --nodocs && dnf clean all #ARG BASE_URL=http://us.download.nvidia.com/XFree86/Linux-x86_64 ARG BASE_URL=https://us.download.nvidia.com/tesla @@ -106,7 +106,7 @@ LABEL description="See summary" # Install / upgrade packages here that are required to resolve CVEs ARG CVE_UPDATES RUN if [ -n "${CVE_UPDATES}" ]; then \ - dnf update -y ${CVE_UPDATES} && \ + dnf update -y --nodocs ${CVE_UPDATES} && \ dnf clean all; \ fi diff --git a/rhel8/install.sh b/rhel8/install.sh index 060bffe33..9a45b0887 100755 --- a/rhel8/install.sh +++ b/rhel8/install.sh @@ -8,7 +8,7 @@ echo "DRIVER_ARCH is $DRIVER_ARCH" dep_installer () { if [ "$DRIVER_ARCH" = "x86_64" ]; then - dnf install -y \ + dnf install -y --nodocs \ libglvnd-glx \ ca-certificates \ curl \ @@ -18,7 +18,7 @@ dep_installer () { cpio \ kmod elif [ "$DRIVER_ARCH" = "ppc64le" ]; then - dnf install -y \ + dnf install -y --nodocs \ libglvnd-glx \ ca-certificates \ curl \ @@ -28,7 +28,7 @@ dep_installer () { cpio \ kmod elif [ "$DRIVER_ARCH" = "aarch64" ]; then - dnf install -y \ + dnf install -y --nodocs \ libglvnd-glx \ ca-certificates \ curl \ @@ -39,7 +39,7 @@ dep_installer () { kmod fi - dnf install -y 'dnf-command(config-manager)' + dnf install -y --nodocs 'dnf-command(config-manager)' rm -rf /var/cache/yum/* } @@ -93,35 +93,35 @@ nvidia_installer () { } fabricmanager_install() { - dnf install -y nvidia-fabricmanager-${DRIVER_VERSION} nvidia-fabric-manager-devel-${DRIVER_VERSION} + dnf install -y --nodocs nvidia-fabricmanager-${DRIVER_VERSION} nvidia-fabric-manager-devel-${DRIVER_VERSION} dnf versionlock add nvidia-fabricmanager nvidia-fabric-manager-devel } nscq_install() { - dnf install -y libnvidia-nscq-${DRIVER_VERSION} + dnf install -y --nodocs libnvidia-nscq-${DRIVER_VERSION} dnf versionlock add libnvidia-nscq } nvsdm_install() { if [ "$TARGETARCH" = "amd64" ]; then - dnf install -y libnvsdm-${DRIVER_VERSION} + dnf install -y --nodocs libnvsdm-${DRIVER_VERSION} dnf versionlock add libnvsdm fi } nvlink5_pkgs_install() { - dnf install -y infiniband-diags nvlsm + dnf install -y --nodocs infiniband-diags nvlsm } imex_install() { - dnf install -y nvidia-imex-${DRIVER_VERSION} + dnf install -y --nodocs nvidia-imex-${DRIVER_VERSION} dnf versionlock add nvidia-imex } extra_pkgs_install() { if [ "$DRIVER_TYPE" != "vgpu" ]; then dnf module enable -y nvidia-driver:${DRIVER_BRANCH}-dkms - dnf install -y 'dnf-command(versionlock)' + dnf install -y --nodocs 'dnf-command(versionlock)' fabricmanager_install nscq_install diff --git a/rhel9/Dockerfile b/rhel9/Dockerfile index ae1d0f37b..acd4d92cb 100644 --- a/rhel9/Dockerfile +++ b/rhel9/Dockerfile @@ -34,7 +34,7 @@ ENV TARGETARCH=$TARGETARCH SHELL ["/bin/bash", "-c"] -RUN dnf update -y && dnf clean all +RUN dnf update -y --nodocs && dnf clean all #ARG BASE_URL=http://us.download.nvidia.com/XFree86/Linux-x86_64 ARG BASE_URL=https://us.download.nvidia.com/tesla @@ -107,7 +107,7 @@ LABEL description="See summary" # Install / upgrade packages here that are required to resolve CVEs ARG CVE_UPDATES RUN if [ -n "${CVE_UPDATES}" ]; then \ - dnf update -y ${CVE_UPDATES} && \ + dnf update -y --nodocs ${CVE_UPDATES} && \ dnf clean all; \ fi diff --git a/rhel9/install.sh b/rhel9/install.sh index dabeba023..8da15cd0a 100755 --- a/rhel9/install.sh +++ b/rhel9/install.sh @@ -8,7 +8,7 @@ echo "DRIVER_ARCH is $DRIVER_ARCH" dep_installer () { if [ "$DRIVER_ARCH" = "x86_64" ]; then - dnf install -y \ + dnf install -y --nodocs \ libglvnd-glx \ ca-certificates \ curl-minimal \ @@ -18,7 +18,7 @@ dep_installer () { cpio \ kmod elif [ "$DRIVER_ARCH" = "ppc64le" ]; then - dnf install -y \ + dnf install -y --nodocs \ libglvnd-glx \ ca-certificates \ curl-minimal \ @@ -28,7 +28,7 @@ dep_installer () { cpio \ kmod elif [ "$DRIVER_ARCH" = "aarch64" ]; then - dnf install -y \ + dnf install -y --nodocs \ libglvnd-glx \ ca-certificates \ curl-minimal \ @@ -42,9 +42,9 @@ dep_installer () { # Download unzboot as kernel images are compressed in the zboot format on RHEL 9 arm64 # unzboot is only available on the EPEL RPM repo rpm --import https://dl.fedoraproject.org/pub/epel/RPM-GPG-KEY-EPEL-9 - dnf install -y https://dl.fedoraproject.org/pub/epel/epel-release-latest-9.noarch.rpm + dnf install -y --nodocs https://dl.fedoraproject.org/pub/epel/epel-release-latest-9.noarch.rpm dnf config-manager --enable epel - dnf install -y unzboot + dnf install -y --nodocs unzboot rm -rf /var/cache/yum/* } @@ -99,35 +99,35 @@ nvidia_installer () { } fabricmanager_install() { - dnf install -y nvidia-fabricmanager-${DRIVER_VERSION} nvidia-fabric-manager-devel-${DRIVER_VERSION} + dnf install -y --nodocs nvidia-fabricmanager-${DRIVER_VERSION} nvidia-fabric-manager-devel-${DRIVER_VERSION} dnf versionlock add nvidia-fabricmanager nvidia-fabric-manager-devel } nscq_install() { - dnf install -y libnvidia-nscq-${DRIVER_VERSION} + dnf install -y --nodocs libnvidia-nscq-${DRIVER_VERSION} dnf versionlock add libnvidia-nscq } # libnvsdm packages are not available for arm64 nvsdm_install() { if [ "$TARGETARCH" = "amd64" ]; then - dnf install -y libnvsdm-${DRIVER_VERSION} + dnf install -y --nodocs libnvsdm-${DRIVER_VERSION} dnf versionlock add libnvsdm fi } nvlink5_pkgs_install() { - dnf install -y infiniband-diags nvlsm + dnf install -y --nodocs infiniband-diags nvlsm } imex_install() { - dnf install -y nvidia-imex-${DRIVER_VERSION} + dnf install -y --nodocs nvidia-imex-${DRIVER_VERSION} dnf versionlock add nvidia-imex } extra_pkgs_install() { if [ "$DRIVER_TYPE" != "vgpu" ]; then dnf module enable -y nvidia-driver:${DRIVER_BRANCH}-dkms - dnf install -y 'dnf-command(versionlock)' + dnf install -y --nodocs 'dnf-command(versionlock)' # If running on a RockyLinux base image, we enable the Code Ready Builder RPM repo (crb) OS_RELEASE_ID=$(grep -oP '(?<=^ID=).+' /etc/os-release | tr -d '"') diff --git a/tests/check-nodocs.sh b/tests/check-nodocs.sh new file mode 100755 index 000000000..280d545c4 --- /dev/null +++ b/tests/check-nodocs.sh @@ -0,0 +1,112 @@ +#!/usr/bin/env bash +# Fail if a dnf transaction that installs into a RHEL image is missing --nodocs. +# +# Documentation installed at image build ends up in the layers we ship, where it +# grows the image and gets picked up by CI scanners. Every install and update in +# these build contexts therefore has to pass --nodocs, and this catches the ones +# that are added later without it. +# +# Continuation lines are joined first, so a command split across lines is still +# seen as one command. Each joined command is then split on &&, ||, ; and | so a +# chain only passes if every transaction in it carries the flag. +# +# Scope. Only the files that put content into the image are checked: +# +# - the RHEL driver and vGPU Manager Dockerfiles and install.sh +# - not rhel*/precompiled, which persists the same setting through +# `dnf config-manager --nodocs --save` instead of per-command flags +# - not nvidia-driver or ocp_dtk_entrypoint, whose dnf calls run in the +# started container rather than at image build +# +# Commands that do not install packages, such as module enable, versionlock, +# config-manager, remove, autoremove and clean, are not matched. +# +# Run from the repository root: +# ./tests/check-nodocs.sh + +set -eu + +cd "$(dirname "$0")/.." + +TARGETS=" +rhel8/Dockerfile +rhel9/Dockerfile +rhel10/Dockerfile +rhel8/install.sh +rhel9/install.sh +rhel10/install.sh +vgpu-manager/rhel8/Dockerfile +vgpu-manager/rhel9/Dockerfile +" + +find_offenders() { + awk ' + # dnf, any number of short or long flags, then a verb that opens a + # transaction. Applied to one command at a time. + function is_transaction(text) { + return text ~ /(^|[^-[:alnum:]_])dnf([[:space:]]+-[^[:space:]]+)*[[:space:]]+(install|update|upgrade|reinstall|downgrade)([[:space:]]|$)/ + } + + function report(command, lineno, parts, count, i) { + if (command ~ /^[[:space:]]*#/) + return + count = split(command, parts, /&&|\|\||;|\|/) + for (i = 1; i <= count; i++) + if (is_transaction(parts[i]) && parts[i] !~ /--nodocs/) { + gsub(/^[[:space:]]+|[[:space:]]+$/, "", parts[i]) + print lineno ": " parts[i] + } + } + + { + if (buffer == "") + start = NR + text = $0 + continued = (text ~ /\\[[:space:]]*$/) + sub(/\\[[:space:]]*$/, "", text) + buffer = (buffer == "" ? text : buffer " " text) + if (continued) + next + report(buffer, start) + buffer = "" + } + + END { + if (buffer != "") + report(buffer, start) + } + ' "$1" +} + +status=0 + +for file in $TARGETS; do + [ -f "$file" ] || { echo "check-nodocs.sh: missing $file" >&2; status=1; continue; } + + offenders=$(find_offenders "$file") + + if [ -n "$offenders" ]; then + status=1 + while IFS= read -r line; do + printf '%s:%s\n' "$file" "$line" + done <<< "$offenders" + fi +done + +if [ "$status" -ne 0 ]; then + cat >&2 <<'EOF' + +dnf transactions above are missing --nodocs. + +Add it so the documentation is never written into the image: + + dnf install -y --nodocs + dnf update -y --nodocs + +If a command genuinely should keep documentation, exclude the file in +tests/check-nodocs.sh and say why. +EOF + exit 1 +fi + +echo "all dnf transactions in the RHEL build contexts pass --nodocs" diff --git a/vgpu-manager/rhel8/Dockerfile b/vgpu-manager/rhel8/Dockerfile index bb4f5b470..d18928ba9 100644 --- a/vgpu-manager/rhel8/Dockerfile +++ b/vgpu-manager/rhel8/Dockerfile @@ -23,7 +23,7 @@ RUN chmod +x NVIDIA-Linux-${DRIVER_ARCH}-${DRIVER_VERSION}-vgpu-kvm.run COPY nvidia-driver /usr/local/bin COPY ocp_dtk_entrypoint /usr/local/bin -RUN dnf install -y pciutils && \ +RUN dnf install -y --nodocs pciutils && \ dnf clean all && \ rm -rf /var/cache/dnf/* @@ -39,7 +39,7 @@ LABEL description="See summary" # Install / upgrade packages here that are required to resolve CVEs ARG CVE_UPDATES RUN if [ -n "${CVE_UPDATES}" ]; then \ - dnf update -y ${CVE_UPDATES} && \ + dnf update -y --nodocs ${CVE_UPDATES} && \ dnf clean all; \ fi diff --git a/vgpu-manager/rhel9/Dockerfile b/vgpu-manager/rhel9/Dockerfile index 2a1ae8520..c43909822 100644 --- a/vgpu-manager/rhel9/Dockerfile +++ b/vgpu-manager/rhel9/Dockerfile @@ -36,7 +36,7 @@ RUN chmod +x NVIDIA-Linux-${DRIVER_ARCH}-${DRIVER_VERSION}-vgpu-kvm.run COPY nvidia-driver /usr/local/bin COPY ocp_dtk_entrypoint /usr/local/bin -RUN dnf install -y pciutils && \ +RUN dnf install -y --nodocs pciutils && \ dnf clean all && \ rm -rf /var/cache/dnf/* @@ -52,7 +52,7 @@ LABEL description="See summary" # Install / upgrade packages here that are required to resolve CVEs ARG CVE_UPDATES RUN if [ -n "${CVE_UPDATES}" ]; then \ - dnf update -y ${CVE_UPDATES} && \ + dnf update -y --nodocs ${CVE_UPDATES} && \ dnf clean all; \ fi