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,212 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "dependabot/github_actions/workflow_file"
|
|
5
|
+
|
|
6
|
+
module Dependabot
|
|
7
|
+
module GithubActions
|
|
8
|
+
class WorkflowFile
|
|
9
|
+
class MetadataBuilder
|
|
10
|
+
extend T::Sig
|
|
11
|
+
|
|
12
|
+
sig { params(workflow_file: WorkflowFile).void }
|
|
13
|
+
def initialize(workflow_file)
|
|
14
|
+
@workflow_file = workflow_file
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
sig { params(declaration: UsesDeclaration).returns(Metadata) }
|
|
18
|
+
def build(declaration)
|
|
19
|
+
metadata = T.let(
|
|
20
|
+
{
|
|
21
|
+
path: declaration.path,
|
|
22
|
+
value: node_metadata(declaration.value_node),
|
|
23
|
+
target: node_metadata(declaration.source_node),
|
|
24
|
+
mapping: node_metadata(declaration.mapping_node)
|
|
25
|
+
},
|
|
26
|
+
Metadata
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
sequence = declaration.steps_sequence
|
|
30
|
+
if sequence
|
|
31
|
+
metadata[:sequence] = sequence_metadata(
|
|
32
|
+
sequence,
|
|
33
|
+
declaration.steps_key_node,
|
|
34
|
+
declaration.sequence_item_index
|
|
35
|
+
)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
metadata
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
sig { params(declaration: UsesDeclaration).returns(T::Array[UsesDeclaration]) }
|
|
42
|
+
def collision_declarations_for(declaration)
|
|
43
|
+
anchor = comment_anchor(declaration)
|
|
44
|
+
return [] unless anchor
|
|
45
|
+
|
|
46
|
+
workflow_file.uses_declarations.select do |item|
|
|
47
|
+
item_anchor = comment_anchor(item)
|
|
48
|
+
item_anchor && item_anchor.first == anchor.first
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
sig { params(declaration: UsesDeclaration).returns(T::Boolean) }
|
|
53
|
+
def comment_line_collision?(declaration)
|
|
54
|
+
anchor = comment_anchor(declaration)
|
|
55
|
+
return false unless anchor
|
|
56
|
+
return true if collision_declarations_for(declaration).length > 1
|
|
57
|
+
|
|
58
|
+
line = anchor.first
|
|
59
|
+
anchor_offset = workflow_file.offset(anchor.first, anchor.last)
|
|
60
|
+
workflow_file.steps_sequences.any? do |sequence|
|
|
61
|
+
sequence_starts_after_anchor?(sequence, line, anchor_offset) ||
|
|
62
|
+
child_starts_after_anchor?(sequence, line, anchor_offset)
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
sig { params(declaration: UsesDeclaration).returns(T::Array[Psych::Nodes::Sequence]) }
|
|
67
|
+
def collision_sequences_for(declaration)
|
|
68
|
+
anchor = comment_anchor(declaration)
|
|
69
|
+
return [] unless anchor
|
|
70
|
+
|
|
71
|
+
line = anchor.first
|
|
72
|
+
workflow_file.steps_sequences.select do |sequence|
|
|
73
|
+
line.between?(
|
|
74
|
+
workflow_file.node_start_line(sequence),
|
|
75
|
+
workflow_file.node_end_line(sequence)
|
|
76
|
+
)
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
sig { params(declaration: UsesDeclaration).returns(T.nilable([Integer, Integer])) }
|
|
81
|
+
def comment_anchor(declaration)
|
|
82
|
+
source_node = declaration.source_node
|
|
83
|
+
return unless source_node
|
|
84
|
+
|
|
85
|
+
if source_node.is_a?(Psych::Nodes::Scalar) && block_scalar?(source_node)
|
|
86
|
+
return [
|
|
87
|
+
workflow_file.node_start_line(source_node),
|
|
88
|
+
workflow_file.node_start_column(source_node)
|
|
89
|
+
]
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
mapping = declaration.mapping_node
|
|
93
|
+
if mapping && workflow_file.mapping_node_style(mapping) == Psych::Nodes::Mapping::FLOW
|
|
94
|
+
return [workflow_file.node_end_line(mapping), workflow_file.node_end_column(mapping)]
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
value_node = declaration.value_node
|
|
98
|
+
return unless value_node
|
|
99
|
+
|
|
100
|
+
[workflow_file.node_end_line(value_node), workflow_file.node_end_column(value_node)]
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
private
|
|
104
|
+
|
|
105
|
+
sig { returns(WorkflowFile) }
|
|
106
|
+
attr_reader :workflow_file
|
|
107
|
+
|
|
108
|
+
sig do
|
|
109
|
+
params(
|
|
110
|
+
sequence: Psych::Nodes::Sequence,
|
|
111
|
+
line: Integer,
|
|
112
|
+
anchor_offset: Integer
|
|
113
|
+
).returns(T::Boolean)
|
|
114
|
+
end
|
|
115
|
+
def sequence_starts_after_anchor?(sequence, line, anchor_offset)
|
|
116
|
+
workflow_file.node_start_line(sequence) == line && node_start_offset(sequence) > anchor_offset
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
sig do
|
|
120
|
+
params(
|
|
121
|
+
sequence: Psych::Nodes::Sequence,
|
|
122
|
+
line: Integer,
|
|
123
|
+
anchor_offset: Integer
|
|
124
|
+
).returns(T::Boolean)
|
|
125
|
+
end
|
|
126
|
+
def child_starts_after_anchor?(sequence, line, anchor_offset)
|
|
127
|
+
workflow_file.node_children(sequence).any? do |child|
|
|
128
|
+
workflow_file.node_start_line(child) == line && node_start_offset(child) > anchor_offset
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
sig { params(node: Psych::Nodes::Node).returns(Integer) }
|
|
133
|
+
def node_start_offset(node)
|
|
134
|
+
workflow_file.offset(
|
|
135
|
+
workflow_file.node_start_line(node),
|
|
136
|
+
workflow_file.node_start_column(node)
|
|
137
|
+
)
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
sig { params(node: Psych::Nodes::Scalar).returns(T::Boolean) }
|
|
141
|
+
def block_scalar?(node)
|
|
142
|
+
style = workflow_file.scalar_node_style(node)
|
|
143
|
+
[Psych::Nodes::Scalar::LITERAL, Psych::Nodes::Scalar::FOLDED].include?(style)
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
sig { params(node: T.nilable(Psych::Nodes::Node)).returns(Metadata) }
|
|
147
|
+
def node_metadata(node)
|
|
148
|
+
return {} unless node
|
|
149
|
+
|
|
150
|
+
metadata = T.let(
|
|
151
|
+
{
|
|
152
|
+
kind: node_kind(node),
|
|
153
|
+
start_line: workflow_file.node_start_line(node),
|
|
154
|
+
start_column: workflow_file.node_start_column(node),
|
|
155
|
+
end_line: workflow_file.node_end_line(node),
|
|
156
|
+
end_column: workflow_file.node_end_column(node)
|
|
157
|
+
},
|
|
158
|
+
Metadata
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
if node.is_a?(Psych::Nodes::Scalar)
|
|
162
|
+
metadata[:style] = scalar_style(workflow_file.scalar_node_style(node))
|
|
163
|
+
metadata[:anchor] = node.anchor if node.anchor
|
|
164
|
+
elsif node.is_a?(Psych::Nodes::Alias)
|
|
165
|
+
metadata[:anchor] = node.anchor
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
metadata
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
sig do
|
|
172
|
+
params(
|
|
173
|
+
sequence: Psych::Nodes::Sequence,
|
|
174
|
+
key_node: T.nilable(Psych::Nodes::Scalar),
|
|
175
|
+
item_index: T.nilable(Integer)
|
|
176
|
+
).returns(Metadata)
|
|
177
|
+
end
|
|
178
|
+
def sequence_metadata(sequence, key_node, item_index)
|
|
179
|
+
metadata = node_metadata(sequence)
|
|
180
|
+
metadata[:style] =
|
|
181
|
+
workflow_file.sequence_node_style(sequence) == Psych::Nodes::Sequence::FLOW ? "flow" : "block"
|
|
182
|
+
metadata[:item_index] = item_index if item_index
|
|
183
|
+
metadata[:item_count] = workflow_file.node_children(sequence).length
|
|
184
|
+
metadata[:key_start_column] = workflow_file.node_start_column(key_node) if key_node
|
|
185
|
+
metadata
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
sig { params(node: Psych::Nodes::Node).returns(String) }
|
|
189
|
+
def node_kind(node)
|
|
190
|
+
case node
|
|
191
|
+
when Psych::Nodes::Alias then "alias"
|
|
192
|
+
when Psych::Nodes::Scalar then "scalar"
|
|
193
|
+
when Psych::Nodes::Mapping then "mapping"
|
|
194
|
+
when Psych::Nodes::Sequence then "sequence"
|
|
195
|
+
else "node"
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
sig { params(style: Integer).returns(String) }
|
|
200
|
+
def scalar_style(style)
|
|
201
|
+
case style
|
|
202
|
+
when Psych::Nodes::Scalar::SINGLE_QUOTED then "single_quoted"
|
|
203
|
+
when Psych::Nodes::Scalar::DOUBLE_QUOTED then "double_quoted"
|
|
204
|
+
when Psych::Nodes::Scalar::LITERAL then "literal"
|
|
205
|
+
when Psych::Nodes::Scalar::FOLDED then "folded"
|
|
206
|
+
else "plain"
|
|
207
|
+
end
|
|
208
|
+
end
|
|
209
|
+
end
|
|
210
|
+
end
|
|
211
|
+
end
|
|
212
|
+
end
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# typed: strong
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "dependabot/github_actions/workflow_file"
|
|
5
|
+
|
|
6
|
+
module Dependabot
|
|
7
|
+
module GithubActions
|
|
8
|
+
class WorkflowFile
|
|
9
|
+
class NodeContext
|
|
10
|
+
extend T::Sig
|
|
11
|
+
|
|
12
|
+
sig do
|
|
13
|
+
params(
|
|
14
|
+
node: Psych::Nodes::Node,
|
|
15
|
+
mapping_node: T.nilable(Psych::Nodes::Mapping),
|
|
16
|
+
key_node: T.nilable(Psych::Nodes::Scalar),
|
|
17
|
+
steps_sequence: T.nilable(Psych::Nodes::Sequence),
|
|
18
|
+
steps_key_node: T.nilable(Psych::Nodes::Scalar),
|
|
19
|
+
sequence_item: T.nilable(Psych::Nodes::Node),
|
|
20
|
+
sequence_item_index: T.nilable(Integer)
|
|
21
|
+
).void
|
|
22
|
+
end
|
|
23
|
+
def initialize(
|
|
24
|
+
node:,
|
|
25
|
+
mapping_node:,
|
|
26
|
+
key_node:,
|
|
27
|
+
steps_sequence:,
|
|
28
|
+
steps_key_node:,
|
|
29
|
+
sequence_item:,
|
|
30
|
+
sequence_item_index:
|
|
31
|
+
)
|
|
32
|
+
@node = node
|
|
33
|
+
@mapping_node = mapping_node
|
|
34
|
+
@key_node = key_node
|
|
35
|
+
@steps_sequence = steps_sequence
|
|
36
|
+
@steps_key_node = steps_key_node
|
|
37
|
+
@sequence_item = sequence_item
|
|
38
|
+
@sequence_item_index = sequence_item_index
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
sig { returns(Psych::Nodes::Node) }
|
|
42
|
+
attr_reader :node
|
|
43
|
+
|
|
44
|
+
sig { returns(T.nilable(Psych::Nodes::Mapping)) }
|
|
45
|
+
attr_reader :mapping_node
|
|
46
|
+
|
|
47
|
+
sig { returns(T.nilable(Psych::Nodes::Scalar)) }
|
|
48
|
+
attr_reader :key_node
|
|
49
|
+
|
|
50
|
+
sig { returns(T.nilable(Psych::Nodes::Sequence)) }
|
|
51
|
+
attr_reader :steps_sequence
|
|
52
|
+
|
|
53
|
+
sig { returns(T.nilable(Psych::Nodes::Scalar)) }
|
|
54
|
+
attr_reader :steps_key_node
|
|
55
|
+
|
|
56
|
+
sig { returns(T.nilable(Psych::Nodes::Node)) }
|
|
57
|
+
attr_reader :sequence_item
|
|
58
|
+
|
|
59
|
+
sig { returns(T.nilable(Integer)) }
|
|
60
|
+
attr_reader :sequence_item_index
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "dependabot/github_actions/workflow_file"
|
|
5
|
+
|
|
6
|
+
module Dependabot
|
|
7
|
+
module GithubActions
|
|
8
|
+
class WorkflowFile
|
|
9
|
+
class UsesCollector
|
|
10
|
+
extend T::Sig
|
|
11
|
+
|
|
12
|
+
class ResolvedUse < T::Struct
|
|
13
|
+
const :value, String
|
|
14
|
+
const :path, Path
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
sig { params(data: T.nilable(Object)).void }
|
|
18
|
+
def initialize(data)
|
|
19
|
+
@data = data
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
sig { returns(T::Array[ResolvedUse]) }
|
|
23
|
+
def uses
|
|
24
|
+
root, path = workflow_root
|
|
25
|
+
return [] unless root && path
|
|
26
|
+
|
|
27
|
+
collect_uses(root, path)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
private
|
|
31
|
+
|
|
32
|
+
sig { returns(T.nilable(Object)) }
|
|
33
|
+
attr_reader :data
|
|
34
|
+
|
|
35
|
+
sig { returns([T.nilable(Object), T.nilable(Path)]) }
|
|
36
|
+
def workflow_root
|
|
37
|
+
return [nil, nil] unless data.is_a?(Hash)
|
|
38
|
+
|
|
39
|
+
workflow_data = T.cast(data, T::Hash[Object, Object])
|
|
40
|
+
if workflow_data.key?("jobs")
|
|
41
|
+
[workflow_data["jobs"], ["jobs"]]
|
|
42
|
+
elsif workflow_data.key?("runs")
|
|
43
|
+
[workflow_data["runs"], ["runs"]]
|
|
44
|
+
else
|
|
45
|
+
[nil, nil]
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
sig { params(value: Object, path: Path).returns(T::Array[ResolvedUse]) }
|
|
50
|
+
def collect_uses(value, path)
|
|
51
|
+
case value
|
|
52
|
+
when Hash then collect_uses_from_hash(value, path)
|
|
53
|
+
when Array
|
|
54
|
+
value.each_with_index.flat_map do |child, index|
|
|
55
|
+
collect_uses(child, path + [index])
|
|
56
|
+
end
|
|
57
|
+
else
|
|
58
|
+
[]
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
sig { params(value: T::Hash[Object, Object], path: Path).returns(T::Array[ResolvedUse]) }
|
|
63
|
+
def collect_uses_from_hash(value, path)
|
|
64
|
+
if value.key?(USES_KEY)
|
|
65
|
+
declaration_value = value[USES_KEY]
|
|
66
|
+
return [] unless declaration_value.is_a?(String)
|
|
67
|
+
|
|
68
|
+
[ResolvedUse.new(value: declaration_value, path: path + [USES_KEY])]
|
|
69
|
+
elsif value.key?(STEPS_KEY)
|
|
70
|
+
collect_uses(value[STEPS_KEY], path + [STEPS_KEY])
|
|
71
|
+
else
|
|
72
|
+
value.flat_map { |key, child| collect_uses(child, path + [key.to_s]) }
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# typed: strong
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "dependabot/github_actions/workflow_file"
|
|
5
|
+
|
|
6
|
+
module Dependabot
|
|
7
|
+
module GithubActions
|
|
8
|
+
class WorkflowFile
|
|
9
|
+
class UsesDeclaration
|
|
10
|
+
extend T::Sig
|
|
11
|
+
|
|
12
|
+
sig do
|
|
13
|
+
params(
|
|
14
|
+
value: String,
|
|
15
|
+
path: Path,
|
|
16
|
+
value_node: T.nilable(Psych::Nodes::Node),
|
|
17
|
+
source_node: T.nilable(Psych::Nodes::Node),
|
|
18
|
+
mapping_node: T.nilable(Psych::Nodes::Mapping),
|
|
19
|
+
steps_sequence: T.nilable(Psych::Nodes::Sequence),
|
|
20
|
+
steps_key_node: T.nilable(Psych::Nodes::Scalar),
|
|
21
|
+
sequence_item: T.nilable(Psych::Nodes::Node),
|
|
22
|
+
sequence_item_index: T.nilable(Integer)
|
|
23
|
+
).void
|
|
24
|
+
end
|
|
25
|
+
def initialize(
|
|
26
|
+
value:,
|
|
27
|
+
path:,
|
|
28
|
+
value_node:,
|
|
29
|
+
source_node:,
|
|
30
|
+
mapping_node:,
|
|
31
|
+
steps_sequence:,
|
|
32
|
+
steps_key_node:,
|
|
33
|
+
sequence_item:,
|
|
34
|
+
sequence_item_index:
|
|
35
|
+
)
|
|
36
|
+
@value = value
|
|
37
|
+
@path = path
|
|
38
|
+
@value_node = value_node
|
|
39
|
+
@source_node = source_node
|
|
40
|
+
@mapping_node = mapping_node
|
|
41
|
+
@steps_sequence = steps_sequence
|
|
42
|
+
@steps_key_node = steps_key_node
|
|
43
|
+
@sequence_item = sequence_item
|
|
44
|
+
@sequence_item_index = sequence_item_index
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
sig { returns(String) }
|
|
48
|
+
attr_reader :value
|
|
49
|
+
|
|
50
|
+
sig { returns(Path) }
|
|
51
|
+
attr_reader :path
|
|
52
|
+
|
|
53
|
+
sig { returns(T.nilable(Psych::Nodes::Node)) }
|
|
54
|
+
attr_reader :value_node
|
|
55
|
+
|
|
56
|
+
sig { returns(T.nilable(Psych::Nodes::Node)) }
|
|
57
|
+
attr_reader :source_node
|
|
58
|
+
|
|
59
|
+
sig { returns(T.nilable(Psych::Nodes::Mapping)) }
|
|
60
|
+
attr_reader :mapping_node
|
|
61
|
+
|
|
62
|
+
sig { returns(T.nilable(Psych::Nodes::Sequence)) }
|
|
63
|
+
attr_reader :steps_sequence
|
|
64
|
+
|
|
65
|
+
sig { returns(T.nilable(Psych::Nodes::Scalar)) }
|
|
66
|
+
attr_reader :steps_key_node
|
|
67
|
+
|
|
68
|
+
sig { returns(T.nilable(Psych::Nodes::Node)) }
|
|
69
|
+
attr_reader :sequence_item
|
|
70
|
+
|
|
71
|
+
sig { returns(T.nilable(Integer)) }
|
|
72
|
+
attr_reader :sequence_item_index
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|