@@ -37,6 +37,9 @@ def initialize(dependency_files:, dependency:)
3737 )
3838 end
3939
40+ # rubocop:disable Metrics/AbcSize
41+ # rubocop:disable Metrics/MethodLength
42+ # rubocop:disable Metrics/PerceivedComplexity
4043 sig { params ( build_file : Dependabot ::DependencyFile ) . returns ( T ::Array [ Dependabot ::DependencyFile ] ) }
4144 def update_files ( build_file )
4245 # We only run this updater if it's a distribution dependency
@@ -49,29 +52,57 @@ def update_files(build_file)
4952 # If we don't have any files in the build files don't generate one
5053 return [ ] unless local_files . any?
5154
55+ # we only run this updater if the build file has a requirement for this dependency
56+ target_requirements = dependency . requirements . select do |req |
57+ T . let ( req [ :file ] , String ) == build_file . name
58+ end
59+ return [ ] unless target_requirements . any?
60+
5261 updated_files = dependency_files . dup
5362 SharedHelpers . in_a_temporary_directory do |temp_dir |
5463 populate_temp_directory ( temp_dir )
5564 cwd = File . join ( temp_dir , base_path ( build_file ) )
5665
57- # Create gradle.properties file with proxy settings
58- # Would prefer to use command line arguments, but they don't work.
59- properties_filename = File . join ( temp_dir , build_file . directory , "gradle.properties" )
60- write_properties_file ( properties_filename )
61-
62- command_parts = %w( gradle --no-daemon --stacktrace ) + command_args
63- command = Shellwords . join ( command_parts )
66+ has_local_script = File . exist? ( File . join ( cwd , "./gradlew" ) )
67+ command_parts = %w( --no-daemon --stacktrace ) + command_args ( target_requirements )
68+ command = Shellwords . join ( [ has_local_script ? "./gradlew" : "gradle" ] + command_parts )
6469
6570 Dir . chdir ( cwd ) do
66- SharedHelpers . run_shell_command ( command , cwd : cwd )
71+ FileUtils . chmod ( "+x" , "./gradlew" ) if has_local_script
72+
73+ properties_file = File . join ( cwd , "gradle/wrapper/gradle-wrapper.properties" )
74+ validate_option = get_validate_distribution_url_option ( properties_file )
75+ env = { "JAVA_OPTS" => proxy_args . join ( " " ) } # set proxy for gradle execution
76+
77+ begin
78+ # first attempt: run the wrapper task via the local gradle wrapper (if present)
79+ # `gradle-wrapper.jar` might be too old to run on host's Java version
80+ SharedHelpers . run_shell_command ( command , cwd : cwd , env : env )
81+ rescue SharedHelpers ::HelperSubprocessFailed => e
82+ raise e unless has_local_script # already field with system one, there is no point to retry
83+
84+ Dependabot . logger . warn ( "Running #{ command } failed, retrying first with system Gradle: #{ e . message } " )
85+
86+ # second attempt: run the wrapper task via system gradle and then retry via local wrapper
87+ system_command = Shellwords . join ( [ "gradle" ] + command_parts )
88+ SharedHelpers . run_shell_command ( system_command , cwd : cwd , env : env ) # run via system gradle
89+ SharedHelpers . run_shell_command ( command , cwd : cwd , env : env ) # retry via local wrapper
90+ end
91+
92+ # Restore previous validateDistributionUrl option if it existed
93+ override_validate_distribution_url_option ( properties_file , validate_option )
94+
6795 update_files_content ( temp_dir , local_files , updated_files )
6896 rescue SharedHelpers ::HelperSubprocessFailed => e
69- puts "Failed to update files: #{ e . message } "
97+ Dependabot . logger . error ( "Failed to update files: #{ e . message } " )
7098 return updated_files
7199 end
72100 end
73101 updated_files
74102 end
103+ # rubocop:enable Metrics/AbcSize
104+ # rubocop:enable Metrics/MethodLength
105+ # rubocop:enable Metrics/PerceivedComplexity
75106
76107 private
77108
@@ -80,12 +111,20 @@ def target_file?(file)
80111 @target_files . any? { |r | "/#{ file . name } " . end_with? ( r ) }
81112 end
82113
83- sig { returns ( T ::Array [ String ] ) }
84- def command_args
85- version = T . let ( dependency . requirements [ 0 ] &.[]( :requirement ) , String )
86- checksum = T . let ( dependency . requirements [ 1 ] &.[]( :requirement ) , String ) if dependency . requirements . size > 1
87-
88- args = %W( wrapper --no-validate-url --gradle-version #{ version } )
114+ sig { params ( requirements : T ::Array [ T ::Hash [ Symbol , T . untyped ] ] ) . returns ( T ::Array [ String ] ) }
115+ def command_args ( requirements )
116+ version = T . let ( requirements [ 0 ] &.[]( :requirement ) , String )
117+ checksum = T . let ( requirements [ 1 ] &.[]( :requirement ) , String ) if dependency . requirements . size > 1
118+ distribution_url = T . let ( requirements [ 0 ] &.[]( :source ) , T ::Hash [ Symbol , String ] ) [ :url ]
119+ distribution_type = distribution_url &.match ( /\b (bin|all)\b / ) &.captures &.first
120+
121+ # --no-validate-url is required to bypass HTTP proxy issues when running ./gradlew
122+ # This prevents validation failures during the wrapper update process
123+ # Note: This temporarily sets validateDistributionUrl=false in gradle-wrapper.properties
124+ # The original value is restored after the wrapper task completes
125+ # see method `get_validate_distribution_url_option` for more details
126+ args = %W( wrapper --gradle-version #{ version } --no-validate-url ) # see
127+ args += %W( --distribution-type #{ distribution_type } ) if distribution_type
89128 args += %W( --gradle-distribution-sha256-sum #{ checksum } ) if checksum
90129 args
91130 end
@@ -135,8 +174,35 @@ def populate_temp_directory(temp_dir)
135174 end
136175 end
137176
138- sig { params ( file_name : String ) . void }
139- def write_properties_file ( file_name ) # rubocop:disable Metrics/PerceivedComplexity
177+ # This is a consequence of the lack of proper proxy support in Gradle Wrapper
178+ # During the update process, Gradle Wrapper logic will try to validate the distribution URL
179+ # by performing an HTTP request. If the environment requires a proxy, this validation will fail
180+ # We need to add the `--no-validate-url` the commandline args to disable this validation
181+ # However, this change is persistent in the `gradle-wrapper.properties` file
182+ # To avoid side effects, we read the existing value before the update and restore it afterward
183+ sig { params ( properties_file : T . any ( Pathname , String ) ) . returns ( T . nilable ( String ) ) }
184+ def get_validate_distribution_url_option ( properties_file )
185+ return nil unless File . exist? ( properties_file )
186+
187+ properties_content = File . read ( properties_file )
188+ properties_content . match ( /^validateDistributionUrl=(.*)$/ ) &.captures &.first
189+ end
190+
191+ sig { params ( properties_file : T . any ( Pathname , String ) , value : T . nilable ( String ) ) . void }
192+ def override_validate_distribution_url_option ( properties_file , value )
193+ return unless File . exist? ( properties_file )
194+
195+ properties_content = File . read ( properties_file )
196+ updated_content = properties_content . gsub (
197+ /^validateDistributionUrl=(.*)\n / ,
198+ value ? "validateDistributionUrl=#{ value } \n " : ""
199+ )
200+ File . write ( properties_file , updated_content )
201+ end
202+
203+ # rubocop:disable Metrics/PerceivedComplexity
204+ sig { returns ( T ::Array [ String ] ) }
205+ def proxy_args
140206 http_proxy = ENV . fetch ( "HTTP_PROXY" , nil )
141207 https_proxy = ENV . fetch ( "HTTPS_PROXY" , nil )
142208 http_split = http_proxy &.split ( ":" )
@@ -145,13 +211,15 @@ def write_properties_file(file_name) # rubocop:disable Metrics/PerceivedComplexi
145211 https_proxy_host = https_split &.fetch ( 1 , nil ) &.gsub ( "//" , "" ) || "host.docker.internal"
146212 http_proxy_port = http_split &.fetch ( 2 ) || "1080"
147213 https_proxy_port = https_split &.fetch ( 2 ) || "1080"
148- properties_content = "
149- systemProp.http.proxyHost=#{ http_proxy_host }
150- systemProp.http.proxyPort=#{ http_proxy_port }
151- systemProp.https.proxyHost=#{ https_proxy_host }
152- systemProp.https.proxyPort=#{ https_proxy_port } "
153- File . write ( file_name , properties_content )
214+
215+ args = [ ]
216+ args += %W( -Dhttp.proxyHost=#{ http_proxy_host } ) if http_proxy_host
217+ args += %W( -Dhttp.proxyPort=#{ http_proxy_port } ) if http_proxy_port
218+ args += %W( -Dhttps.proxyHost=#{ https_proxy_host } ) if https_proxy_host
219+ args += %W( -Dhttps.proxyPort=#{ https_proxy_port } ) if https_proxy_port
220+ args
154221 end
222+ # rubocop:enable Metrics/PerceivedComplexity
155223
156224 sig { returns ( T ::Array [ Dependabot ::DependencyFile ] ) }
157225 attr_reader :dependency_files
0 commit comments