Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Project Guidelines

## Architecture
- This repo has two main testing domains:
- Ruby tooling code in `lib/` with tests in `test/` (Minitest).
- Dev Container Features in `features/src/*` with integration tests in `features/test/*` (devcontainer CLI + shell scripts/scenarios).
- Entry-point scripts in `bin/` usually orchestrate updates that must be validated by Ruby tests and feature tests.

## Build and Test
- Prefer running the smallest relevant test set first, then broader suites.

### Ruby tests (root)
- Install deps: `bundle install`
- Run all Ruby tests: `bundle exec rake test`
- Equivalent: `rake test`

### Feature tests (from repo root)
- Install Node deps (if needed): `npm install`
- Run all feature tests via package script: `npm test`
- Preferred shortcut via Rake: `rake features:test`

### Feature tests (targeted, from repo root)
- Prerequisite: install project deps from repo root (`npm install`) to get the devcontainer CLI from `package.json`.
- Autogenerated tests for one feature:
- `rake features:autogenerated FEATURE=ruby IMAGE=ubuntu:latest`
- Scenario tests for one feature:
- `rake features:scenarios FEATURE=ruby`
- Direct CLI equivalents (run from `features/`):
- `npx devcontainer features test --skip-scenarios -f ruby -i ubuntu:latest .`
- `npx devcontainer features test -f ruby --skip-autogenerated --skip-duplicated .`

### Shell script linting (from `features/`)
- `find . -name "*.sh" -type f -exec shellcheck {} +`

## Conventions
- Keep tests close to the affected area:
- `lib/**` changes should include/adjust `test/**/*_test.rb`.
- `features/src/<name>/**` changes should include/adjust `features/test/<name>/**`.
- Preserve current patterns in existing tests:
- Ruby tests use Minitest style and temp-directory isolation.
- Feature tests use `dev-container-features-test-lib` assertions (`check`, `reportResults`) and scenario matrices in `scenarios.json`.
25 changes: 25 additions & 0 deletions Rakefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# frozen_string_literal: true

require "rake/testtask"
require "shellwords"

Rake::TestTask.new(:test) do |t|
t.libs << "test"
Expand All @@ -9,3 +10,27 @@ Rake::TestTask.new(:test) do |t|
end

task default: :test

namespace :features do
desc "Run all devcontainer feature tests via npm"
task :test do
sh "npm test"
end

desc "Run autogenerated tests for one feature (FEATURE=ruby IMAGE=ubuntu:latest)"
task :autogenerated do
feature = ENV["FEATURE"]
abort "Set FEATURE, for example: rake features:autogenerated FEATURE=ruby" if feature.nil? || feature.empty?

image = ENV.fetch("IMAGE", "ubuntu:latest")
sh "cd features && npx devcontainer features test --skip-scenarios -f #{Shellwords.escape(feature)} -i #{Shellwords.escape(image)} ."
end

desc "Run scenario tests for one feature (FEATURE=ruby)"
task :scenarios do
feature = ENV["FEATURE"]
abort "Set FEATURE, for example: rake features:scenarios FEATURE=ruby" if feature.nil? || feature.empty?

sh "cd features && npx devcontainer features test -f #{Shellwords.escape(feature)} --skip-autogenerated --skip-duplicated ."
end
end
13 changes: 13 additions & 0 deletions features/src/ruby/NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,16 @@ This Feature should work on recent versions of Debian/Ubuntu-based distributions
}
}
```

## Opting In to Precompiled Rubies with mise

```json
"features": {
"ghcr.io/rails/devcontainer/features/ruby:1": {
"versionManager": "mise",
"usePrecompiledRubies": true
}
}
```

When using `mise`, this feature keeps `ruby.compile=true` by default so Ruby is compiled from source. Set `usePrecompiledRubies` to `true` to make mise prefer precompiled Rubies when they are available.
15 changes: 15 additions & 0 deletions features/src/ruby/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,27 @@ Installs Ruby and a version manager (mise or rbenv) along with the dependencies
}
```

### Opting in to precompiled Rubies with mise

```json
"features": {
"ghcr.io/rails/devcontainer/features/ruby:1": {
"version": "3.3.0",
"versionManager": "mise",
"usePrecompiledRubies": true
}
}
```

When using `mise`, this feature keeps `ruby.compile=true` by default so Ruby is compiled from source. Set `usePrecompiledRubies` to `true` to make mise prefer precompiled Rubies when they are available.

## Options

| Options Id | Description | Type | Default Value |
|-----|-----|-----|-----|
| version | The version of ruby to be installed | string | 4.0.2 |
| versionManager | The version manager to use for Ruby (mise or rbenv) | string | mise |
| usePrecompiledRubies | Use precompiled Rubies with mise when available | boolean | false |

## Customizations

Expand Down
7 changes: 6 additions & 1 deletion features/src/ruby/devcontainer-feature.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"id": "ruby",
"version": "2.1.4",
"version": "2.2.0",
"name": "Ruby",
"description": "Installs Ruby and a version manager (mise or rbenv) along with libraries needed to build Ruby.",
"documentationURL": "https://github.com/rails/devcontainer/tree/main/features/src/ruby",
Expand Down Expand Up @@ -30,6 +30,11 @@
],
"default": "mise",
"description": "The version manager to use for Ruby (mise or rbenv)"
},
"usePrecompiledRubies": {
"type": "boolean",
"default": false,
"description": "Use precompiled Rubies with mise when available"
}
}
}
15 changes: 14 additions & 1 deletion features/src/ruby/install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ set -e

USERNAME="${USERNAME:-"${_REMOTE_USER:-"automatic"}"}"
VERSION_MANAGER="${VERSIONMANAGER:-"mise"}"
USE_PRECOMPILED_RUBIES="${USEPRECOMPILEDRUBIES:-"false"}"

# Function to install dependencies needed for building Ruby
install_dependencies() {
Expand Down Expand Up @@ -83,8 +84,20 @@ install_ruby_rbenv() {
# Function to setup mise
setup_mise() {
_user="$1"
_use_precompiled_rubies="$2"

if [ "$_user" = "root" ]; then
_home_dir="/root"
else
_home_dir="/home/$_user"
fi

su "$_user" -c "curl https://mise.run | sh"
if [ "$_use_precompiled_rubies" = "true" ]; then
su "$_user" -c "$_home_dir/.local/bin/mise settings ruby.compile=false"
else
su "$_user" -c "$_home_dir/.local/bin/mise settings ruby.compile=true"
fi

# shellcheck disable=SC2016
add_to_shell_init "$_user" 'eval "$(~/.local/bin/mise activate bash)"' 'eval "$(~/.local/bin/mise activate zsh)"'
Expand Down Expand Up @@ -113,7 +126,7 @@ if [ "$VERSION_MANAGER" = "rbenv" ]; then
setup_rbenv "$USERNAME"
install_ruby_rbenv "$USERNAME" "$VERSION"
else
setup_mise "$USERNAME"
setup_mise "$USERNAME" "$USE_PRECOMPILED_RUBIES"
install_ruby_mise "$USERNAME" "$VERSION"
fi

Expand Down
11 changes: 10 additions & 1 deletion features/test/ruby/scenarios.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
"image": "mcr.microsoft.com/devcontainers/base:2-trixie",
"features": {
"ruby": {
"version": "3.3.0"
"version": "3.3.0",
"usePrecompiledRubies": true
}
}
},
Expand All @@ -14,5 +15,13 @@
"versionManager": "rbenv"
}
}
},
"with_precompiled_rubies": {
"image": "mcr.microsoft.com/devcontainers/base:2-trixie",
"features": {
"ruby": {
"usePrecompiledRubies": true
}
}
}
}
1 change: 1 addition & 0 deletions features/test/ruby/test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ source dev-container-features-test-lib

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 is configured to compile Ruby from source by default" bash -c "mise settings | grep ruby.compile | grep true"
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 4.0.2" bash -c "mise use -g ruby | grep 4.0.2"
Expand Down
1 change: 1 addition & 0 deletions features/test/ruby/version_3_3_0.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ source dev-container-features-test-lib
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 uses precompiled Rubies when enabled" bash -c "mise settings | grep ruby.compile | grep false"
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 "mise use -g ruby | grep 3.3.0"
Expand Down
13 changes: 13 additions & 0 deletions features/test/ruby/with_precompiled_rubies.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#!/bin/bash
set -e

# shellcheck source=/dev/null
source dev-container-features-test-lib

check "mise is installed" bash -c "mise --version"
check "mise uses precompiled Rubies when enabled" bash -c "mise settings | grep ruby.compile | grep false"
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 4.0.2" bash -c "mise use -g ruby | grep 4.0.2"

reportResults
3 changes: 2 additions & 1 deletion images/ruby/.devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
"ppa": "false"
},
"ghcr.io/rails/devcontainer/features/ruby": {
"version": "${localEnv:RUBY_VERSION}"
"version": "${localEnv:RUBY_VERSION}",
"usePrecompiledRubies": "true"
}
},
// Set `remoteUser` to `root` to connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root.
Expand Down
45 changes: 5 additions & 40 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 3 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
{
"name": "ruby-version-script",
"name": "rails-devcontainer-features",
"version": "1.0.0",
"description": "Script dependencies for adding Ruby versions",
"description": "Rails DevContainer features and images",
"devDependencies": {
"js-yaml": "^4.1.0",
"semver": "^7.5.4",
"@devcontainers/cli": "^0.56.0"
"@devcontainers/cli": ">= 0.84"
},
"scripts": {
"test": "devcontainer features test features -i ubuntu:noble"
Expand Down
6 changes: 6 additions & 0 deletions test/commands/add_ruby_version_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,11 @@ def create_feature_json(version: "2.0.0", default_ruby: "3.3.0")
"type" => "string",
"default" => default_ruby,
"description" => "The ruby version to be installed"
},
"usePrecompiledRubies" => {
"type" => "boolean",
"default" => false,
"description" => "Use precompiled Rubies with mise when available"
}
}
}
Expand All @@ -184,6 +189,7 @@ def create_readme(default_version: "3.3.0")
|-----|-----|-----|-----|
| version | The version of ruby to be installed | string | #{default_version} |
| versionManager | The version manager to use | string | mise |
| usePrecompiledRubies | Use precompiled Rubies with mise when available | boolean | false |
README
File.write(File.join(@temp_dir, "features/src/ruby/README.md"), content)
end
Expand Down
6 changes: 6 additions & 0 deletions test/ruby_version_adder_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -431,6 +431,11 @@ def create_feature_json(version: "2.0.0", default_ruby: "3.3.0")
"type" => "string",
"default" => default_ruby,
"description" => "The ruby version to be installed"
},
"usePrecompiledRubies" => {
"type" => "boolean",
"default" => false,
"description" => "Use precompiled Rubies with mise when available"
}
}
}
Expand All @@ -453,6 +458,7 @@ def create_readme(default_version: "3.3.0")
|-----|-----|-----|-----|
| version | The version of ruby to be installed | string | #{default_version} |
| versionManager | The version manager to use | string | mise |
| usePrecompiledRubies | Use precompiled Rubies with mise when available | boolean | false |
README
File.write(File.join(@temp_dir, "features/src/ruby/README.md"), content)
end
Expand Down