Skip to content

Commit cfd4a1f

Browse files
Update Gradle.lockfile's
1 parent 09bd3c7 commit cfd4a1f

File tree

19 files changed

+695
-9
lines changed

19 files changed

+695
-9
lines changed

.devcontainer/devcontainer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
"ghcr.io/devcontainers/features/node": "lts",
2525
"ghcr.io/devcontainers/features/go": "latest",
2626
"ghcr.io/devcontainers/features/ruby": "3.3.6",
27-
"ghcr.io/devcontainers/features/rust": "latest",
27+
"ghcr.io/devcontainers/features/rust": "1.86.0",
2828
"ghcr.io/devcontainers/features/dotnet": "latest",
2929
"ghcr.io/devcontainers/features/sshd:1": {
3030
"version": "latest"

gradle/Dockerfile

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,43 @@
11
FROM ghcr.io/dependabot/dependabot-updater-core
22

3+
# Install Java
4+
RUN apt-get update && apt-get install -y --no-install-recommends \
5+
openjdk-11-jdk \
6+
# avoids keytool usage
7+
ca-certificates-java \
8+
wget \
9+
# we need to allow the dependabot user to write to these files for when update-ca-certificates is run
10+
&& chgrp dependabot /etc/default/cacerts \
11+
&& chmod g+rw /etc/default/cacerts \
12+
&& chgrp dependabot /etc/ssl/certs/java/cacerts \
13+
&& chmod g+rw /etc/ssl/certs/java/cacerts \
14+
&& rm -rf /var/lib/apt/lists/*
15+
16+
# Install Gradle
17+
ENV GRADLE_HOME=/opt/gradle
18+
ENV GRADLE_VERSION=8.14
19+
ARG GRADLE_DOWNLOAD_SHA256=61ad310d3c7d3e5da131b76bbf22b5a4c0786e9d892dae8c1658d4b484de3caa
20+
RUN set -o errexit -o nounset \
21+
&& echo "Downloading Gradle" \
22+
&& wget --no-verbose --output-document=gradle.zip "https://services.gradle.org/distributions/gradle-${GRADLE_VERSION}-bin.zip" \
23+
\
24+
&& echo "Checking Gradle download hash" \
25+
&& echo "${GRADLE_DOWNLOAD_SHA256} *gradle.zip" | sha256sum -c - \
26+
\
27+
&& echo "Installing Gradle" \
28+
&& unzip gradle.zip \
29+
&& rm gradle.zip \
30+
&& mv "gradle-${GRADLE_VERSION}" "${GRADLE_HOME}/" \
31+
&& ln -s "${GRADLE_HOME}/bin/gradle" /usr/bin/gradle
32+
333
USER dependabot
434

35+
ENV PATH=/usr/bin/gradle:$PATH
36+
37+
RUN set -o errexit -o nounset \
38+
&& echo "Testing Gradle installation" \
39+
&& gradle --version
40+
541
COPY --chown=dependabot:dependabot maven $DEPENDABOT_HOME/maven
642
COPY --chown=dependabot:dependabot gradle $DEPENDABOT_HOME/gradle
743
COPY --chown=dependabot:dependabot common $DEPENDABOT_HOME/common

gradle/lib/dependabot/gradle/file_updater.rb

Lines changed: 38 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
require "dependabot/file_updaters"
77
require "dependabot/file_updaters/base"
88
require "dependabot/gradle/file_parser"
9+
require "dependabot/gradle/file_updater/lockfile_updater"
910

1011
module Dependabot
1112
module Gradle
@@ -17,6 +18,7 @@ class FileUpdater < Dependabot::FileUpdaters::Base
1718

1819
SUPPORTED_BUILD_FILE_NAMES = %w(build.gradle build.gradle.kts).freeze
1920

21+
sig { override.returns(T::Array[Regexp]) }
2022
def self.updated_files_regex
2123
[
2224
# Matches build.gradle or build.gradle.kts in root directory
@@ -26,10 +28,12 @@ def self.updated_files_regex
2628
# Matches settings.gradle or settings.gradle.kts in root or any subdirectory
2729
%r{(^|.*/)settings\.gradle(\.kts)?$},
2830
# Matches dependencies.gradle in root or any subdirectory
29-
%r{(^|.*/)dependencies\.gradle$}
31+
%r{(^|.*/)dependencies\.gradle$},
32+
%r{(^|.*/)?gradle.lockfile$}
3033
]
3134
end
3235

36+
sig { override.returns(T::Array[::Dependabot::DependencyFile]) }
3337
def updated_dependency_files
3438
updated_files = buildfiles.dup
3539

@@ -53,26 +57,33 @@ def updated_dependency_files
5357

5458
private
5559

60+
sig { override.void }
5661
def check_required_files
5762
raise "No build.gradle or build.gradle.kts!" if dependency_files.empty?
5863
end
5964

65+
sig { void }
6066
def original_file
6167
dependency_files.find do |f|
6268
SUPPORTED_BUILD_FILE_NAMES.include?(f.name)
6369
end
6470
end
6571

72+
sig do
73+
params(buildfiles: T::Array[Dependabot::DependencyFile], dependency: Dependabot::Dependency)
74+
.returns(T::Array[Dependabot::DependencyFile])
75+
end
6676
def update_buildfiles_for_dependency(buildfiles:, dependency:)
6777
files = buildfiles.dup
6878

6979
# The UpdateChecker ensures the order of requirements is preserved
7080
# when updating, so we can zip them together in new/old pairs.
71-
reqs = dependency.requirements.zip(dependency.previous_requirements)
81+
reqs = dependency.requirements.zip(T.must(dependency.previous_requirements))
7282
.reject { |new_req, old_req| new_req == old_req }
7383

7484
# Loop through each changed requirement and update the buildfiles
7585
reqs.each do |new_req, old_req|
86+
raise "Bad req match" if old_req.nil?
7687
raise "Bad req match" unless new_req[:file] == old_req[:file]
7788
next if new_req[:requirement] == old_req[:requirement]
7889

@@ -92,23 +103,34 @@ def update_buildfiles_for_dependency(buildfiles:, dependency:)
92103
elsif new_req.dig(:metadata, :dependency_set)
93104
files = update_files_for_dep_set_change(files, old_req, new_req)
94105
else
95-
files[files.index(buildfile)] =
106+
files[T.must(files.index(buildfile))] =
96107
update_version_in_buildfile(
97108
dependency,
98109
buildfile,
99110
old_req,
100111
new_req
101112
)
102113
end
114+
115+
updater = Dependabot::Gradle::LockfileUpdater.new(dependency_files: files)
116+
lockfiles = updater.update_lockfiles(buildfile)
117+
files.concat(lockfiles) unless lockfiles.empty?
103118
end
104119

105120
files
106121
end
107122

123+
sig do
124+
params(
125+
buildfiles: T::Array[Dependabot::DependencyFile],
126+
old_req: T::Hash[Symbol, T.untyped],
127+
new_req: T::Hash[Symbol, T.untyped])
128+
.returns(T::Array[Dependabot::DependencyFile])
129+
end
108130
def update_files_for_property_change(buildfiles, old_req, new_req)
109131
files = buildfiles.dup
110132
property_name = new_req.fetch(:metadata).fetch(:property_name)
111-
buildfile = files.find { |f| f.name == new_req.fetch(:file) }
133+
buildfile = T.must(files.find { |f| f.name == new_req.fetch(:file) })
112134

113135
PropertyValueUpdater.new(dependency_files: files)
114136
.update_files_for_property_change(
@@ -119,10 +141,17 @@ def update_files_for_property_change(buildfiles, old_req, new_req)
119141
)
120142
end
121143

144+
sig do
145+
params(
146+
buildfiles: T::Array[Dependabot::DependencyFile],
147+
old_req: T::Hash[Symbol, T.untyped],
148+
new_req: T::Hash[Symbol, T.untyped])
149+
.returns(T::Array[Dependabot::DependencyFile])
150+
end
122151
def update_files_for_dep_set_change(buildfiles, old_req, new_req)
123152
files = buildfiles.dup
124153
dependency_set = new_req.fetch(:metadata).fetch(:dependency_set)
125-
buildfile = files.find { |f| f.name == new_req.fetch(:file) }
154+
buildfile = T.must(files.find { |f| f.name == new_req.fetch(:file) })
126155

127156
DependencySetUpdater.new(dependency_files: files)
128157
.update_files_for_dep_set_change(
@@ -157,8 +186,9 @@ def update_version_in_buildfile(dependency, buildfile, previous_req,
157186
def original_buildfile_declarations(dependency, requirement)
158187
# This implementation is limited to declarations that appear on a
159188
# single line.
160-
buildfile = buildfiles.find { |f| f.name == requirement.fetch(:file) }
161-
buildfile.content.lines.select do |line|
189+
buildfile = T.must(buildfiles.find { |f| f.name == requirement.fetch(:file) })
190+
191+
T.must(buildfile.content).lines.select do |line|
162192
line = evaluate_properties(line, buildfile)
163193
line = line.gsub(%r{(?<=^|\s)//.*$}, "")
164194

@@ -209,6 +239,7 @@ def updated_buildfile_declaration(original_buildfile_declaration, previous_req,
209239
)
210240
end
211241

242+
sig { returns(T::Array[Dependabot::DependencyFile]) }
212243
def buildfiles
213244
@buildfiles ||= dependency_files.reject(&:support_file?)
214245
end
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
# typed: strong
2+
# frozen_string_literal: true
3+
4+
require "sorbet-runtime"
5+
require "shellwords"
6+
7+
require "dependabot/gradle/file_parser"
8+
require "dependabot/gradle/file_updater"
9+
10+
module Dependabot
11+
module Gradle
12+
class LockfileUpdater
13+
extend T::Sig
14+
15+
sig do
16+
params(
17+
dependency_files: T::Array[Dependabot::DependencyFile]
18+
).void
19+
end
20+
def initialize(dependency_files:)
21+
@dependency_files = dependency_files
22+
@lock_files = T.let(dependency_files.select { |f| f.name.end_with?(".lockfile") }, T::Array[Dependabot::DependencyFile])
23+
end
24+
25+
sig do
26+
params(build_file: Dependabot::DependencyFile)
27+
.returns(T::Array[Dependabot::DependencyFile])
28+
end
29+
def update_lockfiles(build_file)
30+
base_dir = build_file.directory
31+
# If we don't have any lockfiles in the build files don't generate one
32+
[] unless @dependency_files.any? do |file|
33+
file.directory == build_file.directory and file.name.end_with?(".lockfile")
34+
end
35+
36+
updated_lockfiles = T.let(Array.new, T::Array[Dependabot::DependencyFile])
37+
SharedHelpers.in_a_temporary_directory do |temp_dir|
38+
for file in @dependency_files
39+
FileUtils.mkdir_p(Pathname.new(file.name).dirname)
40+
File.write(file.name, file.content)
41+
end
42+
43+
command_parts = [
44+
"gradle",
45+
"build",
46+
"--write-locks"
47+
]
48+
49+
command = Shellwords.join(command_parts)
50+
begin
51+
output = SharedHelpers.run_shell_command(command, cwd: File.join(temp_dir, build_file.directory))
52+
for file in @lock_files.select{ |f| f.directory == build_file.directory }
53+
f_content = File.read(File.join(temp_dir, file.name))
54+
tmp_file = file.dup
55+
tmp_file.content = f_content
56+
updated_lockfiles << tmp_file
57+
end
58+
rescue SharedHelpers::HelperSubprocessFailed => e
59+
return updated_lockfiles
60+
end
61+
end
62+
63+
return updated_lockfiles
64+
end
65+
end
66+
end
67+
end

gradle/lib/dependabot/gradle/file_updater/property_value_updater.rb

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,30 @@
11
# typed: true
22
# frozen_string_literal: true
33

4+
require "sorbet-runtime"
5+
46
require "dependabot/gradle/file_updater"
57
require "dependabot/gradle/file_parser/property_value_finder"
68

79
module Dependabot
810
module Gradle
911
class FileUpdater
1012
class PropertyValueUpdater
13+
extend T::Sig
14+
1115
def initialize(dependency_files:)
1216
@dependency_files = dependency_files
1317
end
1418

19+
sig do
20+
params(
21+
property_name: String,
22+
callsite_buildfile: Dependabot::DependencyFile,
23+
previous_value: String,
24+
updated_value: String
25+
)
26+
.returns(T::Array[Dependabot::DependencyFile])
27+
end
1528
def update_files_for_property_change(property_name:,
1629
callsite_buildfile:,
1730
previous_value:,

gradle/spec/dependabot/gradle/file_updater_spec.rb

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,12 @@
2929
package_manager: "gradle"
3030
)
3131
end
32+
let(:buildfile_folder) { "buildfiles" }
3233
let(:buildfile_fixture_name) { "basic_build.gradle" }
3334
let(:buildfile) do
3435
Dependabot::DependencyFile.new(
3536
name: "build.gradle",
36-
content: fixture("buildfiles", buildfile_fixture_name)
37+
content: fixture(buildfile_folder, buildfile_fixture_name)
3738
)
3839
end
3940
let(:dependencies) { [dependency] }
@@ -124,6 +125,61 @@
124125

125126
its(:content) { is_expected.to include "version: '4.2.0'" }
126127

128+
context "with a lockfile" do
129+
let(:buildfile_folder) { "buildfiles/lockfile/app" }
130+
let(:buildfile_fixture_name) { "build.gradle.kts" }
131+
let(:buildfile) do
132+
Dependabot::DependencyFile.new(
133+
name: "app/build.gradle.kts",
134+
directory: "app/",
135+
content: fixture(buildfile_folder, buildfile_fixture_name)
136+
)
137+
end
138+
let(:lockfile) do
139+
Dependabot::DependencyFile.new(
140+
name: "app/gradle.lockfile",
141+
directory: "app/",
142+
content: fixture(buildfile_folder, "gradle.lockfile")
143+
)
144+
end
145+
let(:dependency_files) { [buildfile, lockfile, Dependabot::DependencyFile.new(
146+
name: "settings.gradle.kts",
147+
directory: "/",
148+
content: fixture(buildfile_folder, "..", "settings.gradle.kts")
149+
)] }
150+
let(:dependencies) do
151+
[
152+
Dependabot::Dependency.new(
153+
name: "com.google.code.gson:gson",
154+
version: "2.8.9",
155+
package_manager: "gradle",
156+
requirements: [{
157+
file: "app/build.gradle.kts",
158+
requirement: "2.8.9",
159+
groups: [],
160+
source: nil,
161+
metadata: nil
162+
}],
163+
previous_requirements: [{
164+
file: "app/build.gradle.kts",
165+
requirement: "2.8.8",
166+
groups: [],
167+
source: nil,
168+
metadata: nil
169+
}]
170+
)
171+
]
172+
end
173+
174+
it "updates build.gradle.kts and gradle.lockfile" do
175+
expect(updated_files.map(&:name)).to eq(["app/build.gradle.kts", "app/gradle.lockfile"])
176+
expect(updated_files.first.content)
177+
.to include('implementation("com.google.code.gson:gson:2.8.9")')
178+
expect(updated_files.last.content)
179+
.to include('com.google.code.gson:gson:2.8.9=runtimeClasspath')
180+
end
181+
end
182+
127183
context "with kotlin" do
128184
let(:buildfile_fixture_name) { "build.gradle.kts" }
129185

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Ignore Gradle project-specific cache directory
2+
.gradle
3+
4+
# Ignore Gradle build output directory
5+
build

0 commit comments

Comments
 (0)