diff --git a/README.md b/README.md index 6711a57..5433a08 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,12 @@ # Rails Dev Container Images & Features -This repository contains dev container images and features that can be used to create a convenient and consistent +This repository contains dev container images and features that can be used to create a convenient and consistent development environment for working on Rails applications. ## What is a dev container? -A **dev container** is a running Docker container that provides a fully-featured development environment which can be -used to run an application, to separate tools, libraries, or runtimes needed for working with a codebase, and to aid in +A **dev container** is a running Docker container that provides a fully-featured development environment which can be +used to run an application, to separate tools, libraries, or runtimes needed for working with a codebase, and to aid in continuous integration and testing. **Dev container features** are self-contained units of installation and configuration that can be installed on top of a container image to provide additional functionality in a container. **Dev container images** are prebuilt docker images based on dev container features. For more information on the dev container @@ -30,8 +30,8 @@ would look like this: } ``` -This dev container uses the Ruby image, which includes an installation of Ruby (in this case version 3.3.0) and the RBenv -version manager, as well as other common utilities such as Git. It also uses the Active Storage feature, which installs +This dev container uses the Ruby image, which includes an installation of Ruby (in this case version 3.3.0) and a Ruby +version manager (mise by default, but configurable to use rbenv), as well as other common utilities such as Git. It also uses the Active Storage feature, which installs dependencies needed for Active Storage. The dev container can be initialized [by VSCode](https://code.visualstudio.com/docs/devcontainers/containers) or by using @@ -50,9 +50,9 @@ This repository is open for contributions. See [the contributions guide](CONTRIB You can create your own features, and images based on them. -The best place to start is the [feature starter repository](https://github.com/devcontainers/feature-starter) which is +The best place to start is the [feature starter repository](https://github.com/devcontainers/feature-starter) which is maintained by the devcontainers org. ## License -The repository is released under the [MIT License](https://opensource.org/licenses/MIT). \ No newline at end of file +The repository is released under the [MIT License](https://opensource.org/licenses/MIT). diff --git a/features/src/ruby/NOTES.md b/features/src/ruby/NOTES.md index c2b3ce4..f058ff0 100644 --- a/features/src/ruby/NOTES.md +++ b/features/src/ruby/NOTES.md @@ -1,3 +1,23 @@ ## OS Support This Feature should work on recent versions of Debian/Ubuntu-based distributions with the `apt` package manager installed. + +## Changing the Ruby Version + +```json +"features": { + "ghcr.io/rails/devcontainer/features/ruby:1": { + "version": "3.3.0" + } +} +``` + +## Changing the Version Manager + +```json +"features": { + "ghcr.io/rails/devcontainer/features/ruby:1": { + "versionManager": "rbenv" + } +} +``` diff --git a/features/src/ruby/README.md b/features/src/ruby/README.md index ac84c82..3a40026 100644 --- a/features/src/ruby/README.md +++ b/features/src/ruby/README.md @@ -1,9 +1,11 @@ -# Ruby (via rbenv) +# Ruby -Installs Ruby, rbenv, and ruby-build as well as the dependencies needed to build Ruby. +Installs Ruby and a version manager (mise or rbenv) along with the dependencies needed to build Ruby. ## Example Usage +### Using mise (default) + ```json "features": { "ghcr.io/rails/devcontainer/features/ruby:1": { @@ -12,11 +14,34 @@ Installs Ruby, rbenv, and ruby-build as well as the dependencies needed to build } ``` +### Using rbenv + +```json +"features": { + "ghcr.io/rails/devcontainer/features/ruby:1": { + "version": "3.3.0", + "versionManager": "rbenv" + } +} +``` + +### Using mise explicitly + +```json +"features": { + "ghcr.io/rails/devcontainer/features/ruby:1": { + "version": "3.3.0", + "versionManager": "mise" + } +} +``` + ## Options -| Options Id | Description | Type | -|-----|-----|-----| -| version | The version of ruby to be installed | string | +| Options Id | Description | Type | Default Value | +|-----|-----|-----|-----| +| version | The version of ruby to be installed | string | 3.4.1 | +| versionManager | The version manager to use for Ruby (mise or rbenv) | string | mise | ## Customizations diff --git a/features/src/ruby/devcontainer-feature.json b/features/src/ruby/devcontainer-feature.json index 40ca072..aff89ef 100644 --- a/features/src/ruby/devcontainer-feature.json +++ b/features/src/ruby/devcontainer-feature.json @@ -1,8 +1,8 @@ { "id": "ruby", - "version": "1.1.3", - "name": "Ruby (via rbenv)", - "description": "Installs Ruby, rbenv, and ruby-build as well as the dependencies needed to build Ruby.", + "version": "2.0.0", + "name": "Ruby", + "description": "Installs Ruby and a version manager (mise or rbenv) along with libraries needed to build Ruby.", "customizations": { "vscode": { "extensions": [ @@ -10,9 +10,6 @@ ] } }, - "containerEnv": { - "PATH": "/usr/local/share/rbenv/bin:${PATH}" - }, "installsAfter": [ "ghcr.io/devcontainers/features/common-utils" ], @@ -21,6 +18,15 @@ "type": "string", "default": "3.4.4", "description": "The ruby version to be installed" + }, + "versionManager": { + "type": "string", + "proposals": [ + "mise", + "rbenv" + ], + "default": "mise", + "description": "The version manager to use for Ruby (mise or rbenv)" } } } diff --git a/features/src/ruby/install.sh b/features/src/ruby/install.sh index 2024f31..36972d2 100755 --- a/features/src/ruby/install.sh +++ b/features/src/ruby/install.sh @@ -2,35 +2,119 @@ set -e USERNAME="${USERNAME:-"${_REMOTE_USER:-"automatic"}"}" +VERSION_MANAGER="${VERSIONMANAGER:-"mise"}" -apt-get update -y -apt-get -y install --no-install-recommends git curl ca-certificates libssl-dev libreadline-dev zlib1g-dev autoconf bison build-essential \ - libyaml-dev libncurses5-dev libffi-dev libgdbm-dev libxml2-dev rustc +# Function to install dependencies needed for building Ruby +install_dependencies() { + apt-get update -y + DEBIAN_FRONTEND=noninteractive apt-get -y install --no-install-recommends \ + git \ + curl \ + ca-certificates \ + libssl-dev \ + libreadline-dev \ + zlib1g-dev \ + autoconf \ + bison \ + build-essential \ + libyaml-dev \ + libncurses5-dev \ + libffi-dev \ + libgdbm-dev \ + libxml2-dev \ + rustc +} -git clone https://github.com/rbenv/rbenv.git /usr/local/share/rbenv -git clone https://github.com/rbenv/ruby-build.git /usr/local/share/ruby-build +# Function to add lines to shell initialization files +add_to_shell_init() { + _user="$1" + _bash_line="$2" + _zsh_line="${3:-$_bash_line}" # Use bash_line as default if zsh_line not provided -mkdir -p /root/.rbenv/plugins -ln -s /usr/local/share/ruby-build /root/.rbenv/plugins/ruby-build + if [ "$_user" = "root" ]; then + _home_dir="/root" + else + _home_dir="/home/$_user" + fi + + echo "$_bash_line" >> "$_home_dir/.bashrc" + + if [ -f "$_home_dir/.zshrc" ]; then + echo "$_zsh_line" >> "$_home_dir/.zshrc" + fi +} + +# Function to setup rbenv +setup_rbenv() { + _user="$1" + _user_home="/home/$_user" -if [ "${USERNAME}" != "root" ]; then - user_home="/home/${USERNAME}" - mkdir -p "${user_home}/.rbenv/plugins" - ln -s /usr/local/share/ruby-build "${user_home}/.rbenv/plugins/ruby-build" + # Clone rbenv and ruby-build + git clone https://github.com/rbenv/rbenv.git /usr/local/share/rbenv + git clone https://github.com/rbenv/ruby-build.git /usr/local/share/ruby-build - chown -R "${USERNAME}" "${user_home}/.rbenv/" - chmod -R g+r+w "${user_home}/.rbenv" + # Setup plugins for root + mkdir -p /root/.rbenv/plugins + ln -s /usr/local/share/ruby-build /root/.rbenv/plugins/ruby-build + # Setup for non-root user if needed + if [ "$_user" != "root" ]; then + mkdir -p "$_user_home/.rbenv/plugins" + ln -s /usr/local/share/ruby-build "$_user_home/.rbenv/plugins/ruby-build" + chown -R "$_user" "$_user_home/.rbenv/" + chmod -R g+r+w "$_user_home/.rbenv" + fi + + # shellcheck disable=SC2016 + add_to_shell_init "$_user" 'export PATH="/usr/local/share/rbenv/bin:$PATH"' # shellcheck disable=SC2016 - echo 'eval "$(rbenv init -)"' >> "${user_home}/.bashrc" + add_to_shell_init "$_user" 'eval "$(rbenv init -)"' +} + +# Function to install Ruby with rbenv +install_ruby_rbenv() { + _user="$1" + _version="$2" + + su "$_user" -c "/usr/local/share/rbenv/bin/rbenv install $_version" + su "$_user" -c "/usr/local/share/rbenv/bin/rbenv global $_version" +} - if [ -f "${user_home}/.zshrc" ]; then - # shellcheck disable=SC2016 - echo 'eval "$(rbenv init -)"' >> "${user_home}/.zshrc" +# Function to setup mise +setup_mise() { + _user="$1" + + su "$_user" -c "curl https://mise.run | sh" + + # shellcheck disable=SC2016 + add_to_shell_init "$_user" 'eval "$(~/.local/bin/mise activate bash)"' 'eval "$(~/.local/bin/mise activate zsh)"' +} + +# Function to install Ruby with mise +install_ruby_mise() { + _user="$1" + _version="$2" + + if [ "$_user" = "root" ]; then + _home_dir="/root" + else + _home_dir="/home/$_user" fi -fi -su "${USERNAME}" -c "/usr/local/share/rbenv/bin/rbenv install $VERSION" -su "${USERNAME}" -c "/usr/local/share/rbenv/bin/rbenv global $VERSION" + su "$_user" -c "$_home_dir/.local/bin/mise install ruby@$_version" + su "$_user" -c "$_home_dir/.local/bin/mise use -g ruby@$_version" + su "$_user" -c "$_home_dir/.local/bin/mise settings add idiomatic_version_file_enable_tools ruby" +} + +install_dependencies + +# Setup version manager and install Ruby based on user choice +if [ "$VERSION_MANAGER" = "rbenv" ]; then + setup_rbenv "$USERNAME" + install_ruby_rbenv "$USERNAME" "$VERSION" +else + setup_mise "$USERNAME" + install_ruby_mise "$USERNAME" "$VERSION" +fi rm -rf /var/lib/apt/lists/* diff --git a/features/test/ruby/scenarios.json b/features/test/ruby/scenarios.json index 9d364e5..14061fb 100644 --- a/features/test/ruby/scenarios.json +++ b/features/test/ruby/scenarios.json @@ -6,5 +6,13 @@ "version": "3.3.0" } } + }, + "with_rbenv": { + "image": "mcr.microsoft.com/devcontainers/base:1-bookworm", + "features": { + "ruby": { + "versionManager": "rbenv" + } + } } } diff --git a/features/test/ruby/test.sh b/features/test/ruby/test.sh index 7579ba0..85f0652 100644 --- a/features/test/ruby/test.sh +++ b/features/test/ruby/test.sh @@ -4,11 +4,10 @@ set -e # shellcheck source=/dev/null source dev-container-features-test-lib -check "PATH contains rbenv" bash -c "echo $PATH | grep rbenv" -check "rbenv is installed" bash -c "rbenv --version" -check "ruby-build is installed" bash -c "ls -l $HOME/.rbenv/plugins/ruby-build | grep '\-> /usr/local/share/ruby-build'" -eval "$(rbenv init -)" +check "mise is installed" bash -c "mise --version" +check "mise init is sourced in the bashrc" bash -c "grep 'eval \"\$(~/.local/bin/mise activate bash)\"' $HOME/.bashrc" +check "mise idiomatic version file is enabled for ruby" bash -c "mise settings | grep idiomatic_version_file_enable_tools | grep ruby" check "Ruby is installed with YJIT" bash -c "RUBY_YJIT_ENABLE=1 ruby -v | grep +YJIT" -check "Ruby version is set to 3.4.4" bash -c "rbenv global | grep 3.4.4" +check "Ruby version is set to 3.4.4" bash -c "mise use -g ruby | grep 3.4.4" reportResults diff --git a/features/test/ruby/version_3_3_0.sh b/features/test/ruby/version_3_3_0.sh index e7b9e5b..5857ed7 100644 --- a/features/test/ruby/version_3_3_0.sh +++ b/features/test/ruby/version_3_3_0.sh @@ -4,12 +4,11 @@ set -e # shellcheck source=/dev/null source dev-container-features-test-lib -check "PATH contains rbenv" bash -c "echo $PATH | grep rbenv" -check "rbenv is installed" bash -c "rbenv --version" -check "ruby-build is installed" bash -c "ls -l $HOME/.rbenv/plugins/ruby-build | grep '\-> /usr/local/share/ruby-build'" -check "rbenv init is sourced in the bashrc" bash -c "grep 'eval \"\$(rbenv init -)\"' $HOME/.bashrc" -check "rbenv init is sourced in the zshrc" bash -c "grep 'eval \"\$(rbenv init -)\"' $HOME/.zshrc" +check "mise is installed" bash -c "mise --version" +check "mise init is sourced in the bashrc" bash -c "grep 'eval \"\$(~/.local/bin/mise activate bash)\"' $HOME/.bashrc" +check "mise init is sourced in the zshrc" bash -c "grep 'eval \"\$(~/.local/bin/mise activate zsh)\"' $HOME/.zshrc" +check "mise idiomatic version file is enabled for ruby" bash -c "mise settings | grep idiomatic_version_file_enable_tools | grep ruby" check "Ruby is installed with YJIT" bash -c "RUBY_YJIT_ENABLE=1 ruby -v | grep +YJIT" -check "Ruby version is set to 3.3.0" bash -c "rbenv global | grep 3.3.0" +check "Ruby version is set to 3.3.0" bash -c "mise use -g ruby | grep 3.3.0" reportResults diff --git a/features/test/ruby/with_rbenv.sh b/features/test/ruby/with_rbenv.sh new file mode 100644 index 0000000..7579ba0 --- /dev/null +++ b/features/test/ruby/with_rbenv.sh @@ -0,0 +1,14 @@ +#!/bin/bash +set -e + +# shellcheck source=/dev/null +source dev-container-features-test-lib + +check "PATH contains rbenv" bash -c "echo $PATH | grep rbenv" +check "rbenv is installed" bash -c "rbenv --version" +check "ruby-build is installed" bash -c "ls -l $HOME/.rbenv/plugins/ruby-build | grep '\-> /usr/local/share/ruby-build'" +eval "$(rbenv init -)" +check "Ruby is installed with YJIT" bash -c "RUBY_YJIT_ENABLE=1 ruby -v | grep +YJIT" +check "Ruby version is set to 3.4.4" bash -c "rbenv global | grep 3.4.4" + +reportResults diff --git a/images/ruby/.devcontainer/devcontainer.json b/images/ruby/.devcontainer/devcontainer.json index 5bd0657..0edde11 100644 --- a/images/ruby/.devcontainer/devcontainer.json +++ b/images/ruby/.devcontainer/devcontainer.json @@ -17,7 +17,7 @@ }, "ghcr.io/rails/devcontainer/features/ruby": { "version": "${localEnv:RUBY_VERSION}" - }, + } }, // Set `remoteUser` to `root` to connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root. "remoteUser": "vscode"