scout-gear 9.0.0 → 9.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 98b3f84350f2f451b717f004bfc97cd7767f7e0fed58e1a2d0cd023bc700185f
4
- data.tar.gz: '00847ad4d92b1cab22305ea867dc12c870b22e192e79536917f82aa90cc82989'
3
+ metadata.gz: bbe916f2d82030e26b1049319216f45341b7e7f7410df65e3953c0a733af2c21
4
+ data.tar.gz: 152b31b552f90b2bb06aed9bc3cdd12a3bff48746468e52b39e5754977a40f90
5
5
  SHA512:
6
- metadata.gz: 4af5e13873fa13dea33161e9200aa725bb4329899e9242ae6504ffae2ab82e7eb01b82a53edc2a4331ede06d0f12bb07f86f579cb29ecbf0d36e791bf6d53f3e
7
- data.tar.gz: fc773eb933e5c851aa1d642e872df02d282fbcb29ee1b8e1bf150ffebf9bd4ce8c3670d43a284ff2c8197477a8cf6f71dfcd49b265ad938c723292ec57579998
6
+ metadata.gz: 51f41961b2a95d847e4a60a70a2774c34098c4fa502818be8b9d5ec30ced53fce81d02862a1104498060c04a88138ca329ae82ba09fa264b6d9ec0daca80a3a4
7
+ data.tar.gz: 113d51ff9798416073a86debb07ce47708254a4594e5785cfc4543794c3ff4e09da8809c29d67f64d57aeacd30c1c2ad49998d5eca4f544f3381991c296db1c5
data/VERSION CHANGED
@@ -1 +1 @@
1
- 9.0.0
1
+ 9.1.0
data/bin/scout CHANGED
@@ -99,7 +99,6 @@ if config_keys = options.delete(:config_keys)
99
99
  end
100
100
  end
101
101
 
102
-
103
102
  $scout_command_dir = Scout.bin.scout
104
103
  $scout_command_dir.path_maps[:scout_commands] = File.join(File.dirname(__dir__), "{PATH/bin\\/scout/scout_commands}")
105
104
 
data/lib/rbbt-scout.rb CHANGED
@@ -1,7 +1,8 @@
1
1
  $LOAD_PATH.unshift File.join(__dir__, '../modules/rbbt-util/lib')
2
- module Rbbt
2
+ module Scout
3
3
  extend Resource
4
4
  self.path_maps = Path.path_maps.merge(:rbbt_lib => File.expand_path(File.join(__dir__, '../modules/rbbt-util/', '{TOPLEVEL}','{SUBPATH}')))
5
5
  end
6
+ Rbbt = Scout
6
7
 
7
8
  Resource.set_software_env Rbbt.software
data/lib/scout/config.rb CHANGED
@@ -4,7 +4,7 @@ require_relative 'resource/scout'
4
4
 
5
5
  module Scout::Config
6
6
 
7
- CACHE = IndiferentHash.setup({})
7
+ CACHE ||= IndiferentHash.setup({})
8
8
 
9
9
  GOT_KEYS=[]
10
10
 
@@ -6,7 +6,7 @@ module IndiferentHash
6
6
  defaults = string2hash defaults if String === defaults
7
7
 
8
8
  defaults.each do |key, value|
9
- next if options.include? key
9
+ next if options.include?(key)
10
10
 
11
11
  options[key] = value
12
12
  end
@@ -29,7 +29,7 @@ module Misc
29
29
  obj.to_s
30
30
  when Float
31
31
  if obj % 1 == 0
32
- obj.to_i
32
+ obj.to_i.to_s
33
33
  elsif obj.abs > 10
34
34
  "%.1f" % obj
35
35
  elsif obj.abs > 1
@@ -86,6 +86,26 @@ module Path
86
86
  self.annotate(self.split(".")[0..-2] * ".")
87
87
  end
88
88
 
89
+ def remove_extension(extension = nil)
90
+ if extension.nil?
91
+ unset_extension
92
+ else
93
+ self.annotate(self.sub(/\.#{extension}$/,''))
94
+ end
95
+ end
96
+
97
+ def replace_extension(new_extension = nil, multiple = false)
98
+ if String === multiple
99
+ new_path = self.sub(/(\.[^\.\/]{1,5})(.#{multiple})?$/,'')
100
+ elsif multiple
101
+ new_path = self.sub(/(\.[^\.\/]{1,5})+$/,'')
102
+ else
103
+ new_path = self.sub(/\.[^\.\/]{1,5}$/,'')
104
+ end
105
+ new_path = new_path + "." + new_extension.to_s
106
+ self.annotate(new_path)
107
+ end
108
+
89
109
 
90
110
  # Is 'file' newer than 'path'? return non-true if path is newer than file
91
111
  def self.newer?(path, file, by_link = false)
@@ -28,7 +28,11 @@ module Resource
28
28
  m.named_captures.include?(c) ? m[c] : nil
29
29
  }.compact * "/"
30
30
  unlocated.gsub!(/\/+/,'/')
31
- unlocated[self.subdir] = "" if self.subdir
31
+ if self.subdir && ! self.subdir.empty?
32
+ subdir = self.subdir
33
+ subdir += "/" unless subdir.end_with?("/")
34
+ unlocated[subdir] = ""
35
+ end
32
36
  choices << self.annotate(unlocated)
33
37
  end
34
38
  end
@@ -19,6 +19,7 @@ module TSV
19
19
  preamble_str = nil
20
20
  end
21
21
 
22
+ preamble_str = preamble_str.strip if preamble_str
22
23
  [preamble_str, fields_str].compact * "\n"
23
24
  end
24
25
 
@@ -20,7 +20,7 @@ module Open
20
20
  into.add *res
21
21
  when TSV, Hash
22
22
  key, value = res
23
- if into.type == :double
23
+ if TSV === into && into.type == :double
24
24
  into.zip_new key, value, insitu: false
25
25
  else
26
26
  into[key] = value
@@ -118,6 +118,12 @@ module Open
118
118
  callback.call res if callback
119
119
  nil
120
120
  end
121
+ when Hash
122
+ obj.each do |key,value|
123
+ res = block.call(key,value)
124
+ callback.call res if callback
125
+ nil
126
+ end
121
127
  when Array
122
128
  obj.each do |line|
123
129
  res = block.call(line)
@@ -122,7 +122,7 @@ Example:
122
122
  end
123
123
 
124
124
  def digest_str
125
- fingerprint
125
+ "TSV:{"<< Log.fingerprint(self.all_fields|| []) << ";" << Log.fingerprint(self.keys) << ";" << Log.fingerprint(self.values) << "}"
126
126
  end
127
127
 
128
128
  def inspect
@@ -7,11 +7,12 @@ class Step
7
7
  direct_deps << dep
8
8
  end
9
9
  seen.concat direct_deps.collect{|d| d.path }
10
- direct_deps.inject(direct_deps){|acc,d| acc.concat(d.rec_dependencies(connected, [])); acc }
10
+ direct_deps.inject(direct_deps){|acc,d| acc.concat(d.rec_dependencies(connected, seen)); acc }
11
11
  end
12
12
 
13
13
  def recursive_inputs
14
- recursive_inputs = @inputs.to_hash
14
+ recursive_inputs = NamedArray === @inputs ? @inputs.to_hash : {}
15
+ return recursive_inputs if dependencies.nil?
15
16
  dependencies.inject(recursive_inputs) do |acc,dep|
16
17
  acc.merge(dep.recursive_inputs)
17
18
  end
@@ -2,7 +2,11 @@ class Step
2
2
  def files_dir
3
3
  @files_dir ||= begin
4
4
  dir = @path + ".files"
5
- @path.annotate(dir) if Path === @path
5
+ if Path === @path
6
+ @path.annotate(dir)
7
+ else
8
+ Path.setup(dir)
9
+ end
6
10
  dir.pkgdir = self
7
11
  dir
8
12
  end
@@ -9,12 +9,18 @@ class Step
9
9
  end
10
10
  end
11
11
 
12
+ def self.load_info(info_file)
13
+ info = Persist.load(info_file, SERIALIZER) || {}
14
+ IndiferentHash.setup(info)
15
+ end
16
+
12
17
  def load_info
13
- @info = Persist.load(info_file, SERIALIZER) || {}
14
- IndiferentHash.setup(@info)
18
+ @info = Step.load_info(info_file)
15
19
  @info_load_time = Time.now
16
20
  end
17
21
 
22
+
23
+
18
24
  def save_info(info = nil)
19
25
  Persist.save(info, info_file, SERIALIZER)
20
26
  @info_load_time = Time.now
@@ -83,10 +83,25 @@ class Step
83
83
  end
84
84
  end
85
85
 
86
- str << "\n"
86
+ str
87
87
  end
88
88
 
89
- def self.prov_report(step, offset = 0, task = nil, seen = [], expand_repeats = false, input = nil)
89
+ def self.prov_indent(step, offset, input_dependencies)
90
+ return (" " * (offset))
91
+ if step.alias?
92
+ (" " * offset + "🡵")
93
+ elsif step.overriden_task
94
+ (" " * offset + "🡇")
95
+ elsif input_dependencies.include?(step)
96
+ (" " * offset + "┝")
97
+ elsif step.input_dependencies.any?
98
+ (" " * offset + "╰")
99
+ else
100
+ (" " * (offset+1))
101
+ end
102
+ end
103
+
104
+ def self.prov_report(step, offset = 0, task = nil, seen = [], expand_repeats = false, input = nil, input_dependencies = nil)
90
105
  info = step.info || {}
91
106
  info[:task_name] = task
92
107
  path = step.path
@@ -101,7 +116,7 @@ class Step
101
116
 
102
117
  this_step_msg = prov_report_msg(status, name, path, info, input)
103
118
 
104
- input_dependencies = {}
119
+ input_dependencies ||= {}
105
120
  step.dependencies.each do |dep|
106
121
  if dep.input_dependencies.any?
107
122
  dep.input_dependencies.each do |id|
@@ -116,8 +131,10 @@ class Step
116
131
  end
117
132
  end if step.dependencies
118
133
 
119
- str = ""
120
- str = " " * offset + this_step_msg if ENV["SCOUT_ORIGINAL_STACK"] == 'true'
134
+ str = []
135
+
136
+ indent = prov_indent(step, offset, input_dependencies)
137
+ str << indent + this_step_msg if ENV["SCOUT_ORIGINAL_STACK"] == 'true'
121
138
 
122
139
  step.dependencies.dup.tap{|l|
123
140
  l.reverse! if ENV["SCOUT_ORIGINAL_STACK"] == 'true'
@@ -126,10 +143,10 @@ class Step
126
143
  new = ! seen.include?(path)
127
144
  if new
128
145
  seen << path
129
- str << prov_report(dep, offset + 1, task, seen, expand_repeats, input_dependencies[dep])
146
+ str.concat(prov_report(dep, offset + 1, task, seen, expand_repeats, input_dependencies[dep], input_dependencies.dup).split("\n"))
130
147
  else
131
148
  if expand_repeats
132
- str << Log.color(Step.status_color(dep.status), Log.uncolor(prov_report(dep, offset+1, task)))
149
+ str << Log.color(Step.status_color(dep.status), Log.uncolor(prov_report(dep, offset+1, task, input_dependencies[dep], input_dependencies.dup)))
133
150
  else
134
151
  info = dep.info || {}
135
152
  status = info[:status] || :missing
@@ -138,13 +155,14 @@ class Step
138
155
  status = :unsync if status == :done and not Open.exist?(path)
139
156
  status = :notfound if status == :noinfo and not Open.exist?(path)
140
157
 
141
- str << Log.color(Step.status_color(status), " " * (offset + 1) + Log.uncolor(prov_report_msg(status, name, path, info, input_dependencies[dep])))
158
+ dep_indent = prov_indent(dep, offset+1, input_dependencies)
159
+ str << dep_indent + Log.color(Step.status_color(status), Log.uncolor(prov_report_msg(status, name, path, info, input_dependencies[dep])))
142
160
  end
143
161
  end
144
162
  end if step.dependencies
145
163
 
146
- str += (" " * offset) + this_step_msg unless ENV["SCOUT_ORIGINAL_STACK"] == 'true'
164
+ str << indent + this_step_msg unless ENV["SCOUT_ORIGINAL_STACK"] == 'true'
147
165
 
148
- str
166
+ str * "\n"
149
167
  end
150
168
  end
@@ -33,6 +33,10 @@ class Step
33
33
  Open.rm_rf files_dir if Open.exist?(files_dir)
34
34
  end
35
35
 
36
+ def self.clean(file)
37
+ Step.new(file).clean
38
+ end
39
+
36
40
 
37
41
  def recursive_clean
38
42
  dependencies.each do |dep|
@@ -33,7 +33,9 @@ class Step
33
33
  def inputs
34
34
  @inputs ||= begin
35
35
  if info_file && Open.exists?(info_file)
36
- info[:inputs]
36
+ inputs = info[:inputs]
37
+ NamedArray.setup(inputs, info[:input_names]) if inputs && info[:input_names]
38
+ inputs
37
39
  else
38
40
  []
39
41
  end
@@ -61,6 +63,10 @@ class Step
61
63
  @name ||= File.basename(@path)
62
64
  end
63
65
 
66
+ def short_path
67
+ [workflow.to_s, task_name, name] * "/"
68
+ end
69
+
64
70
  def clean_name
65
71
  return @id if @id
66
72
  return info[:clean_name] if info.include? :clean_name
@@ -122,9 +128,10 @@ class Step
122
128
  begin
123
129
  Persist.persist(name, type, :path => path, :tee_copies => tee_copies) do
124
130
  clear_info
131
+ input_names = (task.respond_to?(:inputs) && task.inputs) ? task.inputs.collect{|name,_| name} : []
125
132
  merge_info :status => :start, :start => Time.now,
126
133
  :pid => Process.pid, :pid_hostname => Misc.hostname,
127
- :inputs => MetaExtension.purge(inputs), :type => type,
134
+ :inputs => MetaExtension.purge(inputs), :input_names => input_names, :type => type,
128
135
  :dependencies => dependencies.collect{|d| d.path }
129
136
 
130
137
  @result = exec
@@ -278,9 +285,10 @@ class Step
278
285
  end
279
286
 
280
287
  def step(task_name)
288
+ task_name = task_name.to_sym
281
289
  dependencies.each do |dep|
282
- return dep if dep.task_name == task_name
283
- return dep if dep.overriden_task == task_name
290
+ return dep if dep.task_name && dep.task_name.to_sym == task_name
291
+ return dep if dep.overriden_task && dep.overriden_task.to_sym == task_name
284
292
  rec_dep = dep.step(task_name)
285
293
  return rec_dep if rec_dep
286
294
  end
@@ -302,4 +310,8 @@ class Step
302
310
  def task_signature
303
311
  [workflow.to_s, task_name] * "#"
304
312
  end
313
+
314
+ def alias?
315
+ task.alias? if task
316
+ end
305
317
  end
@@ -54,6 +54,7 @@ module Task
54
54
  if provided_inputs.include?(overriden = [workflow.name, task] * "#")
55
55
  dep = provided_inputs[overriden]
56
56
  dep = Step.new dep unless Step === dep
57
+ dep = dep.dup
57
58
  dep.type = workflow.tasks[task].type
58
59
  dep.overriden_task = task
59
60
  dep.overriden_workflow = workflow
@@ -87,6 +87,8 @@ module Task
87
87
  if Path.is_filename?(value)
88
88
  relative_file = save_file_input(value, directory)
89
89
  Open.write(input_file + ".as_file", relative_file)
90
+ elsif Step === value
91
+ Open.write(input_file + ".as_step", value.short_path)
90
92
  elsif type == :file
91
93
  relative_file = save_file_input(value, directory)
92
94
  Persist.save(relative_file, input_file, :file)
@@ -115,6 +117,9 @@ module Task
115
117
  value = Open.read(filename).strip
116
118
  value.sub!(/^\./, File.dirname(filename)) if value.start_with?("./")
117
119
  inputs[name] = value
120
+ elsif filename.end_with?('.as_step')
121
+ value = Open.read(filename).strip
122
+ inputs[name] = Step.load value
118
123
  elsif (options && (options[:noload] || options[:stream] || options[:nofile]))
119
124
  inputs[name] = filename
120
125
  else
@@ -45,6 +45,21 @@ module Task
45
45
  IndiferentHash.setup(provided_inputs)
46
46
  id = DEFAULT_NAME if id.nil?
47
47
 
48
+
49
+ missing_inputs = []
50
+ self.inputs.each do |input,type,desc,val,options|
51
+ next unless options && options[:required]
52
+ missing_inputs << input unless provided_inputs.include?(input)
53
+ end if self.inputs
54
+
55
+ if missing_inputs.length == 1
56
+ raise ParameterException, "Input '#{missing_inputs.first}' is required but was not provided or is nil"
57
+ end
58
+
59
+ if missing_inputs.length > 1
60
+ raise ParameterException, "Inputs #{Misc.humanize_list(missing_inputs)} are required but were not provided or are nil"
61
+ end
62
+
48
63
  provided_inputs = load_inputs(provided_inputs[:load_inputs]) if Hash === provided_inputs && provided_inputs[:load_inputs]
49
64
 
50
65
  inputs, non_default_inputs, input_digest_str = process_inputs provided_inputs, id
@@ -52,7 +67,7 @@ module Task
52
67
  compute = {}
53
68
  dependencies = dependencies(id, provided_inputs, non_default_inputs, compute)
54
69
 
55
- non_default_inputs.concat provided_inputs.keys.select{|k| String === k && k.include?("#") } if Hash === provided_inputs
70
+ #non_default_inputs.concat provided_inputs.keys.select{|k| String === k && k.include?("#") } if Hash === provided_inputs
56
71
 
57
72
  non_default_inputs.uniq!
58
73
 
@@ -95,4 +110,8 @@ module Task
95
110
  Step.new path.find, inputs, dependencies, id, non_default_inputs, step_provided_inputs, compute, &self
96
111
  end
97
112
  end
113
+
114
+ def alias?
115
+ @extension == :dep_task
116
+ end
98
117
  end
@@ -136,6 +136,7 @@ module Workflow
136
136
 
137
137
  dep_tree = {}
138
138
  task = self.tasks[task_name]
139
+ raise "TaskNotFound: #{task_name}" if task.nil?
139
140
  task.deps.each do |workflow, task, options|
140
141
  next if seen.include? dep
141
142
  seen << [workflow, task, options.merge(seen_options)]
@@ -27,7 +27,9 @@ module Workflow
27
27
 
28
28
  def self.require_workflow(workflow_name_orig)
29
29
  first = nil
30
- workflow_name_orig.split("+").each do |workflow_name|
30
+ workflow_name_orig.split("+").each do |complete_workflow_name|
31
+ self.main = nil
32
+ workflow_name, *subworkflows = complete_workflow_name.split("::")
31
33
  workflow = workflow_name
32
34
  workflow = Path.setup('workflows')[workflow_name]["workflow.rb"] unless Open.exists?(workflow)
33
35
  workflow = Path.setup('workflows')[Misc.snake_case(workflow_name)]["workflow.rb"] unless Open.exists?(workflow)
@@ -40,7 +42,14 @@ module Workflow
40
42
  else
41
43
  raise "Workflow #{workflow_name} not found"
42
44
  end
43
- first ||= self.main || workflows.last
45
+
46
+ current = begin
47
+ Kernel.const_get(complete_workflow_name)
48
+ rescue
49
+ self.main || workflows.last
50
+ end
51
+
52
+ first ||= current
44
53
  end
45
54
  first
46
55
  end
data/scout-gear.gemspec CHANGED
@@ -2,16 +2,16 @@
2
2
  # DO NOT EDIT THIS FILE DIRECTLY
3
3
  # Instead, edit Juwelier::Tasks in Rakefile, and run 'rake gemspec'
4
4
  # -*- encoding: utf-8 -*-
5
- # stub: scout-gear 9.0.0 ruby lib
5
+ # stub: scout-gear 9.1.0 ruby lib
6
6
 
7
7
  Gem::Specification.new do |s|
8
8
  s.name = "scout-gear".freeze
9
- s.version = "9.0.0"
9
+ s.version = "9.1.0"
10
10
 
11
11
  s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
12
12
  s.require_paths = ["lib".freeze]
13
13
  s.authors = ["Miguel Vazquez".freeze]
14
- s.date = "2023-06-27"
14
+ s.date = "2023-06-30"
15
15
  s.description = "Temporary files, logs, path, resources, persistence, workflows, TSV, etc.".freeze
16
16
  s.email = "mikisvaz@gmail.com".freeze
17
17
  s.executables = ["scout".freeze]
@@ -18,6 +18,7 @@ $ #{$0} [<options>] <workflow> <task>
18
18
  -h--help Print this help
19
19
  --nostream Disable job streaming
20
20
  --update Update jobs with newer dependencies
21
+ --deploy* Deploy mode: serial, local, or SLURM (default 'serial')
21
22
  -jn--jobname* Name to use as job identifier
22
23
  -li--load_inputs* Directory with inputs files to load
23
24
  -pf--print_filepath Print the file path
@@ -25,7 +26,6 @@ $ #{$0} [<options>] <workflow> <task>
25
26
  -cl--clean Clean the last step
26
27
  -rcl--recursive_clean Clean all steps
27
28
  -ct--clean_task* Clean a particular task
28
- -d--deploy* Deploy mode: serial, local, or SLURM (default 'serial')
29
29
  -od--override_deps* Override deps using 'Workflow#task=<path>' array_separated
30
30
  EOF
31
31
 
@@ -86,8 +86,7 @@ if clean_task
86
86
  dep.set_info :status, :cleaned
87
87
  end
88
88
 
89
- job.clean if job.task_name.to_s == clean_task.to_s
90
- job.clean unless job.updated?
89
+ job.clean if (job.task_name.to_s == clean_task.to_s) || ! job.updated?
91
90
  end
92
91
  end
93
92
 
@@ -74,4 +74,19 @@ end
74
74
  assert_equal list, file.list
75
75
  end
76
76
  end
77
+
78
+ def test_produce_compressed
79
+ dir = tmpdir.directory[__method__]
80
+ dir.pkgdir = Scout
81
+ list = %w(a b)
82
+
83
+ Scout.claim dir["foo.gz"], :proc do |filename|
84
+ list
85
+ end
86
+
87
+ Misc.in_dir(dir) do
88
+ file = dir.foo
89
+ assert_include file.produce.find, '.gz'
90
+ end
91
+ end
77
92
  end
@@ -7,6 +7,15 @@ class TestResourceUtil < Test::Unit::TestCase
7
7
  assert_equal 'share/data/somedir/somepath', Scout.identify(path)
8
8
  end
9
9
 
10
+ def test_identify_with_subdir
11
+ m = Module.new
12
+ m.extend Resource
13
+ m.subdir = 'test/subdir'
14
+ path = m.root['share/data/somedir/somepath'].find
15
+ assert_equal 'share/data/somedir/somepath', m.identify(path)
16
+ end
17
+
18
+
10
19
  def test_relocate
11
20
  TmpFile.with_file do |dir|
12
21
  Path.setup dir
@@ -3,7 +3,7 @@ require File.expand_path(__FILE__).sub(%r(.*/test/), '').sub(/test_(.*)\.rb/,'\1
3
3
 
4
4
  require 'scout/workflow'
5
5
  class TestStepProvenance < Test::Unit::TestCase
6
- def test_true
6
+ def test_prov_report
7
7
  tmpfile = tmpdir.test_step
8
8
  step1 = Step.new tmpfile.step1, ["12"] do |s|
9
9
  s.length
@@ -21,5 +21,28 @@ class TestStepProvenance < Test::Unit::TestCase
21
21
  assert_include Step.prov_report(step2), 'step1'
22
22
  assert_include Step.prov_report(step2), 'step2'
23
23
  end
24
+
25
+ def __test_input_dependencies
26
+ wf = Workflow.annonymous_workflow "TaskInputDepProv" do
27
+ input :value1, :integer
28
+ task :number1 => :integer do |i| i end
29
+
30
+ input :value2, :integer
31
+ task :number2 => :integer do |i| i end
32
+
33
+ input :number1, :integer
34
+ input :number2, :integer
35
+ task :sum => :integer do |i1,i2| i1 + i2 end
36
+
37
+ dep :number1, :value1 => 1
38
+ dep :number2, :value1 => 2
39
+ dep_task :my_sum, self, :sum, :number1 => :number1, :number2 => :number2
40
+
41
+ end
42
+ job = wf.job(:my_sum)
43
+
44
+ ppp Step.prov_report(job)
45
+
46
+ end
24
47
  end
25
48
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: scout-gear
3
3
  version: !ruby/object:Gem::Version
4
- version: 9.0.0
4
+ version: 9.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Miguel Vazquez
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-06-27 00:00:00.000000000 Z
11
+ date: 2023-06-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: term-ansicolor