dependabot-github_actions 0.393.0 → 0.394.0
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 +4 -4
- data/lib/dependabot/github_actions/file_parser.rb +101 -77
- data/lib/dependabot/github_actions/file_updater/workflow_updater/flow_sequence_comments.rb +244 -0
- data/lib/dependabot/github_actions/file_updater/workflow_updater/source_comment_locator.rb +147 -0
- data/lib/dependabot/github_actions/file_updater/workflow_updater/source_comment_updater.rb +191 -0
- data/lib/dependabot/github_actions/file_updater/workflow_updater/source_updater.rb +387 -0
- data/lib/dependabot/github_actions/file_updater/workflow_updater/version_commenter.rb +117 -0
- data/lib/dependabot/github_actions/file_updater/workflow_updater/yaml_comment_finder.rb +96 -0
- data/lib/dependabot/github_actions/file_updater/workflow_updater.rb +222 -0
- data/lib/dependabot/github_actions/file_updater.rb +19 -77
- data/lib/dependabot/github_actions/update_checker.rb +56 -7
- data/lib/dependabot/github_actions/workflow_file/metadata_builder.rb +212 -0
- data/lib/dependabot/github_actions/workflow_file/node_context.rb +64 -0
- data/lib/dependabot/github_actions/workflow_file/uses_collector.rb +78 -0
- data/lib/dependabot/github_actions/workflow_file/uses_declaration.rb +76 -0
- data/lib/dependabot/github_actions/workflow_file.rb +414 -0
- metadata +18 -6
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# typed: strong
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "dependabot/github_actions/file_updater/workflow_updater"
|
|
5
|
+
|
|
6
|
+
module Dependabot
|
|
7
|
+
module GithubActions
|
|
8
|
+
class FileUpdater < Dependabot::FileUpdaters::Base
|
|
9
|
+
class WorkflowUpdater
|
|
10
|
+
class YamlCommentFinder
|
|
11
|
+
extend T::Sig
|
|
12
|
+
|
|
13
|
+
sig { params(suffix: String).returns(T.nilable(String)) }
|
|
14
|
+
def find(suffix)
|
|
15
|
+
previous_non_whitespace = T.let(nil, T.nilable(String))
|
|
16
|
+
index = 0
|
|
17
|
+
|
|
18
|
+
while index < suffix.length
|
|
19
|
+
character = T.must(suffix[index])
|
|
20
|
+
if quoted_scalar_start?(character, previous_non_whitespace)
|
|
21
|
+
previous_non_whitespace = character
|
|
22
|
+
index = quoted_scalar_end(suffix, index)
|
|
23
|
+
next
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
return comment_with_leading_whitespace(suffix, index) if comment_start?(suffix, index)
|
|
27
|
+
|
|
28
|
+
previous_non_whitespace = character unless character.match?(/\s/)
|
|
29
|
+
index += 1
|
|
30
|
+
end
|
|
31
|
+
nil
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
sig { params(suffix: String).returns(T.nilable(String)) }
|
|
35
|
+
def find_immediate(suffix)
|
|
36
|
+
comment = find(suffix)
|
|
37
|
+
return unless comment
|
|
38
|
+
|
|
39
|
+
comment_start = T.must(suffix.b.index(comment.b))
|
|
40
|
+
prefix = suffix.byteslice(0...comment_start) || ""
|
|
41
|
+
comment if prefix.match?(/\A[ \t]*,?[ \t]*\z/)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
sig { params(line: String).returns(T.nilable(String)) }
|
|
45
|
+
def find_line(line)
|
|
46
|
+
stripped = line.lstrip
|
|
47
|
+
return stripped if stripped.start_with?("#")
|
|
48
|
+
|
|
49
|
+
find(line)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
private
|
|
53
|
+
|
|
54
|
+
sig { params(character: String, previous_non_whitespace: T.nilable(String)).returns(T::Boolean) }
|
|
55
|
+
def quoted_scalar_start?(character, previous_non_whitespace)
|
|
56
|
+
return false unless character == "'" || character == '"'
|
|
57
|
+
|
|
58
|
+
previous_non_whitespace.nil? || ":,[{?-".include?(previous_non_whitespace)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
sig { params(suffix: String, quote_index: Integer).returns(Integer) }
|
|
62
|
+
def quoted_scalar_end(suffix, quote_index)
|
|
63
|
+
quote = T.must(suffix[quote_index])
|
|
64
|
+
index = quote_index + 1
|
|
65
|
+
|
|
66
|
+
while index < suffix.length
|
|
67
|
+
character = T.must(suffix[index])
|
|
68
|
+
if quote == "'" && character == "'" && suffix[index + 1] == "'"
|
|
69
|
+
index += 2
|
|
70
|
+
elsif quote == '"' && character == "\\"
|
|
71
|
+
index += 2
|
|
72
|
+
elsif character == quote
|
|
73
|
+
return index + 1
|
|
74
|
+
else
|
|
75
|
+
index += 1
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
suffix.length
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
sig { params(suffix: String, index: Integer).returns(T::Boolean) }
|
|
82
|
+
def comment_start?(suffix, index)
|
|
83
|
+
suffix[index] == "#" && index.positive? && T.must(suffix[index - 1]).match?(/\s/)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
sig { params(suffix: String, hash_index: Integer).returns(String) }
|
|
87
|
+
def comment_with_leading_whitespace(suffix, hash_index)
|
|
88
|
+
comment_start = hash_index
|
|
89
|
+
comment_start -= 1 while comment_start.positive? && T.must(suffix[comment_start - 1]).match?(/\s/)
|
|
90
|
+
T.must(suffix[comment_start..])
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "dependabot/github_actions/file_updater"
|
|
5
|
+
|
|
6
|
+
module Dependabot
|
|
7
|
+
module GithubActions
|
|
8
|
+
class FileUpdater < Dependabot::FileUpdaters::Base
|
|
9
|
+
class WorkflowUpdater
|
|
10
|
+
extend T::Sig
|
|
11
|
+
|
|
12
|
+
class SourceUpdate < T::Struct
|
|
13
|
+
const :declaration, WorkflowFile::UsesDeclaration
|
|
14
|
+
const :old_declaration, String
|
|
15
|
+
const :new_declaration, String
|
|
16
|
+
const :old_ref, String
|
|
17
|
+
const :new_ref, String
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
class ContentEdit < T::Struct
|
|
21
|
+
const :start_offset, Integer
|
|
22
|
+
const :end_offset, Integer
|
|
23
|
+
const :replacement, String
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
require_relative "workflow_updater/version_commenter"
|
|
27
|
+
require_relative "workflow_updater/yaml_comment_finder"
|
|
28
|
+
require_relative "workflow_updater/source_comment_locator"
|
|
29
|
+
require_relative "workflow_updater/source_comment_updater"
|
|
30
|
+
require_relative "workflow_updater/flow_sequence_comments"
|
|
31
|
+
require_relative "workflow_updater/source_updater"
|
|
32
|
+
|
|
33
|
+
sig do
|
|
34
|
+
params(
|
|
35
|
+
file: Dependabot::DependencyFile,
|
|
36
|
+
dependency: Dependabot::Dependency,
|
|
37
|
+
commenter: VersionCommenter
|
|
38
|
+
).void
|
|
39
|
+
end
|
|
40
|
+
def initialize(file:, dependency:, commenter:)
|
|
41
|
+
@file = file
|
|
42
|
+
@dependency = dependency
|
|
43
|
+
@commenter = commenter
|
|
44
|
+
@comment_finder = T.let(YamlCommentFinder.new, YamlCommentFinder)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
sig { returns(String) }
|
|
48
|
+
def updated_content
|
|
49
|
+
workflow_file = WorkflowFile.new(T.must(file.content))
|
|
50
|
+
source_updates, fallback_pairs = requirement_updates(workflow_file)
|
|
51
|
+
|
|
52
|
+
content = SourceUpdater.new(
|
|
53
|
+
workflow_file: workflow_file,
|
|
54
|
+
updates: source_updates,
|
|
55
|
+
commenter: commenter,
|
|
56
|
+
comment_finder: comment_finder
|
|
57
|
+
).updated_content
|
|
58
|
+
fallback_pairs.each do |new_req, old_req|
|
|
59
|
+
content = apply_text_update(content, new_req, old_req)
|
|
60
|
+
end
|
|
61
|
+
content
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
private
|
|
65
|
+
|
|
66
|
+
sig { returns(Dependabot::DependencyFile) }
|
|
67
|
+
attr_reader :file
|
|
68
|
+
|
|
69
|
+
sig { returns(Dependabot::Dependency) }
|
|
70
|
+
attr_reader :dependency
|
|
71
|
+
|
|
72
|
+
sig { returns(VersionCommenter) }
|
|
73
|
+
attr_reader :commenter
|
|
74
|
+
|
|
75
|
+
sig { returns(YamlCommentFinder) }
|
|
76
|
+
attr_reader :comment_finder
|
|
77
|
+
|
|
78
|
+
sig do
|
|
79
|
+
params(workflow_file: WorkflowFile).returns(
|
|
80
|
+
[
|
|
81
|
+
T::Array[SourceUpdate],
|
|
82
|
+
T::Array[[Dependabot::DependencyRequirement, Dependabot::DependencyRequirement]]
|
|
83
|
+
]
|
|
84
|
+
)
|
|
85
|
+
end
|
|
86
|
+
def requirement_updates(workflow_file)
|
|
87
|
+
source_updates = T.let([], T::Array[SourceUpdate])
|
|
88
|
+
fallback_pairs = T.let(
|
|
89
|
+
[],
|
|
90
|
+
T::Array[[Dependabot::DependencyRequirement, Dependabot::DependencyRequirement]]
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
dependency.requirements.zip(T.must(dependency.previous_requirements)).each do |new_req, old_req|
|
|
94
|
+
previous_requirement = T.must(old_req)
|
|
95
|
+
next if new_req.file != file.name || new_req.source == previous_requirement.source
|
|
96
|
+
next unless new_req.source_string("type") == "git"
|
|
97
|
+
|
|
98
|
+
source_update = source_update_for(workflow_file, new_req, previous_requirement)
|
|
99
|
+
source_update ? source_updates << source_update : fallback_pairs << [new_req, previous_requirement]
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
[source_updates, fallback_pairs]
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
sig do
|
|
106
|
+
params(
|
|
107
|
+
workflow_file: WorkflowFile,
|
|
108
|
+
new_req: Dependabot::DependencyRequirement,
|
|
109
|
+
old_req: Dependabot::DependencyRequirement
|
|
110
|
+
).returns(T.nilable(SourceUpdate))
|
|
111
|
+
end
|
|
112
|
+
def source_update_for(workflow_file, new_req, old_req)
|
|
113
|
+
path = yaml_source_path(old_req)
|
|
114
|
+
return unless path
|
|
115
|
+
|
|
116
|
+
declaration = workflow_file.declaration_at(path)
|
|
117
|
+
return unless declaration
|
|
118
|
+
return unless declaration.value_node && declaration.source_node && declaration.mapping_node
|
|
119
|
+
|
|
120
|
+
old_declaration = T.must(old_req.metadata_string("declaration_string"))
|
|
121
|
+
new_ref = T.must(new_req.source_string("ref"))
|
|
122
|
+
SourceUpdate.new(
|
|
123
|
+
declaration: declaration,
|
|
124
|
+
old_declaration: old_declaration,
|
|
125
|
+
new_declaration: old_declaration.gsub(/@.*+/, "@#{new_ref}"),
|
|
126
|
+
old_ref: T.must(old_req.source_string("ref")),
|
|
127
|
+
new_ref: new_ref
|
|
128
|
+
)
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
sig { params(requirement: Dependabot::DependencyRequirement).returns(T.nilable(WorkflowFile::Path)) }
|
|
132
|
+
def yaml_source_path(requirement)
|
|
133
|
+
metadata = requirement.metadata
|
|
134
|
+
return unless metadata
|
|
135
|
+
|
|
136
|
+
raw_source = metadata[:yaml_source] || metadata["yaml_source"]
|
|
137
|
+
return unless raw_source.is_a?(Hash)
|
|
138
|
+
|
|
139
|
+
raw_path = raw_source[:path] || raw_source["path"]
|
|
140
|
+
return unless raw_path.is_a?(Array)
|
|
141
|
+
return unless raw_path.all? { |part| part.is_a?(String) || part.is_a?(Integer) }
|
|
142
|
+
|
|
143
|
+
path = T.let([], WorkflowFile::Path)
|
|
144
|
+
raw_path.each { |part| path << part }
|
|
145
|
+
path
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
sig do
|
|
149
|
+
params(
|
|
150
|
+
content: String,
|
|
151
|
+
new_req: Dependabot::DependencyRequirement,
|
|
152
|
+
old_req: Dependabot::DependencyRequirement
|
|
153
|
+
).returns(String)
|
|
154
|
+
end
|
|
155
|
+
def apply_text_update(content, new_req, old_req)
|
|
156
|
+
old_ref = T.must(old_req.source_string("ref"))
|
|
157
|
+
new_ref = T.must(new_req.source_string("ref"))
|
|
158
|
+
old_declaration = T.must(old_req.metadata_string("declaration_string"))
|
|
159
|
+
new_declaration = old_declaration.gsub(/@.*+/, "@#{new_ref}")
|
|
160
|
+
declaration_pattern = uses_declaration_pattern(old_declaration)
|
|
161
|
+
|
|
162
|
+
content.gsub(/#{declaration_pattern}(?<suffix>[^\r\n]*)/) do |match|
|
|
163
|
+
updated_workflow_line(
|
|
164
|
+
match: match,
|
|
165
|
+
comment: comment_finder.find(T.must(Regexp.last_match(:suffix))),
|
|
166
|
+
declaration_pattern: declaration_pattern,
|
|
167
|
+
old_declaration: old_declaration,
|
|
168
|
+
new_declaration: new_declaration,
|
|
169
|
+
old_ref: old_ref,
|
|
170
|
+
new_ref: new_ref
|
|
171
|
+
)
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
sig { params(declaration: String).returns(Regexp) }
|
|
176
|
+
def uses_declaration_pattern(declaration)
|
|
177
|
+
escaped_declaration = Regexp.escape(declaration)
|
|
178
|
+
|
|
179
|
+
/
|
|
180
|
+
(?:^|[ \t{,])
|
|
181
|
+
(?:"uses"|'uses'|uses)[ \t]*:[ \t]*
|
|
182
|
+
(?:"#{escaped_declaration}"|'#{escaped_declaration}'|#{escaped_declaration})
|
|
183
|
+
(?=[ \t]|$|[,}\]])
|
|
184
|
+
/x
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
sig do
|
|
188
|
+
params(
|
|
189
|
+
match: String,
|
|
190
|
+
comment: T.nilable(String),
|
|
191
|
+
declaration_pattern: Regexp,
|
|
192
|
+
old_declaration: String,
|
|
193
|
+
new_declaration: String,
|
|
194
|
+
old_ref: String,
|
|
195
|
+
new_ref: String
|
|
196
|
+
).returns(String)
|
|
197
|
+
end
|
|
198
|
+
def updated_workflow_line(
|
|
199
|
+
match:,
|
|
200
|
+
comment:,
|
|
201
|
+
declaration_pattern:,
|
|
202
|
+
old_declaration:,
|
|
203
|
+
new_declaration:,
|
|
204
|
+
old_ref:,
|
|
205
|
+
new_ref:
|
|
206
|
+
)
|
|
207
|
+
line_without_comment = comment ? match.delete_suffix(comment) : match
|
|
208
|
+
updated_match = line_without_comment.gsub(declaration_pattern) do |declaration|
|
|
209
|
+
declaration.sub(old_declaration, new_declaration)
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
if comment
|
|
213
|
+
updated_match << (commenter.updated_comment(comment, old_ref, new_ref) || comment)
|
|
214
|
+
elsif (new_comment = commenter.new_comment(old_ref, new_ref))
|
|
215
|
+
updated_match << new_comment
|
|
216
|
+
end
|
|
217
|
+
updated_match
|
|
218
|
+
end
|
|
219
|
+
end
|
|
220
|
+
end
|
|
221
|
+
end
|
|
222
|
+
end
|
|
@@ -8,12 +8,15 @@ require "dependabot/file_updaters"
|
|
|
8
8
|
require "dependabot/file_updaters/base"
|
|
9
9
|
require "dependabot/github_actions/constants"
|
|
10
10
|
require "dependabot/github_actions/lockfile"
|
|
11
|
+
require "dependabot/github_actions/workflow_file"
|
|
11
12
|
|
|
12
13
|
module Dependabot
|
|
13
14
|
module GithubActions
|
|
14
15
|
class FileUpdater < Dependabot::FileUpdaters::Base
|
|
15
16
|
extend T::Sig
|
|
16
17
|
|
|
18
|
+
require_relative "file_updater/workflow_updater"
|
|
19
|
+
|
|
17
20
|
sig { override.returns(T::Array[Dependabot::DependencyFile]) }
|
|
18
21
|
def updated_dependency_files
|
|
19
22
|
updated_files = changed_workflow_files.map do |file|
|
|
@@ -138,85 +141,24 @@ module Dependabot
|
|
|
138
141
|
file.path.delete_prefix("/")
|
|
139
142
|
end
|
|
140
143
|
|
|
141
|
-
# rubocop:disable Metrics/AbcSize
|
|
142
144
|
sig { params(file: Dependabot::DependencyFile).returns(String) }
|
|
143
145
|
def updated_workflow_file_content(file)
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
old_declaration = T.must(T.must(old_req).metadata_string("declaration_string"))
|
|
162
|
-
new_declaration =
|
|
163
|
-
old_declaration
|
|
164
|
-
.gsub(/@.*+/, "@#{new_ref}")
|
|
165
|
-
|
|
166
|
-
# Replace the old declaration that's preceded by a non-word character (unless it's a hyphen)
|
|
167
|
-
# and followed by a whitespace character (comments) or EOL.
|
|
168
|
-
# If the declaration is followed by a comment that lists the version associated
|
|
169
|
-
# with the SHA source ref, then update the comment to the human-readable new version.
|
|
170
|
-
# However, if the comment includes additional text beyond the version, for safety
|
|
171
|
-
# we skip updating the comment in case it's a custom note, todo, warning etc of some kind.
|
|
172
|
-
# See the related unit tests for examples.
|
|
173
|
-
updated_content =
|
|
174
|
-
updated_content
|
|
175
|
-
.gsub(
|
|
176
|
-
/(?<=[^a-zA-Z_-]|"|')#{Regexp.escape(old_declaration)}["']?(?<comment>\s+#.*)?(?=\s|$)/
|
|
177
|
-
) do |match|
|
|
178
|
-
comment = Regexp.last_match(:comment)
|
|
179
|
-
match.gsub!(old_declaration, new_declaration)
|
|
180
|
-
if comment && (updated_comment = updated_version_comment(comment, old_ref, new_ref))
|
|
181
|
-
match.gsub!(comment, updated_comment)
|
|
182
|
-
end
|
|
183
|
-
match
|
|
184
|
-
end
|
|
185
|
-
end
|
|
186
|
-
|
|
187
|
-
updated_content
|
|
188
|
-
end
|
|
189
|
-
# rubocop:enable Metrics/AbcSize
|
|
190
|
-
|
|
191
|
-
sig { params(comment: T.nilable(String), old_ref: String, new_ref: String).returns(T.nilable(String)) }
|
|
192
|
-
def updated_version_comment(comment, old_ref, new_ref)
|
|
193
|
-
raise "No comment!" unless comment
|
|
194
|
-
|
|
195
|
-
comment = comment.rstrip
|
|
196
|
-
git_checker = Dependabot::GitCommitChecker.new(dependency: dependency, credentials: credentials)
|
|
197
|
-
return unless git_checker.ref_looks_like_commit_sha?(old_ref)
|
|
198
|
-
|
|
199
|
-
previous_version_tags = git_checker.most_specific_version_tags_for_sha(old_ref)
|
|
200
|
-
return unless previous_version_tags.any? # There's no tag for this commit
|
|
201
|
-
|
|
202
|
-
# Use the most specific (longest) matching version to avoid partial replacements.
|
|
203
|
-
# Tags are sorted ascending, so ["v1", "v1.0", "v1.0.1"] maps to ["1", "1.0", "1.0.1"].
|
|
204
|
-
# Without this, "1" could match the end of "v1.0.1", causing gsub("1", "1.1") => "v1.1.0.1.1".
|
|
205
|
-
previous_version = previous_version_tags.map { |tag| version_class.new(tag).to_s }
|
|
206
|
-
.select { |version| comment.end_with?(version) }
|
|
207
|
-
.max_by(&:length)
|
|
208
|
-
return unless previous_version
|
|
209
|
-
|
|
210
|
-
new_version_tag = git_checker.most_specific_version_tag_for_sha(new_ref)
|
|
211
|
-
return unless new_version_tag
|
|
212
|
-
|
|
213
|
-
new_version = version_class.new(new_version_tag).to_s
|
|
214
|
-
comment.gsub(previous_version, new_version)
|
|
215
|
-
end
|
|
216
|
-
|
|
217
|
-
sig { returns(T.class_of(Dependabot::GithubActions::Version)) }
|
|
218
|
-
def version_class
|
|
219
|
-
GithubActions::Version
|
|
146
|
+
WorkflowUpdater.new(
|
|
147
|
+
file: file,
|
|
148
|
+
dependency: dependency,
|
|
149
|
+
commenter: version_commenter
|
|
150
|
+
).updated_content
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
sig { returns(WorkflowUpdater::VersionCommenter) }
|
|
154
|
+
def version_commenter
|
|
155
|
+
@version_commenter ||= T.let(
|
|
156
|
+
WorkflowUpdater::VersionCommenter.new(
|
|
157
|
+
dependency: dependency,
|
|
158
|
+
credentials: credentials
|
|
159
|
+
),
|
|
160
|
+
T.nilable(WorkflowUpdater::VersionCommenter)
|
|
161
|
+
)
|
|
220
162
|
end
|
|
221
163
|
end
|
|
222
164
|
end
|
|
@@ -59,7 +59,7 @@ module Dependabot
|
|
|
59
59
|
|
|
60
60
|
sig { override.returns(T::Boolean) }
|
|
61
61
|
def up_to_date?
|
|
62
|
-
super && !
|
|
62
|
+
super && !relevant_requirements_changed?
|
|
63
63
|
end
|
|
64
64
|
|
|
65
65
|
sig { override.returns(T::Array[Dependabot::DependencyRequirement]) }
|
|
@@ -82,16 +82,36 @@ module Dependabot
|
|
|
82
82
|
return true if super
|
|
83
83
|
return false unless requirements_to_unlock == :own
|
|
84
84
|
|
|
85
|
-
|
|
85
|
+
relevant_requirements_changed?
|
|
86
86
|
end
|
|
87
87
|
|
|
88
88
|
sig { returns(T::Boolean) }
|
|
89
|
-
def
|
|
89
|
+
def relevant_requirements_changed?
|
|
90
90
|
dependency.requirements.zip(updated_requirements).any? do |current, updated|
|
|
91
|
-
|
|
91
|
+
next false unless updated
|
|
92
|
+
|
|
93
|
+
current != updated && (onboarded_requirement?(current) || tag_to_sha_requirement?(current, updated))
|
|
92
94
|
end
|
|
93
95
|
end
|
|
94
96
|
|
|
97
|
+
sig do
|
|
98
|
+
params(
|
|
99
|
+
current: Dependabot::DependencyRequirement,
|
|
100
|
+
updated: Dependabot::DependencyRequirement
|
|
101
|
+
).returns(T::Boolean)
|
|
102
|
+
end
|
|
103
|
+
def tag_to_sha_requirement?(current, updated)
|
|
104
|
+
return false unless pin_to_sha?
|
|
105
|
+
|
|
106
|
+
current_ref = current.source_string("ref")
|
|
107
|
+
updated_ref = updated.source_string("ref")
|
|
108
|
+
return false unless current_ref && updated_ref
|
|
109
|
+
return false unless version_class.correct?(current_ref)
|
|
110
|
+
return false if Dependabot::GithubActions::Version.path_based?(current_ref)
|
|
111
|
+
|
|
112
|
+
git_commit_checker.ref_looks_like_commit_sha?(updated_ref)
|
|
113
|
+
end
|
|
114
|
+
|
|
95
115
|
# A requirement is "onboarded" when the repo carries an `actions.lock` that is
|
|
96
116
|
# authoritative for the requirement's workflow. Only onboarded requirements get
|
|
97
117
|
# per-source precision selection; everything else flows through the combined
|
|
@@ -246,11 +266,12 @@ module Dependabot
|
|
|
246
266
|
# TODO: Support Docker sources
|
|
247
267
|
return unless git_commit_checker.git_dependency?
|
|
248
268
|
|
|
249
|
-
git_source =
|
|
269
|
+
git_source = git_source_for(source)
|
|
270
|
+
current_ref = git_source&.fetch(:ref, nil)
|
|
250
271
|
finder = onboarded ? latest_version_finder_for(git_source) : T.must(latest_version_finder)
|
|
251
272
|
|
|
252
273
|
if vulnerable? && (new_tag = finder.lowest_security_fix_release)
|
|
253
|
-
return new_tag
|
|
274
|
+
return ref_for_release(new_tag, current_ref: current_ref)
|
|
254
275
|
end
|
|
255
276
|
|
|
256
277
|
source_git_commit_checker = git_helper.git_commit_checker_for(git_source)
|
|
@@ -258,7 +279,7 @@ module Dependabot
|
|
|
258
279
|
# Return the git tag if updating a pinned version
|
|
259
280
|
if source_git_commit_checker.pinned_ref_looks_like_version? &&
|
|
260
281
|
(new_tag = finder.latest_version_tag_respecting_cooldown)
|
|
261
|
-
return new_tag
|
|
282
|
+
return ref_for_release(new_tag, current_ref: current_ref)
|
|
262
283
|
end
|
|
263
284
|
|
|
264
285
|
# Return the pinned git commit if one is available
|
|
@@ -271,6 +292,34 @@ module Dependabot
|
|
|
271
292
|
nil
|
|
272
293
|
end
|
|
273
294
|
|
|
295
|
+
sig { returns(T::Boolean) }
|
|
296
|
+
def pin_to_sha?
|
|
297
|
+
T.cast(options.fetch(:github_actions_pin_to_sha, false), T::Boolean)
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
sig do
|
|
301
|
+
params(
|
|
302
|
+
release: T::Hash[Symbol, Object],
|
|
303
|
+
current_ref: T.nilable(String)
|
|
304
|
+
).returns(String)
|
|
305
|
+
end
|
|
306
|
+
def ref_for_release(release, current_ref:)
|
|
307
|
+
return T.cast(release.fetch(:tag), String) unless pin_to_sha?
|
|
308
|
+
if current_ref && Dependabot::GithubActions::Version.path_based?(current_ref)
|
|
309
|
+
return T.cast(release.fetch(:tag), String)
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
T.cast(release.fetch(:commit_sha), String)
|
|
313
|
+
end
|
|
314
|
+
|
|
315
|
+
sig do
|
|
316
|
+
params(source: T.nilable(Dependabot::DependencyRequirement::ObjectHash))
|
|
317
|
+
.returns(T.nilable(GitSource))
|
|
318
|
+
end
|
|
319
|
+
def git_source_for(source)
|
|
320
|
+
source && symbolize_source(source)
|
|
321
|
+
end
|
|
322
|
+
|
|
274
323
|
sig do
|
|
275
324
|
params(source: Dependabot::DependencyRequirement::ObjectHash)
|
|
276
325
|
.returns(GitSource)
|