From 75fc2c211fb33e21955744573ce94c7f7d7a38e3 Mon Sep 17 00:00:00 2001 From: Alfred Mazimbe Date: Wed, 27 Nov 2024 15:45:39 +0000 Subject: [PATCH 1/3] Add support for hex ecosystem metrics collection --- hex/lib/dependabot/hex/file_parser.rb | 61 +++++++++++++++++++ hex/lib/dependabot/hex/language.rb | 21 +++++++ hex/lib/dependabot/hex/package_manager.rb | 41 +++++++++++++ hex/spec/dependabot/hex/file_parser_spec.rb | 28 +++++++++ hex/spec/dependabot/hex/language_spec.rb | 44 +++++++++++++ .../dependabot/hex/package_manager_spec.rb | 36 +++++++++++ 6 files changed, 231 insertions(+) create mode 100644 hex/lib/dependabot/hex/language.rb create mode 100644 hex/lib/dependabot/hex/package_manager.rb create mode 100644 hex/spec/dependabot/hex/language_spec.rb create mode 100644 hex/spec/dependabot/hex/package_manager_spec.rb diff --git a/hex/lib/dependabot/hex/file_parser.rb b/hex/lib/dependabot/hex/file_parser.rb index f575b744d8..7ffcb51e3f 100644 --- a/hex/lib/dependabot/hex/file_parser.rb +++ b/hex/lib/dependabot/hex/file_parser.rb @@ -7,12 +7,16 @@ require "dependabot/file_parsers/base" require "dependabot/hex/file_updater/mixfile_sanitizer" require "dependabot/hex/native_helpers" +require "dependabot/hex/language" +require "dependabot/hex/package_manager" +require "dependabot/hex/requirement" require "dependabot/shared_helpers" require "dependabot/errors" # For docs, see https://hexdocs.pm/mix/Mix.Tasks.Deps.html module Dependabot module Hex + extend T::Sig class FileParser < Dependabot::FileParsers::Base extend T::Sig require "dependabot/file_parsers/base/dependency_set" @@ -44,6 +48,17 @@ def parse dependency_set.dependencies.sort_by(&:name) end + sig { returns(Ecosystem) } + def ecosystem + @ecosystem ||= T.let(begin + Ecosystem.new( + name: ECOSYSTEM, + package_manager: package_manager, + language: language + ) + end, T.nilable(Dependabot::Ecosystem)) + end + private sig { returns(T::Array[T.any(T::Hash[String, String], T::Hash[String, T.untyped])]) } @@ -137,6 +152,52 @@ def mixfiles def lockfile @lockfile ||= T.let(get_original_file("mix.lock"), T.nilable(Dependabot::DependencyFile)) end + + sig { returns(Ecosystem::VersionManager) } + def package_manager + @package_manager ||= T.let( + PackageManager.new(hex_version), + T.nilable(Dependabot::Hex::PackageManager) + ) + end + + sig { returns(T.nilable(Ecosystem::VersionManager)) } + def language + @language ||= T.let( + Language.new(elixir_version, language_requirement), + T.nilable(Dependabot::Hex::Language) + ) + end + + sig { returns(String) } + def hex_version + T.must(T.must(hex_info).fetch(:hex_version)) + end + + sig { returns(String) } + def elixir_version + T.must(T.must(hex_info).fetch(:elixir_version)) + end + + sig { returns(T.nilable(T::Hash[Symbol, T.nilable(String)])) } + def hex_info + @hex_info ||= T.let(begin + version = SharedHelpers.run_shell_command("mix hex.info") + { + hex_version: version.match(/Hex: \s*(\d+\.\d+(.\d+)*)/)&.captures&.first, + elixir_version: version.match(/Elixir: \s*(\d+\.\d+(.\d+)*)/)&.captures&.first + } + end, T.nilable(T::Hash[Symbol, T.nilable(String)])) + end + + sig { returns(T.nilable(Dependabot::Hex::Requirement)) } + def language_requirement + command = "mix run --eval 'Mix.Project.config()[:elixir] |> IO.puts()'" + requirement = SharedHelpers.run_shell_command(command) + return if requirement.empty? || requirement.nil? + + Dependabot::Hex::Requirement.new(requirement) + end end end end diff --git a/hex/lib/dependabot/hex/language.rb b/hex/lib/dependabot/hex/language.rb new file mode 100644 index 0000000000..e5eea84127 --- /dev/null +++ b/hex/lib/dependabot/hex/language.rb @@ -0,0 +1,21 @@ +# typed: strong +# frozen_string_literal: true + +require "sorbet-runtime" +require "dependabot/ecosystem" +require "dependabot/hex/version" + +module Dependabot + module Hex + LANGUAGE = "elixir" + + class Language < Dependabot::Ecosystem::VersionManager + extend T::Sig + + sig { params(raw_version: String, requirement: T.nilable(Requirement)).void } + def initialize(raw_version, requirement = nil) + super(LANGUAGE, Version.new(raw_version), [], [], requirement) + end + end + end +end diff --git a/hex/lib/dependabot/hex/package_manager.rb b/hex/lib/dependabot/hex/package_manager.rb new file mode 100644 index 0000000000..f973be7d12 --- /dev/null +++ b/hex/lib/dependabot/hex/package_manager.rb @@ -0,0 +1,41 @@ +# typed: strong +# frozen_string_literal: true + +require "sorbet-runtime" +require "dependabot/ecosystem" +require "dependabot/hex/version" + +module Dependabot + module Hex + ECOSYSTEM = "hex" + PACKAGE_MANAGER = "hex" + SUPPORTED_HEX_VERSIONS = T.let([].freeze, T::Array[Dependabot::Version]) + + # When a version is going to be unsupported, it will be added here + DEPRECATED_HEX_VERSIONS = T.let([].freeze, T::Array[Dependabot::Version]) + + class PackageManager < Dependabot::Ecosystem::VersionManager + extend T::Sig + + sig { params(raw_version: String).void } + def initialize(raw_version) + super( + PACKAGE_MANAGER, + Version.new(raw_version), + DEPRECATED_HEX_VERSIONS, + SUPPORTED_HEX_VERSIONS + ) + end + + sig { returns(T::Boolean) } + def deprecated? + false + end + + sig { returns(T::Boolean) } + def unsupported? + false + end + end + end +end diff --git a/hex/spec/dependabot/hex/file_parser_spec.rb b/hex/spec/dependabot/hex/file_parser_spec.rb index efc026fbb4..38de1f66e0 100644 --- a/hex/spec/dependabot/hex/file_parser_spec.rb +++ b/hex/spec/dependabot/hex/file_parser_spec.rb @@ -452,4 +452,32 @@ end end end + + describe "#ecosystem" do + subject(:ecosystem) { parser.ecosystem } + + it "has the correct name" do + expect(ecosystem.name).to eq "hex" + end + + describe "#package_manager" do + subject(:package_manager) { ecosystem.package_manager } + + it "returns the correct package manager" do + expect(package_manager.name).to eq "hex" + expect(package_manager.requirement).to be_nil + expect(package_manager.version.to_s).to eq "2.1.1" + end + end + + describe "#language" do + subject(:language) { ecosystem.language } + + it "returns the correct language" do + expect(language.name).to eq "elixir" + expect(language.requirement).to be_nil + expect(language.version.to_s).to eq "1.17.3" + end + end + end end diff --git a/hex/spec/dependabot/hex/language_spec.rb b/hex/spec/dependabot/hex/language_spec.rb new file mode 100644 index 0000000000..36ee98e6ba --- /dev/null +++ b/hex/spec/dependabot/hex/language_spec.rb @@ -0,0 +1,44 @@ +# typed: false +# frozen_string_literal: true + +require "dependabot/hex/requirement" +require "dependabot/hex/language" +require "dependabot/ecosystem" +require "spec_helper" + +RSpec.describe Dependabot::Hex::Language do + subject(:language) { described_class.new(version, requirement) } + + let(:version) { "1.17.3" } + let(:requirement) { Dependabot::Hex::Requirement.new("~> 1.5") } + + describe "#version" do + it "returns the version" do + expect(language.version).to eq(Dependabot::Hex::Version.new(version)) + end + end + + describe "#name" do + it "returns the name" do + expect(language.name).to eq(Dependabot::Hex::LANGUAGE) + end + end + + describe "#requirement" do + it "returns the requirement" do + expect(language.requirement).to eq(requirement) + end + end + + describe "#unsupported?" do + it "returns false by default" do + expect(language.unsupported?).to be false + end + end + + describe "#deprecated?" do + it "returns false by default" do + expect(language.deprecated?).to be false + end + end +end diff --git a/hex/spec/dependabot/hex/package_manager_spec.rb b/hex/spec/dependabot/hex/package_manager_spec.rb new file mode 100644 index 0000000000..da86d34185 --- /dev/null +++ b/hex/spec/dependabot/hex/package_manager_spec.rb @@ -0,0 +1,36 @@ +# typed: false +# frozen_string_literal: true + +require "dependabot/hex/package_manager" +require "dependabot/ecosystem" +require "spec_helper" + +RSpec.describe Dependabot::Hex::PackageManager do + subject(:package_manager) { described_class.new(version) } + + let(:version) { "2.1.1" } + + describe "#version" do + it "returns the version" do + expect(package_manager.version).to eq(Dependabot::Hex::Version.new(version)) + end + end + + describe "#name" do + it "returns the name" do + expect(package_manager.name).to eq(Dependabot::Hex::PACKAGE_MANAGER) + end + end + + describe "#deprecated_versions" do + it "returns deprecated versions" do + expect(package_manager.deprecated_versions).to eq(Dependabot::Hex::DEPRECATED_HEX_VERSIONS) + end + end + + describe "#supported_versions" do + it "returns supported versions" do + expect(package_manager.supported_versions).to eq(Dependabot::Hex::SUPPORTED_HEX_VERSIONS) + end + end +end From 9381b0d714481c8f76872954b77847ad382bc278 Mon Sep 17 00:00:00 2001 From: Alfred Mazimbe Date: Thu, 28 Nov 2024 12:07:58 +0000 Subject: [PATCH 2/3] Temporarily disable language requirement --- hex/lib/dependabot/hex/file_parser.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hex/lib/dependabot/hex/file_parser.rb b/hex/lib/dependabot/hex/file_parser.rb index 7ffcb51e3f..574fbd92fb 100644 --- a/hex/lib/dependabot/hex/file_parser.rb +++ b/hex/lib/dependabot/hex/file_parser.rb @@ -164,7 +164,7 @@ def package_manager sig { returns(T.nilable(Ecosystem::VersionManager)) } def language @language ||= T.let( - Language.new(elixir_version, language_requirement), + Language.new(elixir_version, nil), T.nilable(Dependabot::Hex::Language) ) end From 142f767b4e566fc7999ea02fc6a7c8c49ec90472 Mon Sep 17 00:00:00 2001 From: Alfred Mazimbe Date: Thu, 28 Nov 2024 15:50:51 +0000 Subject: [PATCH 3/3] Remove inconsistent language requirement --- hex/lib/dependabot/hex/file_parser.rb | 11 +---------- hex/lib/dependabot/hex/language.rb | 6 +++--- hex/spec/dependabot/hex/file_parser_spec.rb | 4 ++-- hex/spec/dependabot/hex/language_spec.rb | 10 +--------- 4 files changed, 7 insertions(+), 24 deletions(-) diff --git a/hex/lib/dependabot/hex/file_parser.rb b/hex/lib/dependabot/hex/file_parser.rb index 574fbd92fb..d39067d116 100644 --- a/hex/lib/dependabot/hex/file_parser.rb +++ b/hex/lib/dependabot/hex/file_parser.rb @@ -164,7 +164,7 @@ def package_manager sig { returns(T.nilable(Ecosystem::VersionManager)) } def language @language ||= T.let( - Language.new(elixir_version, nil), + Language.new(elixir_version), T.nilable(Dependabot::Hex::Language) ) end @@ -189,15 +189,6 @@ def hex_info } end, T.nilable(T::Hash[Symbol, T.nilable(String)])) end - - sig { returns(T.nilable(Dependabot::Hex::Requirement)) } - def language_requirement - command = "mix run --eval 'Mix.Project.config()[:elixir] |> IO.puts()'" - requirement = SharedHelpers.run_shell_command(command) - return if requirement.empty? || requirement.nil? - - Dependabot::Hex::Requirement.new(requirement) - end end end end diff --git a/hex/lib/dependabot/hex/language.rb b/hex/lib/dependabot/hex/language.rb index e5eea84127..b315e6ab61 100644 --- a/hex/lib/dependabot/hex/language.rb +++ b/hex/lib/dependabot/hex/language.rb @@ -12,9 +12,9 @@ module Hex class Language < Dependabot::Ecosystem::VersionManager extend T::Sig - sig { params(raw_version: String, requirement: T.nilable(Requirement)).void } - def initialize(raw_version, requirement = nil) - super(LANGUAGE, Version.new(raw_version), [], [], requirement) + sig { params(raw_version: String).void } + def initialize(raw_version) + super(LANGUAGE, Version.new(raw_version)) end end end diff --git a/hex/spec/dependabot/hex/file_parser_spec.rb b/hex/spec/dependabot/hex/file_parser_spec.rb index 38de1f66e0..bc5fca2d5e 100644 --- a/hex/spec/dependabot/hex/file_parser_spec.rb +++ b/hex/spec/dependabot/hex/file_parser_spec.rb @@ -466,7 +466,7 @@ it "returns the correct package manager" do expect(package_manager.name).to eq "hex" expect(package_manager.requirement).to be_nil - expect(package_manager.version.to_s).to eq "2.1.1" + expect(package_manager.version.to_s).to eq "2.0.6" end end @@ -476,7 +476,7 @@ it "returns the correct language" do expect(language.name).to eq "elixir" expect(language.requirement).to be_nil - expect(language.version.to_s).to eq "1.17.3" + expect(language.version.to_s).to eq "1.14.4" end end end diff --git a/hex/spec/dependabot/hex/language_spec.rb b/hex/spec/dependabot/hex/language_spec.rb index 36ee98e6ba..f4b7602c59 100644 --- a/hex/spec/dependabot/hex/language_spec.rb +++ b/hex/spec/dependabot/hex/language_spec.rb @@ -1,16 +1,14 @@ # typed: false # frozen_string_literal: true -require "dependabot/hex/requirement" require "dependabot/hex/language" require "dependabot/ecosystem" require "spec_helper" RSpec.describe Dependabot::Hex::Language do - subject(:language) { described_class.new(version, requirement) } + subject(:language) { described_class.new(version) } let(:version) { "1.17.3" } - let(:requirement) { Dependabot::Hex::Requirement.new("~> 1.5") } describe "#version" do it "returns the version" do @@ -24,12 +22,6 @@ end end - describe "#requirement" do - it "returns the requirement" do - expect(language.requirement).to eq(requirement) - end - end - describe "#unsupported?" do it "returns false by default" do expect(language.unsupported?).to be false