Last active
August 27, 2026 01:43
-
-
Save alexishida/655fb139c759393ae5fe47dacd163f99 to your computer and use it in GitHub Desktop.
Script to install rbenv, Ruby, nodejs and yarn
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/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