Module: Dependabot::Bundler::Helpers

Extended by:
T::Helpers, T::Sig
Defined in:
lib/dependabot/bundler/helpers.rb

Constant Summary collapse

V1 =
"1"
V2 =
"2"
V4 =

Bundler 3 was intentionally skipped upstream — Bundler jumped from 2.7 directly to 4.0 to align its major version with RubyGems — so there is no V3 helper tree.

"4"
DEFAULT =
V2
BUNDLER_MAJOR_VERSION_REGEX =
/BUNDLED WITH\s+(?<version>\d+)\./m
RUBY_GEMFILE_REGEX =
/^ruby\s+['"]([^'"]+)['"]/
RUBY_GEMSPEC_REGEX =
/required_ruby_version\s+=\s+['"]([^'"]+)['"]/
GEMFILE =
"Gemfile"
GEMSPEC_EXTENSION =
".gemspec"
BUNDLER_GEM_NAME =
"bundler"
LANGUAGE =
"ruby"

Class Method Summary collapse

Class Method Details

.bundler_version(lockfile) ⇒ Object



29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# File 'lib/dependabot/bundler/helpers.rb', line 29

def self.bundler_version(lockfile)
  return DEFAULT unless lockfile

  if (matches = T.let(lockfile.content, T.nilable(String))&.match(BUNDLER_MAJOR_VERSION_REGEX))
    major = matches[:version].to_i
    if major >= 4
      V4
    elsif major >= 2
      V2
    else
      V1
    end
  else
    DEFAULT
  end
end

.combined_dependency_constraints(files, dependency_name) ⇒ Object



82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
# File 'lib/dependabot/bundler/helpers.rb', line 82

def self.combined_dependency_constraints(files, dependency_name)
  files.each_with_object([]) do |file, result|
    content = file.content
    next unless content

    # Select the appropriate regex based on file type and dependency name
    regex = if dependency_name == LANGUAGE
              ruby_version_regex(file.name)
            elsif file.name.end_with?(GEMFILE)
              gemfile_dependency_regex(dependency_name)
            elsif file.name.end_with?(GEMSPEC_EXTENSION)
              gemspec_dependency_regex(dependency_name)
            else
              next # Skip unsupported file types, including .ruby-version
            end

    # If regex is nil (unsupported for this file type), skip to the next file
    next unless regex

    # Extract constraints using the chosen regex
    result.concat(extract_constraints_from_file(content, regex))
  end.uniq
end

.dependency_requirement(dependency_name, files) ⇒ Object



64
65
66
67
68
69
70
71
72
73
74
75
76
# File 'lib/dependabot/bundler/helpers.rb', line 64

def self.dependency_requirement(dependency_name, files)
  constraints = combined_dependency_constraints(files, dependency_name)
  return nil if constraints.empty?

  combined_constraint = constraints.join(", ")

  Dependabot::Bundler::Requirement.new(combined_constraint)
rescue StandardError => e
  Dependabot.logger.error(
    "Failed to create Requirement with constraints '#{constraints&.join(', ')}': #{e.message}"
  )
  nil
end

.detected_bundler_version(lockfile) ⇒ Object



47
48
49
50
51
52
53
54
55
# File 'lib/dependabot/bundler/helpers.rb', line 47

def self.detected_bundler_version(lockfile)
  return "unknown" unless lockfile

  if (matches = T.let(lockfile.content, T.nilable(String))&.match(BUNDLER_MAJOR_VERSION_REGEX))
    matches[:version].to_i.to_s
  else
    "unspecified"
  end
end

.extract_constraints_from_file(content, regex) ⇒ Object



130
131
132
133
134
135
136
# File 'lib/dependabot/bundler/helpers.rb', line 130

def self.extract_constraints_from_file(content, regex)
  if content.match(regex)
    content.scan(regex).flatten
  else
    []
  end
end

.gemfile_dependency_regex(dependency_name) ⇒ Object



118
119
120
# File 'lib/dependabot/bundler/helpers.rb', line 118

def self.gemfile_dependency_regex(dependency_name)
  /gem\s+['"]#{Regexp.escape(dependency_name)}['"](?:,\s*['"]([^'"]+)['"])?/
end

.gemspec_dependency_regex(dependency_name) ⇒ Object



124
125
126
# File 'lib/dependabot/bundler/helpers.rb', line 124

def self.gemspec_dependency_regex(dependency_name)
  /add_(?:runtime_)?dependency\s+['"]#{Regexp.escape(dependency_name)}['"],\s*['"]([^'"]+)['"]/
end

.ruby_version_regex(file_name) ⇒ Object



108
109
110
111
112
113
114
# File 'lib/dependabot/bundler/helpers.rb', line 108

def self.ruby_version_regex(file_name)
  if file_name.end_with?(GEMFILE)
    RUBY_GEMFILE_REGEX
  elsif file_name.end_with?(GEMSPEC_EXTENSION)
    RUBY_GEMSPEC_REGEX
  end
end