dependabot-bundler 0.396.0 → 0.397.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/helpers/v2/spec/functions/file_parser_spec.rb +68 -67
- data/helpers/v4/spec/functions/file_parser_spec.rb +68 -67
- data/lib/dependabot/bundler/file_parser/gemfile_declaration_finder.rb +22 -23
- data/lib/dependabot/bundler/file_parser/gemspec_declaration_finder.rb +18 -18
- data/lib/dependabot/bundler/file_parser/helper_dependencies.rb +116 -0
- data/lib/dependabot/bundler/file_parser.rb +21 -18
- data/lib/dependabot/bundler/package/package_details_fetcher.rb +49 -2
- data/lib/dependabot/bundler/update_checker.rb +6 -6
- metadata +5 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 43d670382313df98efe56ca75538daf1ef494b65bdb4dfa87e79bcb2f5ae3fdc
|
|
4
|
+
data.tar.gz: 46faf363b2a8e37874cc5516425abf14bf31a50c12573fac6c837d78aba6a182
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 24a9688ad6a277c68a763f39a298e13b9c2a8a64fea6ecc57d2414ea55555d9cfb49ec5feeddc1ad99a99ce475625ef18a4f35ee83a1bb2e26666fecb166ae58
|
|
7
|
+
data.tar.gz: 9b8e28ec1cc08b2d841112f29d75e3d7545be0563dd69dd1f0a67cf693cf66759e175245cf88b658229ca90878aaaa53cc13ea40c5e400ae23a2738efdde0150
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
require "native_spec_helper"
|
|
5
5
|
require "shared_contexts"
|
|
6
|
+
require "json"
|
|
6
7
|
|
|
7
8
|
RSpec.describe Functions::FileParser do
|
|
8
9
|
include_context "when in a temporary bundler directory"
|
|
@@ -23,87 +24,87 @@ RSpec.describe Functions::FileParser do
|
|
|
23
24
|
let(:project_name) { "gemfile" }
|
|
24
25
|
|
|
25
26
|
it "parses gemfile" do
|
|
26
|
-
|
|
27
|
+
expected = [
|
|
27
28
|
{
|
|
28
|
-
groups
|
|
29
|
-
name
|
|
30
|
-
requirement
|
|
31
|
-
source
|
|
32
|
-
type
|
|
29
|
+
"groups" => ["default"],
|
|
30
|
+
"name" => "business",
|
|
31
|
+
"requirement" => "~> 1.4.0",
|
|
32
|
+
"source" => nil,
|
|
33
|
+
"type" => "runtime"
|
|
33
34
|
},
|
|
34
35
|
{
|
|
35
|
-
groups
|
|
36
|
-
name
|
|
37
|
-
requirement
|
|
38
|
-
source
|
|
39
|
-
type
|
|
36
|
+
"groups" => ["default"],
|
|
37
|
+
"name" => "statesman",
|
|
38
|
+
"requirement" => "~> 1.2.0",
|
|
39
|
+
"source" => nil,
|
|
40
|
+
"type" => "runtime"
|
|
40
41
|
}
|
|
41
42
|
]
|
|
42
|
-
expect(parsed_gemfile).
|
|
43
|
+
expect(JSON.parse(JSON.dump(parsed_gemfile))).to eq(expected)
|
|
43
44
|
end
|
|
44
45
|
|
|
45
46
|
context "with a git source" do
|
|
46
47
|
let(:project_name) { "git_source" }
|
|
47
48
|
|
|
48
49
|
it "parses gemfile" do
|
|
49
|
-
|
|
50
|
+
expected = [
|
|
50
51
|
{
|
|
51
|
-
groups
|
|
52
|
-
name
|
|
53
|
-
requirement
|
|
54
|
-
source
|
|
55
|
-
branch
|
|
56
|
-
ref
|
|
57
|
-
type
|
|
58
|
-
url
|
|
52
|
+
"groups" => ["default"],
|
|
53
|
+
"name" => "business",
|
|
54
|
+
"requirement" => "~> 1.6.0",
|
|
55
|
+
"source" => {
|
|
56
|
+
"branch" => nil,
|
|
57
|
+
"ref" => "a1b78a9",
|
|
58
|
+
"type" => "git",
|
|
59
|
+
"url" => "git@github.com:dependabot-fixtures/business"
|
|
59
60
|
},
|
|
60
|
-
type
|
|
61
|
+
"type" => "runtime"
|
|
61
62
|
},
|
|
62
63
|
{
|
|
63
|
-
groups
|
|
64
|
-
name
|
|
65
|
-
requirement
|
|
66
|
-
source
|
|
67
|
-
type
|
|
64
|
+
"groups" => ["default"],
|
|
65
|
+
"name" => "statesman",
|
|
66
|
+
"requirement" => "~> 1.2.0",
|
|
67
|
+
"source" => nil,
|
|
68
|
+
"type" => "runtime"
|
|
68
69
|
},
|
|
69
70
|
{
|
|
70
|
-
groups
|
|
71
|
-
name
|
|
72
|
-
requirement
|
|
73
|
-
source
|
|
74
|
-
branch
|
|
75
|
-
ref
|
|
76
|
-
type
|
|
77
|
-
url
|
|
71
|
+
"groups" => ["default"],
|
|
72
|
+
"name" => "prius",
|
|
73
|
+
"requirement" => ">= 0",
|
|
74
|
+
"source" => {
|
|
75
|
+
"branch" => nil,
|
|
76
|
+
"ref" => nil,
|
|
77
|
+
"type" => "git",
|
|
78
|
+
"url" => "https://github.com/dependabot-fixtures/prius"
|
|
78
79
|
},
|
|
79
|
-
type
|
|
80
|
+
"type" => "runtime"
|
|
80
81
|
},
|
|
81
82
|
{
|
|
82
|
-
groups
|
|
83
|
-
name
|
|
84
|
-
requirement
|
|
85
|
-
source
|
|
86
|
-
branch
|
|
87
|
-
ref
|
|
88
|
-
type
|
|
89
|
-
url
|
|
83
|
+
"groups" => ["default"],
|
|
84
|
+
"name" => "que",
|
|
85
|
+
"requirement" => ">= 0",
|
|
86
|
+
"source" => {
|
|
87
|
+
"branch" => nil,
|
|
88
|
+
"ref" => "v0.11.6",
|
|
89
|
+
"type" => "git",
|
|
90
|
+
"url" => "git@github.com:dependabot-fixtures/que"
|
|
90
91
|
},
|
|
91
|
-
type
|
|
92
|
+
"type" => "runtime"
|
|
92
93
|
},
|
|
93
94
|
{
|
|
94
|
-
groups
|
|
95
|
-
name
|
|
96
|
-
requirement
|
|
97
|
-
source
|
|
98
|
-
branch
|
|
99
|
-
ref
|
|
100
|
-
type
|
|
101
|
-
url
|
|
95
|
+
"groups" => ["default"],
|
|
96
|
+
"name" => "uk_phone_numbers",
|
|
97
|
+
"requirement" => ">= 0",
|
|
98
|
+
"source" => {
|
|
99
|
+
"branch" => nil,
|
|
100
|
+
"ref" => nil,
|
|
101
|
+
"type" => "git",
|
|
102
|
+
"url" => "https://github.com/dependabot-fixtures/uk_phone_numbers"
|
|
102
103
|
},
|
|
103
|
-
type
|
|
104
|
+
"type" => "runtime"
|
|
104
105
|
}
|
|
105
106
|
]
|
|
106
|
-
expect(parsed_gemfile).
|
|
107
|
+
expect(JSON.parse(JSON.dump(parsed_gemfile))).to eq(expected)
|
|
107
108
|
end
|
|
108
109
|
end
|
|
109
110
|
end
|
|
@@ -118,23 +119,23 @@ RSpec.describe Functions::FileParser do
|
|
|
118
119
|
let(:project_name) { "gemfile_exact" }
|
|
119
120
|
|
|
120
121
|
it "parses gemspec" do
|
|
121
|
-
|
|
122
|
+
expected = [
|
|
122
123
|
{
|
|
123
|
-
groups
|
|
124
|
-
name
|
|
125
|
-
requirement
|
|
126
|
-
source
|
|
127
|
-
type
|
|
124
|
+
"groups" => nil,
|
|
125
|
+
"name" => "business",
|
|
126
|
+
"requirement" => "= 1.0.0",
|
|
127
|
+
"source" => nil,
|
|
128
|
+
"type" => "runtime"
|
|
128
129
|
},
|
|
129
130
|
{
|
|
130
|
-
groups
|
|
131
|
-
name
|
|
132
|
-
requirement
|
|
133
|
-
source
|
|
134
|
-
type
|
|
131
|
+
"groups" => nil,
|
|
132
|
+
"name" => "statesman",
|
|
133
|
+
"requirement" => "= 1.0.0",
|
|
134
|
+
"source" => nil,
|
|
135
|
+
"type" => "runtime"
|
|
135
136
|
}
|
|
136
137
|
]
|
|
137
|
-
expect(parsed_gemspec).
|
|
138
|
+
expect(JSON.parse(JSON.dump(parsed_gemspec))).to eq(expected)
|
|
138
139
|
end
|
|
139
140
|
end
|
|
140
141
|
end
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
require "native_spec_helper"
|
|
5
5
|
require "shared_contexts"
|
|
6
|
+
require "json"
|
|
6
7
|
|
|
7
8
|
RSpec.describe Functions::FileParser do
|
|
8
9
|
include_context "when in a temporary bundler directory"
|
|
@@ -23,87 +24,87 @@ RSpec.describe Functions::FileParser do
|
|
|
23
24
|
let(:project_name) { "gemfile" }
|
|
24
25
|
|
|
25
26
|
it "parses gemfile" do
|
|
26
|
-
|
|
27
|
+
expected = [
|
|
27
28
|
{
|
|
28
|
-
groups
|
|
29
|
-
name
|
|
30
|
-
requirement
|
|
31
|
-
source
|
|
32
|
-
type
|
|
29
|
+
"groups" => ["default"],
|
|
30
|
+
"name" => "business",
|
|
31
|
+
"requirement" => "~> 1.4.0",
|
|
32
|
+
"source" => nil,
|
|
33
|
+
"type" => "runtime"
|
|
33
34
|
},
|
|
34
35
|
{
|
|
35
|
-
groups
|
|
36
|
-
name
|
|
37
|
-
requirement
|
|
38
|
-
source
|
|
39
|
-
type
|
|
36
|
+
"groups" => ["default"],
|
|
37
|
+
"name" => "statesman",
|
|
38
|
+
"requirement" => "~> 1.2.0",
|
|
39
|
+
"source" => nil,
|
|
40
|
+
"type" => "runtime"
|
|
40
41
|
}
|
|
41
42
|
]
|
|
42
|
-
expect(parsed_gemfile).
|
|
43
|
+
expect(JSON.parse(JSON.dump(parsed_gemfile))).to eq(expected)
|
|
43
44
|
end
|
|
44
45
|
|
|
45
46
|
context "with a git source" do
|
|
46
47
|
let(:project_name) { "git_source" }
|
|
47
48
|
|
|
48
49
|
it "parses gemfile" do
|
|
49
|
-
|
|
50
|
+
expected = [
|
|
50
51
|
{
|
|
51
|
-
groups
|
|
52
|
-
name
|
|
53
|
-
requirement
|
|
54
|
-
source
|
|
55
|
-
branch
|
|
56
|
-
ref
|
|
57
|
-
type
|
|
58
|
-
url
|
|
52
|
+
"groups" => ["default"],
|
|
53
|
+
"name" => "business",
|
|
54
|
+
"requirement" => "~> 1.6.0",
|
|
55
|
+
"source" => {
|
|
56
|
+
"branch" => nil,
|
|
57
|
+
"ref" => "a1b78a9",
|
|
58
|
+
"type" => "git",
|
|
59
|
+
"url" => "git@github.com:dependabot-fixtures/business"
|
|
59
60
|
},
|
|
60
|
-
type
|
|
61
|
+
"type" => "runtime"
|
|
61
62
|
},
|
|
62
63
|
{
|
|
63
|
-
groups
|
|
64
|
-
name
|
|
65
|
-
requirement
|
|
66
|
-
source
|
|
67
|
-
type
|
|
64
|
+
"groups" => ["default"],
|
|
65
|
+
"name" => "statesman",
|
|
66
|
+
"requirement" => "~> 1.2.0",
|
|
67
|
+
"source" => nil,
|
|
68
|
+
"type" => "runtime"
|
|
68
69
|
},
|
|
69
70
|
{
|
|
70
|
-
groups
|
|
71
|
-
name
|
|
72
|
-
requirement
|
|
73
|
-
source
|
|
74
|
-
branch
|
|
75
|
-
ref
|
|
76
|
-
type
|
|
77
|
-
url
|
|
71
|
+
"groups" => ["default"],
|
|
72
|
+
"name" => "prius",
|
|
73
|
+
"requirement" => ">= 0",
|
|
74
|
+
"source" => {
|
|
75
|
+
"branch" => nil,
|
|
76
|
+
"ref" => nil,
|
|
77
|
+
"type" => "git",
|
|
78
|
+
"url" => "https://github.com/dependabot-fixtures/prius"
|
|
78
79
|
},
|
|
79
|
-
type
|
|
80
|
+
"type" => "runtime"
|
|
80
81
|
},
|
|
81
82
|
{
|
|
82
|
-
groups
|
|
83
|
-
name
|
|
84
|
-
requirement
|
|
85
|
-
source
|
|
86
|
-
branch
|
|
87
|
-
ref
|
|
88
|
-
type
|
|
89
|
-
url
|
|
83
|
+
"groups" => ["default"],
|
|
84
|
+
"name" => "que",
|
|
85
|
+
"requirement" => ">= 0",
|
|
86
|
+
"source" => {
|
|
87
|
+
"branch" => nil,
|
|
88
|
+
"ref" => "v0.11.6",
|
|
89
|
+
"type" => "git",
|
|
90
|
+
"url" => "git@github.com:dependabot-fixtures/que"
|
|
90
91
|
},
|
|
91
|
-
type
|
|
92
|
+
"type" => "runtime"
|
|
92
93
|
},
|
|
93
94
|
{
|
|
94
|
-
groups
|
|
95
|
-
name
|
|
96
|
-
requirement
|
|
97
|
-
source
|
|
98
|
-
branch
|
|
99
|
-
ref
|
|
100
|
-
type
|
|
101
|
-
url
|
|
95
|
+
"groups" => ["default"],
|
|
96
|
+
"name" => "uk_phone_numbers",
|
|
97
|
+
"requirement" => ">= 0",
|
|
98
|
+
"source" => {
|
|
99
|
+
"branch" => nil,
|
|
100
|
+
"ref" => nil,
|
|
101
|
+
"type" => "git",
|
|
102
|
+
"url" => "https://github.com/dependabot-fixtures/uk_phone_numbers"
|
|
102
103
|
},
|
|
103
|
-
type
|
|
104
|
+
"type" => "runtime"
|
|
104
105
|
}
|
|
105
106
|
]
|
|
106
|
-
expect(parsed_gemfile).
|
|
107
|
+
expect(JSON.parse(JSON.dump(parsed_gemfile))).to eq(expected)
|
|
107
108
|
end
|
|
108
109
|
end
|
|
109
110
|
end
|
|
@@ -118,23 +119,23 @@ RSpec.describe Functions::FileParser do
|
|
|
118
119
|
let(:project_name) { "gemfile_exact" }
|
|
119
120
|
|
|
120
121
|
it "parses gemspec" do
|
|
121
|
-
|
|
122
|
+
expected = [
|
|
122
123
|
{
|
|
123
|
-
groups
|
|
124
|
-
name
|
|
125
|
-
requirement
|
|
126
|
-
source
|
|
127
|
-
type
|
|
124
|
+
"groups" => nil,
|
|
125
|
+
"name" => "business",
|
|
126
|
+
"requirement" => "= 1.0.0",
|
|
127
|
+
"source" => nil,
|
|
128
|
+
"type" => "runtime"
|
|
128
129
|
},
|
|
129
130
|
{
|
|
130
|
-
groups
|
|
131
|
-
name
|
|
132
|
-
requirement
|
|
133
|
-
source
|
|
134
|
-
type
|
|
131
|
+
"groups" => nil,
|
|
132
|
+
"name" => "statesman",
|
|
133
|
+
"requirement" => "= 1.0.0",
|
|
134
|
+
"source" => nil,
|
|
135
|
+
"type" => "runtime"
|
|
135
136
|
}
|
|
136
137
|
]
|
|
137
|
-
expect(parsed_gemspec).
|
|
138
|
+
expect(JSON.parse(JSON.dump(parsed_gemspec))).to eq(expected)
|
|
138
139
|
end
|
|
139
140
|
end
|
|
140
141
|
end
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
require "prism"
|
|
5
5
|
require "sorbet-runtime"
|
|
6
6
|
|
|
7
|
-
require "dependabot/
|
|
7
|
+
require "dependabot/bundler/file_parser"
|
|
8
8
|
|
|
9
9
|
module Dependabot
|
|
10
10
|
module Bundler
|
|
@@ -16,21 +16,20 @@ module Dependabot
|
|
|
16
16
|
sig { params(gemfile: Dependabot::DependencyFile).void }
|
|
17
17
|
def initialize(gemfile:)
|
|
18
18
|
@gemfile = gemfile
|
|
19
|
-
@declaration_nodes = T.let({}, T::Hash[
|
|
19
|
+
@declaration_nodes = T.let({}, T::Hash[String, T.nilable(Prism::Node)])
|
|
20
20
|
end
|
|
21
21
|
|
|
22
|
-
sig { params(
|
|
23
|
-
def gemfile_includes_dependency?(
|
|
24
|
-
!declaration_node(
|
|
22
|
+
sig { params(dependency_name: String).returns(T::Boolean) }
|
|
23
|
+
def gemfile_includes_dependency?(dependency_name)
|
|
24
|
+
!declaration_node(dependency_name).nil?
|
|
25
25
|
end
|
|
26
26
|
|
|
27
27
|
# rubocop:disable Metrics/PerceivedComplexity
|
|
28
|
-
sig { params(
|
|
29
|
-
def enhanced_req_string(
|
|
30
|
-
return unless gemfile_includes_dependency?(
|
|
28
|
+
sig { params(dependency_name: String, fallback_string: String).returns(T.nilable(String)) }
|
|
29
|
+
def enhanced_req_string(dependency_name, fallback_string)
|
|
30
|
+
return unless gemfile_includes_dependency?(dependency_name)
|
|
31
31
|
|
|
32
|
-
|
|
33
|
-
call_node = declaration_node(dependency)
|
|
32
|
+
call_node = declaration_node(dependency_name)
|
|
34
33
|
return fallback_string unless call_node.is_a?(Prism::CallNode)
|
|
35
34
|
|
|
36
35
|
req_nodes = call_node.arguments&.arguments&.[](1..-1) || []
|
|
@@ -63,32 +62,32 @@ module Dependabot
|
|
|
63
62
|
)
|
|
64
63
|
end
|
|
65
64
|
|
|
66
|
-
sig { params(
|
|
67
|
-
def declaration_node(
|
|
68
|
-
return @declaration_nodes[
|
|
65
|
+
sig { params(dependency_name: String).returns(T.nilable(Prism::Node)) }
|
|
66
|
+
def declaration_node(dependency_name)
|
|
67
|
+
return @declaration_nodes[dependency_name] if @declaration_nodes.key?(dependency_name)
|
|
69
68
|
return unless parsed_gemfile
|
|
70
69
|
|
|
71
|
-
@declaration_nodes[
|
|
70
|
+
@declaration_nodes[dependency_name] = nil
|
|
72
71
|
T.must(parsed_gemfile).child_nodes.any? do |node|
|
|
73
|
-
@declaration_nodes[
|
|
72
|
+
@declaration_nodes[dependency_name] = deep_search_for_gem(node, dependency_name)
|
|
74
73
|
end
|
|
75
|
-
@declaration_nodes[
|
|
74
|
+
@declaration_nodes[dependency_name]
|
|
76
75
|
end
|
|
77
76
|
|
|
78
77
|
sig do
|
|
79
78
|
params(
|
|
80
79
|
node: T.nilable(Prism::Node),
|
|
81
|
-
|
|
80
|
+
dependency_name: String
|
|
82
81
|
)
|
|
83
82
|
.returns(T.nilable(Prism::Node))
|
|
84
83
|
end
|
|
85
|
-
def deep_search_for_gem(node,
|
|
84
|
+
def deep_search_for_gem(node, dependency_name)
|
|
86
85
|
return unless node.is_a?(Prism::Node)
|
|
87
|
-
return node if declares_targeted_gem?(node,
|
|
86
|
+
return node if declares_targeted_gem?(node, dependency_name)
|
|
88
87
|
|
|
89
88
|
declaration_node = T.let(nil, T.nilable(Prism::Node))
|
|
90
89
|
node.child_nodes.find do |child_node|
|
|
91
|
-
declaration_node = deep_search_for_gem(child_node,
|
|
90
|
+
declaration_node = deep_search_for_gem(child_node, dependency_name)
|
|
92
91
|
end
|
|
93
92
|
declaration_node
|
|
94
93
|
end
|
|
@@ -96,18 +95,18 @@ module Dependabot
|
|
|
96
95
|
sig do
|
|
97
96
|
params(
|
|
98
97
|
node: T.nilable(Prism::Node),
|
|
99
|
-
|
|
98
|
+
dependency_name: String
|
|
100
99
|
)
|
|
101
100
|
.returns(T::Boolean)
|
|
102
101
|
end
|
|
103
|
-
def declares_targeted_gem?(node,
|
|
102
|
+
def declares_targeted_gem?(node, dependency_name)
|
|
104
103
|
return false unless node.is_a?(Prism::CallNode)
|
|
105
104
|
return false unless node.name == :gem
|
|
106
105
|
|
|
107
106
|
gem_name_node = node.arguments&.arguments&.first
|
|
108
107
|
return false unless gem_name_node.is_a?(Prism::StringNode)
|
|
109
108
|
|
|
110
|
-
gem_name_node.unescaped ==
|
|
109
|
+
gem_name_node.unescaped == dependency_name
|
|
111
110
|
end
|
|
112
111
|
end
|
|
113
112
|
end
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
require "prism"
|
|
5
5
|
require "sorbet-runtime"
|
|
6
6
|
|
|
7
|
-
require "dependabot/
|
|
7
|
+
require "dependabot/bundler/file_parser"
|
|
8
8
|
|
|
9
9
|
module Dependabot
|
|
10
10
|
module Bundler
|
|
@@ -16,12 +16,12 @@ module Dependabot
|
|
|
16
16
|
sig { params(gemspec: Dependabot::DependencyFile).void }
|
|
17
17
|
def initialize(gemspec:)
|
|
18
18
|
@gemspec = gemspec
|
|
19
|
-
@declaration_nodes = T.let({}, T::Hash[
|
|
19
|
+
@declaration_nodes = T.let({}, T::Hash[String, T.nilable(Prism::Node)])
|
|
20
20
|
end
|
|
21
21
|
|
|
22
|
-
sig { params(
|
|
23
|
-
def gemspec_includes_dependency?(
|
|
24
|
-
!declaration_node(
|
|
22
|
+
sig { params(dependency_name: String).returns(T::Boolean) }
|
|
23
|
+
def gemspec_includes_dependency?(dependency_name)
|
|
24
|
+
!declaration_node(dependency_name).nil?
|
|
25
25
|
end
|
|
26
26
|
|
|
27
27
|
private
|
|
@@ -34,32 +34,32 @@ module Dependabot
|
|
|
34
34
|
@parsed_gemspec ||= T.let(Prism.parse(gemspec.content).value, T.nilable(Prism::Node))
|
|
35
35
|
end
|
|
36
36
|
|
|
37
|
-
sig { params(
|
|
38
|
-
def declaration_node(
|
|
39
|
-
return @declaration_nodes[
|
|
37
|
+
sig { params(dependency_name: String).returns(T.nilable(Prism::Node)) }
|
|
38
|
+
def declaration_node(dependency_name)
|
|
39
|
+
return @declaration_nodes[dependency_name] if @declaration_nodes.key?(dependency_name)
|
|
40
40
|
return unless parsed_gemspec
|
|
41
41
|
|
|
42
|
-
@declaration_nodes[
|
|
42
|
+
@declaration_nodes[dependency_name] = nil
|
|
43
43
|
T.must(parsed_gemspec).child_nodes.any? do |node|
|
|
44
|
-
@declaration_nodes[
|
|
44
|
+
@declaration_nodes[dependency_name] = deep_search_for_gem(node, dependency_name)
|
|
45
45
|
end
|
|
46
|
-
@declaration_nodes[
|
|
46
|
+
@declaration_nodes[dependency_name]
|
|
47
47
|
end
|
|
48
48
|
|
|
49
|
-
sig { params(node: T.nilable(Prism::Node),
|
|
50
|
-
def deep_search_for_gem(node,
|
|
49
|
+
sig { params(node: T.nilable(Prism::Node), dependency_name: String).returns(T.nilable(Prism::Node)) }
|
|
50
|
+
def deep_search_for_gem(node, dependency_name)
|
|
51
51
|
return unless node.is_a?(Prism::Node)
|
|
52
|
-
return T.cast(node, Prism::CallNode) if declares_targeted_gem?(node,
|
|
52
|
+
return T.cast(node, Prism::CallNode) if declares_targeted_gem?(node, dependency_name)
|
|
53
53
|
|
|
54
54
|
declaration_node = T.let(nil, T.nilable(Prism::Node))
|
|
55
55
|
node.child_nodes.find do |child_node|
|
|
56
|
-
declaration_node = deep_search_for_gem(child_node,
|
|
56
|
+
declaration_node = deep_search_for_gem(child_node, dependency_name)
|
|
57
57
|
end
|
|
58
58
|
declaration_node
|
|
59
59
|
end
|
|
60
60
|
|
|
61
|
-
sig { params(node: T.nilable(Prism::Node),
|
|
62
|
-
def declares_targeted_gem?(node,
|
|
61
|
+
sig { params(node: T.nilable(Prism::Node), dependency_name: String).returns(T::Boolean) }
|
|
62
|
+
def declares_targeted_gem?(node, dependency_name)
|
|
63
63
|
return false unless node.is_a?(Prism::CallNode)
|
|
64
64
|
|
|
65
65
|
second_child = node.name
|
|
@@ -69,7 +69,7 @@ module Dependabot
|
|
|
69
69
|
gem_name_node = node.arguments&.child_nodes&.first
|
|
70
70
|
return false unless gem_name_node.is_a?(Prism::StringNode)
|
|
71
71
|
|
|
72
|
-
gem_name_node.unescaped ==
|
|
72
|
+
gem_name_node.unescaped == dependency_name
|
|
73
73
|
end
|
|
74
74
|
end
|
|
75
75
|
end
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# typed: strong
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "sorbet-runtime"
|
|
5
|
+
require "dependabot/errors"
|
|
6
|
+
require "dependabot/bundler/file_parser"
|
|
7
|
+
|
|
8
|
+
module Dependabot
|
|
9
|
+
module Bundler
|
|
10
|
+
class FileParser < Dependabot::FileParsers::Base
|
|
11
|
+
module HelperDependencies
|
|
12
|
+
extend T::Sig
|
|
13
|
+
|
|
14
|
+
Source = T.type_alias { T::Hash[Symbol, T.nilable(String)] }
|
|
15
|
+
ObjectHash = T.type_alias { T::Hash[String, Object] }
|
|
16
|
+
|
|
17
|
+
class GemfileDependency < T::ImmutableStruct
|
|
18
|
+
const :name, String
|
|
19
|
+
const :requirement, String
|
|
20
|
+
const :groups, T::Array[String]
|
|
21
|
+
const :source, T.nilable(Source)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
class GemspecDependency < T::ImmutableStruct
|
|
25
|
+
const :name, String
|
|
26
|
+
const :requirement, String
|
|
27
|
+
const :type, String
|
|
28
|
+
const :source, T.nilable(Source)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Both helper versions serialize these fields in Functions::FileParser#serialize_bundler_dependency.
|
|
32
|
+
sig { params(result: Object, file: Dependabot::DependencyFile).returns(T::Array[GemfileDependency]) }
|
|
33
|
+
def self.from_gemfile_result(result, file:)
|
|
34
|
+
context = "parsed_gemfile result for #{file.path}"
|
|
35
|
+
array(result, context).each_with_index.map do |value, index|
|
|
36
|
+
entry_context = "#{context}[#{index}]"
|
|
37
|
+
entry = object(value, entry_context)
|
|
38
|
+
|
|
39
|
+
GemfileDependency.new(
|
|
40
|
+
name: string(entry["name"], "#{entry_context}.name"),
|
|
41
|
+
requirement: string(entry["requirement"], "#{entry_context}.requirement"),
|
|
42
|
+
groups: strings(entry["groups"], "#{entry_context}.groups"),
|
|
43
|
+
source: source(entry, entry_context)
|
|
44
|
+
)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
sig { params(result: Object, file: Dependabot::DependencyFile).returns(T::Array[GemspecDependency]) }
|
|
49
|
+
def self.from_gemspec_result(result, file:)
|
|
50
|
+
context = "parsed_gemspec result for #{file.path}"
|
|
51
|
+
array(result, context).each_with_index.map do |value, index|
|
|
52
|
+
entry_context = "#{context}[#{index}]"
|
|
53
|
+
entry = object(value, entry_context)
|
|
54
|
+
|
|
55
|
+
GemspecDependency.new(
|
|
56
|
+
name: string(entry["name"], "#{entry_context}.name"),
|
|
57
|
+
requirement: string(entry["requirement"], "#{entry_context}.requirement"),
|
|
58
|
+
type: string(entry["type"], "#{entry_context}.type"),
|
|
59
|
+
source: source(entry, entry_context)
|
|
60
|
+
)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
sig { params(entry: ObjectHash, context: String).returns(T.nilable(Source)) }
|
|
65
|
+
def self.source(entry, context)
|
|
66
|
+
raise DependencyFileNotEvaluatable, "#{context}.source must be present" unless entry.key?("source")
|
|
67
|
+
|
|
68
|
+
value = entry["source"]
|
|
69
|
+
return if value.nil?
|
|
70
|
+
|
|
71
|
+
fields = object(value, "#{context}.source")
|
|
72
|
+
string(fields["type"], "#{context}.source.type")
|
|
73
|
+
fields.to_h do |key, field|
|
|
74
|
+
[key.to_sym, field.nil? ? nil : string(field, "#{context}.source.#{key}")]
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
private_class_method :source
|
|
78
|
+
|
|
79
|
+
sig { params(value: Object, context: String).returns(ObjectHash) }
|
|
80
|
+
def self.object(value, context)
|
|
81
|
+
raise DependencyFileNotEvaluatable, "#{context} must be an object" unless value.is_a?(Hash)
|
|
82
|
+
|
|
83
|
+
value.to_h do |raw_key, raw_value|
|
|
84
|
+
key = T.cast(raw_key, Object)
|
|
85
|
+
raise DependencyFileNotEvaluatable, "#{context} keys must be strings" unless key.is_a?(String)
|
|
86
|
+
|
|
87
|
+
[key, T.cast(raw_value, Object)]
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
private_class_method :object
|
|
91
|
+
|
|
92
|
+
sig { params(value: Object, context: String).returns(T::Array[Object]) }
|
|
93
|
+
def self.array(value, context)
|
|
94
|
+
raise DependencyFileNotEvaluatable, "#{context} must be an array" unless value.is_a?(Array)
|
|
95
|
+
|
|
96
|
+
value.map { |entry| T.cast(entry, Object) }
|
|
97
|
+
end
|
|
98
|
+
private_class_method :array
|
|
99
|
+
|
|
100
|
+
sig { params(value: Object, context: String).returns(String) }
|
|
101
|
+
def self.string(value, context)
|
|
102
|
+
return value if value.is_a?(String)
|
|
103
|
+
|
|
104
|
+
raise DependencyFileNotEvaluatable, "#{context} must be a string"
|
|
105
|
+
end
|
|
106
|
+
private_class_method :string
|
|
107
|
+
|
|
108
|
+
sig { params(value: Object, context: String).returns(T::Array[String]) }
|
|
109
|
+
def self.strings(value, context)
|
|
110
|
+
array(value, context).each_with_index.map { |entry, index| string(entry, "#{context}[#{index}]") }
|
|
111
|
+
end
|
|
112
|
+
private_class_method :strings
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
end
|
|
@@ -22,6 +22,7 @@ module Dependabot
|
|
|
22
22
|
extend T::Sig
|
|
23
23
|
|
|
24
24
|
require "dependabot/file_parsers/base/dependency_set"
|
|
25
|
+
require "dependabot/bundler/file_parser/helper_dependencies"
|
|
25
26
|
require "dependabot/bundler/file_parser/file_preparer"
|
|
26
27
|
require "dependabot/bundler/file_parser/gemfile_declaration_finder"
|
|
27
28
|
require "dependabot/bundler/file_parser/gemspec_declaration_finder"
|
|
@@ -121,15 +122,15 @@ module Dependabot
|
|
|
121
122
|
gemfile_declaration_finder = GemfileDeclarationFinder.new(gemfile: file)
|
|
122
123
|
|
|
123
124
|
parsed_gemfile.each do |dep|
|
|
124
|
-
next unless gemfile_declaration_finder.gemfile_includes_dependency?(dep)
|
|
125
|
+
next unless gemfile_declaration_finder.gemfile_includes_dependency?(dep.name)
|
|
125
126
|
|
|
126
127
|
dependencies << Dependency.new(
|
|
127
|
-
name: dep.
|
|
128
|
-
version: dependency_version(dep.
|
|
128
|
+
name: dep.name,
|
|
129
|
+
version: dependency_version(dep.name)&.to_s,
|
|
129
130
|
requirements: [{
|
|
130
|
-
requirement: gemfile_declaration_finder.enhanced_req_string(dep),
|
|
131
|
-
groups: dep.
|
|
132
|
-
source: dep.
|
|
131
|
+
requirement: gemfile_declaration_finder.enhanced_req_string(dep.name, dep.requirement),
|
|
132
|
+
groups: dep.groups.map(&:to_sym),
|
|
133
|
+
source: dep.source&.dup,
|
|
133
134
|
file: file.name
|
|
134
135
|
}],
|
|
135
136
|
package_manager: "bundler"
|
|
@@ -141,7 +142,7 @@ module Dependabot
|
|
|
141
142
|
end
|
|
142
143
|
|
|
143
144
|
sig { returns(DependencySet) }
|
|
144
|
-
def gemspec_dependencies
|
|
145
|
+
def gemspec_dependencies
|
|
145
146
|
@gemspec_dependencies = T.let(@gemspec_dependencies, T.nilable(DependencySet))
|
|
146
147
|
return @gemspec_dependencies if @gemspec_dependencies
|
|
147
148
|
|
|
@@ -154,19 +155,19 @@ module Dependabot
|
|
|
154
155
|
gemspec_declaration_finder = GemspecDeclarationFinder.new(gemspec: gemspec)
|
|
155
156
|
|
|
156
157
|
parsed_gemspec(gemspec).each do |dependency|
|
|
157
|
-
next unless gemspec_declaration_finder.gemspec_includes_dependency?(dependency)
|
|
158
|
+
next unless gemspec_declaration_finder.gemspec_includes_dependency?(dependency.name)
|
|
158
159
|
|
|
159
160
|
queue << Dependency.new(
|
|
160
|
-
name: dependency.
|
|
161
|
-
version: dependency_version(dependency.
|
|
161
|
+
name: dependency.name,
|
|
162
|
+
version: dependency_version(dependency.name)&.to_s,
|
|
162
163
|
requirements: [{
|
|
163
|
-
requirement: dependency.
|
|
164
|
-
groups: if dependency.
|
|
164
|
+
requirement: dependency.requirement,
|
|
165
|
+
groups: if dependency.type == "runtime"
|
|
165
166
|
["runtime"]
|
|
166
167
|
else
|
|
167
168
|
["development"]
|
|
168
169
|
end,
|
|
169
|
-
source: dependency.
|
|
170
|
+
source: dependency.source&.dup,
|
|
170
171
|
file: gemspec.name
|
|
171
172
|
}],
|
|
172
173
|
package_manager: "bundler"
|
|
@@ -206,7 +207,7 @@ module Dependabot
|
|
|
206
207
|
dependencies
|
|
207
208
|
end
|
|
208
209
|
|
|
209
|
-
sig { returns(T::Array[
|
|
210
|
+
sig { returns(T::Array[HelperDependencies::GemfileDependency]) }
|
|
210
211
|
def parsed_gemfile
|
|
211
212
|
@parsed_gemfile ||= T.let(
|
|
212
213
|
SharedHelpers.in_a_temporary_repo_directory(
|
|
@@ -215,7 +216,7 @@ module Dependabot
|
|
|
215
216
|
) do
|
|
216
217
|
write_temporary_dependency_files
|
|
217
218
|
|
|
218
|
-
NativeHelpers.run_bundler_subprocess(
|
|
219
|
+
result = NativeHelpers.run_bundler_subprocess(
|
|
219
220
|
bundler_version: bundler_version,
|
|
220
221
|
function: "parsed_gemfile",
|
|
221
222
|
options: options,
|
|
@@ -225,8 +226,9 @@ module Dependabot
|
|
|
225
226
|
dir: Dir.pwd
|
|
226
227
|
}
|
|
227
228
|
)
|
|
229
|
+
HelperDependencies.from_gemfile_result(result, file: T.must(gemfile))
|
|
228
230
|
end,
|
|
229
|
-
T.nilable(T::Array[
|
|
231
|
+
T.nilable(T::Array[HelperDependencies::GemfileDependency])
|
|
230
232
|
)
|
|
231
233
|
rescue SharedHelpers::HelperSubprocessFailed => e
|
|
232
234
|
handle_eval_error(e) if e.error_class == "JSON::ParserError"
|
|
@@ -242,9 +244,9 @@ module Dependabot
|
|
|
242
244
|
raise Dependabot::DependencyFileNotEvaluatable, msg
|
|
243
245
|
end
|
|
244
246
|
|
|
245
|
-
sig { params(file: Dependabot::DependencyFile).returns(T::Array[
|
|
247
|
+
sig { params(file: Dependabot::DependencyFile).returns(T::Array[HelperDependencies::GemspecDependency]) }
|
|
246
248
|
def parsed_gemspec(file)
|
|
247
|
-
NativeHelpers.run_bundler_subprocess(
|
|
249
|
+
result = NativeHelpers.run_bundler_subprocess(
|
|
248
250
|
bundler_version: bundler_version,
|
|
249
251
|
function: "parsed_gemspec",
|
|
250
252
|
options: options,
|
|
@@ -254,6 +256,7 @@ module Dependabot
|
|
|
254
256
|
dir: Dir.pwd
|
|
255
257
|
}
|
|
256
258
|
)
|
|
259
|
+
HelperDependencies.from_gemspec_result(result, file: file)
|
|
257
260
|
rescue SharedHelpers::HelperSubprocessFailed => e
|
|
258
261
|
msg = e.error_class + " with message: " + e.message
|
|
259
262
|
raise Dependabot::DependencyFileNotEvaluatable, msg
|
|
@@ -21,6 +21,7 @@ module Dependabot
|
|
|
21
21
|
include Dependabot::Bundler::UpdateChecker::SharedBundlerHelpers
|
|
22
22
|
|
|
23
23
|
RELEASES_URL = "%s/api/v1/versions/%s.json"
|
|
24
|
+
COMPACT_INDEX_URL = "%s/info/%s"
|
|
24
25
|
GEM_URL = "%s/gems/%s.gem"
|
|
25
26
|
PACKAGE_TYPE = "gem"
|
|
26
27
|
PACKAGE_LANGUAGE = "ruby"
|
|
@@ -142,7 +143,7 @@ module Dependabot
|
|
|
142
143
|
error_msg = "Failed to fetch versions for '#{dependency.name}' from '#{registry_url}'. " \
|
|
143
144
|
"Status: #{response.status}"
|
|
144
145
|
log_error(error_msg)
|
|
145
|
-
return
|
|
146
|
+
return fetch_compact_index_response(registry_url)
|
|
146
147
|
end
|
|
147
148
|
|
|
148
149
|
return handle_empty_response(registry_url) if response.body.nil? || response.body.strip.empty?
|
|
@@ -150,6 +151,52 @@ module Dependabot
|
|
|
150
151
|
parse_rubygems_response(response, registry_url)
|
|
151
152
|
end
|
|
152
153
|
|
|
154
|
+
sig { params(registry_url: String).returns(Dependabot::Package::PackageDetails) }
|
|
155
|
+
def fetch_compact_index_response(registry_url)
|
|
156
|
+
response = Dependabot::RegistryClient.get(
|
|
157
|
+
url: format(COMPACT_INDEX_URL, registry_url, dependency.name),
|
|
158
|
+
headers: { "Accept" => "text/plain" }
|
|
159
|
+
)
|
|
160
|
+
return package_details([]) unless response.status == 200
|
|
161
|
+
|
|
162
|
+
lines = response.body.to_s.lines(chomp: true).drop_while { |line| line != "---" }.drop(1)
|
|
163
|
+
releases = lines.filter_map { |line| compact_index_release(line, registry_url) }
|
|
164
|
+
package_details(releases)
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
sig do
|
|
168
|
+
params(line: String, registry_url: String).returns(T.nilable(Dependabot::Package::PackageRelease))
|
|
169
|
+
end
|
|
170
|
+
def compact_index_release(line, registry_url)
|
|
171
|
+
version_and_platform = line.split(" ", 2).first
|
|
172
|
+
return nil unless version_and_platform && line.include?("|")
|
|
173
|
+
|
|
174
|
+
version = T.must(version_and_platform.split("-", 2).first)
|
|
175
|
+
return nil unless Dependabot::Bundler::Version.correct?(version)
|
|
176
|
+
|
|
177
|
+
metadata = line.partition("|").last.split(",").to_h do |field|
|
|
178
|
+
key, _, value = field.partition(":")
|
|
179
|
+
[key, value]
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
package_release(
|
|
183
|
+
version: version,
|
|
184
|
+
released_at: compact_index_release_date(metadata["created_at"]),
|
|
185
|
+
downloads: 0,
|
|
186
|
+
url: format(GEM_URL, registry_url, "#{dependency.name}-#{version_and_platform}"),
|
|
187
|
+
ruby_version: metadata["ruby"]&.tr("&", ",")
|
|
188
|
+
)
|
|
189
|
+
rescue Gem::Requirement::BadRequirementError
|
|
190
|
+
nil
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
sig { params(created_at: T.nilable(String)).returns(T.nilable(Time)) }
|
|
194
|
+
def compact_index_release_date(created_at)
|
|
195
|
+
Time.iso8601(created_at) if created_at
|
|
196
|
+
rescue ArgumentError
|
|
197
|
+
nil
|
|
198
|
+
end
|
|
199
|
+
|
|
153
200
|
sig do
|
|
154
201
|
params(response: Excon::Response, registry_url: String)
|
|
155
202
|
.returns(Dependabot::Package::PackageDetails)
|
|
@@ -269,7 +316,7 @@ module Dependabot
|
|
|
269
316
|
sig do
|
|
270
317
|
params(
|
|
271
318
|
version: String,
|
|
272
|
-
released_at: Time,
|
|
319
|
+
released_at: T.nilable(Time),
|
|
273
320
|
downloads: Integer,
|
|
274
321
|
url: String,
|
|
275
322
|
ruby_version: T.nilable(String),
|
|
@@ -216,10 +216,10 @@ module Dependabot
|
|
|
216
216
|
git_commit_checker.git_dependency?
|
|
217
217
|
end
|
|
218
218
|
|
|
219
|
-
sig { params(version: Dependabot::Bundler::Version).returns(T
|
|
219
|
+
sig { params(version: Dependabot::Bundler::Version).returns(T::Boolean) }
|
|
220
220
|
def resolvable?(version)
|
|
221
|
-
@resolvable ||= T.let({}, T.nilable(T::Hash[
|
|
222
|
-
return @resolvable
|
|
221
|
+
@resolvable ||= T.let({}, T.nilable(T::Hash[Dependabot::Bundler::Version, T::Boolean]))
|
|
222
|
+
return @resolvable.fetch(version) if @resolvable.key?(version)
|
|
223
223
|
|
|
224
224
|
@resolvable[version] =
|
|
225
225
|
begin
|
|
@@ -239,10 +239,10 @@ module Dependabot
|
|
|
239
239
|
end
|
|
240
240
|
end
|
|
241
241
|
|
|
242
|
-
sig { params(tag: T.nilable(String)).returns(T
|
|
242
|
+
sig { params(tag: T.nilable(String)).returns(T::Boolean) }
|
|
243
243
|
def git_tag_resolvable?(tag)
|
|
244
|
-
@git_tag_resolvable ||= T.let({}, T.nilable(T::Hash[T.
|
|
245
|
-
return @git_tag_resolvable
|
|
244
|
+
@git_tag_resolvable ||= T.let({}, T.nilable(T::Hash[T.nilable(String), T::Boolean]))
|
|
245
|
+
return @git_tag_resolvable.fetch(tag) if @git_tag_resolvable.key?(tag)
|
|
246
246
|
|
|
247
247
|
@git_tag_resolvable[tag] =
|
|
248
248
|
begin
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: dependabot-bundler
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.397.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Dependabot
|
|
@@ -15,14 +15,14 @@ dependencies:
|
|
|
15
15
|
requirements:
|
|
16
16
|
- - '='
|
|
17
17
|
- !ruby/object:Gem::Version
|
|
18
|
-
version: 0.
|
|
18
|
+
version: 0.397.0
|
|
19
19
|
type: :runtime
|
|
20
20
|
prerelease: false
|
|
21
21
|
version_requirements: !ruby/object:Gem::Requirement
|
|
22
22
|
requirements:
|
|
23
23
|
- - '='
|
|
24
24
|
- !ruby/object:Gem::Version
|
|
25
|
-
version: 0.
|
|
25
|
+
version: 0.397.0
|
|
26
26
|
- !ruby/object:Gem::Dependency
|
|
27
27
|
name: parallel
|
|
28
28
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -327,6 +327,7 @@ files:
|
|
|
327
327
|
- lib/dependabot/bundler/file_parser/file_preparer.rb
|
|
328
328
|
- lib/dependabot/bundler/file_parser/gemfile_declaration_finder.rb
|
|
329
329
|
- lib/dependabot/bundler/file_parser/gemspec_declaration_finder.rb
|
|
330
|
+
- lib/dependabot/bundler/file_parser/helper_dependencies.rb
|
|
330
331
|
- lib/dependabot/bundler/file_updater.rb
|
|
331
332
|
- lib/dependabot/bundler/file_updater/gemfile_updater.rb
|
|
332
333
|
- lib/dependabot/bundler/file_updater/gemspec_dependency_name_finder.rb
|
|
@@ -360,7 +361,7 @@ licenses:
|
|
|
360
361
|
- MIT
|
|
361
362
|
metadata:
|
|
362
363
|
bug_tracker_uri: https://github.com/dependabot/dependabot-core/issues
|
|
363
|
-
changelog_uri: https://github.com/dependabot/dependabot-core/releases/tag/v0.
|
|
364
|
+
changelog_uri: https://github.com/dependabot/dependabot-core/releases/tag/v0.397.0
|
|
364
365
|
rdoc_options: []
|
|
365
366
|
require_paths:
|
|
366
367
|
- lib
|