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/.github/workflows/ci.yml b/.github/workflows/ci.yml index 24335fc..b8b8006 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,7 +14,7 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest, macos-latest] - ruby: ['3.1', '3.2', '3.3'] + ruby: ['3.4', '4.0'] rust: [stable] # Windows support is currently disabled due to rb-sys compatibility issues # include: @@ -76,7 +76,7 @@ jobs: - name: Set up Ruby uses: ruby/setup-ruby@v1 with: - ruby-version: '3.3' + ruby-version: '4.0' bundler-cache: true - name: Run RuboCop @@ -97,7 +97,7 @@ jobs: - name: Set up Ruby uses: ruby/setup-ruby@v1 with: - ruby-version: '3.3' + ruby-version: '4.0' bundler-cache: true - name: Install dependencies diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 81a7db9..bae7e33 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -14,7 +14,7 @@ jobs: strategy: matrix: ruby: - - '3.3' + - '4.0' steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5ada1a2..a87e0b7 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -18,7 +18,7 @@ jobs: - name: Set up Ruby uses: ruby/setup-ruby@v1 with: - ruby-version: '3.3' + ruby-version: '4.0' - name: Extract version from tag id: extract_version @@ -70,13 +70,13 @@ jobs: - name: Set up Ruby uses: ruby/setup-ruby@v1 with: - ruby-version: '3.3' + ruby-version: '4.0' bundler-cache: true - uses: oxidize-rb/actions/cross-gem@v1 with: platform: ${{ matrix.platform }} - ruby-versions: '3.1,3.2,3.3' + ruby-versions: '3.4,4.0' - name: Upload gem artifact uses: actions/upload-artifact@v4 @@ -95,7 +95,7 @@ jobs: - name: Set up Ruby uses: ruby/setup-ruby@v1 with: - ruby-version: '3.3' + ruby-version: '4.0' bundler-cache: true - name: Build source gem diff --git a/Cargo.lock b/Cargo.lock index b6f4cbc..728cdef 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -838,9 +838,9 @@ dependencies = [ [[package]] name = "magnus" -version = "0.6.4" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1597ef40aa8c36be098249e82c9a20cf7199278ac1c1a1a995eeead6a184479" +checksum = "3b36a5b126bbe97eb0d02d07acfeb327036c6319fd816139a49824a83b7f9012" dependencies = [ "magnus-macros", "rb-sys", @@ -850,9 +850,9 @@ dependencies = [ [[package]] name = "magnus-macros" -version = "0.6.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5968c820e2960565f647819f5928a42d6e874551cab9d88d75e3e0660d7f71e3" +checksum = "47607461fd8e1513cb4f2076c197d8092d921a1ea75bd08af97398f593751892" dependencies = [ "proc-macro2", "quote", @@ -1168,9 +1168,9 @@ dependencies = [ [[package]] name = "rb-sys-env" -version = "0.1.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a35802679f07360454b418a5d1735c89716bde01d35b1560fc953c1415a0b3bb" +checksum = "cca7ad6a7e21e72151d56fe2495a259b5670e204c3adac41ee7ef676ea08117a" [[package]] name = "redox_users" diff --git a/Gemfile.lock b/Gemfile.lock index dda273a..64daf38 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -97,7 +97,7 @@ GEM thor (1.4.0) unicode-display_width (3.2.0) unicode-emoji (~> 4.1) - unicode-emoji (4.1.0) + unicode-emoji (4.2.0) PLATFORMS arm64-darwin-23 diff --git a/docker/Dockerfile.test b/docker/Dockerfile.test new file mode 100644 index 0000000..6d8129b --- /dev/null +++ b/docker/Dockerfile.test @@ -0,0 +1,52 @@ +# 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 + +# Stage 1: Base image with build dependencies +FROM ruby:${RUBY_VERSION} AS builder + +# 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 dependency files first for layer caching +COPY rfmt.gemspec Gemfile Gemfile.lock ./ +COPY lib/rfmt/version.rb ./lib/rfmt/ + +# Copy source code +COPY . . + +# 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 + +# Copy installed gem from builder +COPY --from=builder /usr/local/bundle /usr/local/bundle + +# Verify installation +RUN ruby -e "require 'rfmt'; puts \"rfmt #{Rfmt::VERSION} loaded successfully on Ruby #{RUBY_VERSION}\"" + +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 new file mode 100644 index 0000000..52b251c --- /dev/null +++ b/docker/compose.test.yml @@ -0,0 +1,24 @@ +# Docker Compose for testing rfmt across multiple Ruby versions +# Usage: +# 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: + <<: *build-common + args: + RUBY_VERSION: "3.4" + image: rfmt-test:ruby-3.4 + + test-ruby-4.0: + build: + <<: *build-common + args: + RUBY_VERSION: "4.0" + image: rfmt-test:ruby-4.0 diff --git a/ext/rfmt/Cargo.toml b/ext/rfmt/Cargo.toml index 59cdd2a..cfbccbe 100644 --- a/ext/rfmt/Cargo.toml +++ b/ext/rfmt/Cargo.toml @@ -12,8 +12,8 @@ crate-type = ["cdylib"] [dependencies] # Ruby FFI -magnus = { version = "0.6.2" } -rb-sys = "0.9.117" +magnus = { version = "0.8.2" } +rb-sys = "0.9.124" # Serialization serde = { version = "1.0", features = ["derive"] } diff --git a/ext/rfmt/src/lib.rs b/ext/rfmt/src/lib.rs index bda75e9..51aa4c9 100644 --- a/ext/rfmt/src/lib.rs +++ b/ext/rfmt/src/lib.rs @@ -10,7 +10,7 @@ use policy::SecurityPolicy; use config::Config; use emitter::Emitter; -use magnus::{define_module, function, prelude::*, Error, Ruby}; +use magnus::{function, prelude::*, Error, Ruby}; use parser::{PrismAdapter, RubyParser}; fn format_ruby_code(ruby: &Ruby, source: String, json: String) -> Result { @@ -45,10 +45,10 @@ fn rust_version() -> String { } #[magnus::init] -fn init(_ruby: &Ruby) -> Result<(), Error> { +fn init(ruby: &Ruby) -> Result<(), Error> { logging::RfmtLogger::init(); - let module = define_module("Rfmt")?; + let module = ruby.define_module("Rfmt")?; module.define_singleton_method("format_code", function!(format_ruby_code, 2))?; module.define_singleton_method("parse_to_json", function!(parse_to_json, 1))?; diff --git a/scripts/test-gem-install.sh b/scripts/test-gem-install.sh new file mode 100755 index 0000000..e3bcfc3 --- /dev/null +++ b/scripts/test-gem-install.sh @@ -0,0 +1,88 @@ +#!/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 + +PROJECT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +DEFAULT_VERSIONS=("3.4" "4.0") + +readonly RED='\033[0;31m' +readonly GREEN='\033[0;32m' +readonly BLUE='\033[0;34m' +readonly NC='\033[0m' + +log_info() { echo -e "${BLUE}[INFO]${NC} $1"; } +log_success() { echo -e "${GREEN}[SUCCESS]${NC} $1"; } +log_error() { echo -e "${RED}[ERROR]${NC} $1"; } + +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}" + + if docker build \ + --build-arg RUBY_VERSION="${version}" \ + --target test \ + -f docker/Dockerfile.test \ + -t "$IMAGE_NAME" \ + . 2>&1; then + + log_success "Ruby ${version}: gem install succeeded!" + PASSED_VERSIONS+=("$version") + + log_info "Running verification tests..." + docker run --rm "$IMAGE_NAME" ruby -e " + require 'rfmt' + puts \" Version: #{Rfmt::VERSION}\" + puts \" Rust version: #{Rfmt.rust_version}\" + puts \" Ruby: #{RUBY_VERSION}\" + " && log_success "Ruby ${version}: verification passed!" + else + log_error "Ruby ${version}: gem install FAILED!" + FAILED_VERSIONS+=("$version") + fi + + echo "" +done + +echo "========================================" +echo " Summary" +echo "========================================" +echo "" + +[[ ${#PASSED_VERSIONS[@]} -gt 0 ]] && log_success "Passed: ${PASSED_VERSIONS[*]}" + +if [[ ${#FAILED_VERSIONS[@]} -gt 0 ]]; then + log_error "Failed: ${FAILED_VERSIONS[*]}" + echo "" + exit 1 +fi + +log_success "All tests passed!" +echo ""