diff --git a/common/lib/dependabot/ecosystem.rb b/common/lib/dependabot/ecosystem.rb index 8b7c67d88c..b4d7c55129 100644 --- a/common/lib/dependabot/ecosystem.rb +++ b/common/lib/dependabot/ecosystem.rb @@ -12,6 +12,8 @@ class VersionManager extend T::Sig extend T::Helpers + DEFAULT_VERSION_PATTERN = /(\d+\.\d+(.\d+)*)/ + abstract! # Initialize version information for a package manager or language. # @param name [String] the name of the package manager or language (e.g., "bundler", "ruby"). diff --git a/devcontainers/lib/dependabot/devcontainers/file_parser.rb b/devcontainers/lib/dependabot/devcontainers/file_parser.rb index 6bd5d64b4b..2fef50f6d5 100644 --- a/devcontainers/lib/dependabot/devcontainers/file_parser.rb +++ b/devcontainers/lib/dependabot/devcontainers/file_parser.rb @@ -6,6 +6,8 @@ require "dependabot/file_parsers" require "dependabot/file_parsers/base" require "dependabot/devcontainers/version" +require "dependabot/devcontainers/language" +require "dependabot/devcontainers/package_manager" require "dependabot/devcontainers/file_parser/feature_dependency_parser" module Dependabot @@ -28,6 +30,17 @@ def parse dependency_set.dependencies 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 { override.void } @@ -55,6 +68,44 @@ def config_dependency_files T.nilable(T::Array[Dependabot::DependencyFile]) ) end + + sig { returns(Ecosystem::VersionManager) } + def package_manager + @package_manager ||= T.let( + PackageManager.new(T.must(devcontainer_version)), + T.nilable(Dependabot::Devcontainers::PackageManager) + ) + end + + sig { returns(T.nilable(Ecosystem::VersionManager)) } + def language + @language ||= T.let( + Language.new(T.must(node_version)), + T.nilable(Dependabot::Devcontainers::Language) + ) + end + + sig { returns(T.nilable(String)) } + def devcontainer_version + @devcontainer_version ||= T.let( + begin + version = SharedHelpers.run_shell_command("devcontainer --version") + version.match(Dependabot::Ecosystem::VersionManager::DEFAULT_VERSION_PATTERN)&.captures&.first + end, + T.nilable(String) + ) + end + + sig { returns(T.nilable(String)) } + def node_version + @node_version ||= T.let( + begin + version = SharedHelpers.run_shell_command("node --version") + version.match(Dependabot::Ecosystem::VersionManager::DEFAULT_VERSION_PATTERN)&.captures&.first + end, + T.nilable(String) + ) + end end end end diff --git a/devcontainers/lib/dependabot/devcontainers/language.rb b/devcontainers/lib/dependabot/devcontainers/language.rb new file mode 100644 index 0000000000..0d75a1838b --- /dev/null +++ b/devcontainers/lib/dependabot/devcontainers/language.rb @@ -0,0 +1,21 @@ +# typed: strong +# frozen_string_literal: true + +require "sorbet-runtime" +require "dependabot/ecosystem" +require "dependabot/devcontainers/version" + +module Dependabot + module Devcontainers + LANGUAGE = "node" + + class Language < Dependabot::Ecosystem::VersionManager + extend T::Sig + + sig { params(raw_version: String).void } + def initialize(raw_version) + super(LANGUAGE, Version.new(raw_version)) + end + end + end +end diff --git a/devcontainers/lib/dependabot/devcontainers/package_manager.rb b/devcontainers/lib/dependabot/devcontainers/package_manager.rb new file mode 100644 index 0000000000..60b8557c5c --- /dev/null +++ b/devcontainers/lib/dependabot/devcontainers/package_manager.rb @@ -0,0 +1,41 @@ +# typed: strong +# frozen_string_literal: true + +require "sorbet-runtime" +require "dependabot/ecosystem" +require "dependabot/devcontainers/version" + +module Dependabot + module Devcontainers + ECOSYSTEM = "devcontainers" + PACKAGE_MANAGER = "devcontainers" + SUPPORTED_DEVCONTAINER_VERSIONS = T.let([].freeze, T::Array[Dependabot::Version]) + + # When a version is going to be unsupported, it will be added here + DEPRECATED_DEVCONTAINER_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_DEVCONTAINER_VERSIONS, + SUPPORTED_DEVCONTAINER_VERSIONS + ) + end + + sig { returns(T::Boolean) } + def deprecated? + false + end + + sig { returns(T::Boolean) } + def unsupported? + false + end + end + end +end diff --git a/devcontainers/spec/dependabot/devcontainers/file_parser_spec.rb b/devcontainers/spec/dependabot/devcontainers/file_parser_spec.rb index ccf394c75f..23d1b0baa5 100644 --- a/devcontainers/spec/dependabot/devcontainers/file_parser_spec.rb +++ b/devcontainers/spec/dependabot/devcontainers/file_parser_spec.rb @@ -217,4 +217,35 @@ expect(dependencies).to be_empty end end + + describe "#ecosystem" do + subject(:ecosystem) { parser.ecosystem } + + let(:project_name) { "config_in_root" } + let(:directory) { "/" } + + it "has the correct name" do + expect(ecosystem.name).to eq "devcontainers" + end + + describe "#package_manager" do + subject(:package_manager) { ecosystem.package_manager } + + it "returns the correct package manager" do + expect(package_manager.name).to eq "devcontainers" + expect(package_manager.requirement).to be_nil + expect(package_manager.version.to_s).to eq "0.72.0" + end + end + + describe "#language" do + subject(:language) { ecosystem.language } + + it "returns the correct language" do + expect(language.name).to eq "node" + expect(language.requirement).to be_nil + expect(language.version.to_s).to eq "18.20.5" + end + end + end end diff --git a/devcontainers/spec/dependabot/devcontainers/language_spec.rb b/devcontainers/spec/dependabot/devcontainers/language_spec.rb new file mode 100644 index 0000000000..bfbc38aa8a --- /dev/null +++ b/devcontainers/spec/dependabot/devcontainers/language_spec.rb @@ -0,0 +1,36 @@ +# typed: false +# frozen_string_literal: true + +require "dependabot/devcontainers/language" +require "dependabot/ecosystem" +require "spec_helper" + +RSpec.describe Dependabot::Devcontainers::Language do + subject(:language) { described_class.new(version) } + + let(:version) { "1.17.3" } + + describe "#version" do + it "returns the version" do + expect(language.version.to_s).to eq version + end + end + + describe "#name" do + it "returns the name" do + expect(language.name).to eq(Dependabot::Devcontainers::LANGUAGE) + 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/devcontainers/spec/dependabot/devcontainers/package_manager_spec.rb b/devcontainers/spec/dependabot/devcontainers/package_manager_spec.rb new file mode 100644 index 0000000000..9f883b5f53 --- /dev/null +++ b/devcontainers/spec/dependabot/devcontainers/package_manager_spec.rb @@ -0,0 +1,36 @@ +# typed: false +# frozen_string_literal: true + +require "dependabot/devcontainers/package_manager" +require "dependabot/ecosystem" +require "spec_helper" + +RSpec.describe Dependabot::Devcontainers::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_s).to eq version + end + end + + describe "#name" do + it "returns the name" do + expect(package_manager.name).to eq(Dependabot::Devcontainers::PACKAGE_MANAGER) + end + end + + describe "#deprecated_versions" do + it "returns deprecated versions" do + expect(package_manager.deprecated_versions).to eq(Dependabot::Devcontainers::DEPRECATED_DEVCONTAINER_VERSIONS) + end + end + + describe "#supported_versions" do + it "returns supported versions" do + expect(package_manager.supported_versions).to eq(Dependabot::Devcontainers::SUPPORTED_DEVCONTAINER_VERSIONS) + end + end +end