From a3e5eb514014b186311f8664ed42d0e0128ca17a Mon Sep 17 00:00:00 2001 From: Christian Sutter Date: Mon, 5 Aug 2024 10:55:45 +0000 Subject: [PATCH] Add `basic-ruby` devcontainer template This provides basic scaffolding for a simple Ruby application with some minimal VS Code extensions and gems set up, and a trivial code structure. --- src/basic-ruby/.devcontainer/Dockerfile | 25 ++++++++++++++++++ .../.devcontainer/devcontainer.json | 26 +++++++++++++++++++ src/basic-ruby/Gemfile | 8 ++++++ src/basic-ruby/Rakefile | 5 ++++ src/basic-ruby/devcontainer-template.json | 20 ++++++++++++++ src/basic-ruby/lib/hello.rb | 8 ++++++ src/basic-ruby/test/test_hello.rb | 15 +++++++++++ test/basic-ruby/test.sh | 14 ++++++++++ test/harness.sh | 3 +++ 9 files changed, 124 insertions(+) create mode 100644 src/basic-ruby/.devcontainer/Dockerfile create mode 100644 src/basic-ruby/.devcontainer/devcontainer.json create mode 100644 src/basic-ruby/Gemfile create mode 100644 src/basic-ruby/Rakefile create mode 100644 src/basic-ruby/devcontainer-template.json create mode 100644 src/basic-ruby/lib/hello.rb create mode 100644 src/basic-ruby/test/test_hello.rb create mode 100755 test/basic-ruby/test.sh diff --git a/src/basic-ruby/.devcontainer/Dockerfile b/src/basic-ruby/.devcontainer/Dockerfile new file mode 100644 index 0000000..bf9dea8 --- /dev/null +++ b/src/basic-ruby/.devcontainer/Dockerfile @@ -0,0 +1,25 @@ +FROM ruby:${templateOption:imageVariant} + +ARG USERNAME=devcontainer +ARG USER_UID=1000 +ARG USER_GID=$USER_UID + +# Install basic development tools +RUN apt update && apt install -y less man-db sudo + +# Set up unprivileged local user +# +# NOTE: The Ruby images will eventually be available with Ubuntu 24.04 (`noble`), the base images of +# which already have a default `ubuntu` user configured. You will need to remove the creation of the +# user and group when you upgrade the Ruby images. +RUN groupadd --gid $USER_GID $USERNAME \ + && groupadd bundler \ + && useradd --uid $USER_UID --gid $USER_GID -m $USERNAME --shell /bin/bash --groups bundler \ + && echo $USERNAME ALL=\(root\) NOPASSWD:ALL > /etc/sudoers.d/$USERNAME \ + && chmod 0440 /etc/sudoers.d/$USERNAME + +# Set unprivileged user as default user +USER $USERNAME + +# Set `DEVCONTAINER` environment variable to help with orientation +ENV DEVCONTAINER=true diff --git a/src/basic-ruby/.devcontainer/devcontainer.json b/src/basic-ruby/.devcontainer/devcontainer.json new file mode 100644 index 0000000..fe8d190 --- /dev/null +++ b/src/basic-ruby/.devcontainer/devcontainer.json @@ -0,0 +1,26 @@ +// See https://containers.dev/implementors/json_reference/ for configuration reference +{ + "name": "New Ruby project", + "build": { + "dockerfile": "Dockerfile" + }, + "remoteUser": "devcontainer", + "postCreateCommand": "bundle install", + "customizations": { + "vscode": { + "extensions": [ + "Shopify.ruby-lsp", + "KoichiSasada.vscode-rdbg" + ], + "settings": { + "rubyLsp.rubyVersionManager": { + "identifier": "none" // Force native container Ruby + }, + "[ruby]": { + "editor.defaultFormatter": "Shopify.ruby-lsp", + "editor.formatOnSave": true + } + } + } + }, +} diff --git a/src/basic-ruby/Gemfile b/src/basic-ruby/Gemfile new file mode 100644 index 0000000..0f6cb4b --- /dev/null +++ b/src/basic-ruby/Gemfile @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +source 'https://rubygems.org' + +group :development, :test do + gem 'minitest' + gem 'rubocop' +end diff --git a/src/basic-ruby/Rakefile b/src/basic-ruby/Rakefile new file mode 100644 index 0000000..6b8a857 --- /dev/null +++ b/src/basic-ruby/Rakefile @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +require 'minitest/test_task' + +Minitest::TestTask.create diff --git a/src/basic-ruby/devcontainer-template.json b/src/basic-ruby/devcontainer-template.json new file mode 100644 index 0000000..a6fd7b4 --- /dev/null +++ b/src/basic-ruby/devcontainer-template.json @@ -0,0 +1,20 @@ +{ + "id": "basic-ruby", + "version": "1.0.0", + "name": "Basic Ruby application", + "description": "A devcontainer for basic Ruby applications", + "publisher": "Christian Sutter", + "documentationURL": "https://github.com/csutter/devcontainer-templates", + "licenseURL": "https://github.com/csutter/devcontainer-templates/blob/main/LICENSE", + "options": { + "imageVariant": { + "type": "string", + "description": "Upstream 'ruby' image tag (see hub.docker.com):", + "proposals": [ + "3.3" + ], + "default": "3.3" + } + }, + "platforms": ["Ruby"] +} diff --git a/src/basic-ruby/lib/hello.rb b/src/basic-ruby/lib/hello.rb new file mode 100644 index 0000000..4cb19b9 --- /dev/null +++ b/src/basic-ruby/lib/hello.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +# An example class +class Hello + def message + 'Hello, World!' + end +end diff --git a/src/basic-ruby/test/test_hello.rb b/src/basic-ruby/test/test_hello.rb new file mode 100644 index 0000000..dc71335 --- /dev/null +++ b/src/basic-ruby/test/test_hello.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +require 'minitest/autorun' + +require 'hello' + +class TestHello < Minitest::Test + def setup + @hello = Hello.new + end + + def test_hello + assert_equal 'Hello, World!', @hello.message + end +end diff --git a/test/basic-ruby/test.sh b/test/basic-ruby/test.sh new file mode 100755 index 0000000..45278b3 --- /dev/null +++ b/test/basic-ruby/test.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env bash +set -euo pipefail +source "$(dirname "$0")/../harness.sh" + +setup "basic-ruby" "3.3.4" + +run_test "Ruby version is correct" "ruby -v" "$IMAGE_TAG" +run_test "Container defaults to non-root user" "whoami" "devcontainer" +run_test "Non-root user is able to sudo" "sudo whoami" "root" + +run_test "The bundle is installed after creation" "bundle check" \ + "The Gemfile's dependencies are satisfied" +run_test "The template code satisfies Rubocop" "rubocop" "no offenses detected" +run_test "The example test runs" "rake test" "1 runs, 1 assertions, 0 failures, 0 errors, 0 skips" diff --git a/test/harness.sh b/test/harness.sh index b07f57d..9c61904 100644 --- a/test/harness.sh +++ b/test/harness.sh @@ -26,6 +26,9 @@ setup() { cp -R "$SRC_DIR"/../../src/"$TEMPLATE" $TEST_ROOT/ cp -R "$SRC_DIR"/../../test/"$TEMPLATE" $TEST_ROOT/ + # Ensure temporary directory is writable by the container + chmod -R 777 $TEST_ROOT + # Validate template is valid JSON before doing anything else and getting into a weird place jq . "$TEST_DIR"/devcontainer-template.json > /dev/null