From 483d3fa44ef0a61a77c46ba84718a02a81a50c58 Mon Sep 17 00:00:00 2001 From: Steven Pritchard Date: Wed, 17 Jun 2026 21:38:30 +0000 Subject: [PATCH 1/4] Modernize build/test Dockerfiles: fail-fast scripts, rvm->mise, OpenVox The build-container scripts ran without `set -e`, so a failing command masked by a later success (or a blanket `||:`) let the build complete as though it had succeeded. The AlmaLinux 8/9/10 Beaker images shipped broken this way (e.g. redhat-lsb-core does not exist on EL9/10 and silently no-op'd). Make every script fail-fast and modernize the toolchain. Reliability: - Add `set -euo pipefail` and a purpose/usage header to every script; standardize shebangs to bash. - beaker_packages.sh: drop the blanket `||:` so a missing package fails the build; remove redhat-lsb-core (retired on EL9/10, unused). - container_safe_services.sh: guard `systemctl daemon-reload`, which legitimately fails offline during the image build. - user.sh: fix single-quoted sudoers lines that wrote a literal "$user_id" instead of the username. Toolchain (build images): - Replace rvm with mise. Add per-EL install_mise.sh (EL8 rpm repo, EL9/10 COPR) and common mise.sh provisioning Ruby 3.2 (OpenVox 8) and 4.0 (OpenVox 9); default 3.2 via the ruby_version build arg. - Pin bundler to 2.7.2 (simp-rake-helpers requires < 3.0). - Add libyaml-devel on EL8/EL9 so Ruby's psych extension compiles. - Build dockerfiles: default ruby_version=3.2; call install_mise.sh + mise.sh instead of rvm.sh. Docs/CI: - README: document the two image families and per-script purpose table; reframe build args around OpenVox; fix stale Dockerhub -> ghcr.io note. - build_containers.yml: Ruby choices 2.7/3.1/3.3 -> 3.2/4.0, default 3.2. All six images (EL8/9/10 Beaker + Build) build and smoke-test clean locally with podman. Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/workflows/build_containers.yml | 8 +-- build/Dockerfiles/README.md | 52 +++++++++++++++---- build/Dockerfiles/SIMP_EL10_Build.dockerfile | 5 +- build/Dockerfiles/SIMP_EL8_Build.dockerfile | 5 +- build/Dockerfiles/SIMP_EL9_Build.dockerfile | 5 +- .../scripts/common/beaker_packages.sh | 33 ++++++++---- .../scripts/common/container_safe_services.sh | 13 ++++- .../common/minimize_package_installs.sh | 9 ++++ build/Dockerfiles/scripts/common/mise.sh | 46 ++++++++++++++++ .../scripts/common/package_cleanup.sh | 8 ++- .../Dockerfiles/scripts/common/prime_ruby.sh | 21 ++++---- build/Dockerfiles/scripts/common/rvm.sh | 32 ------------ build/Dockerfiles/scripts/common/user.sh | 26 ++++++---- .../scripts/el10/00_setup_vault.sh | 10 +++- .../scripts/el10/00_system_prep.sh | 10 +++- build/Dockerfiles/scripts/el10/05_selinux.sh | 9 +++- .../scripts/el10/10_dev_packages.sh | 9 +++- .../Dockerfiles/scripts/el10/install_mise.sh | 12 +++++ .../Dockerfiles/scripts/el8/00_setup_vault.sh | 10 +++- .../Dockerfiles/scripts/el8/00_system_prep.sh | 10 +++- build/Dockerfiles/scripts/el8/05_selinux.sh | 9 +++- .../scripts/el8/10_dev_packages.sh | 11 +++- build/Dockerfiles/scripts/el8/install_mise.sh | 12 +++++ .../Dockerfiles/scripts/el9/00_setup_vault.sh | 10 +++- .../Dockerfiles/scripts/el9/00_system_prep.sh | 10 +++- build/Dockerfiles/scripts/el9/05_selinux.sh | 9 +++- .../scripts/el9/10_dev_packages.sh | 11 +++- build/Dockerfiles/scripts/el9/install_mise.sh | 12 +++++ 28 files changed, 319 insertions(+), 98 deletions(-) create mode 100755 build/Dockerfiles/scripts/common/mise.sh delete mode 100644 build/Dockerfiles/scripts/common/rvm.sh create mode 100755 build/Dockerfiles/scripts/el10/install_mise.sh create mode 100755 build/Dockerfiles/scripts/el8/install_mise.sh create mode 100755 build/Dockerfiles/scripts/el9/install_mise.sh diff --git a/.github/workflows/build_containers.yml b/.github/workflows/build_containers.yml index a2a15aa3..d49824ca 100644 --- a/.github/workflows/build_containers.yml +++ b/.github/workflows/build_containers.yml @@ -5,13 +5,13 @@ on: workflow_dispatch: inputs: ruby_version: - description: "Ruby version build arg" + description: "Default Ruby version build arg (3.2 = OpenVox 8, 4.0 = OpenVox 9)" required: true type: choice + default: "3.2" options: - - "2.7" - - "3.1" - - "3.3" + - "3.2" + - "4.0" dockerfile: description: > Filename of Dockerfile to build (under build/Dockerfiles/). diff --git a/build/Dockerfiles/README.md b/build/Dockerfiles/README.md index 188d8b66..63db467d 100644 --- a/build/Dockerfiles/README.md +++ b/build/Dockerfiles/README.md @@ -1,13 +1,40 @@ # SIMP Dockerfiles -These files are meant to assist with various SIMP build activities +These files assist with various SIMP build and test activities. There are two +families of image, and they run **different** sets of scripts: -Please read each file for details as to the purpose and usage of the file +| Image family | Dockerfiles | Purpose | +|---|---|---| +| **Beaker SUT** | `SIMP_EL*_Beaker.dockerfile` | Minimal, systemd-enabled containers used by Beaker as acceptance-test nodes (systems under test). They do **not** contain Ruby or an agent — Beaker installs the OpenVox/Puppet agent at test time. Published as `ghcr.io/simp/simp-el-beaker`. | +| **ISO build** | `SIMP_EL*_Build.dockerfile` | Full dev/build toolchain (mise-managed Ruby, rpmbuild, ISO tooling) for building SIMP ISOs and RPMs as the unprivileged `build_user`. | ## Helper Scripts -There are helper scripts in the `scripts` directory that you may find useful if -you are setting up your own development system from scratch. +Scripts live under `scripts/`. `scripts/common/` is shared; `scripts/el8/`, +`scripts/el9/`, and `scripts/el10/` hold per-release variants. Each Dockerfile +`ADD`s the common scripts plus the matching per-release directory, so a +per-release script of the same name overrides the common one. + +Every script carries a header comment describing its purpose and which image +family uses it. In summary: + +| Script | Purpose | Used by | +|---|---|---| +| `common/minimize_package_installs.sh` | Configure dnf for a minimal footprint (no docs/weak deps, single langpack); refresh TLS trust + curl | both | +| `common/package_cleanup.sh` | Clear dnf caches and delete stray doc files to shrink the image | both | +| `el*/00_system_prep.sh` | Install dnf config-manager, bake in minimization settings, rebuild rpmdb, install yum-utils | both | +| `common/beaker_packages.sh` | Install the baseline userland a Beaker node needs | Beaker SUT | +| `common/container_safe_services.sh` | Install a systemd unit that strips container-incompatible directives from unit files | Beaker SUT | +| `el*/00_setup_vault.sh` | Pin repos to AlmaLinux Vault at the oldest point release for a stable library floor | ISO build | +| `el*/05_selinux.sh` | Downgrade to the vault baseline and install SELinux policy/tooling | ISO build | +| `el*/10_dev_packages.sh` | Install the full ISO/RPM build toolchain | ISO build | +| `common/user.sh` | Create the `build_user` build account | ISO build | +| `el*/install_mise.sh` | Install [mise](https://mise.jdx.dev) system-wide (per-release repo setup) | ISO build | +| `common/mise.sh` | Provision Ruby (3.2 + 4.0) for `build_user` via mise and install bundler | ISO build | +| `common/prime_ruby.sh` | Clone simp-core and `bundle install` to warm the gem cache | ISO build | + +All scripts use `set -euo pipefail` so a failing step aborts the image build +instead of silently producing a broken image. ## Building @@ -15,18 +42,21 @@ you are setting up your own development system from scratch. ### Build args -The `SIMP_*_Build.dockerfile` builds support a `--build-arg` to set the initial -`ruby_version` of RVM: +The `SIMP_*_Build.dockerfile` builds support a `--build-arg` to set the default +`ruby_version` that mise activates. Both Ruby **3.2** and **4.0** are always +installed; the build arg only selects which is the global default: ``` -buildah build -t simp_build_centos8_ruby3_1 --build-arg ruby_version=3.1 -f SIMP_EL8_Build.dockerfile +buildah build -t simp_build_el8_ruby40 --build-arg ruby_version=4.0 -f SIMP_EL8_Build.dockerfile ``` -The current default for this argument is `2.7` (to support Puppet 7) +The default for this argument is `3.2`: +- Ruby **3.2** → OpenVox 8 (current) +- Ruby **4.0** → OpenVox 9 (upcoming) ## Pushing -If you build using `buildah`, you'll need to make sure you push to Dockerhub -using `podman push --format=docker ...` - +Images are published to the GitHub Container Registry (`ghcr.io`). If you build +with `buildah`, push using `podman push --format=docker ...`. CI publishing is +handled by `.github/workflows/build_containers.yml`. diff --git a/build/Dockerfiles/SIMP_EL10_Build.dockerfile b/build/Dockerfiles/SIMP_EL10_Build.dockerfile index 149d9914..2e5abb1c 100644 --- a/build/Dockerfiles/SIMP_EL10_Build.dockerfile +++ b/build/Dockerfiles/SIMP_EL10_Build.dockerfile @@ -25,7 +25,7 @@ FROM almalinux:10.0 ENV container docker -ARG ruby_version=3.3 +ARG ruby_version=3.2 RUN mkdir /root/build_scripts ADD scripts/common/* /root/build_scripts/ @@ -40,7 +40,8 @@ RUN ./minimize_package_installs.sh RUN ./05_selinux.sh RUN ./10_dev_packages.sh RUN ./user.sh -RUN ./rvm.sh build_user "$ruby_version" +RUN ./install_mise.sh +RUN ./mise.sh build_user "$ruby_version" RUN ./prime_ruby.sh RUN ./package_cleanup.sh RUN rm -rf /root/build_scripts diff --git a/build/Dockerfiles/SIMP_EL8_Build.dockerfile b/build/Dockerfiles/SIMP_EL8_Build.dockerfile index 182f94fe..0a73fd40 100644 --- a/build/Dockerfiles/SIMP_EL8_Build.dockerfile +++ b/build/Dockerfiles/SIMP_EL8_Build.dockerfile @@ -25,7 +25,7 @@ FROM almalinux:8.4 ENV container docker -ARG ruby_version=2.7 +ARG ruby_version=3.2 RUN mkdir /root/build_scripts ADD scripts/common/* /root/build_scripts/ @@ -40,7 +40,8 @@ RUN ./minimize_package_installs.sh RUN ./05_selinux.sh RUN ./10_dev_packages.sh RUN ./user.sh -RUN ./rvm.sh build_user "$ruby_version" +RUN ./install_mise.sh +RUN ./mise.sh build_user "$ruby_version" RUN ./prime_ruby.sh RUN ./package_cleanup.sh RUN rm -rf /root/build_scripts diff --git a/build/Dockerfiles/SIMP_EL9_Build.dockerfile b/build/Dockerfiles/SIMP_EL9_Build.dockerfile index 4af267d1..8236e575 100644 --- a/build/Dockerfiles/SIMP_EL9_Build.dockerfile +++ b/build/Dockerfiles/SIMP_EL9_Build.dockerfile @@ -25,7 +25,7 @@ FROM almalinux:9.0 ENV container docker -ARG ruby_version=3.1 +ARG ruby_version=3.2 RUN mkdir /root/build_scripts ADD scripts/common/* /root/build_scripts/ @@ -40,7 +40,8 @@ RUN ./minimize_package_installs.sh RUN ./05_selinux.sh RUN ./10_dev_packages.sh RUN ./user.sh -RUN ./rvm.sh build_user "$ruby_version" +RUN ./install_mise.sh +RUN ./mise.sh build_user "$ruby_version" RUN ./prime_ruby.sh RUN ./package_cleanup.sh RUN rm -rf /root/build_scripts diff --git a/build/Dockerfiles/scripts/common/beaker_packages.sh b/build/Dockerfiles/scripts/common/beaker_packages.sh index ee141b74..82e5dae8 100644 --- a/build/Dockerfiles/scripts/common/beaker_packages.sh +++ b/build/Dockerfiles/scripts/common/beaker_packages.sh @@ -1,11 +1,24 @@ -#!/bin/sh +#!/bin/bash +# +# Install the baseline userland a Beaker test node (system under test) needs +# before acceptance tests run. Beaker installs the OpenVox/Puppet agent itself +# at test time, so this only provides the supporting utilities. +# +# Used by: Beaker SUT images (SIMP_EL*_Beaker.dockerfile) +# +# NOTE: redhat-lsb-core was intentionally dropped. It does not exist on EL9/EL10 +# (LSB tooling was retired) and nothing in the SIMP acceptance stack needs it. +# Installs are no longer wrapped in "||:" so a missing package now fails the +# build instead of silently shipping an incomplete image. +# +set -euo pipefail -yum -y install redhat-lsb-core ||: -yum -y install findutils ||: -yum -y install ncurses ||: -yum -y install openssl ||: -yum -y install procps-ng ||: -yum -y install rsync ||: -yum -y install sudo ||: -yum -y install systemd ||: -yum -y install tar ||: +yum -y install \ + findutils \ + ncurses \ + openssl \ + procps-ng \ + rsync \ + sudo \ + systemd \ + tar diff --git a/build/Dockerfiles/scripts/common/container_safe_services.sh b/build/Dockerfiles/scripts/common/container_safe_services.sh index 86cd041b..96ca987d 100644 --- a/build/Dockerfiles/scripts/common/container_safe_services.sh +++ b/build/Dockerfiles/scripts/common/container_safe_services.sh @@ -1,4 +1,13 @@ #!/bin/bash +# +# Install a systemd path+oneshot unit that strips CapabilityBoundingSet and +# PrivateNetwork directives from unit files. Those directives fail inside an +# unprivileged container and cannot be reliably overridden, so this keeps the +# systemd-based test node bootable. +# +# Used by: Beaker SUT images (SIMP_EL*_Beaker.dockerfile) +# +set -euo pipefail if [ -d "/usr/lib/systemd" ]; then mkdir -p "/usr/lib/systemd/system" @@ -29,5 +38,7 @@ ExecStart=/usr/bin/systemctl daemon-reload HERE fi -systemctl daemon-reload +# daemon-reload needs a running systemd, which is absent during the image build; +# ignore failure here. The unit is still enabled so it runs when the node boots. +systemctl daemon-reload || true systemctl enable container_safe_services.path diff --git a/build/Dockerfiles/scripts/common/minimize_package_installs.sh b/build/Dockerfiles/scripts/common/minimize_package_installs.sh index df19e5b8..89ffe3a3 100644 --- a/build/Dockerfiles/scripts/common/minimize_package_installs.sh +++ b/build/Dockerfiles/scripts/common/minimize_package_installs.sh @@ -1,8 +1,17 @@ #!/bin/bash +# +# Configure dnf/yum for a minimal image footprint (no docs, no weak deps, a +# single language pack) and refresh the TLS trust + curl so the build can reach +# current package mirrors. +# +# Used by: both image families (Beaker SUT and ISO build) +# +set -euo pipefail mkdir -p /etc/rpm echo '%_install_langs C:en:en_US:en_US.UTF-8' > /etc/rpm/macros.image-language-conf +# Best-effort: the langpack may already be present or unavailable in vault repos. yum --noplugins \ --setopt=override_install_langs=en_US.utf8 \ --setopt=tsflags=nodocs \ diff --git a/build/Dockerfiles/scripts/common/mise.sh b/build/Dockerfiles/scripts/common/mise.sh new file mode 100755 index 00000000..9f479593 --- /dev/null +++ b/build/Dockerfiles/scripts/common/mise.sh @@ -0,0 +1,46 @@ +#!/bin/bash +# +# Provision Ruby for build_user with mise (https://mise.jdx.dev). mise itself is +# installed system-wide by the per-EL install_mise.sh; this installs the Ruby +# versions SIMP/OpenVox builds need and wires mise into build_user's shell. +# +# Ruby 3.2 -> OpenVox 8 (current) +# Ruby 4.0 -> OpenVox 9 (upcoming) +# +# Both Rubies are installed and kept available; the build arg selects the global +# default (see the SIMP_EL*_Build.dockerfile "ruby_version" ARG). +# +# Used by: ISO build images (SIMP_EL*_Build.dockerfile) +# +set -euo pipefail + +user_id="${1:-build_user}" +default_ruby="${2:-3.2}" + +# Rubies to make available in the image. The default is listed first so mise +# treats it as the global default. +if [ "$default_ruby" = "4.0" ]; then + ruby_list="ruby@4.0 ruby@3.2" +else + ruby_list="ruby@${default_ruby} ruby@4.0" +fi + +# Don't ship gem docs. +runuser "$user_id" -l -c "echo 'gem: --no-document' > ~/.gemrc" + +# Activate mise for both non-login (shims on PATH, used by runuser -l -c) and +# interactive login shells (the container CMD runs 'su -l build_user'). +runuser "$user_id" -l -c 'echo '\''export PATH="$HOME/.local/share/mise/shims:$PATH"'\'' >> ~/.bash_profile' +runuser "$user_id" -l -c 'echo '\''eval "$(mise activate bash)"'\'' >> ~/.bashrc' + +# Install the Rubies and set the global default (first entry in the list). +runuser "$user_id" -l -c "mise use -g ${ruby_list}" + +# Install bundler for every Ruby mise manages. simp-rake-helpers requires +# bundler < 3.0, so pin to the 2.x release simp-core's Gemfile.lock is bundled +# with rather than taking the latest (currently 4.x). +bundler_version='2.7.2' +runuser "$user_id" -l -c "mise exec ruby@3.2 -- gem install bundler -v ${bundler_version}" +runuser "$user_id" -l -c "mise exec ruby@4.0 -- gem install bundler -v ${bundler_version}" + +runuser "$user_id" -l -c "mise ls" diff --git a/build/Dockerfiles/scripts/common/package_cleanup.sh b/build/Dockerfiles/scripts/common/package_cleanup.sh index bd0d462d..05d937e0 100644 --- a/build/Dockerfiles/scripts/common/package_cleanup.sh +++ b/build/Dockerfiles/scripts/common/package_cleanup.sh @@ -1,11 +1,17 @@ #!/bin/bash +# +# Shrink the finished image: clear the dnf/yum caches and delete documentation +# files that were installed despite the nodocs setting. +# +# Used by: both image families (Beaker SUT and ISO build) +# +set -euo pipefail # Clean up yum cache yum clean all rm -rf /var/cache/yum # Clean up docs that ignore the yum settings - docdirs=$(rpm --eval '%{__docdir_path}' | sed 's/:\+/ /g') for dir in $docdirs; do diff --git a/build/Dockerfiles/scripts/common/prime_ruby.sh b/build/Dockerfiles/scripts/common/prime_ruby.sh index 46069d1a..832d3e94 100644 --- a/build/Dockerfiles/scripts/common/prime_ruby.sh +++ b/build/Dockerfiles/scripts/common/prime_ruby.sh @@ -1,13 +1,16 @@ -#!/bin/sh -e +#!/bin/bash +# +# Warm the build workspace: clone simp-core as build_user and run bundle +# install under the mise-managed Ruby so gems are cached in the image. +# +# Used by: ISO build images (SIMP_EL*_Build.dockerfile) +# +set -euo pipefail -user_id="$1" - -if [ -z "$user_id" ]; then - user_id='build_user' -fi +user_id="${1:-build_user}" # Check out a copy of simp-core for building -runuser $user_id -l -c "git clone https://github.com/simp/simp-core" +runuser "$user_id" -l -c "git clone https://github.com/simp/simp-core" -# Prep the build space -runuser $user_id -l -c "cd simp-core; bundle install" +# Prep the build space (ruby/bundler are provided by mise; see mise.sh) +runuser "$user_id" -l -c "cd simp-core && bundle install" diff --git a/build/Dockerfiles/scripts/common/rvm.sh b/build/Dockerfiles/scripts/common/rvm.sh deleted file mode 100644 index ef107068..00000000 --- a/build/Dockerfiles/scripts/common/rvm.sh +++ /dev/null @@ -1,32 +0,0 @@ -#!/bin/sh -e - -user_id="${1:-build_user}" -ruby_version="${2:-2.7}" - -# Set up RVM -runuser $user_id -l -c "echo 'gem: --no-document' > .gemrc" - -# Do our best to get one of the keys from at one of the servers, and to -# trust the right ones if the GPG keyservers return bad keys -# -# This is the key that we want: -# -# 7D2BAF1CF37B13E2069D6956105BD0E739499BDB # piotr.kuczynski@gmail.com -# -# See: -# - https://rvm.io/rvm/security -# - https://github.com/rvm/rvm/blob/master/docs/gpg.md -# - https://github.com/rvm/rvm/issues/4449 -# - https://github.com/rvm/rvm/issues/4250 -# - https://seclists.org/oss-sec/2018/q3/174 -# -key_id='7D2BAF1CF37B13E2069D6956105BD0E739499BDB' -runuser $user_id -l -c "for i in {1..5}; do { gpg2 --keyserver hkp://keys.openpgp.org --recv-keys $key_id || gpg2 --keyserver hkp://keyserver.ubuntu.com --recv-keys $key_id; } && break || sleep 1; done" -#runuser $user_id -l -c "gpg2 --refresh-keys" -runuser $user_id -l -c "curl -sSL https://raw.githubusercontent.com/rvm/rvm/stable/binscripts/rvm-installer -o rvm-installer && curl -sSL https://raw.githubusercontent.com/rvm/rvm/stable/binscripts/rvm-installer.asc -o rvm-installer.asc && gpg2 --verify rvm-installer.asc rvm-installer && bash rvm-installer" -runuser $user_id -l -c "rvm install ${ruby_version}" -runuser $user_id -l -c "rvm use --default ${ruby_version}" -runuser $user_id -l -c "rvm all do gem install bundler -v '~> 1.16'" -runuser $user_id -l -c "rvm ls" -#runuser $user_id -l -c "rvm all do gem install bundler -v '~> 2.0'" -runuser $user_id -l -c "rvm all do gem install bundler -v 2.4.22" diff --git a/build/Dockerfiles/scripts/common/user.sh b/build/Dockerfiles/scripts/common/user.sh index 17d4887f..4cabf7fa 100644 --- a/build/Dockerfiles/scripts/common/user.sh +++ b/build/Dockerfiles/scripts/common/user.sh @@ -1,14 +1,20 @@ -#!/bin/sh -e +#!/bin/bash +# +# Create the unprivileged build account (build_user) used to build SIMP ISOs. +# The account is a passwordless-sudo wheel member so the build tooling (mise, +# Ruby, rpmbuild) can manage system state during the build. +# +# Used by: ISO build images (SIMP_EL*_Build.dockerfile) +# +set -euo pipefail -user_id="$1" +user_id="${1:-build_user}" -if [ -z "$user_id" ]; then - user_id='build_user' -fi +useradd -b /home -G wheel -m -c "Build User" -s /bin/bash -U "$user_id" -useradd -b /home -G wheel -m -c "Build User" -s /bin/bash -U $user_id - -# Ensure that '$user_id' can sudo to root for RVM -echo 'Defaults:$user_id !requiretty' >> /etc/sudoers -echo '$user_id ALL=(ALL) NOPASSWD: ALL' >> /etc/sudoers +# Ensure that "$user_id" can sudo to root for the build tooling. +# NOTE: these use double quotes so "$user_id" expands; the previous single-quoted +# versions wrote a literal "$user_id" into /etc/sudoers. +echo "Defaults:${user_id} !requiretty" >> /etc/sudoers +echo "${user_id} ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers rm -rf /etc/security/limits.d/*.conf diff --git a/build/Dockerfiles/scripts/el10/00_setup_vault.sh b/build/Dockerfiles/scripts/el10/00_setup_vault.sh index 3ac8c812..94808cda 100644 --- a/build/Dockerfiles/scripts/el10/00_setup_vault.sh +++ b/build/Dockerfiles/scripts/el10/00_setup_vault.sh @@ -1,4 +1,12 @@ -#!/bin/sh -e +#!/bin/bash +# +# Replace the default repos with AlmaLinux Vault repos pinned to the oldest +# point release, giving the build a stable library/ABI floor and keeping GPG +# verification working with the base image's bundled signing key. +# +# Used by: ISO build images (SIMP_EL*_Build.dockerfile) +# +set -euo pipefail VAULT="https://vault.almalinux.org/10.0" GPG_KEY="file:///etc/pki/rpm-gpg/RPM-GPG-KEY-AlmaLinux-10" diff --git a/build/Dockerfiles/scripts/el10/00_system_prep.sh b/build/Dockerfiles/scripts/el10/00_system_prep.sh index b8e31051..805ffa74 100644 --- a/build/Dockerfiles/scripts/el10/00_system_prep.sh +++ b/build/Dockerfiles/scripts/el10/00_system_prep.sh @@ -1,4 +1,12 @@ -#!/bin/sh -e +#!/bin/bash +# +# Prepare dnf: install config-manager, bake the minimization settings into the +# dnf config, install the en_US language pack, rebuild the rpm database, and +# install yum-utils. +# +# Used by: both image families (Beaker SUT and ISO build) +# +set -euo pipefail dnf install -y --setopt=override_install_langs=en_US.utf8 --setopt=install_weak_deps=False --setopt=tsflags=nodocs 'dnf-command(config-manager)' dnf config-manager --save --setopt=best=True diff --git a/build/Dockerfiles/scripts/el10/05_selinux.sh b/build/Dockerfiles/scripts/el10/05_selinux.sh index fd4427a7..46bfaf44 100644 --- a/build/Dockerfiles/scripts/el10/05_selinux.sh +++ b/build/Dockerfiles/scripts/el10/05_selinux.sh @@ -1,4 +1,11 @@ -#!/bin/sh -e +#!/bin/bash +# +# Downgrade packages back to the vault baseline (keeping TLS-critical packages +# current) and install the SELinux policy and management tooling. +# +# Used by: ISO build images (SIMP_EL*_Build.dockerfile) +# +set -euo pipefail # Vault repos were set up by 00_setup_vault.sh. Downgrade any packages that # minimize_package_installs.sh may have bumped above 10.0, keeping TLS-critical diff --git a/build/Dockerfiles/scripts/el10/10_dev_packages.sh b/build/Dockerfiles/scripts/el10/10_dev_packages.sh index 6f5965fd..962fbaf7 100644 --- a/build/Dockerfiles/scripts/el10/10_dev_packages.sh +++ b/build/Dockerfiles/scripts/el10/10_dev_packages.sh @@ -1,4 +1,11 @@ -#!/bin/sh -e +#!/bin/bash +# +# Install the full ISO/RPM build toolchain: rpmbuild tooling, ruby-devel and +# compilers, ISO-creation tools, fonts, an SSH server for CI, and helpers. +# +# Used by: ISO build images (SIMP_EL*_Build.dockerfile) +# +set -euo pipefail dnf install -y epel-release ||: dnf config-manager --set-enabled crb diff --git a/build/Dockerfiles/scripts/el10/install_mise.sh b/build/Dockerfiles/scripts/el10/install_mise.sh new file mode 100755 index 00000000..2e9601d3 --- /dev/null +++ b/build/Dockerfiles/scripts/el10/install_mise.sh @@ -0,0 +1,12 @@ +#!/bin/bash +# +# Install mise (Ruby version manager) system-wide on AlmaLinux 10 from COPR. +# The managed Rubies are provisioned later by mise.sh. +# +# Used by: ISO build images (SIMP_EL10_Build.dockerfile) +# +set -euo pipefail + +dnf install -y 'dnf-command(copr)' +dnf copr enable -y jdxcode/mise +dnf install -y mise diff --git a/build/Dockerfiles/scripts/el8/00_setup_vault.sh b/build/Dockerfiles/scripts/el8/00_setup_vault.sh index bc161b7b..c223cdb3 100644 --- a/build/Dockerfiles/scripts/el8/00_setup_vault.sh +++ b/build/Dockerfiles/scripts/el8/00_setup_vault.sh @@ -1,4 +1,12 @@ -#!/bin/sh -e +#!/bin/bash +# +# Replace the default repos with AlmaLinux Vault repos pinned to the oldest +# point release, giving the build a stable library/ABI floor and keeping GPG +# verification working with the base image's bundled signing key. +# +# Used by: ISO build images (SIMP_EL*_Build.dockerfile) +# +set -euo pipefail VAULT="https://vault.almalinux.org/8.4" GPG_KEY="file:///etc/pki/rpm-gpg/RPM-GPG-KEY-AlmaLinux" diff --git a/build/Dockerfiles/scripts/el8/00_system_prep.sh b/build/Dockerfiles/scripts/el8/00_system_prep.sh index b8e31051..805ffa74 100644 --- a/build/Dockerfiles/scripts/el8/00_system_prep.sh +++ b/build/Dockerfiles/scripts/el8/00_system_prep.sh @@ -1,4 +1,12 @@ -#!/bin/sh -e +#!/bin/bash +# +# Prepare dnf: install config-manager, bake the minimization settings into the +# dnf config, install the en_US language pack, rebuild the rpm database, and +# install yum-utils. +# +# Used by: both image families (Beaker SUT and ISO build) +# +set -euo pipefail dnf install -y --setopt=override_install_langs=en_US.utf8 --setopt=install_weak_deps=False --setopt=tsflags=nodocs 'dnf-command(config-manager)' dnf config-manager --save --setopt=best=True diff --git a/build/Dockerfiles/scripts/el8/05_selinux.sh b/build/Dockerfiles/scripts/el8/05_selinux.sh index 4c7f834c..371fc5c4 100644 --- a/build/Dockerfiles/scripts/el8/05_selinux.sh +++ b/build/Dockerfiles/scripts/el8/05_selinux.sh @@ -1,4 +1,11 @@ -#!/bin/sh -e +#!/bin/bash +# +# Downgrade packages back to the vault baseline (keeping TLS-critical packages +# current) and install the SELinux policy and management tooling. +# +# Used by: ISO build images (SIMP_EL*_Build.dockerfile) +# +set -euo pipefail # Vault repos were set up by 00_setup_vault.sh. Downgrade any packages that # minimize_package_installs.sh may have bumped above 8.4, keeping TLS-critical diff --git a/build/Dockerfiles/scripts/el8/10_dev_packages.sh b/build/Dockerfiles/scripts/el8/10_dev_packages.sh index b9a714e8..d9e05231 100644 --- a/build/Dockerfiles/scripts/el8/10_dev_packages.sh +++ b/build/Dockerfiles/scripts/el8/10_dev_packages.sh @@ -1,4 +1,11 @@ -#!/bin/sh -e +#!/bin/bash +# +# Install the full ISO/RPM build toolchain: rpmbuild tooling, ruby-devel and +# compilers, ISO-creation tools, fonts, an SSH server for CI, and helpers. +# +# Used by: ISO build images (SIMP_EL*_Build.dockerfile) +# +set -euo pipefail dnf install -y epel-release dnf config-manager --set-enabled powertools @@ -6,7 +13,7 @@ dnf install -y rpm-build rpmdevtools rpm-devel rpm-sign yum-utils dnf install -y ruby-devel dnf install -y util-linux openssl augeas-libs createrepo genisoimage isomd5sum git gnupg2 libicu-devel libxml2 libxml2-devel libxslt libxslt-devel which dnf install -y python3-virtualenv fontconfig dejavu-sans-fonts dejavu-sans-mono-fonts dejavu-serif-fonts dejavu-fonts-common libjpeg-devel zlib-devel openssl-devel -dnf install -y libyaml glibc-headers autoconf gcc gcc-c++ glibc-devel readline-devel libffi-devel automake libtool bison sqlite-devel pinentry +dnf install -y libyaml libyaml-devel glibc-headers autoconf gcc gcc-c++ glibc-devel readline-devel libffi-devel automake libtool bison sqlite-devel pinentry # Helper packages dnf install -y rubygems vim-enhanced jq diff --git a/build/Dockerfiles/scripts/el8/install_mise.sh b/build/Dockerfiles/scripts/el8/install_mise.sh new file mode 100755 index 00000000..54af3233 --- /dev/null +++ b/build/Dockerfiles/scripts/el8/install_mise.sh @@ -0,0 +1,12 @@ +#!/bin/bash +# +# Install mise (Ruby version manager) system-wide on AlmaLinux 8 from the +# upstream rpm repo. The managed Rubies are provisioned later by mise.sh. +# +# Used by: ISO build images (SIMP_EL8_Build.dockerfile) +# +set -euo pipefail + +dnf install -y dnf-plugins-core +dnf config-manager --add-repo https://mise.en.dev/rpm/mise.repo +dnf install -y mise diff --git a/build/Dockerfiles/scripts/el9/00_setup_vault.sh b/build/Dockerfiles/scripts/el9/00_setup_vault.sh index 4d870392..2cc9e3d0 100644 --- a/build/Dockerfiles/scripts/el9/00_setup_vault.sh +++ b/build/Dockerfiles/scripts/el9/00_setup_vault.sh @@ -1,4 +1,12 @@ -#!/bin/sh -e +#!/bin/bash +# +# Replace the default repos with AlmaLinux Vault repos pinned to the oldest +# point release, giving the build a stable library/ABI floor and keeping GPG +# verification working with the base image's bundled signing key. +# +# Used by: ISO build images (SIMP_EL*_Build.dockerfile) +# +set -euo pipefail VAULT="https://vault.almalinux.org/9.0" GPG_KEY="file:///etc/pki/rpm-gpg/RPM-GPG-KEY-AlmaLinux-9" diff --git a/build/Dockerfiles/scripts/el9/00_system_prep.sh b/build/Dockerfiles/scripts/el9/00_system_prep.sh index b8e31051..805ffa74 100644 --- a/build/Dockerfiles/scripts/el9/00_system_prep.sh +++ b/build/Dockerfiles/scripts/el9/00_system_prep.sh @@ -1,4 +1,12 @@ -#!/bin/sh -e +#!/bin/bash +# +# Prepare dnf: install config-manager, bake the minimization settings into the +# dnf config, install the en_US language pack, rebuild the rpm database, and +# install yum-utils. +# +# Used by: both image families (Beaker SUT and ISO build) +# +set -euo pipefail dnf install -y --setopt=override_install_langs=en_US.utf8 --setopt=install_weak_deps=False --setopt=tsflags=nodocs 'dnf-command(config-manager)' dnf config-manager --save --setopt=best=True diff --git a/build/Dockerfiles/scripts/el9/05_selinux.sh b/build/Dockerfiles/scripts/el9/05_selinux.sh index cc6a3b37..e36525ed 100644 --- a/build/Dockerfiles/scripts/el9/05_selinux.sh +++ b/build/Dockerfiles/scripts/el9/05_selinux.sh @@ -1,4 +1,11 @@ -#!/bin/sh -e +#!/bin/bash +# +# Downgrade packages back to the vault baseline (keeping TLS-critical packages +# current) and install the SELinux policy and management tooling. +# +# Used by: ISO build images (SIMP_EL*_Build.dockerfile) +# +set -euo pipefail # Vault repos were set up by 00_setup_vault.sh. Downgrade any packages that # minimize_package_installs.sh may have bumped above 9.0, keeping TLS-critical diff --git a/build/Dockerfiles/scripts/el9/10_dev_packages.sh b/build/Dockerfiles/scripts/el9/10_dev_packages.sh index f930d530..6955ee56 100644 --- a/build/Dockerfiles/scripts/el9/10_dev_packages.sh +++ b/build/Dockerfiles/scripts/el9/10_dev_packages.sh @@ -1,4 +1,11 @@ -#!/bin/sh -e +#!/bin/bash +# +# Install the full ISO/RPM build toolchain: rpmbuild tooling, ruby-devel and +# compilers, ISO-creation tools, fonts, an SSH server for CI, and helpers. +# +# Used by: ISO build images (SIMP_EL*_Build.dockerfile) +# +set -euo pipefail dnf install -y epel-release dnf config-manager --set-enabled crb @@ -9,7 +16,7 @@ dnf install -y util-linux openssl augeas-libs createrepo_c git gnupg2 libicu-dev dnf install -y genisoimage isomd5sum ||: dnf install -y xorriso ||: dnf install -y python3 fontconfig dejavu-sans-fonts dejavu-sans-mono-fonts dejavu-serif-fonts libjpeg-devel zlib-devel openssl-devel -dnf install -y libyaml autoconf gcc gcc-c++ glibc-devel readline-devel libffi-devel automake libtool bison sqlite-devel pinentry +dnf install -y libyaml libyaml-devel autoconf gcc gcc-c++ glibc-devel readline-devel libffi-devel automake libtool bison sqlite-devel pinentry # Helper packages dnf install -y rubygems vim-enhanced jq diff --git a/build/Dockerfiles/scripts/el9/install_mise.sh b/build/Dockerfiles/scripts/el9/install_mise.sh new file mode 100755 index 00000000..d21586c6 --- /dev/null +++ b/build/Dockerfiles/scripts/el9/install_mise.sh @@ -0,0 +1,12 @@ +#!/bin/bash +# +# Install mise (Ruby version manager) system-wide on AlmaLinux 9 from COPR. +# The managed Rubies are provisioned later by mise.sh. +# +# Used by: ISO build images (SIMP_EL9_Build.dockerfile) +# +set -euo pipefail + +dnf install -y 'dnf-command(copr)' +dnf copr enable -y jdxcode/mise centos-stream+epel-next-9 +dnf install -y mise From aa0df136f66d51a4023cf8751c3d67f407f1822a Mon Sep 17 00:00:00 2001 From: Steven Pritchard Date: Wed, 17 Jun 2026 21:51:17 +0000 Subject: [PATCH 2/4] Automate container builds: PR validation + push/scheduled publish Add CI to build the build/Dockerfiles images automatically, complementing the existing manual workflow. - build_container.yml: new reusable (workflow_call) workflow that builds one image and optionally pushes it to ghcr.io, tagged `latest` and a UTC `YYYYMMDD` date tag so it is clear when an image was built. - containers.yml: builds all six images (EL8/9/10 Beaker + Build) via a matrix. Pull requests touching the Dockerfiles build without pushing (pre-merge validation, which the fail-fast scripts make meaningful); push to master (path-filtered), a weekly schedule, and manual dispatch build and push. The schedule keeps images current with upstream updates. - build_containers.yml: refactored to call the reusable workflow; retained for ad-hoc single-image rebuilds (specific Ruby version, ref, extra tag). - AGENTS.md: document the three container workflows. Validated with actionlint (clean). Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/workflows/build_container.yml | 96 ++++++++++++++++++++++++++ .github/workflows/build_containers.yml | 60 +++++++--------- .github/workflows/containers.yml | 63 +++++++++++++++++ AGENTS.md | 4 +- 4 files changed, 186 insertions(+), 37 deletions(-) create mode 100644 .github/workflows/build_container.yml create mode 100644 .github/workflows/containers.yml diff --git a/.github/workflows/build_container.yml b/.github/workflows/build_container.yml new file mode 100644 index 00000000..7784d927 --- /dev/null +++ b/.github/workflows/build_container.yml @@ -0,0 +1,96 @@ +--- +# Reusable workflow: build one container image from build/Dockerfiles/ and +# optionally push it to ghcr.io. Called by build_containers.yml (manual, +# single image) and containers.yml (PR validation + push/schedule publish). +name: 'Reusable: build container image' + +on: + workflow_call: + inputs: + dockerfile: + description: "Dockerfile filename under build/Dockerfiles/" + required: true + type: string + image_name: + description: "Image name to publish under ghcr.io//" + required: true + type: string + ruby_version: + description: "Default Ruby version build arg (ignored by Beaker images)" + required: false + type: string + default: "3.2" + ref: + description: "Branch, tag, or SHA to build from" + required: false + type: string + default: "" + push: + description: "Push the image to ghcr.io (false = build only, for PRs)" + required: false + type: boolean + default: false + extra_tag: + description: "Optional additional tag (in addition to latest + date)" + required: false + type: string + default: "" + +permissions: + contents: read + packages: write + +jobs: + build: + name: "${{ inputs.image_name }}" + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + with: + ref: ${{ inputs.ref }} + + - name: Compute image tags + id: meta + env: + IMAGE: "ghcr.io/${{ github.repository_owner }}/${{ inputs.image_name }}" + EXTRA_TAG: ${{ inputs.extra_tag }} + run: | + # Date-based tag (UTC) so it is clear when an image was built. + date_tag="$(date -u +%Y%m%d)" + tags="${IMAGE}:${date_tag},${IMAGE}:latest" + if [ -n "$EXTRA_TAG" ]; then + tags="${tags},${IMAGE}:${EXTRA_TAG}" + fi + echo "tags=${tags}" >> "$GITHUB_OUTPUT" + echo "Image tags: ${tags}" + + - name: Log in to GitHub Container Registry + if: ${{ inputs.push }} + uses: docker/login-action@v4 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build${{ inputs.push && ' and push' || ' (no push)' }} + env: + DOCKERFILE: ${{ inputs.dockerfile }} + RUBY_VERSION: ${{ inputs.ruby_version }} + TAGS: ${{ steps.meta.outputs.tags }} + PUSH: ${{ inputs.push }} + run: | + cd build/Dockerfiles + IFS=',' read -ra image_tags <<< "$TAGS" + tag_args=() + for t in "${image_tags[@]}"; do + tag_args+=( -t "$t" ) + done + docker build --pull \ + --build-arg ruby_version="$RUBY_VERSION" \ + -f "$DOCKERFILE" \ + "${tag_args[@]}" . + if [ "$PUSH" = "true" ]; then + for t in "${image_tags[@]}"; do + docker push "$t" + done + fi diff --git a/.github/workflows/build_containers.yml b/.github/workflows/build_containers.yml index d49824ca..a6e10a65 100644 --- a/.github/workflows/build_containers.yml +++ b/.github/workflows/build_containers.yml @@ -1,17 +1,12 @@ --- +# Manual, single-image build + publish. For ad-hoc rebuilds with a specific +# Ruby version, git ref, or extra tag. The full set of images is built +# automatically by containers.yml. name: 'RELENG: Build + publish SIMP container image' on: workflow_dispatch: inputs: - ruby_version: - description: "Default Ruby version build arg (3.2 = OpenVox 8, 4.0 = OpenVox 9)" - required: true - type: choice - default: "3.2" - options: - - "3.2" - - "4.0" dockerfile: description: > Filename of Dockerfile to build (under build/Dockerfiles/). @@ -20,12 +15,20 @@ on: image_name: description: > Image name to publish under ghcr.io//. - Example: simp-build-el9 + Example: simp-el9-build required: true - container_tag: - description: "Image tag. Example: latest, 20250427" + ruby_version: + description: "Default Ruby version build arg (3.2 = OpenVox 8, 4.0 = OpenVox 9)" required: true - default: latest + type: choice + default: "3.2" + options: + - "3.2" + - "4.0" + container_tag: + description: "Optional extra tag (latest + a YYYYMMDD date tag are always applied)" + required: false + default: "" git_ref: description: "Branch, tag, or SHA to build from" required: true @@ -36,27 +39,12 @@ permissions: packages: write jobs: - build_container: - name: Build and push container - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v6 - with: - ref: ${{ inputs.git_ref }} - - - name: Log in to GitHub Container Registry - uses: docker/login-action@v4 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Build and push - env: - RUBY_VERSION: ${{ inputs.ruby_version }} - DOCKERFILE: ${{ inputs.dockerfile }} - IMAGE_REF: ghcr.io/${{ github.repository_owner }}/${{ inputs.image_name }}:${{ inputs.container_tag }} - run: | - cd build/Dockerfiles - docker build --build-arg ruby_version="$RUBY_VERSION" -f "$DOCKERFILE" -t "$IMAGE_REF" . - docker push "$IMAGE_REF" + build: + uses: ./.github/workflows/build_container.yml + with: + dockerfile: ${{ inputs.dockerfile }} + image_name: ${{ inputs.image_name }} + ruby_version: ${{ inputs.ruby_version }} + ref: ${{ inputs.git_ref }} + push: true + extra_tag: ${{ inputs.container_tag }} diff --git a/.github/workflows/containers.yml b/.github/workflows/containers.yml new file mode 100644 index 00000000..3d5b74d4 --- /dev/null +++ b/.github/workflows/containers.yml @@ -0,0 +1,63 @@ +--- +# Build all container images from build/Dockerfiles/. +# +# - Pull requests touching the Dockerfiles build every image WITHOUT pushing, +# to catch breakage before merge (the scripts are fail-fast, so a broken +# build fails the check instead of shipping a broken image). +# - Pushes to master touching the Dockerfiles, the weekly schedule, and manual +# runs build AND push to ghcr.io. Images are tagged `latest` and `YYYYMMDD`. +# +# The weekly schedule keeps the images current with upstream base/package +# updates even when the Dockerfiles themselves have not changed. +name: 'Containers: build + publish' + +on: + pull_request: + paths: + - 'build/Dockerfiles/**' + - '.github/workflows/containers.yml' + - '.github/workflows/build_container.yml' + push: + branches: + - master + paths: + - 'build/Dockerfiles/**' + - '.github/workflows/containers.yml' + - '.github/workflows/build_container.yml' + schedule: + # Weekly, Mondays 06:17 UTC + - cron: '17 6 * * 1' + workflow_dispatch: + +permissions: + contents: read + packages: write + +concurrency: + group: containers-${{ github.ref }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + +jobs: + containers: + strategy: + fail-fast: false + matrix: + include: + - dockerfile: SIMP_EL8_Beaker.dockerfile + image_name: simp-el8-beaker + - dockerfile: SIMP_EL9_Beaker.dockerfile + image_name: simp-el9-beaker + - dockerfile: SIMP_EL10_Beaker.dockerfile + image_name: simp-el10-beaker + - dockerfile: SIMP_EL8_Build.dockerfile + image_name: simp-el8-build + - dockerfile: SIMP_EL9_Build.dockerfile + image_name: simp-el9-build + - dockerfile: SIMP_EL10_Build.dockerfile + image_name: simp-el10-build + uses: ./.github/workflows/build_container.yml + with: + dockerfile: ${{ matrix.dockerfile }} + image_name: ${{ matrix.image_name }} + # Build only on PRs; build + push everywhere else. + push: ${{ github.event_name != 'pull_request' }} diff --git a/AGENTS.md b/AGENTS.md index 98ad87d9..fad7cbcc 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -67,7 +67,9 @@ The Rakefile also loads `simp-rake-helpers` (`Simp::Rake::Build::Helpers`) which ### CI (`.github/workflows/`) - **`pr_checks.yml`** — runs on PRs: YAML lint, RPM file checks (`rake check:dot_underscore`, `rake check:test_file`), metadata lint, and `pdk build`; sets `SIMP_RPM_dist=.el7` -- **`build_containers.yml`** — manual workflow to build and push Docker build/test images to a registry +- **`containers.yml`** — builds all `build/Dockerfiles/` images. On PRs touching the Dockerfiles it builds every image without pushing (pre-merge validation); on push to master (path-filtered), a weekly schedule, and manual dispatch it builds and pushes to `ghcr.io` tagged `latest` + `YYYYMMDD` +- **`build_container.yml`** — reusable (`workflow_call`) workflow that builds/pushes a single image; called by `containers.yml` and `build_containers.yml` +- **`build_containers.yml`** — manual (`workflow_dispatch`) single-image build + publish for ad-hoc rebuilds (specific Ruby version, git ref, or extra tag) ### Gemfile Notes From 8b3817285b56748c928cc588f052cb2dd687b1c8 Mon Sep 17 00:00:00 2001 From: Steven Pritchard Date: Thu, 18 Jun 2026 16:28:12 +0000 Subject: [PATCH 3/4] Address review: validate ruby_version, use sudoers.d drop-in Two fixes from PR review: - mise.sh: always install both supported Rubies (3.2 + 4.0) and reject any ruby_version other than 3.2/4.0, instead of letting an unexpected value drop Ruby 3.2 and fail the later `mise exec ruby@3.2` bundler install. The build arg now only selects which Ruby is the global default. - user.sh: write sudo config to a 0440 /etc/sudoers.d/ drop-in (idempotent across rebuilds) and validate it with `visudo -cf` instead of appending to /etc/sudoers, so a malformed line can't break sudo for the whole image. sudo is installed in 05_selinux.sh, before user.sh. Co-Authored-By: Claude Opus 4.8 (1M context) --- build/Dockerfiles/scripts/common/mise.sh | 13 ++++++++++++- build/Dockerfiles/scripts/common/user.sh | 17 ++++++++++++----- 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/build/Dockerfiles/scripts/common/mise.sh b/build/Dockerfiles/scripts/common/mise.sh index 9f479593..a00f4204 100755 --- a/build/Dockerfiles/scripts/common/mise.sh +++ b/build/Dockerfiles/scripts/common/mise.sh @@ -17,12 +17,23 @@ set -euo pipefail user_id="${1:-build_user}" default_ruby="${2:-3.2}" +# Both supported Rubies are always installed; the build arg only selects which +# one is the global default. Reject anything else so a typo can't silently ship +# an image missing a Ruby the build expects. +case "$default_ruby" in + 3.2|4.0) ;; + *) + echo "ERROR: unsupported ruby_version '${default_ruby}' (expected 3.2 or 4.0)" >&2 + exit 1 + ;; +esac + # Rubies to make available in the image. The default is listed first so mise # treats it as the global default. if [ "$default_ruby" = "4.0" ]; then ruby_list="ruby@4.0 ruby@3.2" else - ruby_list="ruby@${default_ruby} ruby@4.0" + ruby_list="ruby@3.2 ruby@4.0" fi # Don't ship gem docs. diff --git a/build/Dockerfiles/scripts/common/user.sh b/build/Dockerfiles/scripts/common/user.sh index 4cabf7fa..1851b21c 100644 --- a/build/Dockerfiles/scripts/common/user.sh +++ b/build/Dockerfiles/scripts/common/user.sh @@ -12,9 +12,16 @@ user_id="${1:-build_user}" useradd -b /home -G wheel -m -c "Build User" -s /bin/bash -U "$user_id" -# Ensure that "$user_id" can sudo to root for the build tooling. -# NOTE: these use double quotes so "$user_id" expands; the previous single-quoted -# versions wrote a literal "$user_id" into /etc/sudoers. -echo "Defaults:${user_id} !requiretty" >> /etc/sudoers -echo "${user_id} ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers +# Ensure that "$user_id" can sudo to root for the build tooling. Write a +# dedicated /etc/sudoers.d/ drop-in (idempotent across rebuilds) with the +# 0440 perms sudo requires, and validate it with visudo before it takes effect +# so a malformed line can't break sudo for the whole image. +sudoers_file="/etc/sudoers.d/${user_id}" +cat > "$sudoers_file" < Date: Thu, 18 Jun 2026 16:48:05 +0000 Subject: [PATCH 4/4] Containers workflow: don't imply "publish" on PRs The static workflow name "Containers: build + publish" mislabeled PR runs, which build images but never publish them (push: false on pull_request). - Rename the workflow to the neutral "Containers" and add a dynamic run-name that reads "build (no publish)" on PRs and "build + publish" otherwise. - Rename the caller job containers -> build so the PR check reads "Containers / build / " instead of "containers / containers". Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/workflows/containers.yml | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/.github/workflows/containers.yml b/.github/workflows/containers.yml index 3d5b74d4..642a7813 100644 --- a/.github/workflows/containers.yml +++ b/.github/workflows/containers.yml @@ -9,7 +9,12 @@ # # The weekly schedule keeps the images current with upstream base/package # updates even when the Dockerfiles themselves have not changed. -name: 'Containers: build + publish' +name: Containers + +# On PRs the images are built but NOT published (push: false below), so the +# run title reflects build-only vs. build + publish per trigger. +run-name: >- + Containers: ${{ github.event_name == 'pull_request' && 'build (no publish)' || 'build + publish' }} on: pull_request: @@ -38,7 +43,7 @@ concurrency: cancel-in-progress: ${{ github.event_name == 'pull_request' }} jobs: - containers: + build: strategy: fail-fast: false matrix: