dependabot-rust_toolchain 0.321.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: a5830c9f4d86184e1c80384cae659ffaf4e70b7e45a1834f9737dda7c227411d
4
+ data.tar.gz: 1f9e76c27dd366c5bc3aaed7e0cae480bd9fe7e69c4f850659b51d6275becc4c
5
+ SHA512:
6
+ metadata.gz: 9267d58423d657c68aebdbd98c3ecd012b13a780c8ec011c803625fe028a03016ac2edc94b2839378c86fef06070fe48dce65d4a3be07babbd48e3b6ab24f3e3
7
+ data.tar.gz: '0943acc3a484ec3fba4ddaa1ea5e9d67f80b4d709f52909112dd0d3cc3c1e5ec6d7d926dda2a0d3d16f75f2e3680b4a842e9ae4a20cbf72d4e717a54ea2b8798'
@@ -0,0 +1,115 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ require "sorbet-runtime"
5
+
6
+ require "dependabot/rust_toolchain/channel_type"
7
+
8
+ module Dependabot
9
+ module RustToolchain
10
+ class Channel
11
+ extend T::Sig
12
+
13
+ include Comparable
14
+
15
+ sig { returns(T.nilable(String)) }
16
+ attr_reader :stability
17
+
18
+ sig { returns(T.nilable(String)) }
19
+ attr_reader :date
20
+
21
+ sig { returns(T.nilable(String)) }
22
+ attr_accessor :version
23
+
24
+ sig { params(stability: T.nilable(String), date: T.nilable(String), version: T.nilable(String)).void }
25
+ def initialize(stability: nil, date: nil, version: nil)
26
+ @stability = stability
27
+ @date = date
28
+ @version = version
29
+ end
30
+
31
+ # Factory method to create a Channel from parser output
32
+ sig { params(parsed_data: T::Hash[Symbol, T.nilable(String)]).returns(Channel) }
33
+ def self.from_parsed_data(parsed_data)
34
+ new(
35
+ stability: parsed_data[:stability],
36
+ date: parsed_data[:date],
37
+ version: parsed_data[:version]
38
+ )
39
+ end
40
+
41
+ # Returns the channel type for comparison purposes
42
+ sig { returns(Dependabot::RustToolchain::ChannelType) }
43
+ def channel_type
44
+ case [!!version, !!(stability && date), !!stability]
45
+ in [true, _, _] then ChannelType::Version
46
+ in [false, true, _] then ChannelType::DatedStability
47
+ in [false, false, true] then ChannelType::Stability
48
+ else ChannelType::Unknown
49
+ end
50
+ end
51
+
52
+ # Comparable implementation - only compare channels of the same type
53
+ sig { params(other: Object).returns(T.nilable(Integer)) }
54
+ def <=>(other)
55
+ return nil unless other.is_a?(Channel)
56
+ return nil unless channel_type == other.channel_type
57
+
58
+ case channel_type
59
+ in ChannelType::Version
60
+ compare_versions(T.must(version), T.must(other.version))
61
+ in ChannelType::DatedStability
62
+ # Channels must be of the same type to compare dates
63
+ # i.e. cannot compare "nightly-2023-10-01" with "beta-2023-10-01"
64
+ return nil unless stability == other.stability
65
+
66
+ compare_dates(T.must(date), T.must(other.date))
67
+ end
68
+ end
69
+
70
+ sig { params(other: Object).returns(T::Boolean) }
71
+ def ==(other)
72
+ return false unless other.is_a?(Channel)
73
+
74
+ stability == other.stability && date == other.date && version == other.version
75
+ end
76
+
77
+ sig { returns(String) }
78
+ def to_s
79
+ case [version, stability, date]
80
+ in [String => v, _, _] then v
81
+ in [nil, String => c, String => d] then "#{c}-#{d}"
82
+ in [nil, String => c, _] then c
83
+ else "unknown"
84
+ end
85
+ end
86
+
87
+ sig { returns(String) }
88
+ def inspect
89
+ "#<#{self.class.name} channel=#{stability.inspect} date=#{date.inspect} version=#{version.inspect}>"
90
+ end
91
+
92
+ private
93
+
94
+ # Compare semantic versions (e.g., "1.72.0" vs "1.73.1")
95
+ sig { params(version1: String, version2: String).returns(Integer) }
96
+ def compare_versions(version1, version2)
97
+ v1_parts = version1.split(".").map(&:to_i)
98
+ v2_parts = version2.split(".").map(&:to_i)
99
+
100
+ # Pad shorter version parts with zeros to ensure equal length
101
+ max_length = [v1_parts.size, v2_parts.size].max
102
+ v1_parts.fill(0, v1_parts.size...max_length) if v1_parts.size < max_length
103
+ v2_parts.fill(0, v2_parts.size...max_length) if v2_parts.size < max_length
104
+
105
+ v1_parts <=> v2_parts
106
+ end
107
+
108
+ # Compare dates in YYYY-MM-DD format
109
+ sig { params(date1: String, date2: String).returns(Integer) }
110
+ def compare_dates(date1, date2)
111
+ T.must(date1 <=> date2)
112
+ end
113
+ end
114
+ end
115
+ end
@@ -0,0 +1,58 @@
1
+ # typed: strong
2
+ # frozen_string_literal: true
3
+
4
+ require "sorbet-runtime"
5
+
6
+ require "dependabot/rust_toolchain"
7
+ require "dependabot/rust_toolchain/channel"
8
+
9
+ module Dependabot
10
+ module RustToolchain
11
+ class ChannelParser
12
+ extend T::Sig
13
+
14
+ sig { params(toolchain: String).void }
15
+ def initialize(toolchain)
16
+ @toolchain = toolchain
17
+ end
18
+
19
+ # Parse Rust toolchain and extract components (stability, date, version)
20
+ #
21
+ # This doesn't support the full range of Rust toolchain formats, but we cover
22
+ # those that Dependabot is likely to encounter.
23
+ #
24
+ # See https://rust-lang.github.io/rustup/concepts/toolchains.html
25
+ sig { returns(T.nilable(Dependabot::RustToolchain::Channel)) }
26
+ def parse
27
+ case toolchain
28
+ # Specific version like 1.72.0 or 1.72
29
+ when /^\d+\.\d+(\.\d+)?$/
30
+ Channel.new(
31
+ stability: nil,
32
+ date: nil,
33
+ version: toolchain
34
+ )
35
+ # With date: nightly-2025-01-0, beta-2025-01-0, stable-2025-01-0
36
+ when /^(nightly|beta|stable)-(\d{4}-\d{2}-\d{2})$/
37
+ Channel.new(
38
+ stability: ::Regexp.last_match(1),
39
+ date: ::Regexp.last_match(2),
40
+ version: nil
41
+ )
42
+ # Without date: stable, beta, nightly
43
+ when STABLE_CHANNEL, BETA_CHANNEL, NIGHTLY_CHANNEL
44
+ Channel.new(
45
+ stability: toolchain,
46
+ date: nil,
47
+ version: nil
48
+ )
49
+ end
50
+ end
51
+
52
+ private
53
+
54
+ sig { returns(String) }
55
+ attr_reader :toolchain
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,24 @@
1
+ # typed: strong
2
+ # frozen_string_literal: true
3
+
4
+ require "sorbet-runtime"
5
+
6
+ module Dependabot
7
+ module RustToolchain
8
+ class ChannelType < T::Enum
9
+ enums do
10
+ # Represents a version with a specific version number
11
+ Version = new("Version")
12
+
13
+ # Represents a channel with a date, e.g., "nightly-2023-10-01"
14
+ DatedStability = new("DatedStability")
15
+
16
+ # Represents a channel without a date, e.g., "stable" or "beta"
17
+ Stability = new("Stability")
18
+
19
+ # Represents an unknown or unsupported type
20
+ Unknown = new("Unknown")
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,52 @@
1
+ # typed: strong
2
+ # frozen_string_literal: true
3
+
4
+ require "sorbet-runtime"
5
+
6
+ require "dependabot/file_fetchers"
7
+ require "dependabot/file_fetchers/base"
8
+
9
+ require "dependabot/rust_toolchain"
10
+
11
+ module Dependabot
12
+ module RustToolchain
13
+ class FileFetcher < Dependabot::FileFetchers::Base
14
+ extend T::Sig
15
+
16
+ sig { override.params(filenames: T::Array[String]).returns(T::Boolean) }
17
+ def self.required_files_in?(filenames)
18
+ filenames.any? do |filename|
19
+ basename = File.basename(filename)
20
+ basename == RUST_TOOLCHAIN_TOML_FILENAME || basename == RUST_TOOLCHAIN_FILENAME
21
+ end
22
+ end
23
+
24
+ sig { override.returns(String) }
25
+ def self.required_files_message
26
+ "Repo must contain a #{RUST_TOOLCHAIN_TOML_FILENAME} or #{RUST_TOOLCHAIN_FILENAME} file"
27
+ end
28
+
29
+ sig { override.returns(T::Array[Dependabot::DependencyFile]) }
30
+ def fetch_files
31
+ files = [rust_toolchain_toml_file, rust_toolchain_file].compact
32
+
33
+ return files unless files.empty?
34
+
35
+ raise Dependabot::DependencyFileNotFound.new(
36
+ nil,
37
+ self.class.required_files_message
38
+ )
39
+ end
40
+
41
+ private
42
+
43
+ sig { returns(T.nilable(Dependabot::DependencyFile)) }
44
+ def rust_toolchain_toml_file = fetch_file_if_present(RUST_TOOLCHAIN_TOML_FILENAME)
45
+
46
+ sig { returns(T.nilable(Dependabot::DependencyFile)) }
47
+ def rust_toolchain_file = fetch_file_if_present(RUST_TOOLCHAIN_FILENAME)
48
+ end
49
+ end
50
+ end
51
+
52
+ Dependabot::FileFetchers.register("rust_toolchain", Dependabot::RustToolchain::FileFetcher)
@@ -0,0 +1,109 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ require "sorbet-runtime"
5
+ require "toml-rb"
6
+
7
+ require "dependabot/dependency"
8
+ require "dependabot/errors"
9
+ require "dependabot/file_parsers"
10
+ require "dependabot/file_parsers/base"
11
+ require "dependabot/file_parsers/base/dependency_set"
12
+
13
+ require "dependabot/rust_toolchain/package_manager"
14
+
15
+ module Dependabot
16
+ module RustToolchain
17
+ class FileParser < Dependabot::FileParsers::Base
18
+ extend T::Sig
19
+
20
+ sig { override.returns(T::Array[Dependabot::Dependency]) }
21
+ def parse
22
+ dependency_set = DependencySet.new
23
+
24
+ dependency_files.each do |dependency_file|
25
+ dependency = parse_dependency_file(dependency_file)
26
+ next unless dependency
27
+
28
+ dependency_set << dependency
29
+ end
30
+
31
+ dependency_set.dependencies
32
+ end
33
+
34
+ sig { returns(Dependabot::Ecosystem) }
35
+ def ecosystem
36
+ @ecosystem ||= T.let(
37
+ Dependabot::Ecosystem.new(
38
+ name: "rust_toolchain",
39
+ package_manager: package_manager
40
+ ),
41
+ T.nilable(Dependabot::Ecosystem)
42
+ )
43
+ end
44
+
45
+ private
46
+
47
+ sig { override.void }
48
+ def check_required_files
49
+ return if dependency_files.any?
50
+
51
+ raise "Could not find any dependency files to parse. " \
52
+ "Expected to find a file named 'rust-toolchain' or 'rust-toolchain.toml'."
53
+ end
54
+
55
+ sig { params(dependency_file: Dependabot::DependencyFile).returns(T.nilable(Dependabot::Dependency)) }
56
+ def parse_dependency_file(dependency_file)
57
+ content = T.must(dependency_file.content).strip
58
+
59
+ channel = case dependency_file.name
60
+ when /\.toml$/
61
+ parse_toml_toolchain(content)
62
+ else
63
+ parse_plaintext_toolchain(content)
64
+ end
65
+
66
+ return if channel.empty?
67
+
68
+ Dependency.new(
69
+ name: "rust-toolchain",
70
+ version: channel,
71
+ requirements: [{
72
+ file: dependency_file.name,
73
+ requirement: channel,
74
+ groups: [],
75
+ source: nil
76
+ }],
77
+ package_manager: "rust_toolchain"
78
+ )
79
+ end
80
+
81
+ sig { params(content: String).returns(String) }
82
+ def parse_toml_toolchain(content)
83
+ parsed = TomlRB.parse(content)
84
+
85
+ channel = parsed.dig("toolchain", "channel")
86
+ return channel if channel
87
+
88
+ Dependabot.logger.warn("No toolchain section found in rust-toolchain.toml file.")
89
+ raise Dependabot::DependencyFileNotParseable, "rust-toolchain.toml"
90
+ rescue TomlRB::ParseError => e
91
+ Dependabot.logger.warn("Failed to parse rust-toolchain.toml file: #{e.message}")
92
+ raise Dependabot::DependencyFileNotParseable, "rust-toolchain.toml"
93
+ end
94
+
95
+ sig { params(content: String).returns(String) }
96
+ def parse_plaintext_toolchain(content) = content.strip
97
+
98
+ sig { returns(Dependabot::Ecosystem::VersionManager) }
99
+ def package_manager
100
+ @package_manager ||= T.let(
101
+ Dependabot::RustToolchain::RustToolchainPackageManager.new,
102
+ T.nilable(Dependabot::Ecosystem::VersionManager)
103
+ )
104
+ end
105
+ end
106
+ end
107
+ end
108
+
109
+ Dependabot::FileParsers.register("rust_toolchain", Dependabot::RustToolchain::FileParser)
@@ -0,0 +1,67 @@
1
+ # typed: strong
2
+ # frozen_string_literal: true
3
+
4
+ require "sorbet-runtime"
5
+
6
+ require "dependabot/file_updaters"
7
+ require "dependabot/file_updaters/base"
8
+
9
+ require "dependabot/rust_toolchain"
10
+
11
+ module Dependabot
12
+ module RustToolchain
13
+ class FileUpdater < Dependabot::FileUpdaters::Base
14
+ extend T::Sig
15
+
16
+ sig { override.returns(T::Array[Regexp]) }
17
+ def self.updated_files_regex
18
+ [
19
+ /rust-toolchain$/,
20
+ /rust-toolchain\.toml$/
21
+ ]
22
+ end
23
+
24
+ sig { override.returns(T::Array[Dependabot::DependencyFile]) }
25
+ def updated_dependency_files
26
+ updated_files = []
27
+
28
+ rust_toolchain_files.each do |file|
29
+ next unless file_changed?(file)
30
+
31
+ updated_files << updated_file(file: file, content: update(T.must(file.content)))
32
+ end
33
+
34
+ updated_files
35
+ end
36
+
37
+ private
38
+
39
+ sig { returns(Dependabot::Dependency) }
40
+ def dependency
41
+ T.must(dependencies.first)
42
+ end
43
+
44
+ sig { override.void }
45
+ def check_required_files
46
+ return if dependency_files.any?
47
+
48
+ raise "Could not find any dependency files to update. " \
49
+ "Expected to find a file named 'rust-toolchain' or 'rust-toolchain.toml'."
50
+ end
51
+
52
+ sig { returns(T::Array[Dependabot::DependencyFile]) }
53
+ def rust_toolchain_files
54
+ dependency_files.select do |f|
55
+ f.name == RUST_TOOLCHAIN_FILENAME || f.name == RUST_TOOLCHAIN_TOML_FILENAME
56
+ end
57
+ end
58
+
59
+ sig { params(content: String).returns(String) }
60
+ def update(content)
61
+ content.gsub(T.must(dependency.previous_version), T.must(dependency.version))
62
+ end
63
+ end
64
+ end
65
+ end
66
+
67
+ Dependabot::FileUpdaters.register("rust_toolchain", Dependabot::RustToolchain::FileUpdater)
@@ -0,0 +1,28 @@
1
+ # typed: strong
2
+ # frozen_string_literal: true
3
+
4
+ require "nokogiri"
5
+ require "sorbet-runtime"
6
+
7
+ require "dependabot/metadata_finders"
8
+ require "dependabot/metadata_finders/base"
9
+ require "dependabot/registry_client"
10
+
11
+ require "dependabot/rust_toolchain"
12
+
13
+ module Dependabot
14
+ module RustToolchain
15
+ class MetadataFinder < Dependabot::MetadataFinders::Base
16
+ extend T::Sig
17
+
18
+ private
19
+
20
+ sig { override.returns(T.nilable(Dependabot::Source)) }
21
+ def look_up_source
22
+ Dependabot::Source.from_url(RUST_GITHUB_URL)
23
+ end
24
+ end
25
+ end
26
+ end
27
+
28
+ Dependabot::MetadataFinders.register("rust_toolchain", Dependabot::RustToolchain::MetadataFinder)
@@ -0,0 +1,97 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ require "sorbet-runtime"
5
+ require "uri"
6
+
7
+ require "dependabot/package/package_details"
8
+ require "dependabot/registry_client"
9
+ require "dependabot/update_checkers/base"
10
+
11
+ require "dependabot/rust_toolchain"
12
+ require "dependabot/rust_toolchain/channel"
13
+ require "dependabot/rust_toolchain/channel_parser"
14
+ require "dependabot/rust_toolchain/version"
15
+
16
+ module Dependabot
17
+ module RustToolchain
18
+ module Package
19
+ class PackageDetailsFetcher
20
+ extend T::Sig
21
+
22
+ MANIFESTS_URL = "https://static.rust-lang.org/manifests.txt"
23
+
24
+ sig do
25
+ params(
26
+ dependency: Dependabot::Dependency
27
+ ).void
28
+ end
29
+ def initialize(dependency:)
30
+ @dependency = dependency
31
+ end
32
+
33
+ sig { returns(Dependabot::Package::PackageDetails) }
34
+ def fetch
35
+ if all_versions.nil? || all_versions.empty?
36
+ raise Dependabot::DependencyFileNotResolvable, "No versions found in manifests.txt"
37
+ end
38
+
39
+ Dependabot::Package::PackageDetails.new(
40
+ dependency: dependency,
41
+ releases: all_versions.map { |v| Dependabot::Package::PackageRelease.new(version: v) },
42
+ dist_tags: nil
43
+ )
44
+ end
45
+
46
+ private
47
+
48
+ sig { returns(T::Array[Dependabot::RustToolchain::Version]) }
49
+ def all_versions
50
+ @all_versions ||= T.let(fetch_and_parse_manifests, T.nilable(T::Array[Dependabot::RustToolchain::Version]))
51
+ end
52
+
53
+ # Fetch the manifests.txt file and parse each line to extract version information
54
+ sig { returns(T::Array[Dependabot::RustToolchain::Version]) }
55
+ def fetch_and_parse_manifests
56
+ response = Dependabot::RegistryClient.get(url: MANIFESTS_URL)
57
+ manifests_content = response.body
58
+
59
+ channels = T.let([], T::Array[Dependabot::RustToolchain::Version])
60
+
61
+ manifests_content.each_line do |line|
62
+ line = line.strip
63
+ next if line.empty?
64
+
65
+ channel = parse_manifest_line(line)
66
+ channels << channel if channel
67
+ end
68
+
69
+ channels.uniq
70
+ end
71
+
72
+ sig { params(line: String).returns(T.nilable(Dependabot::RustToolchain::Version)) }
73
+ def parse_manifest_line(line)
74
+ match = line.match(%r{static\.rust-lang\.org/dist/(\d{4}-\d{2}-\d{2})/channel-rust-(.+)\.toml})
75
+ return nil unless match
76
+
77
+ date = match[1]
78
+ channel_part = match[2]
79
+
80
+ case channel_part
81
+ when STABLE_CHANNEL
82
+ Version.new("#{STABLE_CHANNEL}-#{date}")
83
+ when BETA_CHANNEL
84
+ Version.new("#{BETA_CHANNEL}-#{date}")
85
+ when NIGHTLY_CHANNEL
86
+ Version.new("#{NIGHTLY_CHANNEL}-#{date}")
87
+ when /^\d+\.\d+(\.\d+)?$/
88
+ Version.new(channel_part)
89
+ end
90
+ end
91
+
92
+ sig { returns(Dependabot::Dependency) }
93
+ attr_reader :dependency
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,46 @@
1
+ # typed: strong
2
+ # frozen_string_literal: true
3
+
4
+ require "sorbet-runtime"
5
+
6
+ require "dependabot/ecosystem"
7
+
8
+ require "dependabot/rust_toolchain/requirement"
9
+ require "dependabot/rust_toolchain/version"
10
+
11
+ module Dependabot
12
+ module RustToolchain
13
+ class RustToolchainPackageManager < Dependabot::Ecosystem::VersionManager
14
+ extend T::Sig
15
+
16
+ # This is a placeholder version for the package manager.
17
+ VERSION = "1.0.0"
18
+
19
+ SUPPORTED_VERSIONS = T.let([].freeze, T::Array[Dependabot::Version])
20
+
21
+ DEPRECATED_VERSIONS = T.let([].freeze, T::Array[Dependabot::Version])
22
+
23
+ sig do
24
+ void
25
+ end
26
+ def initialize
27
+ super(
28
+ name: PACKAGE_MANAGER,
29
+ version: Version.new(VERSION),
30
+ deprecated_versions: DEPRECATED_VERSIONS,
31
+ supported_versions: SUPPORTED_VERSIONS
32
+ )
33
+ end
34
+
35
+ sig { override.returns(T::Boolean) }
36
+ def deprecated?
37
+ false
38
+ end
39
+
40
+ sig { override.returns(T::Boolean) }
41
+ def unsupported?
42
+ false
43
+ end
44
+ end
45
+ end
46
+ end