Skip to content

Commit 7da4bdd

Browse files
authored
Fixed issue related to calculate cooldown period by providing semver major version as highest priority. (#12869)
* Fixed issue related to calculate cooldown max date v/s calculate cooldown days by giving priority to major_days if there then minor days if there then patch if any. * Added feature to not to execute cooldown associated code if user has not added it in D.yml * Modified specs since method signature changed and added check for cooldown enabled flag with new logic.
1 parent b12ec08 commit 7da4bdd

File tree

2 files changed

+145
-47
lines changed

2 files changed

+145
-47
lines changed

swift/lib/dependabot/swift/update_checker/latest_version_resolver.rb

Lines changed: 76 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -37,36 +37,44 @@ def initialize(dependency:, credentials:, cooldown_options:, git_commit_checker:
3737
sig { returns(Dependabot::Dependency) }
3838
attr_reader :dependency
3939

40+
sig { returns(T::Array[Dependabot::Credential]) }
41+
attr_reader :credentials
42+
43+
sig { returns(T.nilable(Dependabot::Package::ReleaseCooldownOptions)) }
44+
attr_reader :cooldown_options
45+
46+
sig { returns(Dependabot::GitCommitChecker) }
47+
attr_reader :git_commit_checker
48+
4049
# Return latest version tag for the dependency, it removes tags that are in cooldown period
41-
# and returns the latest version tag that is not in cooldown period. If eexception occurs
42-
# it will return the latest version tag from the git_commit_checker. as it was before
50+
# and returns the latest version tag that is not in cooldown period. If an exception occurs
51+
# and returns the latest version tag that is not in cooldown period. If an exception occurs,
52+
# it will return the latest version tag from the git_commit_checker, as before.
4353
sig { returns(T.nilable(T::Hash[Symbol, T.untyped])) }
4454
def latest_version_tag
4555
# step one fetch allowed version tags and
56+
return git_commit_checker.local_tag_for_latest_version unless cooldown_enabled?
57+
4658
allowed_version_tags = git_commit_checker.allowed_version_tags
47-
begin
48-
# sort the allowed version tags by name in descending order
49-
select_version_tags_in_cooldown_period&.each do |tag_name|
50-
# filter out if name is not in cooldown period
51-
allowed_version_tags.reject! do |gitref_filtered|
52-
true if gitref_filtered.name == tag_name
53-
end
59+
select_version_tags_in_cooldown_period&.each do |tag_name|
60+
# filter out if name is in cooldown period
61+
allowed_version_tags.reject! do |gitref_filtered|
62+
gitref_filtered.name == tag_name
5463
end
55-
Dependabot.logger.info("Allowed version tags after filtering versions in cooldown:
56-
#{allowed_version_tags.map(&:name).join(', ')}")
57-
git_commit_checker.max_local_tag(allowed_version_tags)
58-
rescue StandardError => e
59-
Dependabot.logger.error("Error fetching latest version tag: #{e.message}")
60-
git_commit_checker.local_tag_for_latest_version
6164
end
65+
66+
git_commit_checker.max_local_tag(allowed_version_tags)
67+
rescue StandardError => e
68+
Dependabot.logger.error("Error fetching latest version tag: #{e.message}")
69+
git_commit_checker.local_tag_for_latest_version
6270
end
6371

6472
sig { returns(T.nilable(T::Array[String])) }
6573
def select_version_tags_in_cooldown_period
6674
version_tags_in_cooldown_period = T.let([], T::Array[String])
6775

6876
package_details_fetcher.fetch_tag_and_release_date.each do |git_tag_with_detail|
69-
if check_if_version_in_cooldown_period?(T.must(git_tag_with_detail.release_date))
77+
if check_if_version_in_cooldown_period?(git_tag_with_detail)
7078
version_tags_in_cooldown_period << git_tag_with_detail.tag
7179
end
7280
end
@@ -76,27 +84,59 @@ def select_version_tags_in_cooldown_period
7684
version_tags_in_cooldown_period
7785
end
7886

79-
sig { params(release_date: String).returns(T::Boolean) }
80-
def check_if_version_in_cooldown_period?(release_date)
81-
return false unless release_date.length.positive?
87+
sig { params(tag_with_detail: Dependabot::GitTagWithDetail).returns(T::Boolean) }
88+
def check_if_version_in_cooldown_period?(tag_with_detail)
89+
return false unless tag_with_detail.release_date
8290

83-
cooldown = @cooldown_options
84-
return false unless cooldown
91+
current_version = version_class.correct?(dependency.version) ? version_class.new(dependency.version) : nil
92+
days = cooldown_days_for(current_version, version_class.new(tag_with_detail.tag.delete("v")))
8593

86-
return false if cooldown.nil?
87-
88-
# Get maximum cooldown days based on semver parts
89-
days = [cooldown.default_days, cooldown.semver_major_days].max
90-
days = cooldown.semver_minor_days unless days > cooldown.semver_minor_days
91-
days = cooldown.semver_patch_days unless days > cooldown.semver_patch_days
9294
# Calculate the number of seconds passed since the release
93-
passed_seconds = Time.now.to_i - release_date_to_seconds(release_date)
95+
passed_seconds = Time.now.to_i - release_date_to_seconds(tag_with_detail.release_date)
9496
# Check if the release is within the cooldown period
9597
passed_seconds < days * DAY_IN_SECONDS
9698
end
9799

98-
sig { params(release_date: String).returns(Integer) }
100+
sig do
101+
params(
102+
current_version: T.nilable(Dependabot::Version),
103+
new_version: Dependabot::Version
104+
).returns(Integer)
105+
end
106+
def cooldown_days_for(current_version, new_version)
107+
return 0 unless cooldown_enabled?
108+
109+
cooldown = T.must(cooldown_options)
110+
return 0 unless cooldown.included?(dependency.name)
111+
return cooldown.default_days if current_version.nil?
112+
113+
current_version_semver = current_version.semver_parts
114+
new_version_semver = new_version.semver_parts
115+
116+
# If semver_parts is nil for either, return default cooldown
117+
return cooldown.default_days if current_version_semver.nil? || new_version_semver.nil?
118+
119+
# Ensure values are always integers
120+
current_major, current_minor, current_patch = current_version_semver
121+
new_major, new_minor, new_patch = new_version_semver
122+
123+
# Determine cooldown based on version difference
124+
return cooldown.semver_major_days if new_major > current_major
125+
return cooldown.semver_minor_days if new_minor > current_minor
126+
return cooldown.semver_patch_days if new_patch > current_patch
127+
128+
cooldown.default_days
129+
end
130+
131+
sig { returns(T.class_of(Dependabot::Version)) }
132+
def version_class
133+
dependency.version_class
134+
end
135+
136+
sig { params(release_date: T.nilable(String)).returns(Integer) }
99137
def release_date_to_seconds(release_date)
138+
return 0 unless release_date
139+
100140
Time.parse(release_date).to_i
101141
rescue ArgumentError => e
102142
Dependabot.logger.error("Invalid release date format: #{release_date} and error: #{e.message}")
@@ -114,17 +154,16 @@ def package_details_fetcher
114154
)
115155
end
116156

117-
# Since base class is returning false, we need to override it.
118157
sig { returns(T::Boolean) }
119158
def cooldown_enabled?
120-
true
121-
end
122-
123-
sig { returns(Dependabot::GitCommitChecker) }
124-
attr_reader :git_commit_checker
159+
return false if cooldown_options.nil?
125160

126-
sig { returns(T::Array[Dependabot::Credential]) }
127-
attr_reader :credentials
161+
cooldown = T.must(cooldown_options)
162+
cooldown.default_days.to_i.positive? ||
163+
cooldown.semver_major_days.to_i.positive? ||
164+
cooldown.semver_minor_days.to_i.positive? ||
165+
cooldown.semver_patch_days.to_i.positive?
166+
end
128167
end
129168
end
130169
end

swift/spec/dependabot/swift/update_checker/latest_version_resolver_spec.rb

Lines changed: 69 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -101,21 +101,80 @@
101101
end
102102

103103
describe "#check_if_version_in_cooldown_period?" do
104-
it "returns true if the release date is within the cooldown period" do
105-
release_date = (Time.now - (10 * 24 * 60 * 60)).iso8601 # 10 days ago
106-
expect(resolver.check_if_version_in_cooldown_period?(release_date)).to be true
104+
before do
105+
allow(Time).to receive(:now).and_return(Time.parse("2025-08-08T17:30:00.000Z"))
106+
end
107+
108+
let(:tag_with_detail) do
109+
Dependabot::GitTagWithDetail.new(
110+
tag: "1.2.0",
111+
release_date: release_date
112+
)
107113
end
108114

109-
it "returns false if the release date is outside the cooldown period" do
110-
release_date = (Time.now - (100 * 24 * 60 * 60)).iso8601 # 100 days ago
111-
expect(resolver.check_if_version_in_cooldown_period?(release_date)).to be false
115+
context "when tag has no release date" do
116+
let(:release_date) { nil }
117+
118+
it "returns false" do
119+
expect(resolver.check_if_version_in_cooldown_period?(tag_with_detail)).to be false
120+
end
121+
end
122+
123+
context "when tag has a release date" do
124+
let(:release_date) { (Time.now - (10 * 24 * 60 * 60)).iso8601 } # 10 days ago
125+
126+
before do
127+
allow(resolver).to receive(:cooldown_days_for).and_return(30)
128+
end
129+
130+
it "returns false if the release date is outside the cooldown period" do
131+
allow(resolver).to receive(:cooldown_days_for).and_return(5)
132+
expect(resolver.check_if_version_in_cooldown_period?(tag_with_detail)).to be false
133+
end
112134
end
113135
end
114136

115-
describe "#release_date_to_seconds" do
116-
it "converts a valid release date string to seconds" do
117-
release_date = "2025-05-27T12:34:56Z"
118-
expect(resolver.release_date_to_seconds(release_date)).to eq(Time.parse(release_date).to_i)
137+
describe "#cooldown_enabled?" do
138+
context "when cooldown_options is nil" do
139+
let(:cooldown_options) { nil }
140+
141+
it "returns false" do
142+
expect(resolver.cooldown_enabled?).to be false
143+
end
144+
end
145+
146+
context "when cooldown_options has positive days" do
147+
it "returns true when default_days is positive" do
148+
allow(cooldown_options).to receive_messages(
149+
default_days: 1,
150+
semver_major_days: 0,
151+
semver_minor_days: 0,
152+
semver_patch_days: 0
153+
)
154+
expect(resolver.cooldown_enabled?).to be true
155+
end
156+
157+
it "returns true when semver_major_days is positive" do
158+
allow(cooldown_options).to receive_messages(
159+
default_days: 0,
160+
semver_major_days: 1,
161+
semver_minor_days: 0,
162+
semver_patch_days: 0
163+
)
164+
expect(resolver.cooldown_enabled?).to be true
165+
end
166+
end
167+
168+
context "when all cooldown days are zero" do
169+
it "returns false" do
170+
allow(cooldown_options).to receive_messages(
171+
default_days: 0,
172+
semver_major_days: 0,
173+
semver_minor_days: 0,
174+
semver_patch_days: 0
175+
)
176+
expect(resolver.cooldown_enabled?).to be false
177+
end
119178
end
120179
end
121180
end

0 commit comments

Comments
 (0)