docker: install from the docker-ce repo on RHEL-family hosts

bootstrap failed outright on Oracle Linux: docker/install.sh piped
get.docker.com to sh, and that script allowlists distros by /etc/os-release
ID, so it aborted with "ERROR: Unsupported distribution 'ol'".

On a distro it does recognize, the convenience script only adds the repo and
installs the packages -- exactly what install_rhel() now does directly. Doing
it ourselves drops the allowlist, so every EL rebuild (Oracle, Rocky, Alma,
CentOS, RHEL) goes through one code path instead of needing to be enumerated.
Debian-family still uses get.docker.com. containerd.io needs --allowerasing to
replace the runc that Oracle Linux ships.

Also stop one failed module from killing the whole run: modules were bare
calls under `set -eu`, so docker/'s exit meant bin/, node/ and ansible/
silently never ran -- an unsupported distro string left the box with no CLIs,
no npx and no ansible. run_module warns and continues, then bootstrap exits
non-zero with a summary of what failed. Same guard 35befbe added for
individual tools in bin/install.sh, one level up.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-16 06:59:18 -04:00
co-authored by Claude Opus 4.8
parent 836eea6fc6
commit fdc3a71236
3 changed files with 102 additions and 24 deletions
+5 -2
View File
@@ -32,8 +32,11 @@ Already have a clone? Just run it directly:
- `git/` — sets git `user.name`/`user.email` and configures - `git/` — sets git `user.name`/`user.email` and configures
`credential.helper store` (plaintext `~/.git-credentials`, 0600) for `credential.helper store` (plaintext `~/.git-credentials`, 0600) for
HTTPS auth. HTTPS auth.
- `docker/` — installs full (rootful) Docker Engine via get.docker.com, - `docker/` — installs full (rootful) Docker Engine from Docker's upstream
enables the service (systemd hosts), and adds the invoking user to the package repo (get.docker.com on Debian-family; the docker-ce repo wired up
directly on RHEL-family, since the convenience script rejects EL rebuilds
like Oracle Linux by name), enables the service (systemd hosts), and adds
the invoking user to the
`docker` group (takes effect at next login). No-ops where a rootless `docker` group (takes effect at next login). No-ops where a rootless
Docker daemon is already running (e.g. Kasm) so it never fights that Docker daemon is already running (e.g. Kasm) so it never fights that
setup. Uses sudo. setup. Uses sudo.
+30 -7
View File
@@ -43,15 +43,38 @@ if ! command -v python3 >/dev/null 2>&1 && ! command -v unzip >/dev/null 2>&1; t
pkg_install python3 || echo "bootstrap: warning: could not install python3" >&2 pkg_install python3 || echo "bootstrap: warning: could not install python3" >&2
fi fi
"$SCRIPT_DIR/zsh/install.sh" # run_module <path> — run one module; on failure, warn and keep going.
"$SCRIPT_DIR/git/install.sh" #
# WHY: these used to be bare calls, so under `set -eu` the FIRST failing module
# aborted bootstrap and every later module silently never ran — one unsupported
# distro in docker/ was enough to leave a box with no CLIs, no npx and no
# ansible. The modules are independent, so a failure in one is not a reason to
# skip the rest. (bin/install.sh does the same for individual tools; this is
# that guard one level up.)
FAILED=""
run_module() {
if ( set -e; "$SCRIPT_DIR/$1/install.sh" ); then return 0; fi
FAILED="$FAILED $1"
echo "bootstrap: WARNING: $1 module failed; continuing." >&2
}
run_module zsh
run_module git
# Runs before bin/ so the kind install gate can see the rootful daemon. # Runs before bin/ so the kind install gate can see the rootful daemon.
"$SCRIPT_DIR/docker/install.sh" run_module docker
"$SCRIPT_DIR/bin/install.sh" run_module bin
# Provides npx, which opencode needs to spawn the npm-based MCP servers in # Provides npx, which opencode needs to spawn the npm-based MCP servers in
# mcp-config/. Without it every MCP server silently fails to start. # mcp-config/. Without it every MCP server silently fails to start.
"$SCRIPT_DIR/node/install.sh" run_module node
"$SCRIPT_DIR/ansible/install.sh" run_module ansible
# Runs last: needs rootless Docker already up (started at boot) so it can # Runs last: needs rootless Docker already up (started at boot) so it can
# delegate cgroups to it. No-ops where that does not apply. # delegate cgroups to it. No-ops where that does not apply.
"$SCRIPT_DIR/docker-rootless-cgroup/install.sh" run_module docker-rootless-cgroup
if [ -n "$FAILED" ]; then
echo "" >&2
echo "bootstrap: WARNING: these modules did NOT complete:$FAILED" >&2
echo "bootstrap: everything else was applied. Fix the cause and re-run." >&2
exit 1
fi
echo "bootstrap: all modules applied." >&2
+67 -15
View File
@@ -3,10 +3,10 @@
# invoking user talk to it without sudo. # invoking user talk to it without sudo.
# #
# What it does: # What it does:
# 1. Installs Docker Engine via Docker's official convenience script # 1. Installs Docker Engine from Docker's upstream package repo. Skipped when
# (get.docker.com — supports both RHEL- and Debian-family distros and # `docker` is already installed. Debian-family hosts go through Docker's
# sets up the upstream package repo). Skipped when `docker` is already # convenience script (get.docker.com); RHEL-family hosts wire up the
# installed. # docker-ce repo directly — see install_rhel() for why.
# 2. Enables and starts the service (systemd hosts only). # 2. Enables and starts the service (systemd hosts only).
# 3. Adds the invoking user to the `docker` group so the daemon socket is # 3. Adds the invoking user to the `docker` group so the daemon socket is
# usable without sudo. Takes effect at next login. # usable without sudo. Takes effect at next login.
@@ -18,8 +18,66 @@
# Idempotent and safe to re-run. Uses sudo. # Idempotent and safe to re-run. Uses sudo.
set -eu set -eu
SCRIPT_DIR="$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)"
REPO_ROOT="$(dirname -- "$SCRIPT_DIR")"
# shellcheck source=../lib/os-detect.sh
. "$REPO_ROOT/lib/os-detect.sh"
say() { printf '%s\n' "$*" >&2; } say() { printf '%s\n' "$*" >&2; }
DOCKER_PKGS="docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin"
# install_rhel — install docker-ce from Docker's EL repo.
#
# WHY NOT get.docker.com: the convenience script allowlists distros by
# /etc/os-release ID and aborts on anything it does not recognize —
# "ERROR: Unsupported distribution 'ol'" on Oracle Linux, and the same for
# other EL rebuilds it has not enumerated. On a match it does nothing more
# than what this function does: add the repo, then install the packages.
# Wiring the repo up directly drops the allowlist, so every EL rebuild
# (Oracle, Rocky, Alma, CentOS, RHEL) installs from one code path.
#
# The repo URL is keyed on $releasever/$basearch, which dnf expands per host,
# so EL9 and EL10 both resolve without a version table here.
install_rhel() {
say "docker: adding Docker CE repo (EL \$releasever/\$basearch)..."
sudo tee /etc/yum.repos.d/docker-ce.repo >/dev/null <<'REPO'
[docker-ce-stable]
name=Docker CE Stable - $basearch
baseurl=https://download.docker.com/linux/centos/$releasever/$basearch/stable
enabled=1
gpgcheck=1
gpgkey=https://download.docker.com/linux/centos/gpg
REPO
# --allowerasing: containerd.io conflicts with the distro's own runc/containerd
# on EL rebuilds that ship them (Oracle Linux does). Let dnf swap them out
# rather than dead-end on the conflict.
if command -v dnf >/dev/null 2>&1; then
# shellcheck disable=SC2086
sudo dnf install -y --allowerasing $DOCKER_PKGS
else
# shellcheck disable=SC2086
sudo yum install -y --allowerasing $DOCKER_PKGS
fi
}
# install_debian — Docker's convenience script; it supports every Debian-family
# distro this repo targets and sets up the apt repo and keyring.
install_debian() {
say "docker: installing Docker Engine via get.docker.com..."
tmp="$(mktemp)"
if command -v curl >/dev/null 2>&1; then
curl -fsSL https://get.docker.com -o "$tmp"
elif command -v wget >/dev/null 2>&1; then
wget -qO "$tmp" https://get.docker.com
else
say "docker: need curl or wget"; rm -f "$tmp"; return 1
fi
sudo sh "$tmp"
rm -f "$tmp"
}
# --- skip on rootless-Docker hosts ------------------------------------------- # --- skip on rootless-Docker hosts -------------------------------------------
if command -v docker >/dev/null 2>&1 && if command -v docker >/dev/null 2>&1 &&
docker info -f '{{println .SecurityOptions}}' 2>/dev/null | grep -q 'name=rootless'; then docker info -f '{{println .SecurityOptions}}' 2>/dev/null | grep -q 'name=rootless'; then
@@ -31,17 +89,11 @@ fi
if command -v docker >/dev/null 2>&1; then if command -v docker >/dev/null 2>&1; then
say "docker: engine already installed ($(docker --version 2>/dev/null || echo 'version unknown'))." say "docker: engine already installed ($(docker --version 2>/dev/null || echo 'version unknown'))."
else else
say "docker: installing Docker Engine via get.docker.com..." case "$(detect_pkg_family)" in
tmp="$(mktemp)" rhel) install_rhel ;;
if command -v curl >/dev/null 2>&1; then debian) install_debian ;;
curl -fsSL https://get.docker.com -o "$tmp" *) say "docker: unsupported/undetected OS family; install Docker manually."; exit 1 ;;
elif command -v wget >/dev/null 2>&1; then esac
wget -qO "$tmp" https://get.docker.com
else
say "docker: need curl or wget"; exit 1
fi
sudo sh "$tmp"
rm -f "$tmp"
fi fi
# --- 2. enable + start the service (systemd only) ---------------------------- # --- 2. enable + start the service (systemd only) ----------------------------