ops_team 1.13.0 → 1.16.0.pre.rc1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +1 -0
- data/bin/benchmark +185 -0
- data/bin/ops +15 -3
- data/lib/action.rb +3 -3
- data/lib/action_list.rb +0 -2
- data/lib/builtin.rb +20 -1
- data/lib/builtins/background.rb +1 -3
- data/lib/builtins/background_log.rb +0 -3
- data/lib/builtins/common/up_down.rb +0 -7
- data/lib/builtins/countdown.rb +0 -3
- data/lib/builtins/down.rb +0 -5
- data/lib/builtins/env.rb +0 -3
- data/lib/builtins/envdiff.rb +1 -5
- data/lib/builtins/exec.rb +2 -5
- data/lib/builtins/help.rb +0 -4
- data/lib/builtins/helpers/dependency_handler.rb +0 -6
- data/lib/builtins/helpers/enumerator.rb +3 -0
- data/lib/builtins/init.rb +0 -3
- data/lib/builtins/up.rb +0 -6
- data/lib/builtins/version.rb +0 -3
- data/lib/dependencies/apk.rb +0 -2
- data/lib/dependencies/apt.rb +0 -3
- data/lib/dependencies/brew.rb +0 -2
- data/lib/dependencies/cask.rb +0 -2
- data/lib/dependencies/custom.rb +0 -2
- data/lib/dependencies/dir.rb +0 -2
- data/lib/dependencies/docker.rb +0 -2
- data/lib/dependencies/gem.rb +0 -3
- data/lib/dependencies/pip.rb +0 -3
- data/lib/dependencies/sshkey.rb +0 -2
- data/lib/dependency.rb +0 -3
- data/lib/environment.rb +0 -4
- data/lib/forward.rb +0 -2
- data/lib/forwards.rb +0 -2
- data/lib/hook_handler.rb +0 -2
- data/lib/ops.rb +9 -10
- data/lib/profiler.rb +47 -0
- data/lib/runner.rb +5 -7
- data/lib/secrets.rb +0 -4
- data/loader.rb +7 -1
- data/ops_team.gemspec +1 -1
- metadata +10 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e5aa70e4e711e46c9e7661013c62fc3f40db454caeee504c0ff3c49258ea3951
|
4
|
+
data.tar.gz: c1a491887c6629b419b3397aed7b0ad0e1b74a19bcfda45fc79fb15d481f517c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b92b2d6583d86f2a0fccf7d17d6d713aaf062eed7837836fb0904c7bafee5ded631a9d26b22c96eb429e629f54d7a9e06f5f2ace0411743e57005ec410086808
|
7
|
+
data.tar.gz: f8199c0e807ed8fa7e1d3bfba3928c51fa32dca844bdaa103d3d18afb35b8ef017ddae71a8371a3d5fbb1a3ec3b9bd475105754e6e5f0b7b439b90279171c28c
|
data/Gemfile
CHANGED
data/bin/benchmark
ADDED
@@ -0,0 +1,185 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'json'
|
5
|
+
require 'colorize'
|
6
|
+
|
7
|
+
COMMANDS = [
|
8
|
+
"version",
|
9
|
+
"env",
|
10
|
+
"help",
|
11
|
+
"exec echo hi",
|
12
|
+
"env OPS_AUTO_COMPLETE=true"
|
13
|
+
].freeze
|
14
|
+
|
15
|
+
class Runner
|
16
|
+
RUNS = 10
|
17
|
+
WARMUPS = 3
|
18
|
+
|
19
|
+
attr_reader :command, :version
|
20
|
+
|
21
|
+
def initialize(command, version)
|
22
|
+
@command = command
|
23
|
+
@version = version
|
24
|
+
end
|
25
|
+
|
26
|
+
def run
|
27
|
+
unless File.exist?(json_file)
|
28
|
+
system(
|
29
|
+
"hyperfine -m #{RUNS} \
|
30
|
+
--export-json #{json_file} \
|
31
|
+
--export-markdown #{markdown_file} \
|
32
|
+
--warmup #{WARMUPS} \
|
33
|
+
'#{@command}'"
|
34
|
+
)
|
35
|
+
end
|
36
|
+
|
37
|
+
results
|
38
|
+
end
|
39
|
+
|
40
|
+
def results
|
41
|
+
# for now, only ever one result, since we're not having hyperfine vary params
|
42
|
+
@results ||= JSON.parse(results_data)["results"].first
|
43
|
+
end
|
44
|
+
|
45
|
+
def method_missing(method, *_, &_)
|
46
|
+
results[method.to_s]
|
47
|
+
end
|
48
|
+
|
49
|
+
def respond_to_missing?(method, _ = false)
|
50
|
+
results&.keys&.include?(method.to_s)
|
51
|
+
end
|
52
|
+
|
53
|
+
def json_file
|
54
|
+
@json_file ||= "#{output_file}.json"
|
55
|
+
end
|
56
|
+
|
57
|
+
def markdown_file
|
58
|
+
@markdown_file ||= "#{output_file}.md"
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
def output_file
|
64
|
+
"benchmark/#{@version}-#{@command.gsub(" ", "_").gsub("/", "-")}"
|
65
|
+
end
|
66
|
+
|
67
|
+
def results_data
|
68
|
+
File.read(json_file)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
class Benchmark
|
73
|
+
attr_reader :executable
|
74
|
+
|
75
|
+
def initialize(executable)
|
76
|
+
@executable = executable
|
77
|
+
end
|
78
|
+
|
79
|
+
def run
|
80
|
+
runners.map(&:run)
|
81
|
+
|
82
|
+
runners.map(&:mean)
|
83
|
+
end
|
84
|
+
|
85
|
+
def version
|
86
|
+
@version ||= `#{@executable} version`.chomp
|
87
|
+
end
|
88
|
+
|
89
|
+
def runners
|
90
|
+
@runners ||= COMMANDS.map do |cmd|
|
91
|
+
Runner.new("#{@executable} #{cmd}", version)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
class Benchmarker
|
97
|
+
CLR_THRESHOLD = 0.1
|
98
|
+
CLR_WIDTH = 14
|
99
|
+
COL_WIDTH = 24
|
100
|
+
CMD_WIDTH = 30
|
101
|
+
|
102
|
+
def initialize(*executables)
|
103
|
+
@executables = executables
|
104
|
+
end
|
105
|
+
|
106
|
+
def summary
|
107
|
+
result_pairs = results.first.zip(results.last)
|
108
|
+
|
109
|
+
output = header
|
110
|
+
COMMANDS.length.times do |index|
|
111
|
+
output << format(
|
112
|
+
"%#{CMD_WIDTH + CLR_WIDTH}s %#{COL_WIDTH}s %#{COL_WIDTH + CLR_WIDTH}s",
|
113
|
+
COMMANDS[index].white,
|
114
|
+
*result_strings(result_pairs[index])
|
115
|
+
)
|
116
|
+
end
|
117
|
+
output << summary_numbers
|
118
|
+
|
119
|
+
output.join("\n")
|
120
|
+
end
|
121
|
+
|
122
|
+
def benchmarks
|
123
|
+
@benchmarks ||= @executables.map do |executable|
|
124
|
+
Benchmark.new(executable)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def results
|
129
|
+
@results ||= benchmarks.map(&:run)
|
130
|
+
end
|
131
|
+
|
132
|
+
private
|
133
|
+
|
134
|
+
def result_pairs
|
135
|
+
@result_pairs ||= results.first.zip(results.last)
|
136
|
+
end
|
137
|
+
|
138
|
+
def header
|
139
|
+
[
|
140
|
+
format(
|
141
|
+
"%#{CMD_WIDTH}s %#{COL_WIDTH + CLR_WIDTH}s %#{COL_WIDTH + CLR_WIDTH}s",
|
142
|
+
"", *benchmarks.map { |b| b.executable.white }
|
143
|
+
),
|
144
|
+
format(
|
145
|
+
"%#{CMD_WIDTH}s %#{COL_WIDTH + CLR_WIDTH}s %#{COL_WIDTH + CLR_WIDTH}s",
|
146
|
+
"", *benchmarks.map { |b| b.version.white }
|
147
|
+
)
|
148
|
+
]
|
149
|
+
end
|
150
|
+
|
151
|
+
def result_strings(pair)
|
152
|
+
colour = :blue
|
153
|
+
if pair.last < (pair.first * (1 - CLR_THRESHOLD))
|
154
|
+
colour = :green
|
155
|
+
elsif pair.last < (pair.first * CLR_THRESHOLD)
|
156
|
+
colour = :red
|
157
|
+
end
|
158
|
+
|
159
|
+
outputs = pair.map { |number| format("%02.3f", number) }
|
160
|
+
|
161
|
+
outputs[1] = outputs.last.send(colour)
|
162
|
+
|
163
|
+
outputs
|
164
|
+
end
|
165
|
+
|
166
|
+
def summary_numbers
|
167
|
+
format(
|
168
|
+
"%#{CMD_WIDTH + CLR_WIDTH}s %#{COL_WIDTH}s %#{COL_WIDTH + CLR_WIDTH}s",
|
169
|
+
"Avg difference".white, "-", avg_diff_string.white
|
170
|
+
)
|
171
|
+
end
|
172
|
+
|
173
|
+
def avg_diff_string
|
174
|
+
format("%02.3f", avg_diff)
|
175
|
+
end
|
176
|
+
|
177
|
+
def avg_diff
|
178
|
+
result_pairs.each_with_object([]) do |pair, diff|
|
179
|
+
diff << pair.first - pair.last
|
180
|
+
end.sum / result_pairs.length
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
`rm -f benchmark/*.json benchmark/*.md` unless %w[--skip -s].include?(ARGV[0])
|
185
|
+
puts Benchmarker.new("ops", "bin/ops").summary
|
data/bin/ops
CHANGED
@@ -1,6 +1,9 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
+
start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
5
|
+
|
6
|
+
require_relative '../lib/profiler'
|
4
7
|
require 'optparse'
|
5
8
|
|
6
9
|
def usage
|
@@ -12,6 +15,8 @@ def usage
|
|
12
15
|
end
|
13
16
|
|
14
17
|
options = {}
|
18
|
+
status = -1
|
19
|
+
|
15
20
|
while ARGV[0]&.match(/^-/)
|
16
21
|
opt = ARGV.shift
|
17
22
|
case opt
|
@@ -24,7 +29,14 @@ while ARGV[0]&.match(/^-/)
|
|
24
29
|
end
|
25
30
|
end
|
26
31
|
|
27
|
-
|
28
|
-
|
32
|
+
Profiler.measure("bin:require") do
|
33
|
+
require_relative "../loader"
|
34
|
+
end
|
35
|
+
|
36
|
+
Profiler.measure("bin:run") do
|
37
|
+
status = Ops.new(ARGV, config_file: options[:file]).run
|
38
|
+
end
|
29
39
|
|
30
|
-
|
40
|
+
Profiler.add_measurement("bin:all", Process.clock_gettime(Process::CLOCK_MONOTONIC) - start_time)
|
41
|
+
Output.error(Profiler.summary) if Profiler.summary
|
42
|
+
exit status
|
data/lib/action.rb
CHANGED
@@ -1,7 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'secrets'
|
4
|
-
|
5
3
|
# represents one action to be performed in the shell
|
6
4
|
# can assemble a command line from a command and args
|
7
5
|
class Action
|
@@ -9,11 +7,13 @@ class Action
|
|
9
7
|
|
10
8
|
def initialize(name, config, args)
|
11
9
|
@name = name
|
12
|
-
@config = config
|
10
|
+
@config = config || {}
|
13
11
|
@args = args
|
14
12
|
end
|
15
13
|
|
16
14
|
def run
|
15
|
+
Output.error(Profiler.summary) if Profiler.summary
|
16
|
+
|
17
17
|
if perform_shell_expansion?
|
18
18
|
Kernel.exec(to_s)
|
19
19
|
else
|
data/lib/action_list.rb
CHANGED
data/lib/builtin.rb
CHANGED
@@ -6,16 +6,35 @@ class Builtin
|
|
6
6
|
attr_reader :args, :config
|
7
7
|
|
8
8
|
class << self
|
9
|
+
BUILTIN_DIR = "builtins"
|
10
|
+
|
9
11
|
def description
|
10
12
|
"no description"
|
11
13
|
end
|
12
14
|
|
13
15
|
def class_for(name:)
|
14
|
-
|
16
|
+
file = file_for(name: name)
|
17
|
+
unless File.exist?(file)
|
18
|
+
require 'require_all'
|
19
|
+
require_rel "builtins"
|
20
|
+
end
|
21
|
+
|
22
|
+
get_const(name: builtin_class_name_for(name: name))
|
15
23
|
end
|
16
24
|
|
17
25
|
private
|
18
26
|
|
27
|
+
def get_const(name:)
|
28
|
+
Builtins.const_get(name)
|
29
|
+
rescue NameError
|
30
|
+
# no such constant
|
31
|
+
nil
|
32
|
+
end
|
33
|
+
|
34
|
+
def file_for(name:)
|
35
|
+
File.join(File.dirname(__FILE__), BUILTIN_DIR, "#{name}.rb")
|
36
|
+
end
|
37
|
+
|
19
38
|
def builtin_class_name_for(name:)
|
20
39
|
name.capitalize.to_sym
|
21
40
|
end
|
data/lib/builtins/background.rb
CHANGED
data/lib/builtins/countdown.rb
CHANGED
data/lib/builtins/down.rb
CHANGED
data/lib/builtins/env.rb
CHANGED
data/lib/builtins/envdiff.rb
CHANGED
@@ -1,9 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'builtin'
|
4
|
-
|
5
3
|
module Builtins
|
6
|
-
class
|
4
|
+
class Envdiff < Builtin
|
7
5
|
class << self
|
8
6
|
def description
|
9
7
|
"compares keys present in config and secrets between different environments"
|
@@ -126,6 +124,4 @@ module Builtins
|
|
126
124
|
Options.get("envdiff.ignored_keys") || []
|
127
125
|
end
|
128
126
|
end
|
129
|
-
|
130
|
-
Envdiff = EnvDiff
|
131
127
|
end
|
data/lib/builtins/exec.rb
CHANGED
@@ -1,10 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'builtin'
|
4
|
-
require 'output'
|
5
|
-
require 'secrets'
|
6
|
-
require 'options'
|
7
|
-
|
8
3
|
module Builtins
|
9
4
|
class Exec < Builtin
|
10
5
|
class << self
|
@@ -17,9 +12,11 @@ module Builtins
|
|
17
12
|
Secrets.load if Options.get("exec.load_secrets")
|
18
13
|
|
19
14
|
if args.any?
|
15
|
+
Output.error(Profiler.summary) if Profiler.summary
|
20
16
|
Kernel.exec(args.join(" "))
|
21
17
|
else
|
22
18
|
Output.error("Usage: ops exec '<command>'")
|
19
|
+
|
23
20
|
false
|
24
21
|
end
|
25
22
|
end
|
data/lib/builtins/help.rb
CHANGED
data/lib/builtins/init.rb
CHANGED
data/lib/builtins/up.rb
CHANGED
data/lib/builtins/version.rb
CHANGED
data/lib/dependencies/apk.rb
CHANGED
data/lib/dependencies/apt.rb
CHANGED
data/lib/dependencies/brew.rb
CHANGED
data/lib/dependencies/cask.rb
CHANGED
data/lib/dependencies/custom.rb
CHANGED
data/lib/dependencies/dir.rb
CHANGED
data/lib/dependencies/docker.rb
CHANGED
data/lib/dependencies/gem.rb
CHANGED
data/lib/dependencies/pip.rb
CHANGED
data/lib/dependencies/sshkey.rb
CHANGED
data/lib/dependency.rb
CHANGED
data/lib/environment.rb
CHANGED
data/lib/forward.rb
CHANGED
data/lib/forwards.rb
CHANGED
data/lib/hook_handler.rb
CHANGED
data/lib/ops.rb
CHANGED
@@ -1,16 +1,13 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
-
|
5
|
-
require '
|
6
|
-
require "rubygems"
|
7
|
-
|
8
|
-
require 'output'
|
9
|
-
require 'options'
|
10
|
-
require 'version'
|
11
|
-
require 'runner'
|
4
|
+
Profiler.measure("ops:requires_external") do
|
5
|
+
require 'yaml'
|
6
|
+
require "rubygems"
|
7
|
+
end
|
12
8
|
|
13
|
-
|
9
|
+
Profiler.measure("ops:requires_internal") do
|
10
|
+
end
|
14
11
|
|
15
12
|
# executes commands based on local `ops.yml`
|
16
13
|
class Ops
|
@@ -45,7 +42,9 @@ class Ops
|
|
45
42
|
return exit(INVALID_SYNTAX_EXIT_CODE) unless syntax_valid?
|
46
43
|
return exit(MIN_VERSION_NOT_MET_EXIT_CODE) unless min_version_met?
|
47
44
|
|
48
|
-
runner
|
45
|
+
Profiler.measure("runner:run") do
|
46
|
+
runner.run
|
47
|
+
end
|
49
48
|
rescue Runner::UnknownActionError => e
|
50
49
|
Output.error(e.to_s)
|
51
50
|
Output.out(RECOMMEND_HELP_TEXT) unless print_did_you_mean
|
data/lib/profiler.rb
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Profiler
|
4
|
+
INDENT = " "
|
5
|
+
|
6
|
+
@measurements = {}
|
7
|
+
|
8
|
+
class << self
|
9
|
+
attr_reader :measurements
|
10
|
+
|
11
|
+
def measure(tag)
|
12
|
+
start = time_now
|
13
|
+
result = yield
|
14
|
+
add_measurement(tag, time_now - start)
|
15
|
+
|
16
|
+
result
|
17
|
+
end
|
18
|
+
|
19
|
+
def add_measurement(tag, seconds)
|
20
|
+
return unless profiling?
|
21
|
+
|
22
|
+
@measurements[tag] ||= []
|
23
|
+
|
24
|
+
@measurements[tag] << seconds
|
25
|
+
end
|
26
|
+
|
27
|
+
def summary
|
28
|
+
return unless profiling?
|
29
|
+
|
30
|
+
@summary ||= measurements.reverse_each.each_with_object([]) do |(tag, values), output|
|
31
|
+
output << "#{tag}:\n"
|
32
|
+
values.sort.reverse.each do |value|
|
33
|
+
value_str = format("%.3f", value * 1000)
|
34
|
+
output << format("%<indent>s%9<value>sms\n", indent: INDENT, value: value_str)
|
35
|
+
end
|
36
|
+
end.join
|
37
|
+
end
|
38
|
+
|
39
|
+
def profiling?
|
40
|
+
!ENV["OPS_PROFILE"].nil?
|
41
|
+
end
|
42
|
+
|
43
|
+
def time_now
|
44
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
data/lib/runner.rb
CHANGED
@@ -1,11 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
require 'action_list'
|
6
|
-
require 'action_suggester'
|
7
|
-
require 'forwards'
|
8
|
-
require 'environment'
|
3
|
+
module Builtins
|
4
|
+
end
|
9
5
|
|
10
6
|
class Runner
|
11
7
|
class UnknownActionError < StandardError; end
|
@@ -61,7 +57,9 @@ class Runner
|
|
61
57
|
end
|
62
58
|
|
63
59
|
def builtin
|
64
|
-
|
60
|
+
Profiler.measure("runner:require_builtin") do
|
61
|
+
@builtin ||= Builtin.class_for(name: @action_name)&.new(@args, @config)
|
62
|
+
end
|
65
63
|
rescue NameError
|
66
64
|
# this means there isn't a builtin with that name in that module
|
67
65
|
nil
|
data/lib/secrets.rb
CHANGED
data/loader.rb
CHANGED
@@ -1,4 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# for convenience, add "lib" to the load path
|
4
|
-
$LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__) + "/lib"))
|
4
|
+
# $LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__) + "/lib"))
|
5
|
+
|
6
|
+
require 'zeitwerk'
|
7
|
+
|
8
|
+
loader = Zeitwerk::Loader.new
|
9
|
+
loader.push_dir(File.expand_path("#{__dir__}/lib"))
|
10
|
+
loader.setup
|
data/ops_team.gemspec
CHANGED
metadata
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ops_team
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.16.0.pre.rc1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- nickthecook@gmail.com
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
date: 2021-10-25 00:00:00.000000000 Z
|
@@ -150,14 +150,15 @@ dependencies:
|
|
150
150
|
- - ">="
|
151
151
|
- !ruby/object:Gem::Version
|
152
152
|
version: 1.1.6
|
153
|
-
description:
|
154
|
-
email:
|
153
|
+
description:
|
154
|
+
email:
|
155
155
|
executables:
|
156
156
|
- ops
|
157
157
|
extensions: []
|
158
158
|
extra_rdoc_files: []
|
159
159
|
files:
|
160
160
|
- Gemfile
|
161
|
+
- bin/benchmark
|
161
162
|
- bin/ops
|
162
163
|
- bin/print_config
|
163
164
|
- bin/print_secrets
|
@@ -205,6 +206,7 @@ files:
|
|
205
206
|
- lib/ops.rb
|
206
207
|
- lib/options.rb
|
207
208
|
- lib/output.rb
|
209
|
+
- lib/profiler.rb
|
208
210
|
- lib/runner.rb
|
209
211
|
- lib/secrets.rb
|
210
212
|
- lib/version.rb
|
@@ -214,7 +216,7 @@ homepage: https://github.com/nickthecook/ops
|
|
214
216
|
licenses:
|
215
217
|
- GPL-3.0-only
|
216
218
|
metadata: {}
|
217
|
-
post_install_message:
|
219
|
+
post_install_message:
|
218
220
|
rdoc_options: []
|
219
221
|
require_paths:
|
220
222
|
- lib
|
@@ -225,12 +227,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
225
227
|
version: '2.5'
|
226
228
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
227
229
|
requirements:
|
228
|
-
- - "
|
230
|
+
- - ">"
|
229
231
|
- !ruby/object:Gem::Version
|
230
|
-
version:
|
232
|
+
version: 1.3.1
|
231
233
|
requirements: []
|
232
234
|
rubygems_version: 3.2.15
|
233
|
-
signing_key:
|
235
|
+
signing_key:
|
234
236
|
specification_version: 4
|
235
237
|
summary: ops_team handles basic automation for your project, driven by self-documenting
|
236
238
|
YAML config
|