Skip to content

Instantly share code, notes, and snippets.

@alexishida
Last active August 27, 2026 01:43
Show Gist options
  • Select an option

  • Save alexishida/655fb139c759393ae5fe47dacd163f99 to your computer and use it in GitHub Desktop.

Select an option

Save alexishida/655fb139c759393ae5fe47dacd163f99 to your computer and use it in GitHub Desktop.
Script to install rbenv, Ruby, nodejs and yarn
#!/usr/bin/env bash
#---------------------------------------------------------------------------------------
# Script to install rbenv, Ruby, nodejs and yarn
# Source: https://gist.github.com/alexishida/655fb139c759393ae5fe47dacd163f99
#
# Author: Alex Ishida <alexishida@gmail.com>
# Version: 1.8.3 - 26/08/2026
#---------------------------------------------------------------------------------------
#
# HOW TO INSTALL A SCRIPT
#
# - This installer targets Ruby 4.0.6.
# - Ruby 4.0.6 supports OpenSSL 3 and works on Ubuntu 22.04 and newer.
#
# - Download Raw install-ruby-rbenv.sh from Gist
#
# - Change permission to run script
# $ chmod +x install-ruby-rbenv.sh
#
# - Execute script with sudo
# $ sudo ./install-ruby-rbenv.sh
#
# - Remove NVM, shared rbenv/Ruby/Rails, and shell configuration
# $ sudo ./install-ruby-rbenv.sh --uninstall
#
# - Reload terminal (Bash or Zsh)
# $ bash or $ zsh
#
# If you want update rbenv and last ruby version please using this script:
# https://gist.github.com/alexishida/015b074ae54e1c7101335a2a63518924
#
#---------------------------------------------------------------------------------------
set -Eeuo pipefail
RUBY_VERSION="4.0.6"
NODE_INSTALL_TARGET="node"
NVM_VERSION="0.40.6"
SCRIPT_VERSION="1.8.3"
SCRIPT_AUTHOR="Alex Ishida <alexishida@gmail.com>"
SHARED_RBENV_ROOT="/usr/local/rbenv"
RBENV_GROUP="rbenv"
INSTALL_ACTION="${1:-install}"
STATUS_PANEL_ACTIVE=false
STATUS_PANEL_LINES=9
case "$INSTALL_ACTION" in
install | --install | uninstall | --uninstall)
;;
*)
printf 'Usage: sudo %s [--install|--uninstall]\n' "$0" >&2
exit 64
;;
esac
show_cli_error() {
local message="$1"
printf '\n' >&2
printf '%s\n' ' +--[ CLI ERROR ]-------------------------------------------+' >&2
printf ' | %-56s |\n' "$message" >&2
printf '%s\n\n' ' +----------------------------------------------------------+' >&2
}
if [[ ${EUID} -ne 0 ]]; then
cat >&2 <<'EOF'
.--.
|o_o | Sudo, please!
|:_/ |
// \ \
(| | )
/'\_ _/`\
\___)=(___/
+----------------------------------------------------------+
| This installer needs administrator privileges. |
+----------------------------------------------------------+
EOF
printf ' Please run this script with sudo: sudo %s\n\n' "$0" >&2
exit 1
fi
SCRIPT_USER="${SUDO_USER:-}"
if [[ -z "$SCRIPT_USER" || "$SCRIPT_USER" == "root" ]]; then
show_cli_error "Run with sudo from a regular user; do not use sudo -i."
exit 1
fi
USER_HOME="$(getent passwd "$SCRIPT_USER" | cut -d: -f6 || true)"
if [[ -z "$USER_HOME" || ! -d "$USER_HOME" ]]; then
show_cli_error "Could not find the home directory for $SCRIPT_USER."
exit 1
fi
APT_GET="$(command -v apt-get || true)"
if [[ -z "$APT_GET" ]]; then
show_cli_error "apt-get was not found. Ubuntu or Debian is required."
exit 1
fi
NVM_INSTALLER="$(mktemp /tmp/nvm-install.XXXXXX)"
trap 'stop_status_panel; rm -f "$NVM_INSTALLER"' EXIT
show_banner() {
if start_status_panel; then
render_status_panel 'Preparing installer'
return
fi
cat <<'EOF'
______________________________________
/ /|
/ $ ruby -v / |
/ $ rails new awesome_app / |
/ $ node --version / |
+--------------------------------------+ |
| RUBY DEVELOPMENT TERMINAL | |
| | /
| READY >_ | /
+--------------------------------------+ /
|______________________________________|/
EOF
printf '%s\n' ' +============================================================+'
printf ' | %-56s |\n' 'Project : Ruby, Rails, Node.js and Yarn installer'
printf ' | %-56s |\n' "Version : $SCRIPT_VERSION"
printf ' | %-56s |\n' "Author : $SCRIPT_AUTHOR"
printf '%s\n' ' +============================================================+'
printf '\n'
}
show_dependencies_stage() {
cat <<'EOF'
.--. +------------------+
|o_o | | APT TOOLBOX |
|:_/ | | [#] compiling |
// \ \ | [#] libraries |
(| | ) | [#] image tools |
/'\_ _/`\ +------------------+
\___)=(___/
[1/5] LINUX PACKAGES
Installing the build tools and libraries required by Ruby and Rails...
EOF
}
show_download_stage() {
cat <<'EOF'
[ GitHub ] ===== curl =====> [ /tmp/nvm-installer ]
\ /
+-------- verified URL ------+
[2/5] NVM INSTALLER
Downloading the pinned nvm installer...
EOF
}
show_ruby_stage() {
cat <<'EOF'
+------------------------------------------------+
| root@linux:/usr/local/rbenv$ rbenv install ... |
| |
| Ruby -> Bundler -> Rails -> Dev gems |
+------------------------------------------------+
[3/5] SHARED RUBY TOOLCHAIN
Installing one rbenv environment shared by root and trusted users...
EOF
}
show_account_stage() {
local target_user="$1"
local step_number="$2"
cat <<'EOF'
+-----------------------------------------------+
| user@linux:~$ source ~/.bashrc |
| user@linux:~$ node -v |
| user@linux:~$ ruby -v |
| user@linux:~$ _ |
+-----------------------------------------------+
EOF
printf '\n [%s/5] SHELL ENVIRONMENT: %s\n' "$step_number" "$target_user"
printf ' Configuring nvm, Node.js, Yarn, Bash, Zsh, and shared rbenv...\n'
}
show_completion_banner() {
show_installer_identity
cat <<'EOF'
+----------------------------------+
| INSTALLATION COMPLETE |
| Ruby, Rails, Node.js and Yarn |
| are ready to use. |
+----------------------------------+
EOF
printf ' Start a new terminal session, then run: ruby -v && rails -v && node -v\n\n'
}
show_uninstall_completion_banner() {
show_installer_identity
cat <<'EOF'
+----------------------------------+
| UNINSTALL COMPLETE |
| NVM, rbenv, Ruby, Rails & gems |
| were removed. |
+----------------------------------+
Open a new terminal before running the installer again.
EOF
}
show_installer_identity() {
printf '%s\n' ' +============================================================+'
printf ' | %-56s |\n' 'Project : Ruby, Rails, Node.js and Yarn installer'
printf ' | %-56s |\n' "Version : $SCRIPT_VERSION"
printf ' | %-56s |\n' "Author : $SCRIPT_AUTHOR"
printf '%s\n\n' ' +============================================================+'
}
show_group_notice() {
local target_user="$1"
cat <<'EOF'
+--[ ONE LAST CLI QUEST ]----------------------------------+
| |
| Group membership starts with a fresh login session. |
| |
EOF
printf ' | User : %-44s |\n' "$target_user"
printf ' | Group : %-44s |\n' "$RBENV_GROUP"
printf ' | Command : %-44s |\n' "newgrp $RBENV_GROUP"
printf '%s\n\n' ' +----------------------------------------------------------+'
}
start_status_panel() {
local terminal_lines
[[ "$STATUS_PANEL_ACTIVE" == true ]] && return 0
[[ -t 1 && "${TERM:-dumb}" != "dumb" ]] || return 1
terminal_lines="$(tput lines 2>/dev/null || true)"
[[ "$terminal_lines" =~ ^[0-9]+$ ]] || return 1
(( terminal_lines > STATUS_PANEL_LINES + 2 )) || return 1
# Reserve the first lines for status; command output scrolls below them.
printf '\033[%d;%dr' "$((STATUS_PANEL_LINES + 1))" "$terminal_lines"
printf '\033[%d;1H' "$((STATUS_PANEL_LINES + 1))"
STATUS_PANEL_ACTIVE=true
}
render_status_panel() {
local label="$1"
local visible_label="${label:0:56}"
# Save the log cursor, redraw the fixed banner and current step, then resume.
printf '\0337\033[1;1H'
printf '\033[2K%s\n' ' +============================================================+'
printf '\033[2K | %-56s |\n' 'Project : Ruby, Rails, Node.js and Yarn installer'
printf '\033[2K | %-56s |\n' "Version : $SCRIPT_VERSION"
printf '\033[2K | %-56s |\n' "Author : $SCRIPT_AUTHOR"
printf '\033[2K%s\n' ' +============================================================+'
printf '%s\n' ' +--[ CURRENT INSTALLER STEP ]--------------------------------+'
printf '\033[2K | %-56s |\n' "$visible_label"
printf '%s\n' ' | Detailed command output scrolls below this panel. |'
printf '%s\n' ' +------------------------------------------------------------+'
printf '\033[2K\0338'
}
stop_status_panel() {
[[ "$STATUS_PANEL_ACTIVE" == true ]] || return 0
# Restore the terminal's normal full-height scrolling region.
printf '\033[r'
STATUS_PANEL_ACTIVE=false
}
prepare_final_message_screen() {
# Leave the installation log intact and print the final banner below it.
[[ -t 1 && "${TERM:-dumb}" != "dumb" ]] || return 0
printf '\033[999;1H'
}
prepare_initial_screen() {
[[ -t 1 && "${TERM:-dumb}" != "dumb" ]] || return 0
printf '\033[2J\033[H'
}
show_command_separator() {
local label="$1"
if start_status_panel; then
render_status_panel "$label"
return
fi
printf '\n ------------------------------------------------------------\n'
printf ' %s\n' "$label"
printf ' ------------------------------------------------------------\n\n'
}
remove_development_environment_from_profile() {
local shell_rc="$1"
[[ -f "$shell_rc" ]] || return 0
# Remove the exact NVM and rbenv setup forms written by this installer,
# plus compatible forms written by the upstream NVM installer.
sed -i \
-e '/^[[:space:]]*# nvm[[:space:]]*$/d' \
-e '/^[[:space:]]*export NVM_DIR=.*\.nvm[[:space:]]*$/d' \
-e '/nvm\.sh/d' \
-e '/NVM_DIR\/bash_completion/d' \
-e '/^[[:space:]]*# rbenv[[:space:]]*$/d' \
-e '/^[[:space:]]*export RBENV_ROOT=.*rbenv[[:space:]]*$/d' \
-e '/^[[:space:]]*export PATH=.*RBENV_ROOT\/bin.*$/d' \
-e '/^[[:space:]]*eval "\$(rbenv init.*$/d' \
"$shell_rc"
}
uninstall_development_environment_for_user() {
local target_user="$1"
local target_home="$2"
local shell_rc
show_command_separator "Removing NVM and shell configuration for ${target_user}"
rm -rf "$target_home/.nvm" "$target_home/.rbenv"
for shell_rc in "$target_home/.bashrc" "$target_home/.zshrc"; do
remove_development_environment_from_profile "$shell_rc"
done
}
uninstall_development_environment() {
show_command_separator 'Removing shared rbenv, Ruby, Rails, and installed gems'
# All Rubies, Rails, and gems installed by this script live under this root.
rm -rf "$SHARED_RBENV_ROOT"
uninstall_development_environment_for_user "$SCRIPT_USER" "$USER_HOME"
uninstall_development_environment_for_user "root" "/root"
}
run_setup_as() {
local target_user="$1"
local target_home="$2"
sudo -H -u "$target_user" env \
HOME="$target_home" \
SHARED_RBENV_ROOT="$SHARED_RBENV_ROOT" \
NODE_INSTALL_TARGET="$NODE_INSTALL_TARGET" \
NVM_INSTALLER="$NVM_INSTALLER" \
bash -s
}
if [[ "$INSTALL_ACTION" == "uninstall" || "$INSTALL_ACTION" == "--uninstall" ]]; then
prepare_initial_screen
show_banner
uninstall_development_environment
stop_status_panel
prepare_final_message_screen
show_uninstall_completion_banner
exit 0
fi
prepare_initial_screen
show_banner
show_dependencies_stage
show_command_separator '[1/5] Updating APT metadata and installing Linux packages'
export DEBIAN_FRONTEND=noninteractive
"$APT_GET" update
"$APT_GET" install -y \
autoconf bison build-essential ca-certificates curl git gnupg imagemagick \
libcurl4-openssl-dev libffi-dev libgdbm-dev libncurses-dev libreadline-dev \
libsqlite3-dev libssl-dev libvips-dev libxml2-dev libxslt1-dev libyaml-dev \
patch rustc software-properties-common sqlite3 unzip wget zlib1g-dev zsh
show_download_stage
show_command_separator '[2/5] Downloading the pinned nvm installer'
# Download a specific installer version instead of executing unpinned remote content.
curl --fail --location --silent --show-error \
"https://raw.githubusercontent.com/nvm-sh/nvm/v${NVM_VERSION}/install.sh" \
--output "$NVM_INSTALLER"
chmod 0644 "$NVM_INSTALLER"
show_ruby_stage
show_command_separator '[3/5] Preparing the shared rbenv installation'
if ! getent group "$RBENV_GROUP" >/dev/null; then
groupadd --system "$RBENV_GROUP"
fi
usermod --append --groups "$RBENV_GROUP" "$SCRIPT_USER"
if [[ -e "$SHARED_RBENV_ROOT" && ! -d "$SHARED_RBENV_ROOT/.git" ]]; then
show_cli_error "$SHARED_RBENV_ROOT exists but is not a rbenv Git repository."
exit 1
fi
if [[ -d "$SHARED_RBENV_ROOT/.git" ]]; then
git -C "$SHARED_RBENV_ROOT" pull --ff-only
else
git clone https://github.com/rbenv/rbenv.git "$SHARED_RBENV_ROOT"
fi
if [[ -d "$SHARED_RBENV_ROOT/plugins/ruby-build/.git" ]]; then
git -C "$SHARED_RBENV_ROOT/plugins/ruby-build" pull --ff-only
else
git clone https://github.com/rbenv/ruby-build.git \
"$SHARED_RBENV_ROOT/plugins/ruby-build"
fi
chown -R root:"$RBENV_GROUP" "$SHARED_RBENV_ROOT"
chmod -R g+rwX "$SHARED_RBENV_ROOT"
find "$SHARED_RBENV_ROOT" -type d -exec chmod g+s {} +
export RBENV_ROOT="$SHARED_RBENV_ROOT"
export PATH="$RBENV_ROOT/bin:$PATH"
# The installer itself runs in Bash; don't infer the shell from the environment.
eval "$(rbenv init - --no-rehash bash)"
# Show every compiler/install command emitted by Ruby's Makefile.
export RUBY_MAKE_OPTS="V=1"
export RUBY_MAKE_INSTALL_OPTS="V=1"
show_command_separator "[3/5] Building Ruby ${RUBY_VERSION} (verbose make output)"
rbenv install -sv "$RUBY_VERSION"
rbenv global "$RUBY_VERSION"
rbenv rehash
show_command_separator '[3/5] Installing Ruby development gems'
gem install bundler rails ruby-lsp debug solargraph rufo
rbenv rehash
# Keep the shared installation writable by members of the rbenv group.
chown -R root:"$RBENV_GROUP" "$SHARED_RBENV_ROOT"
chmod -R g+rwX "$SHARED_RBENV_ROOT"
find "$SHARED_RBENV_ROOT" -type d -exec chmod g+s {} +
install_development_environment() {
local target_user="$1"
local target_home="$2"
local step_number="$3"
show_account_stage "$target_user" "$step_number"
show_command_separator "[${step_number}/5] Configuring Node.js, Yarn, and shell profiles for ${target_user}"
run_setup_as "$target_user" "$target_home" <<'ACCOUNT_SCRIPT'
set -Eeuo pipefail
append_if_missing() {
local line="$1" file="$2"
touch "$file"
grep -Fqx -- "$line" "$file" || printf '%s\n' "$line" >> "$file"
}
# PROFILE=/dev/null prevents the installer from duplicating shell configuration.
PROFILE=/dev/null bash "$NVM_INSTALLER"
for shell_rc in "$HOME/.bashrc" "$HOME/.zshrc"; do
append_if_missing '# nvm' "$shell_rc"
append_if_missing 'export NVM_DIR="$HOME/.nvm"' "$shell_rc"
append_if_missing '[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"' "$shell_rc"
append_if_missing '[ -s "$NVM_DIR/bash_completion" ] && . "$NVM_DIR/bash_completion"' "$shell_rc"
done
export NVM_DIR="$HOME/.nvm"
# shellcheck source=/dev/null
. "$NVM_DIR/nvm.sh"
nvm install "$NODE_INSTALL_TARGET"
nvm alias default node
npm install --global corepack@latest
corepack enable
corepack install --global yarn@stable
for shell_rc in "$HOME/.bashrc" "$HOME/.zshrc"; do
sed -i '/^# rbenv$/d' "$shell_rc"
sed -i '/^export RBENV_ROOT="\$HOME\/\.rbenv"$/d' "$shell_rc"
sed -i '/^eval "\$(rbenv init - --no-rehash)"$/d' "$shell_rc"
sed -i '/^eval "\$(rbenv init - --no-rehash bash)"$/d' "$shell_rc"
sed -i '/^eval "\$(rbenv init - --no-rehash zsh)"$/d' "$shell_rc"
append_if_missing '# rbenv' "$shell_rc"
append_if_missing "export RBENV_ROOT=\"$SHARED_RBENV_ROOT\"" "$shell_rc"
append_if_missing 'export PATH="$RBENV_ROOT/bin:$PATH"' "$shell_rc"
if [[ "$shell_rc" == "$HOME/.bashrc" ]]; then
append_if_missing 'eval "$(rbenv init - --no-rehash bash)"' "$shell_rc"
else
append_if_missing 'eval "$(rbenv init - --no-rehash zsh)"' "$shell_rc"
fi
done
git config --global core.autocrlf false
ACCOUNT_SCRIPT
}
install_development_environment "$SCRIPT_USER" "$USER_HOME" 4
install_development_environment "root" "/root" 5
stop_status_panel
prepare_final_message_screen
echo
show_group_notice "$SCRIPT_USER"
show_completion_banner
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment