From fa44f515c24f2a712ddecaad5c23e3b6e9bb6460 Mon Sep 17 00:00:00 2001 From: fujitani sora Date: Sun, 25 Jan 2026 18:10:26 +0900 Subject: [PATCH 1/2] feat: install test env on docker compose --- .dockerignore | 76 ++++++++++++++++++++++++ docker/Dockerfile.test | 39 +++++++++++++ docker/compose.test.yml | 21 +++++++ scripts/test-gem-install.sh | 113 ++++++++++++++++++++++++++++++++++++ 4 files changed, 249 insertions(+) create mode 100644 .dockerignore create mode 100644 docker/Dockerfile.test create mode 100644 docker/compose.test.yml create mode 100755 scripts/test-gem-install.sh diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..418aaee --- /dev/null +++ b/.dockerignore @@ -0,0 +1,76 @@ +# Docker ignore file for rfmt gem installation testing +# This file excludes unnecessary files from the Docker build context + +# Nix development environment cache (very large) +.nix-gem-home/ +.nix-cargo-home/ +.nix-rustup-home/ +.nix-bin/ +.nix-wrappers/ +.dependencies-installed +.direnv/ + +# Rust build artifacts +target/ +ext/*/target/ + +# Git +.git/ +.gitignore + +# IDE and editor files +.vscode/ +.idea/ +*.swp +*.swo +.DS_Store +**/.DS_Store + +# Development tools +.lefthook/ +lefthook-local.yml +.rspec_status +.rake_tasks~ + +# Documentation and build artifacts +coverage/ +doc/ +_yardoc/ +pkg/ +spec/reports/ +tmp/ +build/ +docspriv/ + +# Test results +**/benchmark/results.json +**/benchmarks/*.json +**/test_results/*.json + +# Claude development tools +.claude/ + +# Ruby version manager files +.ruby-version + +# Other development files +.serena/ +.ruby-lsp/ +.ruby-lsp.bak/ + +# Docker files (avoid recursive context) +docker/ +!docker/Dockerfile.test + +# Scripts (not needed in container) +scripts/ + +# Keep these (explicitly) +!Gemfile +!Gemfile.lock +!rfmt.gemspec +!Rakefile +!lib/ +!ext/ +!bin/ +!spec/ diff --git a/docker/Dockerfile.test b/docker/Dockerfile.test new file mode 100644 index 0000000..56fcb03 --- /dev/null +++ b/docker/Dockerfile.test @@ -0,0 +1,39 @@ +# Dockerfile for testing rfmt gem installation across Ruby versions +# Usage: docker build --build-arg RUBY_VERSION=4.0 -f docker/Dockerfile.test -t rfmt-test:4.0 . + +ARG RUBY_VERSION=3.4 +FROM ruby:${RUBY_VERSION} + +# Install Rust toolchain +RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain stable +ENV PATH="/root/.cargo/bin:${PATH}" + +# Install build dependencies +RUN apt-get update && apt-get install -y \ + build-essential \ + libclang-dev \ + clang \ + && rm -rf /var/lib/apt/lists/* + +WORKDIR /rfmt + +# Copy only gemspec and necessary files first (for layer caching) +COPY rfmt.gemspec Gemfile Gemfile.lock ./ +COPY lib/rfmt/version.rb ./lib/rfmt/ + +# Copy source code +COPY . . + +# Build the gem +RUN gem build rfmt.gemspec + +# Test gem installation (the critical test!) +RUN gem install ./rfmt-*.gem --no-document + +# Verify installation works +RUN ruby -e "require 'rfmt'; puts \"rfmt #{Rfmt::VERSION} loaded successfully on Ruby #{RUBY_VERSION}\"" + +# Run a simple format test +RUN echo 'def hello() puts "world" end' | rfmt || echo "CLI test completed" + +CMD ["ruby", "-e", "require 'rfmt'; puts \"rfmt #{Rfmt::VERSION} ready on Ruby #{RUBY_VERSION}\""] diff --git a/docker/compose.test.yml b/docker/compose.test.yml new file mode 100644 index 0000000..b2146af --- /dev/null +++ b/docker/compose.test.yml @@ -0,0 +1,21 @@ +# Docker Compose for testing rfmt across multiple Ruby versions +# Usage: +# docker compose -f docker/docker-compose.test.yml build +# docker compose -f docker/docker-compose.test.yml up + +services: + test-ruby-3.4: + build: + context: .. + dockerfile: docker/Dockerfile.test + args: + RUBY_VERSION: "3.4" + image: rfmt-test:ruby-3.4 + + test-ruby-4.0: + build: + context: .. + dockerfile: docker/Dockerfile.test + args: + RUBY_VERSION: "4.0" + image: rfmt-test:ruby-4.0 diff --git a/scripts/test-gem-install.sh b/scripts/test-gem-install.sh new file mode 100755 index 0000000..e55a733 --- /dev/null +++ b/scripts/test-gem-install.sh @@ -0,0 +1,113 @@ +#!/usr/bin/env bash +# Test gem installation across multiple Ruby versions using Docker +# +# Usage: +# ./scripts/test-gem-install.sh # Test all versions (3.4, 4.0) +# ./scripts/test-gem-install.sh 4.0 # Test specific version +# ./scripts/test-gem-install.sh 3.4 4.0 # Test multiple specific versions + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_DIR="$(dirname "$SCRIPT_DIR")" + +# Default Ruby versions to test +DEFAULT_VERSIONS=("3.4" "4.0") + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +log_info() { + echo -e "${BLUE}[INFO]${NC} $1" +} + +log_success() { + echo -e "${GREEN}[SUCCESS]${NC} $1" +} + +log_error() { + echo -e "${RED}[ERROR]${NC} $1" +} + +log_warn() { + echo -e "${YELLOW}[WARN]${NC} $1" +} + +# Parse arguments +if [ $# -eq 0 ]; then + VERSIONS=("${DEFAULT_VERSIONS[@]}") +else + VERSIONS=("$@") +fi + +echo "" +echo "========================================" +echo " rfmt gem installation test" +echo "========================================" +echo "" +log_info "Testing Ruby versions: ${VERSIONS[*]}" +echo "" + +cd "$PROJECT_DIR" + +FAILED_VERSIONS=() +PASSED_VERSIONS=() + +for version in "${VERSIONS[@]}"; do + echo "----------------------------------------" + log_info "Testing Ruby ${version}..." + echo "----------------------------------------" + + IMAGE_NAME="rfmt-test:ruby-${version}" + + # Build the test image + if docker build \ + --build-arg RUBY_VERSION="${version}" \ + -f docker/Dockerfile.test \ + -t "$IMAGE_NAME" \ + . 2>&1; then + + log_success "Ruby ${version}: gem install succeeded!" + PASSED_VERSIONS+=("$version") + + # Run additional verification + log_info "Running verification tests..." + if docker run --rm "$IMAGE_NAME" ruby -e " + require 'rfmt' + puts \" Version: #{Rfmt::VERSION}\" + puts \" Rust version: #{Rfmt.rust_version}\" + puts \" Ruby: #{RUBY_VERSION}\" + "; then + log_success "Ruby ${version}: verification passed!" + else + log_warn "Ruby ${version}: verification had issues" + fi + else + log_error "Ruby ${version}: gem install FAILED!" + FAILED_VERSIONS+=("$version") + fi + + echo "" +done + +echo "========================================" +echo " Summary" +echo "========================================" +echo "" + +if [ ${#PASSED_VERSIONS[@]} -gt 0 ]; then + log_success "Passed: ${PASSED_VERSIONS[*]}" +fi + +if [ ${#FAILED_VERSIONS[@]} -gt 0 ]; then + log_error "Failed: ${FAILED_VERSIONS[*]}" + echo "" + exit 1 +fi + +log_success "All tests passed!" +echo "" From 88379ff228f6f3be82ed7ff464de8bcc5ec8b717 Mon Sep 17 00:00:00 2001 From: fujitani sora Date: Sun, 25 Jan 2026 18:19:59 +0900 Subject: [PATCH 2/2] refactor: optimize Docker build with multi-stage and caching - Multi-stage build (builder/runtime/test stages) - Minimal Rust profile for faster installs - YAML anchors for DRY compose config - Simplified test script with modern bash Co-Authored-By: Claude --- docker/Dockerfile.test | 39 ++++++++++++++++++--------- docker/compose.test.yml | 15 ++++++----- scripts/test-gem-install.sh | 53 ++++++++++--------------------------- 3 files changed, 49 insertions(+), 58 deletions(-) diff --git a/docker/Dockerfile.test b/docker/Dockerfile.test index 56fcb03..6d8129b 100644 --- a/docker/Dockerfile.test +++ b/docker/Dockerfile.test @@ -2,38 +2,51 @@ # Usage: docker build --build-arg RUBY_VERSION=4.0 -f docker/Dockerfile.test -t rfmt-test:4.0 . ARG RUBY_VERSION=3.4 -FROM ruby:${RUBY_VERSION} -# Install Rust toolchain -RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain stable -ENV PATH="/root/.cargo/bin:${PATH}" +# Stage 1: Base image with build dependencies +FROM ruby:${RUBY_VERSION} AS builder -# Install build dependencies -RUN apt-get update && apt-get install -y \ +# Install Rust and build dependencies in a single layer +RUN apt-get update && apt-get install -y --no-install-recommends \ build-essential \ libclang-dev \ clang \ + curl \ + ca-certificates \ + && curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain stable --profile minimal \ && rm -rf /var/lib/apt/lists/* +ENV PATH="/root/.cargo/bin:${PATH}" + WORKDIR /rfmt -# Copy only gemspec and necessary files first (for layer caching) +# Copy dependency files first for layer caching COPY rfmt.gemspec Gemfile Gemfile.lock ./ COPY lib/rfmt/version.rb ./lib/rfmt/ # Copy source code COPY . . -# Build the gem -RUN gem build rfmt.gemspec +# Build and install the gem +RUN gem build rfmt.gemspec \ + && gem install ./rfmt-*.gem --no-document + +# Stage 2: Runtime image (smaller, no build tools) +FROM ruby:${RUBY_VERSION}-slim AS runtime -# Test gem installation (the critical test!) -RUN gem install ./rfmt-*.gem --no-document +# Copy installed gem from builder +COPY --from=builder /usr/local/bundle /usr/local/bundle -# Verify installation works +# Verify installation RUN ruby -e "require 'rfmt'; puts \"rfmt #{Rfmt::VERSION} loaded successfully on Ruby #{RUBY_VERSION}\"" -# Run a simple format test +CMD ["ruby", "-e", "require 'rfmt'; puts \"rfmt #{Rfmt::VERSION} ready on Ruby #{RUBY_VERSION}\""] + +# Stage 3: Test stage (includes verification) +FROM builder AS test + +# Run verification tests +RUN ruby -e "require 'rfmt'; puts \"rfmt #{Rfmt::VERSION} loaded successfully on Ruby #{RUBY_VERSION}\"" RUN echo 'def hello() puts "world" end' | rfmt || echo "CLI test completed" CMD ["ruby", "-e", "require 'rfmt'; puts \"rfmt #{Rfmt::VERSION} ready on Ruby #{RUBY_VERSION}\""] diff --git a/docker/compose.test.yml b/docker/compose.test.yml index b2146af..52b251c 100644 --- a/docker/compose.test.yml +++ b/docker/compose.test.yml @@ -1,21 +1,24 @@ # Docker Compose for testing rfmt across multiple Ruby versions # Usage: -# docker compose -f docker/docker-compose.test.yml build -# docker compose -f docker/docker-compose.test.yml up +# docker compose -f docker/compose.test.yml build +# docker compose -f docker/compose.test.yml up + +x-build-common: &build-common + context: .. + dockerfile: docker/Dockerfile.test + target: test services: test-ruby-3.4: build: - context: .. - dockerfile: docker/Dockerfile.test + <<: *build-common args: RUBY_VERSION: "3.4" image: rfmt-test:ruby-3.4 test-ruby-4.0: build: - context: .. - dockerfile: docker/Dockerfile.test + <<: *build-common args: RUBY_VERSION: "4.0" image: rfmt-test:ruby-4.0 diff --git a/scripts/test-gem-install.sh b/scripts/test-gem-install.sh index e55a733..e3bcfc3 100755 --- a/scripts/test-gem-install.sh +++ b/scripts/test-gem-install.sh @@ -8,37 +8,19 @@ set -euo pipefail -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -PROJECT_DIR="$(dirname "$SCRIPT_DIR")" - -# Default Ruby versions to test +PROJECT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" DEFAULT_VERSIONS=("3.4" "4.0") -# Colors for output -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -BLUE='\033[0;34m' -NC='\033[0m' # No Color - -log_info() { - echo -e "${BLUE}[INFO]${NC} $1" -} - -log_success() { - echo -e "${GREEN}[SUCCESS]${NC} $1" -} +readonly RED='\033[0;31m' +readonly GREEN='\033[0;32m' +readonly BLUE='\033[0;34m' +readonly NC='\033[0m' -log_error() { - echo -e "${RED}[ERROR]${NC} $1" -} +log_info() { echo -e "${BLUE}[INFO]${NC} $1"; } +log_success() { echo -e "${GREEN}[SUCCESS]${NC} $1"; } +log_error() { echo -e "${RED}[ERROR]${NC} $1"; } -log_warn() { - echo -e "${YELLOW}[WARN]${NC} $1" -} - -# Parse arguments -if [ $# -eq 0 ]; then +if [[ $# -eq 0 ]]; then VERSIONS=("${DEFAULT_VERSIONS[@]}") else VERSIONS=("$@") @@ -64,9 +46,9 @@ for version in "${VERSIONS[@]}"; do IMAGE_NAME="rfmt-test:ruby-${version}" - # Build the test image if docker build \ --build-arg RUBY_VERSION="${version}" \ + --target test \ -f docker/Dockerfile.test \ -t "$IMAGE_NAME" \ . 2>&1; then @@ -74,18 +56,13 @@ for version in "${VERSIONS[@]}"; do log_success "Ruby ${version}: gem install succeeded!" PASSED_VERSIONS+=("$version") - # Run additional verification log_info "Running verification tests..." - if docker run --rm "$IMAGE_NAME" ruby -e " + docker run --rm "$IMAGE_NAME" ruby -e " require 'rfmt' puts \" Version: #{Rfmt::VERSION}\" puts \" Rust version: #{Rfmt.rust_version}\" puts \" Ruby: #{RUBY_VERSION}\" - "; then - log_success "Ruby ${version}: verification passed!" - else - log_warn "Ruby ${version}: verification had issues" - fi + " && log_success "Ruby ${version}: verification passed!" else log_error "Ruby ${version}: gem install FAILED!" FAILED_VERSIONS+=("$version") @@ -99,11 +76,9 @@ echo " Summary" echo "========================================" echo "" -if [ ${#PASSED_VERSIONS[@]} -gt 0 ]; then - log_success "Passed: ${PASSED_VERSIONS[*]}" -fi +[[ ${#PASSED_VERSIONS[@]} -gt 0 ]] && log_success "Passed: ${PASSED_VERSIONS[*]}" -if [ ${#FAILED_VERSIONS[@]} -gt 0 ]; then +if [[ ${#FAILED_VERSIONS[@]} -gt 0 ]]; then log_error "Failed: ${FAILED_VERSIONS[*]}" echo "" exit 1