Skip to content

Commit

Permalink
Merge pull request #6188 from dependabot/deivid-rodriguez/no-omnibus-prs
Browse files Browse the repository at this point in the history
Fix dependabot not updating gemspec dependencies loaded dynamically from other gemspecs
  • Loading branch information
deivid-rodriguez authored Nov 23, 2022
2 parents d053b1d + b901b50 commit 1615fce
Show file tree
Hide file tree
Showing 9 changed files with 243 additions and 69 deletions.
70 changes: 1 addition & 69 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -1,83 +1,15 @@
version: 2
updates:
- package-ecosystem: "bundler"
directory: "/common"
schedule:
interval: "weekly"
- package-ecosystem: "bundler"
directory: "/updater"
schedule:
interval: "weekly"

# Watch the per-ecosystem gemspecs
- package-ecosystem: "bundler"
directory: "/bundler"
schedule:
interval: "weekly"
- package-ecosystem: "bundler"
directory: "/cargo"
schedule:
interval: "weekly"
- package-ecosystem: "bundler"
directory: "/composer"
schedule:
interval: "weekly"
- package-ecosystem: "bundler"
directory: "/docker"
schedule:
interval: "weekly"
- package-ecosystem: "bundler"
directory: "/elm"
schedule:
interval: "weekly"
- package-ecosystem: "bundler"
directory: "/git_submodules"
schedule:
interval: "weekly"
- package-ecosystem: "bundler"
directory: "/github_actions"
schedule:
interval: "weekly"
- package-ecosystem: "bundler"
directory: "/go_modules"
schedule:
interval: "weekly"
- package-ecosystem: "bundler"
directory: "/gradle"
schedule:
interval: "weekly"
- package-ecosystem: "bundler"
directory: "/hex"
schedule:
interval: "weekly"
- package-ecosystem: "bundler"
directory: "/maven"
schedule:
interval: "weekly"
- package-ecosystem: "bundler"
directory: "/npm_and_yarn"
schedule:
interval: "weekly"
- package-ecosystem: "bundler"
directory: "/nuget"
schedule:
interval: "weekly"
# No ecosystem folders are watched because they roll up to the omnibus watcher
- package-ecosystem: "bundler"
directory: "/omnibus"
schedule:
interval: "weekly"
- package-ecosystem: "bundler"
directory: "/pub"
schedule:
interval: "weekly"
- package-ecosystem: "bundler"
directory: "/python"
schedule:
interval: "weekly"
- package-ecosystem: "bundler"
directory: "/terraform"
schedule:
interval: "weekly"

# Watch the per-ecosystem native helpers
- package-ecosystem: "composer"
Expand Down
5 changes: 5 additions & 0 deletions bundler/lib/dependabot/bundler/file_parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ class FileParser < Dependabot::FileParsers::Base
require "dependabot/file_parsers/base/dependency_set"
require "dependabot/bundler/file_parser/file_preparer"
require "dependabot/bundler/file_parser/gemfile_declaration_finder"
require "dependabot/bundler/file_parser/gemspec_declaration_finder"

def parse
dependency_set = DependencySet.new
Expand Down Expand Up @@ -87,7 +88,11 @@ def gemspec_dependencies
dependencies = DependencySet.new

gemspecs.each do |gemspec|
gemspec_declaration_finder = GemspecDeclarationFinder.new(gemspec: gemspec)

parsed_gemspec(gemspec).each do |dependency|
next unless gemspec_declaration_finder.gemspec_includes_dependency?(dependency)

dependencies <<
Dependency.new(
name: dependency.fetch("name"),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# frozen_string_literal: true

require "parser/current"

module Dependabot
module Bundler
class FileParser
# Checks whether a dependency is declared in a gemspec file
class GemspecDeclarationFinder
def initialize(gemspec:)
@gemspec = gemspec
@declaration_nodes = {}
end

def gemspec_includes_dependency?(dependency)
!declaration_node(dependency).nil?
end

private

attr_reader :gemspec

def parsed_gemspec
@parsed_gemspec ||= Parser::CurrentRuby.parse(gemspec.content)
end

def declaration_node(dependency)
return @declaration_nodes[dependency] if @declaration_nodes.key?(dependency)
return unless parsed_gemspec

@declaration_nodes[dependency] = nil
parsed_gemspec.children.any? do |node|
@declaration_nodes[dependency] = deep_search_for_gem(node, dependency)
end
@declaration_nodes[dependency]
end

def deep_search_for_gem(node, dependency)
return node if declares_targeted_gem?(node, dependency)
return unless node.is_a?(Parser::AST::Node)

declaration_node = nil
node.children.find do |child_node|
declaration_node = deep_search_for_gem(child_node, dependency)
end
declaration_node
end

def declares_targeted_gem?(node, dependency)
return false unless node.is_a?(Parser::AST::Node)

second_child = node.children[1]
allowed_declarations = %i(add_dependency add_runtime_dependency add_development_dependency)
return false unless allowed_declarations.include?(second_child)

node.children[2].children.first == dependency.fetch("name")
end
end
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# frozen_string_literal: true

require "spec_helper"
require "dependabot/dependency"
require "dependabot/dependency_file"
require "dependabot/bundler/file_parser/gemspec_declaration_finder"

RSpec.describe Dependabot::Bundler::FileParser::GemspecDeclarationFinder do
let(:checker) do
described_class.new(gemspec: gemspec)
end

let(:dependency) do
dep = ::Bundler::Dependency.new(dependency_name,
dependency_requirement_sting)
{
"name" => dep.name,
"requirement" => dep.requirement.to_s
}
end
let(:dependency_name) { "business" }
let(:dependency_requirement_sting) { "~> 1" }

let(:gemspec) { bundler_project_dependency_file("gemspec_loads_another", filename: "example.gemspec") }

describe "#gemspec_includes_dependency?" do
subject(:gemspec_includes_dependency) do
checker.gemspec_includes_dependency?(dependency)
end

context "when the file does not include the dependency" do
let(:dependency_name) { "dependabot-core" }
it { is_expected.to eq(false) }
end

context "when the file does include the dependency as `add_dependency`" do
let(:dependency_name) { "excon" }
it { is_expected.to eq(true) }
end

context "when the file does include the dependency as `add_runtime_dependency`" do
let(:dependency_name) { "bundler" }
it { is_expected.to eq(true) }
end

context "when the file does include the dependency as `add_development_dependency`" do
let(:dependency_name) { "webmock" }
it { is_expected.to eq(true) }
end

context "when the file loads the dependency dynamically" do
let(:dependency_name) { "rake" }
it { is_expected.to eq(false) }
end
end
end
14 changes: 14 additions & 0 deletions bundler/spec/dependabot/bundler/file_parser_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -714,6 +714,20 @@
end
end

context "with a gemspec that loads dependencies from another gemspec dynamically" do
let(:dependency_files) { bundler_project_dependency_files("gemspec_loads_another") }

describe "a development dependency loaded from an external gemspec" do
subject { dependencies.find { |d| d.name == "rake" } }

it "is only loaded with its own gemspec as requirement" do
expect(subject.name).to eq("rake")
expect(subject.requirements.size).to eq(1)
expect(subject.requirements.first[:file]).to eq("another.gemspec")
end
end
end

context "with a gemspec and Gemfile (no lockfile)" do
let(:dependency_files) { bundler_project_dependency_files("imports_gemspec_no_lockfile") }
its(:length) { is_expected.to eq(13) }
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# frozen_string_literal: true
Gem::Specification.new do |spec|
spec.name = "example"
spec.version = "0.9.3"
spec.summary = "Automated dependency management"
spec.description = "Core logic for updating a GitHub repos dependencies"

spec.author = "Dependabot"
spec.email = "support@dependabot.com"
spec.homepage = "https://github.com/hmarr/example"
spec.license = "MIT"

spec.require_path = "lib"
spec.files = Dir["CHANGELOG.md", "LICENSE.txt", "README.md",
"lib/**/*", "helpers/**/*"]

spec.required_ruby_version = ">= 2.4.0"
spec.required_rubygems_version = ">= 2.6.11"

spec.add_dependency 'business', '~> 1.0'
spec.add_dependency 'statesman', '= 1.0.0'
spec.add_development_dependency 'rake'
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# frozen_string_literal: true
Gem::Specification.new do |spec|
another_gemspec = Bundler.load_gemspec_uncached("another.gemspec")

spec.name = "example"
spec.version = "0.9.3"
spec.summary = "Automated dependency management"
spec.description = "Core logic for updating a GitHub repos dependencies"
spec.date = "2019-08-01"

spec.author = "Dependabot"
spec.email = "support@dependabot.com"
spec.homepage = "https://github.com/hmarr/example"
spec.license = "MIT"

spec.require_path = Dir["lib"]
spec.files = Dir["CHANGELOG.md", "LICENSE.txt", "README.md",
"lib/**/*", "helpers/**/*"]

spec.required_ruby_version = ">= 2.4.0"
spec.required_rubygems_version = ">= 2.6.11"

spec.add_runtime_dependency "bundler", ">= 1.12.0"
spec.add_dependency "excon", "~> 0.55"
spec.add_development_dependency "webmock", "~> 2.3.1"

another_gemspec.development_dependencies.each do |dep|
spec.add_development_dependency dep.name, *dep.requirement.as_list
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# frozen_string_literal: true
Gem::Specification.new do |spec|
spec.name = "example"
spec.version = "0.9.3"
spec.summary = "Automated dependency management"
spec.description = "Core logic for updating a GitHub repos dependencies"

spec.author = "Dependabot"
spec.email = "support@dependabot.com"
spec.homepage = "https://github.com/hmarr/example"
spec.license = "MIT"

spec.require_path = "lib"
spec.files = Dir["CHANGELOG.md", "LICENSE.txt", "README.md",
"lib/**/*", "helpers/**/*"]

spec.required_ruby_version = ">= 2.4.0"
spec.required_rubygems_version = ">= 2.6.11"

spec.add_dependency 'business', '~> 1.0'
spec.add_dependency 'statesman', '= 1.0.0'
spec.add_development_dependency 'rake'
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# frozen_string_literal: true
Gem::Specification.new do |spec|
another_gemspec = Bundler.load_gemspec_uncached("another.gemspec")

spec.name = "example"
spec.version = "0.9.3"
spec.summary = "Automated dependency management"
spec.description = "Core logic for updating a GitHub repos dependencies"
spec.date = "2019-08-01"

spec.author = "Dependabot"
spec.email = "support@dependabot.com"
spec.homepage = "https://github.com/hmarr/example"
spec.license = "MIT"

spec.require_path = Dir["lib"]
spec.files = Dir["CHANGELOG.md", "LICENSE.txt", "README.md",
"lib/**/*", "helpers/**/*"]

spec.required_ruby_version = ">= 2.4.0"
spec.required_rubygems_version = ">= 2.6.11"

spec.add_runtime_dependency "bundler", ">= 1.12.0"
spec.add_dependency "excon", "~> 0.55"
spec.add_development_dependency "webmock", "~> 2.3.1"

another_gemspec.development_dependencies.each do |dep|
spec.add_development_dependency dep.name, *dep.requirement.as_list
end
end

0 comments on commit 1615fce

Please sign in to comment.