Skip to content

Commit

Permalink
Ruby Installer script
Browse files Browse the repository at this point in the history
  • Loading branch information
kigster committed Oct 9, 2024
1 parent 844a90f commit 7bd696d
Showing 1 changed file with 267 additions and 0 deletions.
267 changes: 267 additions & 0 deletions bin/ruby-install
Original file line number Diff line number Diff line change
@@ -0,0 +1,267 @@
#!/usr/bin/env bash
# vim: ft=bash
#
# @uthor Konstantin Gredeskoul
# @project Bashmatic
# @repo https://github.com/kigster/bashmatic
#
# This script installs Ruby with Jemalloc, YJIT and OpenSSL bindings.
# The version of ruby is read from .ruby-version file in the current
# directory, any directory above the current, or the version can be
# passed as an argument, eg:
#
# @example Passing via arguments
# ruby-install [ -f | --force ] 3.3.5
#
# @example Reading .ruby-version
# echo '3.3.5' > .ruby-version
# ruby-install [ -f | --force ]
#
# @description
# By the default, the installer will skip existing installations
# with the same ruby version. If you pass -f or --force, however,
# any existing installation will be entirely replaced.

# We'll be catching $? manually, so for now turn off automatic erroring.
set +e

export ruby_version_file_path=.ruby-version
export ruby_version_from_file=
export project_dir="$(pwd -P)"
export current_dir="${project_dir}"
export osname="$(uname -s | tr '[:upper:]' '[:lower:]')"
export rbenv_force_reinstall=false
export option_quiet=false

if [[ "$*" =~ -f|--force ]]; then
export rbenv_force_reinstall=true
fi

if [[ "$*" =~ -q|--quiet ]]; then
export option_quiet=true
fi

# ┌────────────────────────────────────────────────────────────────────────┐
# │ Helper Functions │
# └────────────────────────────────────────────────────────────────────────┘
log.arrows() {
printf "❯❯❯ "
}

log.ts() {
printf '%10.10s %10.10s' $(date '+%Y-%m-%d %T%P')
}

log.info() {
local line="$*"
printf "\e[7;34m %s | INFO \e[0;34m ${line:0:70} \e[0m\n" "$(log.ts)"
}

log.question() {
local line="$*"
printf "\e[7;35m %s | INPUT \e[0;35m ${line:0:70}\e[0m\e[1;33m " "$(log.ts)"
}

log.warn() {
local line="$*"
printf "\e[7;33m %s | WARN \e[0;33m ${line:0:70} \e[0m\n" "$(log.ts)"
}

log.error() {
local line="$*"
printf "\e[7;31m %s | ERROR \e[0;31m ${line:0:70} \e[0m\n" "$(log.ts)"
}

is.a-function () {
if [[ -n $1 ]] && typeset -f "$1" > /dev/null 2>&1; then
return 0;
else
return 1;
fi
}

# ┌────────────────────────────────────────────────────────────────────────┐
# │ Ruby Validations │
# └────────────────────────────────────────────────────────────────────────┘
function ruby.version-valid() {
local rv="${1:-${ruby_version}}"

[[ ${rv} =~ ^([0-9]\.[0-9]+\.[0-9]+)$ ]]
}

# @description
# This is perhaps the main function that attempts to guess which version
# we should be installing, assuming one wasn't provided as an CLI argument.
# The functions scans the current and all of the parent directories for
# the file .ruby-version
function ruby.detect-version() {
while true; do
if [[ "$1" =~ -f|--force|-q|--quiet ]]; then
shift
else
break
fi
done

local rv="${1}"

ruby.version-valid "${rv}" && {
log.info "Using Ruby Version passed as an argument:"
log.info " * Ruby v${rv}"
export ruby_version="$rv"
return 0
}

# otherwise seed .ruby-version in this and all parent folders

while true; do
if [[ -s "${current_dir}/${ruby_version_file_path}" ]]; then
export ruby_version_from_file="$(/usr/bin/cat "${current_dir}/${ruby_version_file_path}" | tr -d '\n')"

ruby.version-valid "${ruby_version_from_file}" && {
log.info "Found file ${ruby_version_file_path} in"
log.info "$(log.arrows)\e[1;31m${current_dir}"
export ruby_version="${ruby_version_from_file}"
break
}
fi

# Otherwise try the folder above
current_dir="$(dirname "${current_dir}")"
[[ ${current_dir} == "/" ]] && break
done

if [[ -z ${ruby_version} ]]; then
log.error "Can't find ${ruby_version_file_path} locally or in parent folders."
log.error "Nor was ruby-version passed as an argument."
echo
log.warn "$(log.arrows) Please manually enter the desired Ruby Version:"
log.question "What version should we install?"
read ruby_version
fi
printf "\e[0m\n"

if ruby.version-valid "${ruby_version}"; then
log.info "Ruby Version to be installed is: \e[1;31m${ruby_version}"
else
log.error " Unable to detect ruby version, either from STDIN, .ruby-version file, etc."
exit 1
fi
}

function ruby.begin-install() {
ruby.version-valid "${ruby_version}" || {
log.error "Can not install Ruby with invalid version."
log.error "Detected version [v${ruby_version}]"
exit 1
}

log.info "OS detected: \033[1;31m${osname}"

if ${rbenv_force_reinstall}; then
log.warn "Force-installing version ${ruby_version} due to --force flag."
else
echo
read -n 1 -s -r -p "Press any key to continue with the installation, or [Ctrl-C] to abort."
echo; echo
fi

if ruby.version-valid "${ruby_version}"; then
log.info "Starting installation of Ruby v${ruby_version}..."
fi
}

# ┌────────────────────────────────────────────────────────────────────────┐
# │ Installation │
# └────────────────────────────────────────────────────────────────────────┘
#
# @description Actually install Ruby, invoking OS-specific pre-install configurations.
function ruby.install() {
local version="$1"

export ruby_version="${version}"

ruby.begin-install

local pre_install_function="ruby.pre-install-${osname}"
if is.a-function "${pre_install_function}"; then
${pre_install_function} ${version}
fi

local time_begin=$(date '+%s')

# Build Ruby while enabling YJIT and JEMALLOC
# Construct CLI flags for rbenv inst
local extra_flags=
if ${rbenv_force_reinstall}; then
extra_flags="${extra_flags} --force"
else
extra_flags="${extra_flags} --skip-existing"
fi

if [[ ${option_quiet} == "false" ]]; then
extra_flags="${extra_flags} --verbose"
fi

${option_quiet} && log.warn "Building Ruby ${version}, please wait..."
${option_quiet} || log.warn "Building Ruby ${version} in verbose mode:"

# Use up to 8 cores to compile ruby
[[ -n ${RUBY_MAKE_OPTS} ]] || export RUBY_MAKE_OPTS="-j 8"

rbenv install ${extra_flags} ${version}

local time_finish=$(date '+%s')
local duration=$(( time_begin - time_finish ))
log.info "Ruby v${version} has been built in ${duration} seconds."

set -e
rbenv local ${version}
export RUBY_YJIT_ENABLE=1
log.info "Ruby Interpreter:"
log.warn " $(log.arrows)$(ruby -v)"
}

function ruby.pre-install-darwin() {
local version="$1"

log.info "ruby.pre-install-darwin()"

if command -v brew >/dev/null; then
echo "Found brew located at $(command -v brew)"
else
echo "Please install homebrew, by following instructions here:"
echo "https://brew.sh"
exit 1
fi

set -x

brew update && brew upgrade rbenv ruby-build || true
brew install jemalloc rust openssl@3

export RUBY_CFLAGS="-Wno-error=implicit-function-declaration"
export CPPFLAGS="-I$HOMEBREW_PREFIX/opt/jemalloc/include"
export LDFLAGS="-L$HOMEBREW_PREFIX/opt/jemalloc/lib"
export RUBY_CONFIGURE_OPTS="--enable-yjit --with-jemalloc"

local openssl_dir=/opt/homebrew/opt/openssl
if [[ -d ${openssl_dir} ]]; then
export RUBY_CONFIGURE_OPTS="${RUBY_CONFIGURE_OPTS} --with-openssl-dir=${openssl_dir}"
fi
set +x
}

function ruby.pre-install-linux() {
local version="$1"
log.info "ruby.pre-install-linux()"

set -x
sudo apt-get install libjemalloc2 rustc
export RUBY_CONFIGURE_OPTS="--enable-yjit --with-jemalloc --with-openssl"
set +x
}

# sets ${ruby_version}
ruby.detect-version "$@"
ruby.install "${ruby_version}"

0 comments on commit 7bd696d

Please sign in to comment.