Skip to content

Commit

Permalink
Use an NpmAndYarn version class to preserve pre-release formatting
Browse files Browse the repository at this point in the history
  • Loading branch information
greysteil committed Dec 23, 2017
1 parent eb02c4a commit 0280cdc
Show file tree
Hide file tree
Showing 5 changed files with 106 additions and 20 deletions.
11 changes: 9 additions & 2 deletions lib/dependabot/update_checkers/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,12 @@ def updated_requirements
raise NotImplementedError
end

# Update checkers can (optionally) define their own version class, to be
# used when comparing and serializing versions
def version_class
Gem::Version
end

private

def latest_version_resolvable_with_full_unlock?
Expand Down Expand Up @@ -103,7 +109,8 @@ def sha1_version_can_update?(full_unlock: false)
end

def numeric_version_up_to_date?
latest_version && latest_version <= Gem::Version.new(dependency.version)
return false unless latest_version
latest_version <= version_class.new(dependency.version)
end

def numeric_version_can_update?(full_unlock: false)
Expand All @@ -113,7 +120,7 @@ def numeric_version_can_update?(full_unlock: false)

return latest_version_resolvable_with_full_unlock? if full_unlock
return false if latest_resolvable_version.nil?
latest_resolvable_version > Gem::Version.new(dependency.version)
latest_resolvable_version > version_class.new(dependency.version)
end

def requirements_up_to_date?
Expand Down
16 changes: 10 additions & 6 deletions lib/dependabot/update_checkers/java_script/npm_and_yarn.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ module UpdateCheckers
module JavaScript
class NpmAndYarn < Dependabot::UpdateCheckers::Base
require_relative "npm_and_yarn/requirements_updater"
require_relative "npm_and_yarn/version"

def latest_version
@latest_version ||= fetch_latest_version
Expand All @@ -30,6 +31,10 @@ def updated_requirements
).updated_requirements
end

def version_class
NpmAndYarn::Version
end

private

def latest_version_resolvable_with_full_unlock?
Expand All @@ -44,16 +49,16 @@ def updated_dependencies_after_full_unlock
def fetch_latest_version
return nil unless npm_details&.fetch("dist-tags", nil)
latest_dist_tag = npm_details["dist-tags"]["latest"]
latest_dist_tag = Gem::Version.new(latest_dist_tag)
latest_dist_tag = version_class.new(latest_dist_tag)
return latest_dist_tag if use_latest_dist_tag?(latest_dist_tag)

latest_release =
npm_details["versions"].
keys.map { |v| Gem::Version.new(v) }.
keys.map { |v| version_class.new(v) }.
reject { |v| v.prerelease? && !wants_prerelease? }.sort.reverse.
find { |version| !yanked?(version) }

Gem::Version.new(latest_release)
version_class.new(latest_release)
rescue Excon::Error::Socket, Excon::Error::Timeout
raise if dependency_registry == "registry.npmjs.org"
# Sometimes custom registries are flaky. We don't want to make that
Expand All @@ -65,9 +70,8 @@ def use_latest_dist_tag?(version)
end

def yanked?(version)
version_string = version.to_s.gsub(".pre.", "-")
Excon.get(
dependency_url + "/#{version_string}",
dependency_url + "/#{version}",
headers: registry_auth_headers,
idempotent: true,
middlewares: SharedHelpers.excon_middleware
Expand Down Expand Up @@ -120,7 +124,7 @@ def private_dependency_not_reachable?(npm_response)

def wants_prerelease?
current_version = dependency.version
if current_version && Gem::Version.new(current_version).prerelease?
if current_version && version_class.new(current_version).prerelease?
return true
end

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
################################################################################

require "dependabot/update_checkers/java_script/npm_and_yarn"
require "dependabot/update_checkers/java_script/npm_and_yarn/version"

module Dependabot
module UpdateCheckers
Expand All @@ -20,12 +21,14 @@ class RequirementsUpdater
def initialize(requirements:, library:,
latest_version:, latest_resolvable_version:)
@requirements = requirements
@latest_version = Gem::Version.new(latest_version) if latest_version
@library = library
if latest_version
@latest_version = version_class.new(latest_version)
end

return unless latest_resolvable_version
@latest_resolvable_version =
Gem::Version.new(latest_resolvable_version)
version_class.new(latest_resolvable_version)
end

def updated_requirements
Expand Down Expand Up @@ -147,28 +150,26 @@ def update_range_requirement(req_string)
if range_requirements.count == 1
range_requirement = range_requirements.first
versions = range_requirement.scan(VERSION_REGEX)
upper_bound = versions.map { |v| Gem::Version.new(v) }.max
upper_bound = versions.map { |v| version_class.new(v) }.max
new_upper_bound = update_greatest_version(
upper_bound,
latest_resolvable_version
)

req_string.sub(
version_as_string(upper_bound),
version_as_string(new_upper_bound)
upper_bound.to_s,
new_upper_bound.to_s
)
else
version_string = version_as_string(latest_resolvable_version)
req_string + " || ^#{version_string}"
req_string + " || ^#{latest_resolvable_version}"
end
end

def update_version_string(req_string)
new_version_string = version_as_string(latest_resolvable_version)
req_string.
sub(VERSION_REGEX) do |old_version|
old_parts = old_version.split(".")
new_parts = new_version_string.to_s.split(".").
new_parts = latest_resolvable_version.to_s.split(".").
first(old_parts.count)
new_parts.map.with_index do |part, i|
old_parts[i].match?(/^x\b/) ? "x" : part
Expand All @@ -177,7 +178,7 @@ def update_version_string(req_string)
end

def update_greatest_version(old_version, version_to_be_permitted)
version = Gem::Version.new(old_version)
version = version_class.new(old_version)
version = version.release if version.prerelease?

index_to_update =
Expand All @@ -193,8 +194,8 @@ def update_greatest_version(old_version, version_to_be_permitted)
end.join(".")
end

def version_as_string(version)
version.to_s.gsub(".pre.", "-")
def version_class
NpmAndYarn::Version
end
end
end
Expand Down
26 changes: 26 additions & 0 deletions lib/dependabot/update_checkers/java_script/npm_and_yarn/version.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# frozen_string_literal: true

require "dependabot/update_checkers/java_script/npm_and_yarn"

# JavaScript pre-release versions user 1.0.1-rc1 syntax, which Gem::Version
# converts into 1.0.1.pre.rc1. We override the `to_s` method to stop that
# alteration.

module Dependabot
module UpdateCheckers
module JavaScript
class NpmAndYarn
class Version < Gem::Version
def initialize(version)
@version_string = version.to_s
super
end

def to_s
@version_string
end
end
end
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# frozen_string_literal: true

require "spec_helper"
require "dependabot/update_checkers/java_script/npm_and_yarn/version"

RSpec.describe Dependabot::UpdateCheckers::JavaScript::NpmAndYarn::Version do
subject(:version) { described_class.new(version_string) }
let(:version_string) { "1.0.0" }

describe "#to_s" do
subject { version.to_s }

context "with a non-prerelease" do
let(:version_string) { "1.0.0" }
it { is_expected.to eq "1.0.0" }
end

context "with a normal prerelease" do
let(:version_string) { "1.0.0.pre1" }
it { is_expected.to eq "1.0.0.pre1" }
end

context "with a JS-style prerelease" do
let(:version_string) { "1.0.0-pre1" }
it { is_expected.to eq "1.0.0-pre1" }
end
end

describe "compatibility with Gem::Requirement" do
subject { requirement.satisfied_by?(version) }
let(:requirement) { Gem::Requirement.new(">= 1.0.0") }

context "with a valid version" do
let(:version_string) { "1.0.0" }
it { is_expected.to eq(true) }
end

context "with an invalid version" do
let(:version_string) { "0.9.0" }
it { is_expected.to eq(false) }
end

context "with a valid prerelease version" do
let(:version_string) { "1.1.0-pre" }
it { is_expected.to eq(true) }
end
end
end

0 comments on commit 0280cdc

Please sign in to comment.