diff --git a/terraform/lib/dependabot/terraform/requirements_updater.rb b/terraform/lib/dependabot/terraform/requirements_updater.rb index 250e8145cc..aa42da396c 100644 --- a/terraform/lib/dependabot/terraform/requirements_updater.rb +++ b/terraform/lib/dependabot/terraform/requirements_updater.rb @@ -190,17 +190,20 @@ def update_greatest_version(requirement, version_to_be_permitted) op, version = requirement.requirements.first version = version.release if version.prerelease? - index_to_update = - version.segments.map.with_index { |seg, i| seg.zero? ? 0 : i }.max - - new_segments = version.segments.map.with_index do |_, index| - if index < index_to_update + # When 'less than'/'<', + # increment the last available segment only so that the new version is within the constraint + if op == "<" + new_segments = version.segments.map.with_index do |_, index| version_to_be_permitted.segments[index] - elsif index == index_to_update - version_to_be_permitted.segments[index].to_i + 1 - else - 0 end + new_segments[-1] += 1 + # When 'less-than/equal'/'<=', use the new version as-is even when previously set as a non-semver version + # Terraform treats shortened versions the same as a version with any remaining segments as 0 + # Example: '0.2' is treated as '0.2.0' | '1' is treated as '1.0.0' + elsif op == "<=" + new_segments = version_to_be_permitted.segments + else + raise "Unexpected operation: #{op}" end requirement_class.new("#{op} #{new_segments.join('.')}") diff --git a/terraform/spec/dependabot/terraform/requirements_updater_spec.rb b/terraform/spec/dependabot/terraform/requirements_updater_spec.rb index 379c0f604f..b692035338 100644 --- a/terraform/spec/dependabot/terraform/requirements_updater_spec.rb +++ b/terraform/spec/dependabot/terraform/requirements_updater_spec.rb @@ -77,7 +77,49 @@ end end - context "when a =>,< requirement was previously specified" do + context "when <= requirement was previously specified" do + context "when it is satisfied" do + let(:requirement) { "<= 0.3.7" } + + it { is_expected.to eq(requirements.first) } + end + + context "when it is not satisfied" do + let(:requirement) { "<= 0.1.9" } + + its([:requirement]) { is_expected.to eq("<= 0.3.7") } + + context "when specifying two version segments" do + let(:requirement) { "<= 0.3" } + let(:latest_version) { version_class.new("2.8.5") } + + its([:requirement]) { is_expected.to eq("<= 2.8.5") } + end + + context "when specifying three version segments" do + let(:requirement) { "<= 0.3.7" } + let(:latest_version) { version_class.new("2.8.5") } + + its([:requirement]) { is_expected.to eq("<= 2.8.5") } + end + + context "when minor and patch updated" do + let(:requirement) { "<= 0.3.7" } + let(:latest_version) { version_class.new("0.4.0") } + + its([:requirement]) { is_expected.to eq("<= 0.4.0") } + end + + context "when major, minor and patch updated" do + let(:requirement) { "<= 0.3.7" } + let(:latest_version) { version_class.new("1.4.0") } + + its([:requirement]) { is_expected.to eq("<= 1.4.0") } + end + end + end + + context "when a =>,<,<= requirement was previously specified" do context "when satisfied" do let(:requirement) { ">= 0.2.1, < 0.4.0" } let(:latest_version) { "0.3.7" } @@ -85,11 +127,32 @@ its([:requirement]) { is_expected.to eq(">= 0.2.1, < 0.4.0") } end - context "when not satisfied" do - let(:requirement) { ">= 0.2.1, < 0.3.0" } + context "when not satisfied, 0 patch version" do + let(:requirement) { ">= 0.2.1, < 0.3.0, <= 0.3.0" } let(:latest_version) { "0.3.7" } - its([:requirement]) { is_expected.to eq(">= 0.2.1, < 0.4.0") } + its([:requirement]) { is_expected.to eq(">= 0.2.1, < 0.3.8, <= 0.3.7") } + end + + context "when not satisfied, non-0 patch version" do + let(:requirement) { ">= 0.2.1, < 0.3.2, <= 0.3.2" } + let(:latest_version) { "0.3.7" } + + its([:requirement]) { is_expected.to eq(">= 0.2.1, < 0.3.8, <= 0.3.7") } + end + + context "when not satisfied, major and minor only" do + let(:requirement) { ">= 0.2.1, < 0.3, <= 0.3" } + let(:latest_version) { "0.3.7" } + + its([:requirement]) { is_expected.to eq(">= 0.2.1, < 0.4, <= 0.3.7") } + end + + context "when not satisfied, major and minor only" do + let(:requirement) { ">= 0.2.1, < 0.3, <= 0.3" } + let(:latest_version) { "1.4.0" } + + its([:requirement]) { is_expected.to eq(">= 0.2.1, < 1.5, <= 1.4.0") } end end end