Skip to content

Commit 93c109f

Browse files
committed
gradle: Fix wrapper updater to update all 4 wrapper files
The wrapper updater was only updating gradle-wrapper.properties instead of all 4 wrapper files (gradlew, gradlew.bat, gradle-wrapper.jar, and gradle-wrapper.properties). Changes: - Use ./gradlew instead of system gradle command - Run wrapper task twice (download distribution, then regenerate binaries) - Add binary file handling (binread/binwrite with Base64) - Set executable permissions (chmod 0o755) for gradlew scripts - Raise DependencyFileNotResolvable on wrapper task failure - Remove directory restriction from wrapper file selection - Return all dependency_files with wrapper files replaced Fixes: #13501
1 parent 7448483 commit 93c109f

2 files changed

Lines changed: 226 additions & 31 deletions

File tree

gradle/lib/dependabot/gradle/file_updater/wrapper_updater.rb

Lines changed: 47 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -42,12 +42,11 @@ def update_files(build_file)
4242
# We only run this updater if it's a distribution dependency
4343
return [] unless Distributions.distribution_requirements?(dependency.requirements)
4444

45-
local_files = dependency_files.select do |file|
46-
file.directory == build_file.directory && target_file?(file)
47-
end
45+
# Find all wrapper files - they can span multiple directories
46+
local_files = dependency_files.select { |file| target_file?(file) }
4847

4948
# If we don't have any files in the build files don't generate one
50-
return [] unless local_files.any?
49+
return dependency_files unless local_files.any?
5150

5251
updated_files = dependency_files.dup
5352
SharedHelpers.in_a_temporary_directory do |temp_dir|
@@ -59,15 +58,34 @@ def update_files(build_file)
5958
properties_filename = File.join(temp_dir, build_file.directory, "gradle.properties")
6059
write_properties_file(properties_filename)
6160

62-
command_parts = %w(gradle --no-daemon --stacktrace) + command_args
63-
command = Shellwords.join(command_parts)
64-
6561
Dir.chdir(cwd) do
66-
SharedHelpers.run_shell_command(command, cwd: cwd)
67-
update_files_content(temp_dir, local_files, updated_files)
62+
# First run: Use ./gradlew to run wrapper task with new version
63+
# This updates gradle-wrapper.properties and downloads the new distribution
64+
gradlew_script = File.exist?("gradlew.bat") && !File.exist?("gradlew") ? "gradlew.bat" : "./gradlew"
65+
first_command_parts = [gradlew_script, "--no-daemon", "--stacktrace"] + command_args
66+
first_command = Shellwords.join(first_command_parts)
67+
SharedHelpers.run_shell_command(first_command, cwd: cwd)
68+
69+
# Second run: Run wrapper task again to update wrapper binaries (gradlew, gradlew.bat, gradle-wrapper.jar)
70+
# Per Gradle documentation, running wrapper task twice ensures all wrapper files are fully updated
71+
second_command_parts = [gradlew_script, "--no-daemon", "--stacktrace", "wrapper"]
72+
second_command = Shellwords.join(second_command_parts)
73+
SharedHelpers.run_shell_command(second_command, cwd: cwd)
74+
75+
# Read updated content from temp directory and replace in updated_files array
76+
local_files.each do |file|
77+
file_path = File.join(temp_dir, file.directory, file.name)
78+
f_content = if file.binary?
79+
File.binread(file_path)
80+
else
81+
File.read(file_path)
82+
end
83+
tmp_file = file.dup
84+
tmp_file.content = file.binary? ? Base64.encode64(f_content) : f_content
85+
updated_files[T.must(updated_files.index(file))] = tmp_file
86+
end
6887
rescue SharedHelpers::HelperSubprocessFailed => e
69-
puts "Failed to update files: #{e.message}"
70-
return updated_files
88+
handle_wrapper_update_error(e)
7189
end
7290
end
7391
updated_files
@@ -110,31 +128,32 @@ def base_path(build_file)
110128
File.dirname(File.join(build_file.directory, build_file.name)).delete_suffix("/gradle/wrapper")
111129
end
112130

113-
sig do
114-
params(
115-
temp_dir: T.any(Pathname, String),
116-
local_files: T::Array[Dependabot::DependencyFile],
117-
updated_files: T::Array[Dependabot::DependencyFile]
118-
).void
119-
end
120-
def update_files_content(temp_dir, local_files, updated_files)
121-
local_files.each do |file|
122-
f_content = File.read(File.join(temp_dir, file.directory, file.name))
123-
tmp_file = file.dup
124-
tmp_file.content = tmp_file.binary? ? Base64.encode64(f_content) : f_content
125-
updated_files[T.must(updated_files.index(file))] = tmp_file
126-
end
127-
end
128-
129131
sig { params(temp_dir: T.any(Pathname, String)).void }
130132
def populate_temp_directory(temp_dir)
131133
files_to_populate.each do |file|
132134
in_path_name = File.join(temp_dir, file.directory, file.name)
133135
FileUtils.mkdir_p(File.dirname(in_path_name))
134-
File.write(in_path_name, file.content)
136+
137+
# Write file content - binary files need special handling
138+
if file.binary?
139+
File.binwrite(in_path_name, Base64.decode64(file.content))
140+
else
141+
File.write(in_path_name, file.content)
142+
end
143+
144+
# Make gradlew scripts executable so they can be run
145+
FileUtils.chmod(0o755, in_path_name) if file.name.end_with?("gradlew", "gradlew.bat")
135146
end
136147
end
137148

149+
sig { params(error: SharedHelpers::HelperSubprocessFailed).returns(T.noreturn) }
150+
def handle_wrapper_update_error(error)
151+
# Gradle wrapper update failures typically indicate build compatibility issues
152+
# with the new Gradle version. Raise as DependencyFileNotResolvable so the
153+
# service layer can handle appropriately.
154+
raise Dependabot::DependencyFileNotResolvable, error.message
155+
end
156+
138157
sig { params(file_name: String).void }
139158
def write_properties_file(file_name) # rubocop:disable Metrics/PerceivedComplexity
140159
http_proxy = ENV.fetch("HTTP_PROXY", nil)

gradle/spec/dependabot/gradle/file_updater_spec.rb

Lines changed: 179 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -716,20 +716,25 @@
716716
end
717717

718718
its(:content) do
719-
expected_command = "gradle --no-daemon --stacktrace wrapper --no-validate-url --gradle-version 9.0.0"
719+
# First command: Update wrapper with new version
720+
first_expected_command = "./gradlew --no-daemon --stacktrace wrapper --no-validate-url --gradle-version 9.0.0"
721+
# Second command: Regenerate wrapper binaries
722+
second_expected_command = "./gradlew --no-daemon --stacktrace wrapper"
720723

721724
is_expected.to include(
722725
"distributionUrl=https\\://services.gradle.org/distributions/gradle-9.0.0-#{type}.zip"
723726
)
724727

725728
if checksum
726-
expected_command += " --gradle-distribution-sha256-sum #{updated_checksum}"
729+
first_expected_command += " --gradle-distribution-sha256-sum #{updated_checksum}"
727730
is_expected.to include("distributionSha256Sum=#{updated_checksum}")
728731
else
729732
is_expected.not_to include("distributionSha256Sum=")
730733
end
731734

732-
expect(Dependabot::SharedHelpers).to have_received(:run_shell_command).with(expected_command, cwd: anything)
735+
# Verify both commands were called in sequence
736+
expect(Dependabot::SharedHelpers).to have_received(:run_shell_command).with(first_expected_command, cwd: anything).ordered
737+
expect(Dependabot::SharedHelpers).to have_received(:run_shell_command).with(second_expected_command, cwd: anything).ordered
733738
end
734739
end
735740

@@ -745,6 +750,177 @@
745750
"all",
746751
"443c9c8ee2ac1ee0e11881a40f2376d79c66386264a44b24a9f8ca67e633375f",
747752
"f759b8dd5204e2e3fa4ca3e73f452f087153cf81bac9561eeb854229cc2c5365"
753+
754+
context "when wrapper task fails" do
755+
let(:buildfile) { wrapper_file }
756+
let(:wrapper_file) do
757+
Dependabot::DependencyFile.new(
758+
name: "gradle/wrapper/gradle-wrapper.properties",
759+
content: fixture("wrapper_files", "gradle-wrapper-8.14.2-bin.properties")
760+
)
761+
end
762+
763+
let(:dependency) do
764+
Dependabot::Dependency.new(
765+
name: "gradle-wrapper",
766+
version: "9.0.0",
767+
previous_version: "8.14.2",
768+
requirements: [{
769+
file: "gradle/wrapper/gradle-wrapper.properties",
770+
requirement: "9.0.0",
771+
groups: [],
772+
source: { type: "gradle-distribution", url: "https://services.gradle.org", property: "distributionUrl" }
773+
}],
774+
previous_requirements: [{
775+
file: "gradle/wrapper/gradle-wrapper.properties",
776+
requirement: "8.14.2",
777+
groups: [],
778+
source: { type: "gradle-distribution", url: "https://services.gradle.org", property: "distributionUrl" }
779+
}],
780+
package_manager: "gradle"
781+
)
782+
end
783+
784+
before do
785+
allow(Dependabot::SharedHelpers).to receive(:run_shell_command)
786+
.and_raise(
787+
Dependabot::SharedHelpers::HelperSubprocessFailed.new(
788+
message: "Gradle wrapper task failed",
789+
error_context: {}
790+
)
791+
)
792+
end
793+
794+
it "raises DependencyFileNotResolvable" do
795+
expect { updated_files }.to raise_error(Dependabot::DependencyFileNotResolvable)
796+
end
797+
end
798+
799+
context "when updating a non-wrapper dependency" do
800+
let(:buildfile) do
801+
Dependabot::DependencyFile.new(
802+
name: "build.gradle",
803+
content: fixture("buildfiles", "basic_build.gradle")
804+
)
805+
end
806+
807+
let(:dependency_files) { [buildfile, wrapper_file] }
808+
809+
let(:wrapper_file) do
810+
Dependabot::DependencyFile.new(
811+
name: "gradle/wrapper/gradle-wrapper.properties",
812+
content: fixture("wrapper_files", "gradle-wrapper-8.14.2-bin.properties")
813+
)
814+
end
815+
816+
let(:dependency) do
817+
Dependabot::Dependency.new(
818+
name: "co.aikar:acf-paper",
819+
version: "0.6.0-SNAPSHOT",
820+
requirements: [{
821+
file: "build.gradle",
822+
requirement: "0.6.0-SNAPSHOT",
823+
groups: [],
824+
source: nil,
825+
metadata: nil
826+
}],
827+
previous_requirements: [{
828+
file: "build.gradle",
829+
requirement: "0.5.0-SNAPSHOT",
830+
groups: [],
831+
source: nil,
832+
metadata: nil
833+
}],
834+
package_manager: "gradle"
835+
)
836+
end
837+
838+
it "does not run wrapper updater" do
839+
# Should not call run_shell_command for wrapper tasks
840+
updated_files
841+
expect(Dependabot::SharedHelpers).not_to have_received(:run_shell_command)
842+
end
843+
844+
it "updates only the build file" do
845+
expect(updated_files.length).to eq(1)
846+
expect(updated_files.first.name).to eq("build.gradle")
847+
end
848+
end
849+
850+
context "with all wrapper files including binaries" do
851+
let(:buildfile) { wrapper_file }
852+
853+
let(:wrapper_file) do
854+
Dependabot::DependencyFile.new(
855+
name: "gradle/wrapper/gradle-wrapper.properties",
856+
content: fixture("wrapper_files", "gradle-wrapper-8.14.2-bin.properties")
857+
)
858+
end
859+
860+
let(:gradlew_file) do
861+
Dependabot::DependencyFile.new(
862+
name: "gradlew",
863+
content: "#!/bin/sh\necho 'gradlew script'"
864+
)
865+
end
866+
867+
let(:gradlew_bat_file) do
868+
Dependabot::DependencyFile.new(
869+
name: "gradlew.bat",
870+
content: "@echo off\necho gradlew.bat"
871+
)
872+
end
873+
874+
let(:jar_file) do
875+
file = Dependabot::DependencyFile.new(
876+
name: "gradle/wrapper/gradle-wrapper.jar",
877+
content: Base64.encode64("fake jar content")
878+
)
879+
file.content_encoding = Dependabot::DependencyFile::ContentEncoding::BASE64
880+
file
881+
end
882+
883+
let(:dependency_files) { [wrapper_file, gradlew_file, gradlew_bat_file, jar_file] }
884+
885+
let(:dependency) do
886+
Dependabot::Dependency.new(
887+
name: "gradle-wrapper",
888+
version: "9.0.0",
889+
previous_version: "8.14.2",
890+
requirements: [{
891+
file: "gradle/wrapper/gradle-wrapper.properties",
892+
requirement: "9.0.0",
893+
groups: [],
894+
source: { type: "gradle-distribution", url: "https://services.gradle.org", property: "distributionUrl" }
895+
}],
896+
previous_requirements: [{
897+
file: "gradle/wrapper/gradle-wrapper.properties",
898+
requirement: "8.14.2",
899+
groups: [],
900+
source: { type: "gradle-distribution", url: "https://services.gradle.org", property: "distributionUrl" }
901+
}],
902+
package_manager: "gradle"
903+
)
904+
end
905+
906+
before do
907+
allow(Dependabot::SharedHelpers).to receive(:run_shell_command)
908+
end
909+
910+
it "processes all wrapper files" do
911+
# When commands are mocked, files don't actually change content,
912+
# so they may be filtered out. The key is that the wrapper updater
913+
# processes all wrapper files by running the wrapper task.
914+
result = updated_files
915+
# At minimum, the properties file should be in the result
916+
expect(result.map(&:name)).to include("gradle/wrapper/gradle-wrapper.properties")
917+
end
918+
919+
it "runs wrapper task twice" do
920+
updated_files
921+
expect(Dependabot::SharedHelpers).to have_received(:run_shell_command).twice
922+
end
923+
end
748924
end
749925

750926
context "with a version catalog" do

0 commit comments

Comments
 (0)