Skip to content

Commit cc3db48

Browse files
Add pre-commit additional dependencies support for Dart (#14274)
* add additional dependencies support for Dart * fix failing specs
1 parent f954f9f commit cc3db48

File tree

10 files changed

+686
-3
lines changed

10 files changed

+686
-3
lines changed

pre_commit/Dockerfile

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,15 @@
44
# These images contain all the native helpers and tools already built
55
FROM ghcr.io/dependabot/dependabot-updater-gomod:latest AS go_modules
66
FROM ghcr.io/dependabot/dependabot-updater-bundler:latest AS bundler
7+
FROM ghcr.io/dependabot/dependabot-updater-pub:latest AS pub
78

89
# Create an intermediate stage that combines both ecosystems into a single layer
910
FROM scratch AS combined-helpers
1011
COPY --from=go_modules /opt/go /opt/go
1112
COPY --from=go_modules /opt/go_modules /opt/go_modules
1213
COPY --from=bundler /opt/bundler /opt/bundler
14+
COPY --from=pub /opt/dart /opt/dart
15+
COPY --from=pub /opt/pub /opt/pub
1316

1417
# Final stage - copy all helpers in a single layer from the combined stage
1518
FROM ghcr.io/dependabot/dependabot-updater-core
@@ -18,11 +21,15 @@ USER root
1821

1922
# Copy all helpers from the combined intermediate stage (single layer)
2023
COPY --from=combined-helpers --chown=dependabot:dependabot /opt /opt
21-
ENV PATH=/opt/go/bin:$PATH
24+
ENV PATH=/opt/go/bin:/opt/dart/dart-sdk/bin:$PATH
2225

2326
ENV DEPENDABOT_NATIVE_HELPERS_PATH="/opt"
2427

28+
# Set up Dart/pub environment
29+
ENV PUB_CACHE=/opt/dart/pub-cache \
30+
PUB_ENVIRONMENT="dependabot"
31+
2532
USER dependabot
2633

27-
COPY --chown=dependabot:dependabot --parents go_modules cargo npm_and_yarn python pre_commit common bundler $DEPENDABOT_HOME/
34+
COPY --chown=dependabot:dependabot --parents go_modules cargo npm_and_yarn python pre_commit common bundler pub $DEPENDABOT_HOME/
2835
COPY --chown=dependabot:dependabot updater $DEPENDABOT_HOME/dependabot-updater
Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
# typed: strict
2+
# frozen_string_literal: true
3+
4+
require "sorbet-runtime"
5+
require "dependabot/dependency"
6+
require "dependabot/update_checkers"
7+
require "dependabot/pub/version"
8+
require "dependabot/pre_commit/additional_dependency_checkers"
9+
require "dependabot/pre_commit/additional_dependency_checkers/base"
10+
11+
module Dependabot
12+
module PreCommit
13+
module AdditionalDependencyCheckers
14+
class Dart < Base
15+
extend T::Sig
16+
17+
sig { override.returns(T.nilable(String)) }
18+
def latest_version
19+
return nil unless package_name
20+
21+
@latest_version ||= T.let(
22+
fetch_latest_version_via_pub_checker,
23+
T.nilable(String)
24+
)
25+
end
26+
27+
sig { override.params(latest_version: String).returns(T::Array[T::Hash[Symbol, T.untyped]]) }
28+
def updated_requirements(latest_version)
29+
requirements.map do |original_req|
30+
original_source = original_req[:source]
31+
next original_req unless original_source.is_a?(Hash)
32+
next original_req unless original_source[:type] == "additional_dependency"
33+
34+
original_requirement = original_req[:requirement]
35+
new_requirement = build_updated_requirement(original_requirement, latest_version)
36+
37+
new_original_string = build_original_string(
38+
package_name: original_source[:original_name] || original_source[:package_name],
39+
requirement: new_requirement
40+
)
41+
42+
new_source = original_source.merge(original_string: new_original_string)
43+
44+
original_req.merge(
45+
requirement: new_requirement,
46+
source: new_source
47+
)
48+
end
49+
end
50+
51+
private
52+
53+
sig { returns(T.nilable(String)) }
54+
def fetch_latest_version_via_pub_checker
55+
pub_checker = pub_update_checker
56+
return nil unless pub_checker
57+
58+
latest = pub_checker.latest_version
59+
Dependabot.logger.info("Pub UpdateChecker found latest version: #{latest || 'none'}")
60+
61+
latest&.to_s
62+
rescue Dependabot::DependabotError, Excon::Error => e
63+
Dependabot.logger.debug("Error checking Dart package #{package_name}: #{e.message}")
64+
nil
65+
end
66+
67+
sig { returns(T.nilable(Dependabot::UpdateCheckers::Base)) }
68+
def pub_update_checker
69+
@pub_update_checker ||= T.let(
70+
build_pub_update_checker,
71+
T.nilable(Dependabot::UpdateCheckers::Base)
72+
)
73+
end
74+
75+
sig { returns(T.nilable(Dependabot::UpdateCheckers::Base)) }
76+
def build_pub_update_checker
77+
pub_dependency = build_pub_dependency
78+
return nil unless pub_dependency
79+
80+
Dependabot.logger.info("Delegating to pub UpdateChecker for package: #{pub_dependency.name}")
81+
82+
Dependabot::UpdateCheckers.for_package_manager("pub").new(
83+
dependency: pub_dependency,
84+
dependency_files: build_pub_dependency_files,
85+
credentials: credentials,
86+
ignored_versions: [],
87+
security_advisories: [],
88+
raise_on_ignored: false
89+
)
90+
end
91+
92+
sig { returns(T.nilable(Dependabot::Dependency)) }
93+
def build_pub_dependency
94+
return nil unless package_name
95+
96+
version = current_version || extract_version_from_requirement
97+
98+
Dependabot::Dependency.new(
99+
name: T.must(package_name),
100+
version: version,
101+
requirements: [{
102+
requirement: version ? "^#{version}" : nil,
103+
groups: ["dependencies"],
104+
file: "pubspec.yaml",
105+
source: nil
106+
}],
107+
package_manager: "pub"
108+
)
109+
end
110+
111+
sig { returns(T.nilable(String)) }
112+
def extract_version_from_requirement
113+
req_string = requirements.first&.dig(:requirement)
114+
return nil unless req_string
115+
116+
version_part = req_string.to_s.sub(/\A(?:[~^]|[><=]+)\s*/, "")
117+
return version_part if Dependabot::Pub::Version.correct?(version_part)
118+
119+
nil
120+
end
121+
122+
sig { returns(T::Array[Dependabot::DependencyFile]) }
123+
def build_pub_dependency_files
124+
version = current_version || extract_version_from_requirement
125+
requirement = version ? "^#{version}" : "any"
126+
127+
pubspec_content = <<~YAML
128+
name: dependabot_pre_commit_check
129+
version: 0.0.1
130+
environment:
131+
sdk: ">=3.0.0 <4.0.0"
132+
dependencies:
133+
#{T.must(package_name)}: #{requirement}
134+
YAML
135+
136+
[
137+
Dependabot::DependencyFile.new(
138+
name: "pubspec.yaml",
139+
content: pubspec_content
140+
)
141+
]
142+
end
143+
144+
sig do
145+
params(
146+
package_name: T.nilable(String),
147+
requirement: T.nilable(String)
148+
).returns(String)
149+
end
150+
def build_original_string(package_name:, requirement:)
151+
base = package_name.to_s
152+
base = "#{base}:#{requirement}" if requirement
153+
base
154+
end
155+
156+
sig { params(original_requirement: T.nilable(String), new_version: String).returns(String) }
157+
def build_updated_requirement(original_requirement, new_version)
158+
return new_version unless original_requirement
159+
160+
operator_match = original_requirement.match(/\A(?<op>[~^]|[><=]+)\s*/)
161+
if operator_match
162+
"#{operator_match[:op]}#{new_version}"
163+
else
164+
new_version
165+
end
166+
end
167+
end
168+
end
169+
end
170+
end
171+
172+
Dependabot::PreCommit::AdditionalDependencyCheckers.register(
173+
"dart",
174+
Dependabot::PreCommit::AdditionalDependencyCheckers::Dart
175+
)

pre_commit/lib/dependabot/pre_commit/file_parser.rb

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
require "dependabot/pre_commit/requirement"
1313
require "dependabot/cargo/requirement"
1414
require "dependabot/npm_and_yarn/requirement"
15+
require "dependabot/pub/requirement"
1516
require "dependabot/python/requirement_parser"
1617
require "dependabot/bundler/requirement"
1718
require "dependabot/go_modules/requirement_parser"
@@ -32,7 +33,8 @@ class FileParser < Dependabot::FileParsers::Base
3233
"node" => ->(dep_string) { Dependabot::NpmAndYarn::Requirement.parse_dep_string(dep_string) },
3334
"rust" => ->(dep_string) { Dependabot::Cargo::Requirement.parse_dep_string(dep_string) },
3435
"golang" => ->(dep_string) { Dependabot::GoModules::RequirementParser.parse(dep_string) },
35-
"ruby" => ->(dep_string) { Dependabot::Bundler::Requirement.parse_dep_string(dep_string) }
36+
"ruby" => ->(dep_string) { Dependabot::Bundler::Requirement.parse_dep_string(dep_string) },
37+
"dart" => ->(dep_string) { Dependabot::Pub::Requirement.parse_dep_string(dep_string) }
3638
}.freeze,
3739
T::Hash[String, T.proc.params(dep_string: String).returns(T.nilable(T::Hash[Symbol, T.untyped]))]
3840
)

pre_commit/lib/dependabot/pre_commit/update_checker.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
require "dependabot/pre_commit/requirement"
99
require "dependabot/pre_commit/version"
1010
require "dependabot/pre_commit/additional_dependency_checkers"
11+
require "dependabot/pre_commit/additional_dependency_checkers/dart"
1112
require "dependabot/pre_commit/additional_dependency_checkers/node"
1213
require "dependabot/pre_commit/additional_dependency_checkers/python"
1314
require "dependabot/pre_commit/additional_dependency_checkers/ruby"

0 commit comments

Comments
 (0)