Files
tools_scripts/install-ruby-rvm.sh
2026-04-09 10:16:00 +02:00

385 lines
9.5 KiB
Bash

#!/usr/bin/env bash
# Recompile OpenSSL 1.1.1w on RHEL9+ and install old Rubies
# Mateusz Gruszczyński @linuxiarz.pl
set -eo pipefail
OPENSSL_VERSION="1.1.1w"
OPENSSL_PREFIX="/opt/openssl111"
SRC_DIR="/usr/local/src"
FORCE_OPENSSL=0
WEB_PROXY=""
TARGET_RUBY_VERSION=""
usage() {
cat <<EOF
Usage:
$0 --version 2.7.X [--force-openssl] [--webproxy http://proxy:port]
Options:
--version VER Ruby version to install, e.g. 2.7.8
--force-openssl Force rebuild of OpenSSL ${OPENSSL_VERSION}
--webproxy URL Proxy URL used for downloads and RVM, e.g. http://proxy.example:8080
-h, --help Show this help
Examples:
$0 --version 2.7.8
$0 --version 2.7.8 --force-openssl
$0 --version 2.7.8 --webproxy http://proxy.local:8080
Backward compatibility:
$0 2.7.8
EOF
}
die() {
echo "ERROR: $*" >&2
exit 1
}
log() {
echo "$*"
}
command_exists() {
command -v "$1" >/dev/null 2>&1
}
package_installed() {
local pkg="$1"
rpm -q "${pkg}" >/dev/null 2>&1 && return 0
rpm -q --whatprovides "${pkg}" >/dev/null 2>&1 && return 0
return 1
}
parse_args() {
while [ $# -gt 0 ]; do
case "$1" in
--version)
shift
[ $# -gt 0 ] || die "--version requires a value"
[ -z "${TARGET_RUBY_VERSION}" ] || die "Ruby version already provided: ${TARGET_RUBY_VERSION}"
TARGET_RUBY_VERSION="$1"
;;
--force-openssl)
FORCE_OPENSSL=1
;;
--webproxy)
shift
[ $# -gt 0 ] || die "--webproxy requires a value"
WEB_PROXY="$1"
;;
-h|--help)
usage
exit 0
;;
-*)
die "unknown option: $1"
;;
*)
[ -z "${TARGET_RUBY_VERSION}" ] || die "Ruby version already provided: ${TARGET_RUBY_VERSION}"
TARGET_RUBY_VERSION="$1"
;;
esac
shift
done
[ -n "${TARGET_RUBY_VERSION}" ] || die "missing Ruby version, use --version 2.7.X"
if [[ ! "${TARGET_RUBY_VERSION}" =~ ^2\.7\.[0-9]+$ ]]; then
die "only Ruby 2.7.X is allowed, e.g. 2.7.9"
fi
}
apply_proxy_env() {
if [ -n "${WEB_PROXY}" ]; then
export http_proxy="${WEB_PROXY}"
export https_proxy="${WEB_PROXY}"
export ftp_proxy="${WEB_PROXY}"
export all_proxy="${WEB_PROXY}"
export HTTP_PROXY="${WEB_PROXY}"
export HTTPS_PROXY="${WEB_PROXY}"
export FTP_PROXY="${WEB_PROXY}"
export ALL_PROXY="${WEB_PROXY}"
log "Using web proxy: ${WEB_PROXY}"
fi
}
load_rvm() {
export rvm_silence_path_mismatch_check_flag=1
if [ -s /etc/profile.d/rvm.sh ]; then
. /etc/profile.d/rvm.sh
elif [ -s /usr/local/rvm/scripts/rvm ]; then
. /usr/local/rvm/scripts/rvm
elif [ -s /root/.rvm/scripts/rvm ]; then
. /root/.rvm/scripts/rvm
else
die "RVM was not found"
fi
if ! command -v rvm >/dev/null 2>&1; then
die "RVM is not available in this shell"
fi
}
download_file() {
local url="$1"
local output="$2"
if command_exists wget; then
if [ -n "${WEB_PROXY}" ]; then
wget \
-e use_proxy=yes \
-e "http_proxy=${WEB_PROXY}" \
-e "https_proxy=${WEB_PROXY}" \
-O "${output}" \
"${url}"
else
wget -O "${output}" "${url}"
fi
return
fi
if command_exists curl; then
if [ -n "${WEB_PROXY}" ]; then
curl --proxy "${WEB_PROXY}" -fL -o "${output}" "${url}"
else
curl -fL -o "${output}" "${url}"
fi
return
fi
die "neither wget nor curl is available"
}
openssl_installation_ok() {
local openssl_bin="${OPENSSL_PREFIX}/bin/openssl"
local version_output
[ -x "${openssl_bin}" ] || return 1
[ -d "${OPENSSL_PREFIX}/include/openssl" ] || return 1
[ -f "${OPENSSL_PREFIX}/lib/libssl.so" ] || [ -f "${OPENSSL_PREFIX}/lib64/libssl.so" ] || return 1
[ -f "${OPENSSL_PREFIX}/lib/libcrypto.so" ] || [ -f "${OPENSSL_PREFIX}/lib64/libcrypto.so" ] || return 1
version_output="$(
LD_LIBRARY_PATH="${OPENSSL_PREFIX}/lib:${OPENSSL_PREFIX}/lib64:${LD_LIBRARY_PATH:-}" \
"${openssl_bin}" version 2>/dev/null || true
)"
[[ "${version_output}" == OpenSSL\ ${OPENSSL_VERSION}* ]]
}
ruby_in_path_ok() {
local ruby_version openssl_version
command_exists ruby || return 1
ruby_version="$(ruby -e 'print RUBY_VERSION' 2>/dev/null || true)"
[ "${ruby_version}" = "${TARGET_RUBY_VERSION}" ] || return 1
openssl_version="$(ruby -ropenssl -e 'print OpenSSL::OPENSSL_VERSION' 2>/dev/null || true)"
[[ "${openssl_version}" == OpenSSL\ ${OPENSSL_VERSION}* ]]
}
ruby_in_rvm_present() {
rvm list strings | grep -Fxq "ruby-${TARGET_RUBY_VERSION}"
}
ruby_in_rvm_ok() {
local ruby_version openssl_version
ruby_in_rvm_present || return 1
ruby_version="$(rvm "ruby-${TARGET_RUBY_VERSION}" do ruby -e 'print RUBY_VERSION' 2>/dev/null || true)"
[ "${ruby_version}" = "${TARGET_RUBY_VERSION}" ] || return 1
openssl_version="$(rvm "ruby-${TARGET_RUBY_VERSION}" do ruby -ropenssl -e 'print OpenSSL::OPENSSL_VERSION' 2>/dev/null || true)"
[[ "${openssl_version}" == OpenSSL\ ${OPENSSL_VERSION}* ]]
}
ruby_already_installed() {
if ruby_in_rvm_ok; then
log "Ruby ${TARGET_RUBY_VERSION} is already installed in RVM and linked with OpenSSL ${OPENSSL_VERSION}"
return 0
fi
if ruby_in_path_ok; then
log "Ruby ${TARGET_RUBY_VERSION} is already available in PATH and linked with OpenSSL ${OPENSSL_VERSION}"
return 0
fi
return 1
}
install_build_dependencies() {
log "[1/6] Checking build dependencies"
local missing_packages=()
local packages=(
curl
wget
tar
xz
bzip2
git
perl-core
pkgconfig
zlib-devel
readline-devel
libyaml-devel
libffi-devel
gdbm-devel
ncurses-devel
gmp-devel
autoconf
automake
libtool
bison
)
local pkg
for pkg in "${packages[@]}"; do
if ! package_installed "${pkg}"; then
missing_packages+=("${pkg}")
fi
done
local need_devtools=0
if ! command_exists gcc || ! command_exists g++ || ! command_exists make; then
need_devtools=1
fi
if [ "${need_devtools}" -eq 0 ] && [ "${#missing_packages[@]}" -eq 0 ]; then
log "All required build dependencies are already installed"
return 0
fi
if [ "${need_devtools}" -eq 1 ]; then
log "Installing missing group: Development Tools"
dnf groupinstall -y "Development Tools"
fi
if [ "${#missing_packages[@]}" -gt 0 ]; then
log "Installing missing packages: ${missing_packages[*]}"
dnf install -y "${missing_packages[@]}"
fi
}
install_openssl() {
local tarball="openssl-${OPENSSL_VERSION}.tar.gz"
local srcdir="openssl-${OPENSSL_VERSION}"
local url="https://www.openssl.org/source/old/1.1.1/${tarball}"
if [ "${FORCE_OPENSSL}" -eq 0 ] && openssl_installation_ok; then
log "[2/6] OpenSSL ${OPENSSL_VERSION} already exists and works in ${OPENSSL_PREFIX}, skipping"
return 0
fi
log "[2/6] Preparing OpenSSL ${OPENSSL_VERSION}"
mkdir -p "${SRC_DIR}"
mkdir -p "${OPENSSL_PREFIX}"
cd "${SRC_DIR}"
if [ ! -f "${tarball}" ] || [ "${FORCE_OPENSSL}" -eq 1 ]; then
rm -f "${tarball}"
download_file "${url}" "${tarball}"
else
log "Using existing source archive: ${SRC_DIR}/${tarball}"
fi
rm -rf "${srcdir}"
tar xf "${tarball}"
cd "${srcdir}"
log "[3/6] Building OpenSSL into ${OPENSSL_PREFIX}"
./config \
--prefix="${OPENSSL_PREFIX}" \
--openssldir="${OPENSSL_PREFIX}" \
shared zlib
make -j"$(nproc)"
rm -rf "${OPENSSL_PREFIX:?}/"*
make install_sw
openssl_installation_ok || die "OpenSSL ${OPENSSL_VERSION} verification failed after build"
}
install_ruby_with_rvm() {
local -a rvm_args
local action="install"
log "[4/6] Configuring RVM build environment"
rvm autolibs disable
export CPPFLAGS="-I${OPENSSL_PREFIX}/include"
export CFLAGS="-I${OPENSSL_PREFIX}/include"
export LDFLAGS="-L${OPENSSL_PREFIX}/lib -L${OPENSSL_PREFIX}/lib64 -Wl,-rpath,${OPENSSL_PREFIX}/lib -Wl,-rpath,${OPENSSL_PREFIX}/lib64"
export PKG_CONFIG_PATH="${OPENSSL_PREFIX}/lib/pkgconfig:${OPENSSL_PREFIX}/lib64/pkgconfig"
export RUBY_CONFIGURE_OPTS="--with-openssl-dir=${OPENSSL_PREFIX}"
if ruby_in_rvm_present; then
action="reinstall"
fi
rvm_args=("${action}" "${TARGET_RUBY_VERSION}" "--with-openssl-dir=${OPENSSL_PREFIX}")
if [ -n "${WEB_PROXY}" ]; then
rvm_args+=("--proxy" "${WEB_PROXY}")
fi
log "[5/6] ${action^}ing Ruby ${TARGET_RUBY_VERSION} with RVM"
rvm "${rvm_args[@]}"
}
verify_installation() {
log "[6/6] Verifying installation"
local installed_ruby_version installed_openssl_version
if ruby_in_rvm_ok; then
installed_ruby_version="$(rvm "ruby-${TARGET_RUBY_VERSION}" do ruby -e 'print RUBY_VERSION')"
installed_openssl_version="$(rvm "ruby-${TARGET_RUBY_VERSION}" do ruby -ropenssl -e 'print OpenSSL::OPENSSL_VERSION')"
elif ruby_in_path_ok; then
installed_ruby_version="$(ruby -e 'print RUBY_VERSION')"
installed_openssl_version="$(ruby -ropenssl -e 'print OpenSSL::OPENSSL_VERSION')"
else
die "Ruby ${TARGET_RUBY_VERSION} verification failed"
fi
log "Installed Ruby version: ${installed_ruby_version}"
log "Linked OpenSSL version: ${installed_openssl_version}"
}
main() {
[ "${EUID}" -eq 0 ] || die "run this script as root"
parse_args "$@"
log "Requested Ruby version: ${TARGET_RUBY_VERSION}"
apply_proxy_env
load_rvm
if ruby_already_installed; then
log "Nothing to do"
exit 0
fi
install_build_dependencies
install_openssl
if ruby_already_installed; then
log "Ruby is already installed after OpenSSL verification/build, skipping RVM build"
verify_installation
exit 0
fi
install_ruby_with_rvm
verify_installation
}
main "$@"