Skip to content

Commit 7db1e99

Browse files
Merge branch 'main' into copilot/fix-go-redis-processing-error
2 parents 8d6dcde + 47e7666 commit 7db1e99

File tree

2 files changed

+194
-68
lines changed

2 files changed

+194
-68
lines changed

updater/lib/dependabot/dependency_change_builder.rb

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,10 @@ def initialize(job:, dependency_files:, updated_dependencies:, change_source:, n
7272
sig { returns(Dependabot::DependencyChange) }
7373
def run
7474
updated_files = generate_dependency_files
75-
raise DependabotError, "FileUpdater failed" unless updated_files.any?
75+
76+
unless updated_files.any?
77+
raise DependabotError, "FileUpdater failed to update any files for: #{dependency_info_for_error}"
78+
end
7679

7780
# Remove any unchanged dependencies from the updated list
7881
updated_deps = updated_dependencies.reject do |d|
@@ -149,14 +152,36 @@ def generate_dependency_files
149152
file_updater = file_updater_for(relevant_dependencies)
150153

151154
# Exclude support files since they are not manifests, just needed for supporting the update
152-
update_files = file_updater.updated_dependency_files.reject(&:support_file)
155+
all_files = file_updater.updated_dependency_files
153156

154-
# Collect notices from file updater
157+
# Collect notices from file updater after update attempt
155158
updater_notices = T.let(file_updater.notices, T::Array[Dependabot::Notice])
156-
updater_notices.each { |notice| @notices << notice }
159+
@notices.concat(updater_notices)
160+
161+
updated_files = all_files.reject(&:support_file?)
162+
updated_files
163+
end
164+
165+
sig { returns(String) }
166+
def dependency_names_for_error
167+
format_names(updated_dependencies.map(&:name))
168+
end
157169

158-
update_files
170+
sig { params(names: T::Array[String]).returns(String) }
171+
def format_names(names)
172+
names.uniq.sort.join(", ")
159173
end
174+
175+
sig { returns(String) }
176+
def dependency_info_for_error
177+
return dependency_names_for_error unless updated_dependencies.one?
178+
179+
dependency = T.must(updated_dependencies.first)
180+
previous_version = dependency.previous_version || "unknown"
181+
current_version = dependency.version || "unknown"
182+
"#{dependency.name} (#{previous_version}#{current_version})"
183+
end
184+
160185
sig { params(dependencies: T::Array[Dependabot::Dependency]).returns(Dependabot::FileUpdaters::Base) }
161186
def file_updater_for(dependencies)
162187
Dependabot::FileUpdaters.for_package_manager(job.package_manager).new(

updater/spec/dependabot/dependency_change_builder_spec.rb

Lines changed: 164 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -59,30 +59,7 @@
5959
end
6060

6161
let(:updated_dependencies) do
62-
[
63-
Dependabot::Dependency.new(
64-
name: "dummy-pkg-b",
65-
package_manager: "bundler",
66-
version: "1.2.0",
67-
previous_version: "1.1.0",
68-
requirements: [
69-
{
70-
file: "Gemfile",
71-
requirement: "~> 1.2.0",
72-
groups: [],
73-
source: nil
74-
}
75-
],
76-
previous_requirements: [
77-
{
78-
file: "Gemfile",
79-
requirement: "~> 1.1.0",
80-
groups: [],
81-
source: nil
82-
}
83-
]
84-
)
85-
]
62+
[build_dependency(name: "dummy-pkg-b", version: "1.2.0", previous_version: "1.1.0")]
8663
end
8764

8865
describe "::create_from" do
@@ -91,27 +68,66 @@
9168
job: job,
9269
dependency_files: dependency_files,
9370
updated_dependencies: updated_dependencies,
94-
change_source: change_source
71+
change_source: change_source,
72+
notices: notices
9573
)
9674
end
9775

98-
context "when the source is a lead dependency" do
99-
let(:change_source) do
100-
Dependabot::Dependency.new(
101-
name: "dummy-pkg-b",
102-
package_manager: "bundler",
103-
version: "1.1.0",
104-
requirements: [
105-
{
106-
file: "Gemfile",
107-
requirement: "~> 1.1.0",
108-
groups: [],
109-
source: nil
110-
}
111-
]
112-
)
76+
let(:notices) { [] }
77+
let(:lead_dependency_change_source) { build_dependency(name: "dummy-pkg-b", version: "1.1.0") }
78+
let(:single_dependency_info) { "dummy-pkg-b (1.1.0 → 1.2.0)" }
79+
let(:file_updater_class) { class_double(Dependabot::Bundler::FileUpdater) }
80+
81+
def stub_file_updater(updated_dependency_files:, notices: [])
82+
file_updater = instance_double(
83+
Dependabot::Bundler::FileUpdater,
84+
updated_dependency_files: updated_dependency_files,
85+
notices: notices
86+
)
87+
88+
allow(Dependabot::FileUpdaters).to receive(:for_package_manager)
89+
.with("bundler")
90+
.and_return(file_updater_class)
91+
allow(file_updater_class).to receive(:new).and_return(file_updater)
92+
end
93+
94+
def build_dependency(name:, version:, previous_version: nil)
95+
requirement = {
96+
file: "Gemfile",
97+
requirement: "~> #{version}",
98+
groups: [],
99+
source: nil
100+
}
101+
102+
dependency_args = {
103+
name: name,
104+
package_manager: "bundler",
105+
version: version,
106+
requirements: [requirement]
107+
}
108+
109+
if previous_version
110+
previous_requirement = {
111+
file: "Gemfile",
112+
requirement: "~> #{previous_version}",
113+
groups: [],
114+
source: nil
115+
}
116+
117+
dependency_args[:previous_version] = previous_version
118+
dependency_args[:previous_requirements] = [previous_requirement]
113119
end
114120

121+
Dependabot::Dependency.new(**dependency_args)
122+
end
123+
124+
def dependency_group_source
125+
Dependabot::DependencyGroup.new(name: "dummy-pkg-*", rules: { patterns: ["dummy-pkg-*"] })
126+
end
127+
128+
context "when the source is a lead dependency" do
129+
let(:change_source) { lead_dependency_change_source }
130+
115131
it "creates a new DependencyChange with the updated files" do
116132
dependency_change = create_change
117133

@@ -128,9 +144,7 @@
128144
end
129145

130146
it "does not include support files in the updated files" do
131-
allow_any_instance_of(Dependabot::Bundler::FileUpdater)
132-
.to receive(:updated_dependency_files)
133-
.and_return(dependency_files)
147+
stub_file_updater(updated_dependency_files: dependency_files)
134148

135149
dependency_change = described_class.create_from(
136150
job: job,
@@ -145,9 +159,7 @@
145159
end
146160

147161
context "when the source is a dependency group" do
148-
let(:change_source) do
149-
Dependabot::DependencyGroup.new(name: "dummy-pkg-*", rules: { patterns: ["dummy-pkg-*"] })
150-
end
162+
let(:change_source) { dependency_group_source }
151163

152164
it "creates a new DependencyChange flagged as a grouped update" do
153165
dependency_change = create_change
@@ -158,28 +170,117 @@
158170
end
159171

160172
context "when there are no file changes" do
161-
let(:change_source) do
162-
Dependabot::Dependency.new(
163-
name: "dummy-pkg-b",
164-
package_manager: "bundler",
165-
version: "1.1.0",
166-
requirements: [
167-
{
168-
file: "Gemfile",
169-
requirement: "~> 1.1.0",
170-
groups: [],
171-
source: nil
172-
}
173-
]
174-
)
173+
let(:change_source) { lead_dependency_change_source }
174+
175+
before do
176+
stub_file_updater(updated_dependency_files: [])
177+
end
178+
179+
it "raises an exception with diagnostic dependency details" do
180+
expect { create_change }
181+
.to raise_error(
182+
Dependabot::DependabotError,
183+
"FileUpdater failed to update any files for: dummy-pkg-b (1.1.0 → 1.2.0)"
184+
)
185+
end
186+
end
187+
188+
context "when multiple dependencies have no file changes" do
189+
let(:updated_dependencies) do
190+
[
191+
build_dependency(name: "dummy-pkg-b", version: "1.2.0", previous_version: "1.1.0"),
192+
build_dependency(name: "dummy-pkg-a", version: "2.0.0", previous_version: "1.9.0")
193+
]
194+
end
195+
196+
let(:change_source) { dependency_group_source }
197+
198+
before do
199+
stub_file_updater(updated_dependency_files: [])
200+
end
201+
202+
it "raises an exception listing dependency names" do
203+
expect { create_change }
204+
.to raise_error(
205+
Dependabot::DependabotError,
206+
"FileUpdater failed to update any files for: dummy-pkg-a, dummy-pkg-b"
207+
)
208+
end
209+
end
210+
211+
context "when duplicate dependency names have no file changes" do
212+
let(:updated_dependencies) do
213+
[
214+
build_dependency(name: "dummy-pkg-b", version: "1.2.0", previous_version: "1.1.0"),
215+
build_dependency(name: "dummy-pkg-b", version: "1.3.0", previous_version: "1.2.0")
216+
]
217+
end
218+
219+
let(:change_source) { dependency_group_source }
220+
221+
before do
222+
stub_file_updater(updated_dependency_files: [])
223+
end
224+
225+
it "raises an exception with unique dependency names" do
226+
expect { create_change }
227+
.to raise_error(
228+
Dependabot::DependabotError,
229+
"FileUpdater failed to update any files for: dummy-pkg-b"
230+
)
231+
end
232+
end
233+
234+
context "when only support files are returned" do
235+
let(:change_source) { lead_dependency_change_source }
236+
let(:support_files) { dependency_files.select(&:support_file?) }
237+
let(:updated_support_files) { [support_files.last, support_files.first, support_files.last] }
238+
let(:updater_notices) { [instance_double(Dependabot::Notice)] }
239+
240+
before do
241+
stub_file_updater(updated_dependency_files: updated_support_files, notices: updater_notices)
242+
end
243+
244+
it "raises a generic no-files error" do
245+
expect { create_change }
246+
.to raise_error(
247+
Dependabot::DependabotError,
248+
"FileUpdater failed to update any files for: dummy-pkg-b (1.1.0 → 1.2.0)"
249+
)
250+
end
251+
252+
it "collects notices before raising" do
253+
expect { create_change }
254+
.to raise_error(
255+
Dependabot::DependabotError,
256+
"FileUpdater failed to update any files for: dummy-pkg-b (1.1.0 → 1.2.0)"
257+
)
258+
259+
expect(notices).to eq(updater_notices)
260+
end
261+
end
262+
263+
context "when grouped updates return only support files" do
264+
let(:updated_dependencies) do
265+
[
266+
build_dependency(name: "dummy-pkg-b", version: "1.2.0", previous_version: "1.1.0"),
267+
build_dependency(name: "dummy-pkg-a", version: "2.0.0", previous_version: "1.9.0"),
268+
build_dependency(name: "dummy-pkg-b", version: "1.3.0", previous_version: "1.2.0")
269+
]
175270
end
271+
let(:change_source) { dependency_group_source }
272+
let(:support_files) { dependency_files.select(&:support_file?) }
176273

177274
before do
178-
allow_any_instance_of(Dependabot::Bundler::FileUpdater).to receive(:updated_dependency_files).and_return([])
275+
stub_file_updater(updated_dependency_files: support_files)
179276
end
180277

181-
it "raises an exception" do
182-
expect { create_change }.to raise_error(Dependabot::DependabotError)
278+
it "raises a no-files error listing sorted and unique dependency names" do
279+
expect { create_change }
280+
.to raise_error(
281+
Dependabot::DependabotError,
282+
"FileUpdater failed to update any files for: dummy-pkg-a, dummy-pkg-b"
283+
)
183284
end
184285
end
185286
end

0 commit comments

Comments
 (0)