Skip to content
29 changes: 20 additions & 9 deletions src/ruby/devcontainer-feature.json
Original file line number Diff line number Diff line change
@@ -1,21 +1,36 @@
{
"id": "ruby",
"version": "1.2.0",
"name": "Ruby (via rvm)",
"version": "2.0.0",
"name": "Ruby",
"documentationURL": "https://github.com/devcontainers/features/tree/main/src/ruby",
"description": "Installs Ruby, rvm, rbenv, common Ruby utilities, and needed dependencies.",
"description": "Installs Ruby and needed dependencies.",
"options": {
"versionManager": {
"type": "string",
"proposals": [
"none",
"rbenv",
"rvm"
],
"default": "rbenv",
"description": "Select or enter a version manager to use"
},
"version": {
"type": "string",
"proposals": [
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Implementing an os-provided Ruby is difficult.

The mcr.microsoft.com/devcontainers/base:bullseye image does not include Ruby. The Ruby devcontainer image (images/src/ruby/.devcontainer/Dockerfile in the devcontainer/images repository) is based upon a Ruby image from the official Ruby Docker images and not an image in the Microsoft container registry, which does include an installed Ruby. This is why my (not yet committed) test and change to handle this os-provided option isn't working.

I'm even hesitant to include a "none" option. But I did aim for parity with the Node Feature, and it offers a none option which just installs NVM. So I carried that option over here. I did notice, though, that Node also has the issue where their devcontainer image is based on an official Node docker image and not an image in the MCR.

"latest",
"none",
"3.2",
"3.1",
"3.0",
"2.7"
"3.0"
],
"default": "latest",
"description": "Select or enter a Ruby version to install"
},
"additionalVersions": {
"type": "string",
"default": "",
"description": "Enter additional versions, separated by commas."
}
},
"customizations": {
Expand All @@ -26,10 +41,6 @@
}
},
"containerEnv": {
"GEM_PATH": "/usr/local/rvm/gems/default:/usr/local/rvm/gems/default@global",
"GEM_HOME": "/usr/local/rvm/gems/default",
"MY_RUBY_HOME": "/usr/local/rvm/rubies/default",
"PATH": "/usr/local/rvm/gems/default/bin:/usr/local/rvm/gems/default@global/bin:/usr/local/rvm/rubies/default/bin:/usr/local/share/rbenv/bin:${PATH}"
},
"installsAfter": [
"ghcr.io/devcontainers/features/common-utils"
Expand Down
211 changes: 71 additions & 140 deletions src/ruby/install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,26 +7,21 @@
# Docs: https://github.com/microsoft/vscode-dev-containers/blob/main/script-library/docs/ruby.md
# Maintainer: The VS Code and Codespaces Teams

RUBY_VERSION="${VERSION:-"latest"}"

USERNAME="${USERNAME:-"${_REMOTE_USER:-"automatic"}"}"
UPDATE_RC="${UPDATE_RC:-"true"}"
INSTALL_RUBY_TOOLS="${INSTALL_RUBY_TOOLS:-"true"}"
export RUBY_VERSION="${VERSION:-"latest"}"

# Comma-separated list of ruby versions to be installed (with rvm)
# alongside RUBY_VERSION, but not set as default.
ADDITIONAL_VERSIONS="${ADDITIONALVERSIONS:-""}"

# Note: ruby-debug-ide will install the right version of debase if missing and
# installing debase directly fails on Ruby 3.1.0 as of 1/7/2022, so omitting.
# installing ruby-debug-ide on debian fails, so omitting.
DEFAULT_GEMS="rake"
GPG_KEY_SERVERS="keyserver hkps://keys.openpgp.org
keyserver hkp://keyserver.pgp.com
keyserver hkp://pgp.mit.edu
keyserver hkp://ipv4.pool.sks-keyservers.net
keyserver hkp://keyserver.ubuntu.com
keyserver hkp://keyserver.ubuntu.com:80"
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added the keyservers recommended in the RVM documentation in addition to the ones previously included.


RVM_GPG_KEYS="409B6B1796C275462A1703113804BB82D39DC0E3 7D2BAF1CF37B13E2069D6956105BD0E739499BDB"
GPG_KEY_SERVERS="keyserver hkp://keyserver.ubuntu.com
keyserver hkp://keyserver.ubuntu.com:80
keyserver hkps://keys.openpgp.org
keyserver hkp://keyserver.pgp.com"
USERNAME="${USERNAME:-"${_REMOTE_USER:-"automatic"}"}"
UPDATE_RC="${UPDATE_RC:-"true"}"

set -e

Expand Down Expand Up @@ -106,7 +101,7 @@ receive_gpg_keys() {
fi
}

# Figure out correct version of a three part version number is not passed
# Figure out correct version if a three part version number is not passed
find_version_from_git_tags() {
local variable_name=$1
local requested_version=${!variable_name}
Expand Down Expand Up @@ -140,8 +135,7 @@ find_version_from_git_tags() {
echo "${variable_name}=${!variable_name}"
}

apt_get_update()
{
apt_get_update() {
if [ "$(find /var/lib/apt/lists/* | wc -l)" = "0" ]; then
echo "Running apt-get update..."
apt-get update -y
Expand All @@ -156,147 +150,84 @@ check_packages() {
fi
}

# Ensure apt is in non-interactive to avoid prompts
export DEBIAN_FRONTEND=noninteractive

architecture="$(uname -m)"
if [ "${architecture}" != "amd64" ] && [ "${architecture}" != "x86_64" ] && [ "${architecture}" != "arm64" ] && [ "${architecture}" != "aarch64" ]; then
echo "(!) Architecture $architecture unsupported"
exit 1
fi
#install_rubybuild() {
#}

# Install dependencies
check_packages curl ca-certificates software-properties-common build-essential gnupg2 libreadline-dev \
procps dirmngr gawk autoconf automake bison libffi-dev libgdbm-dev libncurses5-dev \
libsqlite3-dev libtool libyaml-dev pkg-config sqlite3 zlib1g-dev libgmp-dev libssl-dev
if ! type git > /dev/null 2>&1; then
check_packages git
fi
#install_rbenv() {
#}

install_rvm() {
if rvm --version > /dev/null; then
echo "Ruby Version Manager already exists."
else
RVM_GPG_KEYS="409B6B1796C275462A1703113804BB82D39DC0E3 7D2BAF1CF37B13E2069D6956105BD0E739499BDB"
receive_gpg_keys RVM_GPG_KEYS

# Figure out correct version of a three part version number is not passed
find_version_from_git_tags RUBY_VERSION "https://github.com/ruby/ruby" "tags/v" "_"
# Create rvm group as a system group to reduce the odds of conflict with local user UIDs
if ! cat /etc/group | grep -e "^rvm:" > /dev/null 2>&1; then
groupadd -r rvm
fi

# Just install Ruby if RVM already installed
if rvm --version > /dev/null; then
echo "Ruby Version Manager already exists."
if [[ "$(ruby -v)" = *"${RUBY_VERSION}"* ]]; then
echo "(!) Ruby is already installed with version ${RUBY_VERSION}. Skipping..."
elif [ "${RUBY_VERSION}" != "none" ]; then
echo "Installing specified Ruby version."
su ${USERNAME} -c "rvm install ruby ${RUBY_VERSION}"
curl -sSL https://get.rvm.io | bash -s stable --ignore-dotfiles ${RVM_INSTALL_ARGS} 2>&1
usermod -aG rvm ${USERNAME}
source /usr/local/rvm/scripts/rvm
rvm fix-permissions system
rm -rf ${GNUPGHOME}
fi
SKIP_GEM_INSTALL="false"
SKIP_RBENV_RBUILD="true"
else
# Install RVM
receive_gpg_keys RVM_GPG_KEYS
# Determine appropriate settings for rvm installer

if [ "${RUBY_VERSION}" = "none" ]; then
RVM_INSTALL_ARGS=""
elif [[ "$(ruby -v)" = *"${RUBY_VERSION}"* ]]; then
echo "(!) Ruby is already installed with version ${RUBY_VERSION}. Skipping..."
RVM_INSTALL_ARGS=""
echo "No Ruby version specified. Skipping Ruby installation."
else
if [ "${RUBY_VERSION}" = "latest" ] || [ "${RUBY_VERSION}" = "current" ] || [ "${RUBY_VERSION}" = "lts" ]; then
RVM_INSTALL_ARGS="--ruby"
RUBY_VERSION=""
else
RVM_INSTALL_ARGS="--ruby=${RUBY_VERSION}"
fi
if [ "${INSTALL_RUBY_TOOLS}" = "true" ]; then
SKIP_GEM_INSTALL="true"
else
DEFAULT_GEMS=""
fi
find_version_from_git_tags RUBY_VERSION "https://github.com/ruby/ruby" "tags/v" "_"
source /usr/local/rvm/scripts/rvm
rvm install ruby ${RUBY_VERSION}
rvm use ${RUBY_VERSION}
fi
# Create rvm group as a system group to reduce the odds of conflict with local user UIDs
if ! cat /etc/group | grep -e "^rvm:" > /dev/null 2>&1; then
groupadd -r rvm
fi
# Install rvm
curl -sSL https://get.rvm.io | bash -s stable --ignore-dotfiles ${RVM_INSTALL_ARGS} --with-default-gems="${DEFAULT_GEMS}" 2>&1
usermod -aG rvm ${USERNAME}
source /usr/local/rvm/scripts/rvm
rvm fix-permissions system
rm -rf ${GNUPGHOME}
fi

if [ "${INSTALL_RUBY_TOOLS}" = "true" ]; then
# Non-root user may not have "gem" in path when script is run and no ruby version
# is installed by rvm, so handle this by using root's default gem in this case
ROOT_GEM="$(which gem || echo "")"
${ROOT_GEM} install ${DEFAULT_GEMS}
fi

# VS Code server usually first in the path, so silence annoying rvm warning (that does not apply) and then source it
updaterc "if ! grep rvm_silence_path_mismatch_check_flag \$HOME/.rvmrc > /dev/null 2>&1; then echo 'rvm_silence_path_mismatch_check_flag=1' >> \$HOME/.rvmrc; fi\nsource /usr/local/rvm/scripts/rvm > /dev/null 2>&1"

# Additional ruby versions to be installed but not be set as default.
if [ ! -z "${ADDITIONAL_VERSIONS}" ]; then
OLDIFS=$IFS
IFS=","
read -a additional_versions <<< "$ADDITIONAL_VERSIONS"
for version in "${additional_versions[@]}"; do
# Figure out correct version of a three part version number is not passed
find_version_from_git_tags version "https://github.com/ruby/ruby" "tags/v" "_"
source /usr/local/rvm/scripts/rvm
rvm install ruby ${version}
done
IFS=$OLDIFS
fi

# Install rbenv/ruby-build for good measure
if [ "${SKIP_RBENV_RBUILD}" != "true" ]; then

if [[ ! -d "/usr/local/share/rbenv" ]]; then
git clone --depth=1 \
-c core.eol=lf \
-c core.autocrlf=false \
-c fsck.zeroPaddedFilemode=ignore \
-c fetch.fsck.zeroPaddedFilemode=ignore \
-c receive.fsck.zeroPaddedFilemode=ignore \
https://github.com/rbenv/rbenv.git /usr/local/share/rbenv
if [ ! -z "${ADDITIONAL_VERSIONS}" ]; then
echo "Installing additional Ruby versions."
OLDIFS=$IFS
IFS=","
read -a additional_versions <<< "$ADDITIONAL_VERSIONS"
for version in "${additional_versions[@]}"; do
# Figure out correct version of a three part version number is not passed
find_version_from_git_tags version "https://github.com/ruby/ruby" "tags/v" "_"
source /usr/local/rvm/scripts/rvm
rvm install ruby ${version}
done
IFS=$OLDIFS
fi

if [[ ! -d "/usr/local/share/ruby-build" ]]; then
git clone --depth=1 \
-c core.eol=lf \
-c core.autocrlf=false \
-c fsck.zeroPaddedFilemode=ignore \
-c fetch.fsck.zeroPaddedFilemode=ignore \
-c receive.fsck.zeroPaddedFilemode=ignore \
https://github.com/rbenv/ruby-build.git /usr/local/share/ruby-build
mkdir -p /root/.rbenv/plugins

ln -s /usr/local/share/ruby-build /root/.rbenv/plugins/ruby-build
fi
# VS Code server usually first in the path, so silence annoying rvm warning (that does not apply) and then source it
updaterc "if ! grep rvm_silence_path_mismatch_check_flag \$HOME/.rvmrc > /dev/null 2>&1; then echo 'rvm_silence_path_mismatch_check_flag=1' >> \$HOME/.rvmrc; fi\nsource /usr/local/rvm/scripts/rvm > /dev/null 2>&1"

if [ "${USERNAME}" != "root" ]; then
mkdir -p /home/${USERNAME}/.rbenv/plugins
chown -R "${USERNAME}:rvm" "/usr/local/rvm/"
chmod -R g+r+w "/usr/local/rvm/"
find "/usr/local/rvm/" -type d | xargs -n 1 chmod g+s

if [[ ! -d "/home/${USERNAME}/.rbenv/plugins/ruby-build" ]]; then
ln -s /usr/local/share/ruby-build /home/${USERNAME}/.rbenv/plugins/ruby-build
fi
rvm cleanup all
}

# Oryx expects ruby to be installed in this specific path, else it breaks the oryx magic for ruby projects.
if [ ! -f /usr/local/rvm/gems/default/bin/ruby ]; then
ln -s /usr/local/rvm/rubies/default/bin/ruby /usr/local/rvm/gems/default/bin
fi
# Ensure apt is in non-interactive to avoid prompts
export DEBIAN_FRONTEND=noninteractive

chown -R "${USERNAME}:rvm" "/home/${USERNAME}/.rbenv/"
chmod -R g+r+w "/home/${USERNAME}/.rbenv"
find "/home/${USERNAME}/.rbenv" -type d | xargs -n 1 chmod g+s
fi
architecture="$(uname -m)"
if [ "${architecture}" != "amd64" ] && [ "${architecture}" != "x86_64" ] && [ "${architecture}" != "arm64" ] && [ "${architecture}" != "aarch64" ]; then
echo "(!) Architecture $architecture unsupported"
exit 1
fi

chown -R "${USERNAME}:rvm" "/usr/local/rvm/"
chmod -R g+r+w "/usr/local/rvm/"
find "/usr/local/rvm/" -type d | xargs -n 1 chmod g+s
# Install dependencies
check_packages curl ca-certificates software-properties-common build-essential gnupg2 libreadline-dev \
procps dirmngr gawk autoconf automake bison libffi-dev libgdbm-dev libncurses5-dev \
libsqlite3-dev libtool libyaml-dev pkg-config sqlite3 zlib1g-dev libgmp-dev libssl-dev

# Clean up
rvm cleanup all
${ROOT_GEM} cleanup
if ! type git > /dev/null 2>&1; then
check_packages git
fi

## TODO INSTALL THE RIGHT STUFF HERE
install_rvm

# Clean up
rm -rf /var/lib/apt/lists/*
Expand Down
16 changes: 0 additions & 16 deletions test/ruby/install_additional_ruby.sh

This file was deleted.

16 changes: 16 additions & 0 deletions test/ruby/rvm_additional_versions.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#!/bin/bash

set -e

# Optional: Import test library
source dev-container-features-test-lib

check "current user" bash -c "whoami"

check "rubies installed" bash -c "rvm list"

check "3.0.6 installed" bash -c "rvm list | fgrep 3.0.6"
check "2.7.8 installed" bash -c "rvm list | fgrep 2.7.8"

# Report result
reportResults
15 changes: 15 additions & 0 deletions test/ruby/rvm_install_gem.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#!/bin/bash

set -e

# Optional: Import test library
source dev-container-features-test-lib

check "current user" bash -c "whoami"

check "current ruby" bash -c "which ruby"

check "gem installed" bash -c "gem install rubocop"

# Report result
reportResults
15 changes: 15 additions & 0 deletions test/ruby/rvm_non_root_user.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#!/bin/bash

set -e

# Optional: Import test library
source dev-container-features-test-lib

check "current user" bash -c "whoami"

check "version" rvm --version

check "ruby version" ruby --version

# Report result
reportResults
5 changes: 3 additions & 2 deletions test/ruby/ruby_debian.sh → test/ruby/rvm_older_version.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ set -e
# Optional: Import test library
source dev-container-features-test-lib

check "ruby" ruby -v
check "rake" bash -c "gem list | grep rake"
check "current user" bash -c "whoami"

check "ruby installed" bash -c "ruby --version | grep 3.0.6"

# Report result
reportResults
Loading