Class: Railstest::DockerManager

Inherits:
Object
  • Object
show all
Defined in:
lib/railstest/docker_manager.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(ruby_version:, rails_version:, gem_path: nil, database: nil) ⇒ DockerManager

Returns a new instance of DockerManager.



10
11
12
13
14
15
16
17
18
# File 'lib/railstest/docker_manager.rb', line 10

def initialize(ruby_version:, rails_version:, gem_path: nil, database: nil)
  @ruby_version = ruby_version
  @rails_version = rails_version
  @gem_path = gem_path
  @database = database
  @target_gem_name = nil
  validate_docker!
  validate_gem_path! if target_gem_mode?
end

Instance Attribute Details

#databaseObject (readonly)

Returns the value of attribute database.



8
9
10
# File 'lib/railstest/docker_manager.rb', line 8

def database
  @database
end

#gem_pathObject (readonly)

Returns the value of attribute gem_path.



8
9
10
# File 'lib/railstest/docker_manager.rb', line 8

def gem_path
  @gem_path
end

#rails_versionObject (readonly)

Returns the value of attribute rails_version.



8
9
10
# File 'lib/railstest/docker_manager.rb', line 8

def rails_version
  @rails_version
end

#ruby_versionObject (readonly)

Returns the value of attribute ruby_version.



8
9
10
# File 'lib/railstest/docker_manager.rb', line 8

def ruby_version
  @ruby_version
end

Instance Method Details

#build_imageObject



20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# File 'lib/railstest/docker_manager.rb', line 20

def build_image
  puts "Building Docker image for Ruby #{ruby_version} and Rails #{rails_version}..."
  dockerfile_path = File.join(Dir.tmpdir, "railstest_dockerfile_#{Process.pid}")

  begin
    File.write(dockerfile_path, generate_dockerfile)

    build_args = [
      '--build-arg', "RUBY_VERSION=#{ruby_version}"
    ]

    if target_gem_mode?
      build_args << '--build-arg' << "TARGET_GEM_NAME=#{target_gem_name}"
      build_args << '--build-arg' << "RAILS_VERSION=#{rails_version}"
    else
      # Local mode: find actual gemfile and pass it
      gemfile = find_gemfile_for_version
      build_args << '--build-arg' << "GEMFILE_PATH=gemfiles/#{gemfile}"
    end

    success = system(
      'docker', 'build', '.',
      '-f', dockerfile_path,
      *build_args,
      '-t', image_name
    )

    raise Error, 'Docker build failed' unless success
  ensure
    FileUtils.rm_f(dockerfile_path)
  end
end

#expanded_gem_pathObject



92
93
94
95
96
# File 'lib/railstest/docker_manager.rb', line 92

def expanded_gem_path
  return nil unless target_gem_mode?

  File.expand_path(gem_path)
end

#find_gemfile_for_versionObject

Raises:



103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
# File 'lib/railstest/docker_manager.rb', line 103

def find_gemfile_for_version
  # Find actual gemfile in gemfiles/ directory that matches the Rails version
  # Handles various naming conventions: rails_7.0.gemfile, Gemfile.rails-5.2-rc1, etc.
  return nil if target_gem_mode?

  gemfiles_dir = 'gemfiles'
  return nil unless File.directory?(gemfiles_dir)

  # Look for any file containing the version pattern, exclude lock files
  matching_files = Dir.glob(File.join(gemfiles_dir, '*')).select do |f|
    basename = File.basename(f)
    rails_version_regex = rails_version.gsub('.', '[.-]')
    basename =~ /#{rails_version_regex}/ && basename !~ /\.lock$/
  end

  raise Error, "No gemfile found in gemfiles/ for Rails #{rails_version}" if matching_files.empty?

  if matching_files.length > 1
    # Prefer exact patterns, warn about multiple matches
    puts "⚠️  Warning: Multiple gemfiles found for Rails #{rails_version}:"
    matching_files.each { |f| puts "   - #{File.basename(f)}" }
    puts "   Using: #{File.basename(matching_files.first)}"
  end

  File.basename(matching_files.first)
end

#image_nameObject



72
73
74
75
76
77
78
79
# File 'lib/railstest/docker_manager.rb', line 72

def image_name
  @image_name ||= begin
    base = File.basename(Dir.pwd).downcase.gsub(/[^a-z0-9._-]/, '-')
    ruby_tag = ruby_version.to_s.gsub(/[^a-z0-9._-]/, '-')
    rails_tag = rails_version.to_s.gsub(/[^a-z0-9._-]/, '-')
    "#{base}-ruby#{ruby_tag}-rails#{rails_tag}-tests"
  end
end

#rails_version_for_gemfileObject



98
99
100
101
# File 'lib/railstest/docker_manager.rb', line 98

def rails_version_for_gemfile
  # Convert dotted format to underscore for gemfile paths (e.g., 7.1 -> 7_1)
  rails_version.tr('.', '_')
end

#run_command(command, env_vars: {}, volumes: [], workdir: nil) ⇒ Object



53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
# File 'lib/railstest/docker_manager.rb', line 53

def run_command(command, env_vars: {}, volumes: [], workdir: nil)
  docker_cmd = ['docker', 'run', '--rm', '--network=host']

  env_vars.each do |key, value|
    docker_cmd << '-e' << "#{key}=#{value}"
  end

  volumes.each do |volume|
    docker_cmd << '-v' << volume
  end

  docker_cmd << '-w' << workdir if workdir

  docker_cmd << image_name
  docker_cmd.concat(Array(command))

  system(*docker_cmd)
end

#target_gem_mode?Boolean

Returns:

  • (Boolean)


81
82
83
# File 'lib/railstest/docker_manager.rb', line 81

def target_gem_mode?
  !gem_path.nil? && !gem_path.empty?
end

#target_gem_nameObject



85
86
87
88
89
90
# File 'lib/railstest/docker_manager.rb', line 85

def target_gem_name
  return @target_gem_name if @target_gem_name
  return nil unless target_gem_mode?

  @target_gem_name = extract_gem_name(gem_path)
end