build-graph 1.5.1 → 2.0.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/.gitignore +7 -0
- data/lib/build/graph/node.rb +11 -9
- data/lib/build/graph/task.rb +31 -28
- data/lib/build/graph/version.rb +1 -1
- data/lib/build/graph/walker.rb +2 -2
- data/spec/build/graph/inherit_spec.rb +3 -3
- data/spec/build/graph/node_spec.rb +46 -54
- data/spec/build/graph/process_graph.rb +12 -2
- data/spec/build/graph/task_spec.rb +14 -6
- data/spec/build/graph/walker_spec.rb +14 -12
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a4e78a7adaa9ae6200d78d04dcad80fb911e55154e49f594070f3784a7ed3510
|
4
|
+
data.tar.gz: fb56d1d8055eb3b9fcaa96470afdb844ff81168cce11d9aa2e3f2cb0ad5f163d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b9187a0ce3b730a9480e8239ac22873949a98f1d5e2911e80d017b8f6b4f919be6decd0aefa55682864d64b15188bff4d27171b5c45d42044daefad353803971
|
7
|
+
data.tar.gz: ea055341ccb20952b7bd6cce5df7624cdc9273a094006d045d6b321d8048b8eccb75583b910e8fab99cc545bdfd788eac5579a3cb57687b341bbca4d1986e00b
|
data/.gitignore
CHANGED
data/lib/build/graph/node.rb
CHANGED
@@ -25,18 +25,14 @@ module Build
|
|
25
25
|
module Graph
|
26
26
|
# This is essentialy a immutable key:
|
27
27
|
class Node
|
28
|
-
|
29
|
-
|
28
|
+
# @param process [Object] Represents an abstract process, e.g. a name or a function.
|
29
|
+
def initialize(inputs, outputs)
|
30
30
|
@inputs = inputs
|
31
31
|
@outputs = outputs
|
32
|
-
|
33
|
-
# Represents an abstract process, e.g. a name or a function.
|
34
|
-
@process = process
|
35
32
|
end
|
36
33
|
|
37
34
|
attr :inputs
|
38
35
|
attr :outputs
|
39
|
-
attr :process
|
40
36
|
|
41
37
|
# Nodes that inherit outputs are special in the sense that outputs are not available until all child nodes have been evaluated.
|
42
38
|
def inherit_outputs?
|
@@ -78,16 +74,22 @@ module Build
|
|
78
74
|
return false
|
79
75
|
end
|
80
76
|
|
77
|
+
def == other
|
78
|
+
self.class == other.class and
|
79
|
+
@inputs == other.inputs and
|
80
|
+
@outputs == other.outputs
|
81
|
+
end
|
82
|
+
|
81
83
|
def eql?(other)
|
82
|
-
|
84
|
+
self.equal?(other) or self == other
|
83
85
|
end
|
84
86
|
|
85
87
|
def hash
|
86
|
-
|
88
|
+
@inputs.hash ^ @outputs.hash
|
87
89
|
end
|
88
90
|
|
89
91
|
def inspect
|
90
|
-
"#<#{self.class} #{@inputs.inspect} => #{@outputs.inspect}
|
92
|
+
"#<#{self.class} #{@inputs.inspect} => #{@outputs.inspect}>"
|
91
93
|
end
|
92
94
|
|
93
95
|
def self.top(inputs = Files::Paths::NONE, outputs = :inherit, **options, &block)
|
data/lib/build/graph/task.rb
CHANGED
@@ -36,7 +36,7 @@ module Build
|
|
36
36
|
end
|
37
37
|
|
38
38
|
class Task
|
39
|
-
def initialize(walker, node
|
39
|
+
def initialize(walker, node)
|
40
40
|
@walker = walker
|
41
41
|
|
42
42
|
@walker.tasks[node] = self
|
@@ -46,14 +46,10 @@ module Build
|
|
46
46
|
|
47
47
|
@error = nil
|
48
48
|
|
49
|
-
# Tasks that must be complete before processing this task.
|
50
|
-
@dependencies = dependencies
|
51
|
-
|
52
49
|
# Tasks that must be complete before finishing this task.
|
53
50
|
@children = []
|
54
51
|
|
55
52
|
@state = nil
|
56
|
-
@annotation = nil
|
57
53
|
|
58
54
|
@inputs_failed = false
|
59
55
|
end
|
@@ -61,13 +57,11 @@ module Build
|
|
61
57
|
attr :inputs
|
62
58
|
attr :outputs
|
63
59
|
|
64
|
-
attr :dependencies
|
65
60
|
attr :children
|
66
61
|
|
62
|
+
# The state of the task, one of nil, :complete or :failed.
|
67
63
|
attr :state
|
68
64
|
|
69
|
-
attr :annotation
|
70
|
-
|
71
65
|
# The error, if the execution of the node fails.
|
72
66
|
attr :error
|
73
67
|
|
@@ -78,7 +72,7 @@ module Build
|
|
78
72
|
# A list of any inputs whose relevant tasks failed:
|
79
73
|
attr :inputs_failed
|
80
74
|
|
81
|
-
# Derived task
|
75
|
+
# Derived task can override this function to provide appropriate behaviour.
|
82
76
|
def visit
|
83
77
|
update_inputs_and_outputs
|
84
78
|
|
@@ -101,7 +95,9 @@ module Build
|
|
101
95
|
fail!(InputsFailed)
|
102
96
|
end
|
103
97
|
|
104
|
-
|
98
|
+
if wait_for_children?
|
99
|
+
update_outputs
|
100
|
+
else
|
105
101
|
fail!(ChildrenFailed)
|
106
102
|
end
|
107
103
|
|
@@ -121,12 +117,15 @@ module Build
|
|
121
117
|
return self
|
122
118
|
end
|
123
119
|
|
120
|
+
# @return [Task] the child task that was created to update the node.
|
124
121
|
def invoke(node)
|
125
122
|
child_task = @walker.call(node, self)
|
126
123
|
|
127
124
|
raise ArgumentError.new("Invalid child task") unless child_task
|
128
125
|
|
129
126
|
@children << child_task
|
127
|
+
|
128
|
+
return child_task
|
130
129
|
end
|
131
130
|
|
132
131
|
def failed?
|
@@ -155,14 +154,28 @@ module Build
|
|
155
154
|
(@inputs.roots + @outputs.roots).collect{|path| path.to_s}
|
156
155
|
end
|
157
156
|
|
158
|
-
def
|
159
|
-
"#<#{self.class}
|
157
|
+
def to_s
|
158
|
+
"#<#{self.class} #{node_string} #{state_string}>"
|
160
159
|
end
|
161
160
|
|
162
161
|
protected
|
163
162
|
|
163
|
+
def state_string
|
164
|
+
if @state
|
165
|
+
@state.to_s
|
166
|
+
elsif @fiber
|
167
|
+
"running"
|
168
|
+
else
|
169
|
+
"new"
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
def node_string
|
174
|
+
@node.inspect
|
175
|
+
end
|
176
|
+
|
177
|
+
# If the node inputs is a glob, this part of the process converts the glob into an actual list of files. If we are not inheriting outputs from children tasks, update our outputs now.
|
164
178
|
def update_inputs_and_outputs
|
165
|
-
# If @node.inputs is a glob, this part of the process converts the glob into an actual list of files.
|
166
179
|
@inputs = Files::State.new(@node.inputs)
|
167
180
|
|
168
181
|
unless @node.inherit_outputs?
|
@@ -170,54 +183,44 @@ module Build
|
|
170
183
|
end
|
171
184
|
end
|
172
185
|
|
186
|
+
# @return [Build::Files::List] the merged list of all children outputs.
|
173
187
|
def children_outputs
|
174
188
|
@children.collect(&:outputs).inject(Files::Paths::NONE, &:+)
|
175
189
|
end
|
176
190
|
|
191
|
+
# If the node's outputs were a glob, this checks the filesystem to figure out what files were actually generated. If it inherits the outputs of the child tasks, merge them into our own outputs.
|
177
192
|
def update_outputs
|
178
193
|
if @node.inherit_outputs?
|
179
194
|
@outputs = Files::State.new(self.children_outputs)
|
180
195
|
else
|
181
|
-
@annotation = "update outputs"
|
182
196
|
# After the task has finished, we update the output states:
|
183
197
|
@outputs.update!
|
184
198
|
end
|
185
199
|
end
|
186
200
|
|
201
|
+
# Fail the task with the given error. Any task which is waiting on this task will also fail (eventually).
|
187
202
|
def fail!(error)
|
188
|
-
@annotation = "failed"
|
189
|
-
|
190
203
|
@walker.logger&.error(self) {error}
|
191
204
|
|
192
205
|
@error = error
|
193
206
|
@state = :failed
|
194
207
|
end
|
195
208
|
|
196
|
-
#
|
209
|
+
# @return [Boolean] if all inputs succeeded.
|
197
210
|
def wait_for_inputs?
|
198
211
|
# Wait on any inputs, returns whether any inputs failed:
|
199
212
|
if @inputs&.any?
|
200
|
-
@annotation = "wait for inputs"
|
201
213
|
unless @walker.wait_on_paths(self, @inputs)
|
202
214
|
return false
|
203
215
|
end
|
204
216
|
end
|
205
217
|
|
206
|
-
if @dependencies&.any?
|
207
|
-
@annotation = "wait for dependencies"
|
208
|
-
unless @walker.wait_for_children(self, @dependencies)
|
209
|
-
return false
|
210
|
-
end
|
211
|
-
end
|
212
|
-
|
213
218
|
return true
|
214
219
|
end
|
215
220
|
|
216
|
-
#
|
221
|
+
# @return [Boolean] if all children succeeded.
|
217
222
|
def wait_for_children?
|
218
223
|
if @children&.any?
|
219
|
-
@annotation = "wait for children"
|
220
|
-
|
221
224
|
unless @walker.wait_for_children(self, @children)
|
222
225
|
return false
|
223
226
|
end
|
data/lib/build/graph/version.rb
CHANGED
data/lib/build/graph/walker.rb
CHANGED
@@ -160,7 +160,7 @@ module Build
|
|
160
160
|
end
|
161
161
|
|
162
162
|
def enter(task)
|
163
|
-
@logger&.debug(self) {"Walker entering: #{task.node
|
163
|
+
@logger&.debug(self) {"Walker entering: #{task.node}"}
|
164
164
|
|
165
165
|
@tasks[task.node] = task
|
166
166
|
|
@@ -182,7 +182,7 @@ module Build
|
|
182
182
|
end
|
183
183
|
|
184
184
|
def exit(task)
|
185
|
-
@logger&.debug(self) {"Walker exiting: #{task.node
|
185
|
+
@logger&.debug(self) {"Walker exiting: #{task.node}, task #{task.failed? ? 'failed' : 'succeeded'}"}
|
186
186
|
|
187
187
|
# Fail outputs if the node failed:
|
188
188
|
if task.failed?
|
@@ -28,14 +28,14 @@ RSpec.describe Build::Graph::Walker do
|
|
28
28
|
test_glob = Build::Files::Glob.new(__dir__, "*.rb")
|
29
29
|
listing_output = Build::Files::Paths.directory(__dir__, ["listing.txt"])
|
30
30
|
|
31
|
-
node_a = Build::Graph::Node.new(Build::Files::Paths::NONE, :inherit
|
32
|
-
node_b = Build::Graph::Node.new(test_glob, listing_output
|
31
|
+
node_a = Build::Graph::Node.new(Build::Files::Paths::NONE, :inherit)
|
32
|
+
node_b = Build::Graph::Node.new(test_glob, listing_output)
|
33
33
|
|
34
34
|
walker = Build::Graph::Walker.new do |walker, node|
|
35
35
|
task = Build::Graph::Task.new(walker, node)
|
36
36
|
|
37
37
|
task.visit do
|
38
|
-
if node
|
38
|
+
if node == node_a
|
39
39
|
task.invoke(node_b)
|
40
40
|
end
|
41
41
|
end
|
@@ -23,66 +23,58 @@ require 'build/graph/node'
|
|
23
23
|
require 'build/files/glob'
|
24
24
|
require 'build/files/system'
|
25
25
|
|
26
|
-
|
26
|
+
RSpec.describe Build::Graph::Node do
|
27
27
|
include Build::Graph
|
28
28
|
include Build::Files
|
29
29
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
30
|
+
let(:test_glob) {Build::Files::Glob.new(__dir__, "*.rb")}
|
31
|
+
let(:listing_output) {Build::Files::Paths.directory(__dir__, ["listing.txt"])}
|
32
|
+
|
33
|
+
it "should be unique" do
|
34
|
+
node_a = Build::Graph::Node.new(test_glob, listing_output)
|
35
|
+
node_b = Build::Graph::Node.new(listing_output, Build::Files::Paths::NONE)
|
36
|
+
|
37
|
+
expect(node_a).to be_eql node_a
|
38
|
+
expect(node_a).to_not be_eql node_b
|
39
|
+
|
40
|
+
node_c = Build::Graph::Node.new(test_glob, listing_output)
|
41
|
+
|
42
|
+
expect(node_a).to be_eql node_c
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should be dirty" do
|
46
|
+
node_a = Build::Graph::Node.new(test_glob, listing_output)
|
47
|
+
|
48
|
+
expect(node_a.dirty?).to be true
|
49
|
+
end
|
50
|
+
|
51
|
+
it "should be clean" do
|
52
|
+
listing_output.first.touch
|
53
|
+
|
54
|
+
node_a = Build::Graph::Node.new(test_glob, listing_output)
|
55
|
+
|
56
|
+
expect(node_a.dirty?).to be false
|
57
|
+
|
58
|
+
listing_output.first.delete
|
59
|
+
end
|
60
|
+
|
61
|
+
it "should be dirty if input files are missing" do
|
62
|
+
input = Build::Files::Paths.directory(__dir__, ["missing-input.txt"])
|
63
|
+
output = Build::Files::Glob.new(__dir__, "*.rb")
|
45
64
|
|
46
|
-
|
47
|
-
test_glob = Glob.new(__dir__, "*.rb")
|
48
|
-
listing_output = Paths.directory(__dir__, ["listing.txt"])
|
49
|
-
|
50
|
-
node_a = Node.new(test_glob, listing_output, "a")
|
51
|
-
|
52
|
-
expect(node_a.dirty?).to be true
|
53
|
-
end
|
65
|
+
node = Build::Graph::Node.new(input, output)
|
54
66
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
expect(node_a.dirty?).to be false
|
64
|
-
|
65
|
-
listing_output.first.delete
|
66
|
-
end
|
67
|
+
expect(node.missing?).to be true
|
68
|
+
expect(node.dirty?).to be true
|
69
|
+
end
|
70
|
+
|
71
|
+
it "should be dirty if output files are missing" do
|
72
|
+
input = Build::Files::Glob.new(__dir__, "*.rb")
|
73
|
+
output = Build::Files::Paths.directory(__dir__, ["missing-output.txt"])
|
67
74
|
|
68
|
-
|
69
|
-
input = Paths.directory(__dir__, ["missing-input.txt"])
|
70
|
-
output = Glob.new(__dir__, "*.rb")
|
71
|
-
|
72
|
-
node = Node.new(input, output, "a")
|
73
|
-
|
74
|
-
expect(node.missing?).to be true
|
75
|
-
expect(node.dirty?).to be true
|
76
|
-
end
|
75
|
+
node = Build::Graph::Node.new(input, output)
|
77
76
|
|
78
|
-
|
79
|
-
|
80
|
-
output = Paths.directory(__dir__, ["missing-output.txt"])
|
81
|
-
|
82
|
-
node = Node.new(input, output, "a")
|
83
|
-
|
84
|
-
expect(node.missing?).to be true
|
85
|
-
expect(node.dirty?).to be true
|
86
|
-
end
|
77
|
+
expect(node.missing?).to be true
|
78
|
+
expect(node.dirty?).to be true
|
87
79
|
end
|
88
80
|
end
|
@@ -7,17 +7,27 @@ require 'console/event/spawn'
|
|
7
7
|
|
8
8
|
class ProcessNode < Build::Graph::Node
|
9
9
|
def initialize(inputs, outputs, block, title: nil)
|
10
|
-
super(inputs, outputs
|
10
|
+
super(inputs, outputs)
|
11
11
|
|
12
12
|
if title
|
13
13
|
@title = title
|
14
14
|
else
|
15
|
-
@title =
|
15
|
+
@title = block.source_location
|
16
16
|
end
|
17
17
|
|
18
18
|
@block = block
|
19
19
|
end
|
20
20
|
|
21
|
+
def == other
|
22
|
+
super and
|
23
|
+
@title == other.title and
|
24
|
+
@block == other.block
|
25
|
+
end
|
26
|
+
|
27
|
+
def hash
|
28
|
+
super ^ @title.hash ^ @block.hash
|
29
|
+
end
|
30
|
+
|
21
31
|
def evaluate(context)
|
22
32
|
context.instance_eval(&@block)
|
23
33
|
end
|
@@ -24,10 +24,12 @@ require 'build/graph/walker'
|
|
24
24
|
require 'build/graph/task'
|
25
25
|
require 'build/files/glob'
|
26
26
|
|
27
|
+
require_relative 'process_graph'
|
28
|
+
|
27
29
|
RSpec.describe Build::Graph::Task do
|
28
30
|
it "should wait for children" do
|
29
|
-
node_a = Build::Graph::Node.new(Build::Files::Paths::NONE, Build::Files::Paths::NONE
|
30
|
-
node_b = Build::Graph::Node.new(Build::Files::Paths::NONE,
|
31
|
+
node_a = Build::Graph::Node.new(Build::Files::Paths::NONE, Build::Files::Paths::NONE)
|
32
|
+
node_b = Build::Graph::Node.new(Build::Files::Paths::NONE, :inherit)
|
31
33
|
|
32
34
|
nodes = Set.new([node_a])
|
33
35
|
|
@@ -38,15 +40,15 @@ RSpec.describe Build::Graph::Task do
|
|
38
40
|
task = Build::Graph::Task.new(walker, node)
|
39
41
|
|
40
42
|
task.visit do
|
41
|
-
sequence << node
|
43
|
+
sequence << [:entered, node]
|
42
44
|
|
43
|
-
if node
|
45
|
+
if node == node_a
|
44
46
|
# This will invoke node_b concurrently, but as it is a child, task.visit won't finish until node_b is done.
|
45
47
|
task.invoke(node_b)
|
46
48
|
end
|
47
49
|
end
|
48
50
|
|
49
|
-
sequence << node
|
51
|
+
sequence << [:exited, node]
|
50
52
|
end
|
51
53
|
|
52
54
|
walker.update(nodes)
|
@@ -56,6 +58,12 @@ RSpec.describe Build::Graph::Task do
|
|
56
58
|
|
57
59
|
task_b = walker.tasks[node_b]
|
58
60
|
expect(walker.tasks[node_a].children).to be == [task_b]
|
59
|
-
|
61
|
+
|
62
|
+
expect(sequence).to be == [
|
63
|
+
[:entered, node_a],
|
64
|
+
[:entered, node_b],
|
65
|
+
[:exited, node_b],
|
66
|
+
[:exited, node_a]
|
67
|
+
]
|
60
68
|
end
|
61
69
|
end
|
@@ -29,8 +29,8 @@ RSpec.describe Build::Graph::Walker do
|
|
29
29
|
test_glob = Build::Files::Glob.new(__dir__, "*.rb")
|
30
30
|
listing_output = Build::Files::Paths.directory(__dir__, ["listing.txt"])
|
31
31
|
|
32
|
-
node_a = Build::Graph::Node.new(test_glob, listing_output
|
33
|
-
node_b = Build::Graph::Node.new(Build::Files::Paths::NONE, listing_output
|
32
|
+
node_a = Build::Graph::Node.new(test_glob, listing_output)
|
33
|
+
node_b = Build::Graph::Node.new(Build::Files::Paths::NONE, listing_output)
|
34
34
|
|
35
35
|
sequence = []
|
36
36
|
|
@@ -39,14 +39,15 @@ RSpec.describe Build::Graph::Walker do
|
|
39
39
|
task = Build::Graph::Task.new(walker, node)
|
40
40
|
|
41
41
|
task.visit do
|
42
|
-
if node
|
42
|
+
if node == node_a
|
43
43
|
task.invoke(node_b)
|
44
44
|
end
|
45
45
|
|
46
46
|
node.outputs.each do |output|
|
47
47
|
output.touch
|
48
48
|
end
|
49
|
-
|
49
|
+
|
50
|
+
sequence << node
|
50
51
|
end
|
51
52
|
end
|
52
53
|
|
@@ -58,15 +59,15 @@ RSpec.describe Build::Graph::Walker do
|
|
58
59
|
|
59
60
|
expect(walker.tasks.count).to be == 2
|
60
61
|
expect(walker.failed_tasks.count).to be == 0
|
61
|
-
expect(sequence).to be == [
|
62
|
+
expect(sequence).to be == [node_b, node_a]
|
62
63
|
end
|
63
64
|
|
64
65
|
it "should be unique" do
|
65
66
|
test_glob = Build::Files::Glob.new(__dir__, "*.rb")
|
66
67
|
listing_output = Build::Files::Paths.directory(__dir__, ["listing.txt"])
|
67
68
|
|
68
|
-
node_a = Build::Graph::Node.new(test_glob, listing_output
|
69
|
-
node_b = Build::Graph::Node.new(listing_output, Build::Files::Paths::NONE
|
69
|
+
node_a = Build::Graph::Node.new(test_glob, listing_output)
|
70
|
+
node_b = Build::Graph::Node.new(listing_output, Build::Files::Paths::NONE)
|
70
71
|
|
71
72
|
sequence = []
|
72
73
|
|
@@ -78,7 +79,8 @@ RSpec.describe Build::Graph::Walker do
|
|
78
79
|
node.outputs.each do |output|
|
79
80
|
output.touch
|
80
81
|
end
|
81
|
-
|
82
|
+
|
83
|
+
sequence << node
|
82
84
|
end
|
83
85
|
end
|
84
86
|
|
@@ -86,7 +88,7 @@ RSpec.describe Build::Graph::Walker do
|
|
86
88
|
|
87
89
|
expect(walker.tasks.count).to be == 2
|
88
90
|
expect(walker.failed_tasks.count).to be == 0
|
89
|
-
expect(sequence).to be == [
|
91
|
+
expect(sequence).to be == [node_a, node_b]
|
90
92
|
end
|
91
93
|
|
92
94
|
it "should cascade failure" do
|
@@ -94,15 +96,15 @@ RSpec.describe Build::Graph::Walker do
|
|
94
96
|
listing_output = Build::Files::Paths.directory(__dir__, ["listing.txt"])
|
95
97
|
summary_output = Build::Files::Paths.directory(__dir__, ["summary.txt"])
|
96
98
|
|
97
|
-
node_a = Build::Graph::Node.new(test_glob, listing_output
|
98
|
-
node_b = Build::Graph::Node.new(listing_output, summary_output
|
99
|
+
node_a = Build::Graph::Node.new(test_glob, listing_output)
|
100
|
+
node_b = Build::Graph::Node.new(listing_output, summary_output)
|
99
101
|
|
100
102
|
# A walker runs repeatedly, updating tasks which have been marked as dirty.
|
101
103
|
walker = Build::Graph::Walker.new do |walker, node|
|
102
104
|
task = Build::Graph::Task.new(walker, node)
|
103
105
|
|
104
106
|
task.visit do
|
105
|
-
if node
|
107
|
+
if node == node_a
|
106
108
|
raise Build::Graph::TransientError.new('Test Failure')
|
107
109
|
end
|
108
110
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: build-graph
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Samuel Williams
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-09-
|
11
|
+
date: 2019-09-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: process-group
|