ops_team 0.19.1 → 1.2.2
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/bin/ops +25 -2
- data/lib/action.rb +15 -5
- data/lib/builtins/help.rb +15 -3
- data/lib/environment.rb +3 -2
- data/lib/forwards.rb +1 -3
- data/lib/ops.rb +41 -108
- data/lib/runner.rb +95 -0
- data/ops_team.gemspec +1 -1
- metadata +2 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6ef9ff578bc479b6bbe53bee8143f2a0477393c32518ae15e417ab8338ac5fb4
|
4
|
+
data.tar.gz: 289a6516bda02ad14993a4529358a671a7ea500269ac9710f42a960cc5a3e524
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 685a469476c1ccbf25511c8ac1b0eec7cfc1eb3e9056eb8102905c88fbc48266a88dc0aaf3969ee4a6bc16bfc3cd840889d2dd3d4a39e84ce70b3eb43bb5594c
|
7
|
+
data.tar.gz: 87e5e4cb4ed35aac7107cd5614ec7fecaa66fa4fc11ffd077df87cf33daea4d324c2a7df99bac2e63507624762a67a925d132a85b6b1e08e9814ac8715cac3c8
|
data/bin/ops
CHANGED
@@ -1,8 +1,31 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
-
|
4
|
+
require 'optparse'
|
5
|
+
|
6
|
+
|
7
|
+
def usage
|
8
|
+
puts "Usage: ops [-f|--file <ops_yml>] action [<action args>"
|
9
|
+
puts " ops_yml: the config file to load instead of './ops.yml'"
|
10
|
+
puts " action_args: arguments to the action loaded from the config file; depends on the action"
|
11
|
+
|
12
|
+
exit(1)
|
13
|
+
end
|
5
14
|
|
15
|
+
options = {}
|
16
|
+
while ARGV[0]&.match(/^-/)
|
17
|
+
opt = ARGV.shift
|
18
|
+
case opt
|
19
|
+
when '-f', '--file'
|
20
|
+
usage unless ARGV.length >= 1
|
21
|
+
|
22
|
+
options[:file] = ARGV.shift
|
23
|
+
else
|
24
|
+
usage
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
require_relative "../loader"
|
6
29
|
require 'ops'
|
7
30
|
|
8
|
-
Ops.new(ARGV).run
|
31
|
+
Ops.new(ARGV, config_file: options[:file]).run
|
data/lib/action.rb
CHANGED
@@ -17,9 +17,11 @@ class Action
|
|
17
17
|
raise NotAllowedInEnvError, "Action not allowed in #{Environment.environment} environment."
|
18
18
|
end
|
19
19
|
|
20
|
-
|
21
|
-
|
22
|
-
|
20
|
+
if perform_shell_expansion?
|
21
|
+
Kernel.exec(to_s)
|
22
|
+
else
|
23
|
+
Kernel.exec(*to_a)
|
24
|
+
end
|
23
25
|
end
|
24
26
|
|
25
27
|
def to_s
|
@@ -56,10 +58,14 @@ class Action
|
|
56
58
|
end
|
57
59
|
end
|
58
60
|
|
61
|
+
def load_secrets?
|
62
|
+
@config["load_secrets"].nil? ? false : @config["load_secrets"]
|
63
|
+
end
|
64
|
+
|
59
65
|
private
|
60
66
|
|
61
|
-
def
|
62
|
-
|
67
|
+
def to_a
|
68
|
+
command.split(" ").reject(&:nil?) | @args
|
63
69
|
end
|
64
70
|
|
65
71
|
def not_in_envs
|
@@ -77,4 +83,8 @@ class Action
|
|
77
83
|
|
78
84
|
true
|
79
85
|
end
|
86
|
+
|
87
|
+
def perform_shell_expansion?
|
88
|
+
@config["shell_expansion"].nil? ? true : @config["shell_expansion"]
|
89
|
+
end
|
80
90
|
end
|
data/lib/builtins/help.rb
CHANGED
@@ -3,12 +3,15 @@
|
|
3
3
|
require 'colorize'
|
4
4
|
|
5
5
|
require 'builtin'
|
6
|
+
require 'forwards'
|
6
7
|
|
7
8
|
module Builtins
|
8
9
|
class Help < Builtin
|
10
|
+
NAME_WIDTH = 35
|
11
|
+
|
9
12
|
class << self
|
10
13
|
def description
|
11
|
-
"displays available builtins and
|
14
|
+
"displays available builtins, actions, and forwards"
|
12
15
|
end
|
13
16
|
end
|
14
17
|
|
@@ -16,15 +19,24 @@ module Builtins
|
|
16
19
|
Output.out("Builtins:")
|
17
20
|
Output.out(" #{builtins.join("\n ")}")
|
18
21
|
Output.out("")
|
22
|
+
Output.out("Forwards:")
|
23
|
+
Output.out(" #{forwards.join("\n ")}")
|
24
|
+
Output.out("")
|
19
25
|
Output.out("Actions:")
|
20
26
|
Output.out(" #{actions.join("\n ")}")
|
21
27
|
end
|
22
28
|
|
23
29
|
private
|
24
30
|
|
31
|
+
def forwards
|
32
|
+
Forwards.new(@config).forwards.map do |name, dir|
|
33
|
+
format("%<name>-#{NAME_WIDTH}s %<desc>s" , name: name.yellow, desc: "#{dir}")
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
25
37
|
def builtins
|
26
38
|
builtin_class_map.map do |klass, name|
|
27
|
-
format("%<name
|
39
|
+
format("%<name>-#{NAME_WIDTH}s %<desc>s", name: name.downcase.to_s.yellow, desc: klass.description)
|
28
40
|
end
|
29
41
|
end
|
30
42
|
|
@@ -55,7 +67,7 @@ module Builtins
|
|
55
67
|
return [] unless @config["actions"]
|
56
68
|
|
57
69
|
@config["actions"].map do |name, action_config|
|
58
|
-
format("%<name
|
70
|
+
format("%<name>-#{NAME_WIDTH}s %<desc>s",
|
59
71
|
name: "#{name.yellow} #{alias_string_for(action_config)}",
|
60
72
|
desc: action_config["description"] || action_config["command"]
|
61
73
|
)
|
data/lib/environment.rb
CHANGED
@@ -13,8 +13,9 @@ class Environment
|
|
13
13
|
end
|
14
14
|
end
|
15
15
|
|
16
|
-
def initialize(env_hash)
|
16
|
+
def initialize(env_hash, config_path)
|
17
17
|
@env_hash = env_hash
|
18
|
+
@config_path = config_path
|
18
19
|
end
|
19
20
|
|
20
21
|
def set_variables
|
@@ -26,7 +27,7 @@ class Environment
|
|
26
27
|
private
|
27
28
|
|
28
29
|
def set_ops_variables
|
29
|
-
ENV["OPS_YML_DIR"] =
|
30
|
+
ENV["OPS_YML_DIR"] = File.dirname(@config_path)
|
30
31
|
ENV["OPS_VERSION"] = Version.version.to_s
|
31
32
|
ENV["OPS_SECRETS_FILE"] = Secrets.config_path_for(Environment.environment)
|
32
33
|
ENV["OPS_CONFIG_FILE"] = AppConfig.config_path_for(Environment.environment)
|
data/lib/forwards.rb
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
require 'forward'
|
4
4
|
|
5
5
|
class Forwards
|
6
|
-
def initialize(config, args)
|
6
|
+
def initialize(config, args = [])
|
7
7
|
@config = config
|
8
8
|
@args = args
|
9
9
|
end
|
@@ -12,8 +12,6 @@ class Forwards
|
|
12
12
|
Forward.new(forwards[name], @args) if forwards[name]
|
13
13
|
end
|
14
14
|
|
15
|
-
private
|
16
|
-
|
17
15
|
def forwards
|
18
16
|
@forwards ||= @config["forwards"] || {}
|
19
17
|
end
|
data/lib/ops.rb
CHANGED
@@ -5,25 +5,15 @@ require 'yaml'
|
|
5
5
|
require 'require_all'
|
6
6
|
require "rubygems"
|
7
7
|
|
8
|
-
require 'hook_handler'
|
9
|
-
require 'action'
|
10
8
|
require 'output'
|
11
9
|
require 'options'
|
12
|
-
require 'environment'
|
13
10
|
require 'version'
|
14
|
-
require '
|
15
|
-
require 'action_suggester'
|
16
|
-
require 'forwards'
|
11
|
+
require 'runner'
|
17
12
|
|
18
13
|
require_rel "builtins"
|
19
14
|
|
20
15
|
# executes commands based on local `ops.yml`
|
21
16
|
class Ops
|
22
|
-
class UnknownActionError < StandardError; end
|
23
|
-
class ActionConfigError < StandardError; end
|
24
|
-
|
25
|
-
CONFIG_FILE = "ops.yml"
|
26
|
-
|
27
17
|
INVALID_SYNTAX_EXIT_CODE = 64
|
28
18
|
UNKNOWN_ACTION_EXIT_CODE = 65
|
29
19
|
ERROR_LOADING_APP_CONFIG_EXIT_CODE = 66
|
@@ -40,50 +30,55 @@ class Ops
|
|
40
30
|
end
|
41
31
|
end
|
42
32
|
|
43
|
-
def initialize(argv)
|
33
|
+
def initialize(argv, config_file: nil)
|
44
34
|
@action_name = argv[0]
|
45
35
|
@args = argv[1..-1]
|
36
|
+
@config_file = config_file || "ops.yml"
|
46
37
|
|
47
38
|
Options.set(config["options"] || {})
|
48
39
|
end
|
49
40
|
|
41
|
+
# rubocop:disable Metrics/MethodLength
|
42
|
+
# better to have all the rescues in one place
|
50
43
|
def run
|
51
44
|
# "return" is here to allow specs to stub "exit" without executing everything after it
|
52
45
|
return exit(INVALID_SYNTAX_EXIT_CODE) unless syntax_valid?
|
53
46
|
return exit(MIN_VERSION_NOT_MET_EXIT_CODE) unless min_version_met?
|
54
47
|
|
55
|
-
|
56
|
-
rescue UnknownActionError => e
|
48
|
+
runner.run
|
49
|
+
rescue Runner::UnknownActionError => e
|
57
50
|
Output.error(e.to_s)
|
58
51
|
Output.out(RECOMMEND_HELP_TEXT) unless print_did_you_mean
|
59
52
|
exit(UNKNOWN_ACTION_EXIT_CODE)
|
60
|
-
rescue ActionConfigError => e
|
53
|
+
rescue Runner::ActionConfigError => e
|
61
54
|
Output.error("Error(s) running action '#{@action_name}': #{e}")
|
62
55
|
exit(ACTION_CONFIG_ERROR_EXIT_CODE)
|
56
|
+
rescue Builtin::ArgumentError => e
|
57
|
+
Output.error("Error running builtin '#{@action_name}': #{e}")
|
58
|
+
exit(BUILTIN_SYNTAX_ERROR_EXIT_CODE)
|
59
|
+
rescue AppConfig::ParsingError => e
|
60
|
+
Output.error("Error parsing app config: #{e}")
|
61
|
+
exit(ERROR_LOADING_APP_CONFIG_EXIT_CODE)
|
62
|
+
rescue Action::NotAllowedInEnvError => e
|
63
|
+
Output.error("Error running action #{@action_name}: #{e}")
|
64
|
+
exit(ACTION_NOT_ALLOWED_IN_ENV_EXIT_CODE)
|
63
65
|
end
|
66
|
+
# rubocop:enable Metrics/MethodLength
|
64
67
|
|
65
68
|
private
|
66
69
|
|
67
70
|
def syntax_valid?
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
true
|
74
|
-
end
|
71
|
+
return true unless @action_name.nil?
|
72
|
+
|
73
|
+
Output.error("Usage: ops <action>")
|
74
|
+
Output.out(RECOMMEND_HELP_TEXT)
|
75
|
+
false
|
75
76
|
end
|
76
77
|
|
77
78
|
def print_did_you_mean
|
78
|
-
|
79
|
+
Output.out("Did you mean '#{runner.suggestions.join(", ")}'?") if runner.suggestions.any?
|
79
80
|
|
80
|
-
|
81
|
-
|
82
|
-
suggestions.any?
|
83
|
-
end
|
84
|
-
|
85
|
-
def did_you_mean
|
86
|
-
ActionSuggester.new(action_list.names + action_list.aliases + builtin_names)
|
81
|
+
runner.suggestions.any?
|
87
82
|
end
|
88
83
|
|
89
84
|
def min_version_met?
|
@@ -101,96 +96,34 @@ class Ops
|
|
101
96
|
config["min_version"]
|
102
97
|
end
|
103
98
|
|
104
|
-
def
|
105
|
-
|
106
|
-
|
107
|
-
do_before_all
|
108
|
-
|
109
|
-
return builtin.run if builtin
|
110
|
-
|
111
|
-
raise ActionConfigError, action.config_errors.join("; ") unless action.config_valid?
|
112
|
-
|
113
|
-
do_before_action
|
114
|
-
Output.notice("Running '#{action}' from #{CONFIG_FILE} in environment '#{ENV['environment']}'...")
|
115
|
-
action.run
|
116
|
-
rescue Builtin::ArgumentError => e
|
117
|
-
Output.error("Error running builtin '#{@action_name}': #{e}")
|
118
|
-
exit(BUILTIN_SYNTAX_ERROR_EXIT_CODE)
|
119
|
-
rescue AppConfig::ParsingError => e
|
120
|
-
Output.error("Error parsing app config: #{e}")
|
121
|
-
exit(ERROR_LOADING_APP_CONFIG_EXIT_CODE)
|
122
|
-
rescue Action::NotAllowedInEnvError => e
|
123
|
-
Output.error("Error running action #{@action_name}: #{e}")
|
124
|
-
exit(ACTION_NOT_ALLOWED_IN_ENV_EXIT_CODE)
|
125
|
-
end
|
126
|
-
|
127
|
-
def do_before_all
|
128
|
-
environment.set_variables
|
129
|
-
AppConfig.load
|
130
|
-
end
|
131
|
-
|
132
|
-
def do_before_action
|
133
|
-
return if ENV["OPS_RUNNING"] || action.skip_hooks?("before")
|
134
|
-
|
135
|
-
# this prevents before hooks from running in ops executed by ops
|
136
|
-
ENV["OPS_RUNNING"] = "1"
|
137
|
-
hook_handler.do_hooks("before")
|
138
|
-
end
|
139
|
-
|
140
|
-
def hook_handler
|
141
|
-
@hook_handler ||= HookHandler.new(config)
|
142
|
-
end
|
143
|
-
|
144
|
-
def builtin
|
145
|
-
@builtin ||= Builtin.class_for(name: @action_name).new(@args, config)
|
146
|
-
rescue NameError
|
147
|
-
# this means there isn't a builtin with that name in that module
|
148
|
-
nil
|
149
|
-
end
|
150
|
-
|
151
|
-
def builtin_names
|
152
|
-
Builtins.constants.select { |c| Builtins.const_get(c).is_a? Class }.map(&:downcase)
|
153
|
-
end
|
154
|
-
|
155
|
-
def forward
|
156
|
-
@forward ||= Forwards.new(@config, @args).get(@action_name)
|
157
|
-
end
|
158
|
-
|
159
|
-
def action
|
160
|
-
return action_list.get(@action_name) if action_list.get(@action_name)
|
161
|
-
return action_list.get_by_alias(@action_name) if action_list.get_by_alias(@action_name)
|
162
|
-
|
163
|
-
raise UnknownActionError, "Unknown action: #{@action_name}"
|
164
|
-
end
|
165
|
-
|
166
|
-
def action_list
|
167
|
-
@action_list ||= begin
|
168
|
-
Output.warn("'ops.yml' has no 'actions' defined.") if config.any? && config["actions"].nil?
|
169
|
-
|
170
|
-
ActionList.new(config["actions"], @args)
|
171
|
-
end
|
99
|
+
def runner
|
100
|
+
@runner ||= Runner.new(@action_name, @args, config, config_file_absolute_path)
|
172
101
|
end
|
173
102
|
|
174
103
|
def config
|
175
104
|
@config ||= begin
|
176
|
-
if
|
177
|
-
|
105
|
+
if config_file_exists?
|
106
|
+
parsed_config_contents
|
178
107
|
else
|
179
|
-
Output.warn("File '#{
|
108
|
+
Output.warn("File '#{@config_file}' does not exist.") unless @action_name == "init"
|
180
109
|
{}
|
181
110
|
end
|
182
|
-
rescue StandardError => e
|
183
|
-
Output.warn("Error parsing '#{CONFIG_FILE}': #{e}")
|
184
|
-
{}
|
185
111
|
end
|
186
112
|
end
|
187
113
|
|
188
|
-
def
|
189
|
-
|
114
|
+
def parsed_config_contents
|
115
|
+
YAML.load_file(@config_file)
|
116
|
+
rescue StandardError => e
|
117
|
+
Output.warn("Error parsing '#{@config_file}': #{e}")
|
118
|
+
{}
|
119
|
+
end
|
120
|
+
|
121
|
+
def config_file_exists?
|
122
|
+
File.exist?(@config_file)
|
190
123
|
end
|
191
124
|
|
192
|
-
def
|
193
|
-
|
125
|
+
def config_file_absolute_path
|
126
|
+
File.expand_path(@config_file)
|
194
127
|
end
|
195
128
|
end
|
196
129
|
|
data/lib/runner.rb
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'hook_handler'
|
4
|
+
require 'action'
|
5
|
+
require 'action_list'
|
6
|
+
require 'action_suggester'
|
7
|
+
require 'forwards'
|
8
|
+
require 'environment'
|
9
|
+
|
10
|
+
class Runner
|
11
|
+
class UnknownActionError < StandardError; end
|
12
|
+
class ActionConfigError < StandardError; end
|
13
|
+
|
14
|
+
def initialize(action_name, args, config, config_path)
|
15
|
+
@action_name = action_name
|
16
|
+
@args = args
|
17
|
+
@config = config
|
18
|
+
@config_path = config_path
|
19
|
+
end
|
20
|
+
|
21
|
+
def run
|
22
|
+
return forward.run if forward
|
23
|
+
|
24
|
+
do_before_all
|
25
|
+
|
26
|
+
return builtin.run if builtin
|
27
|
+
|
28
|
+
raise UnknownActionError, "Unknown action: #{@action_name}" unless action
|
29
|
+
raise ActionConfigError, action.config_errors.join("; ") unless action.config_valid?
|
30
|
+
|
31
|
+
do_before_action
|
32
|
+
Output.notice("Running '#{action}' in environment '#{ENV['environment']}'...")
|
33
|
+
action.run
|
34
|
+
end
|
35
|
+
|
36
|
+
def suggestions
|
37
|
+
@suggestions ||= ActionSuggester.new(action_list.names + action_list.aliases + builtin_names).check(@action_name)
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def do_before_all
|
43
|
+
AppConfig.load
|
44
|
+
Secrets.load if action&.load_secrets?
|
45
|
+
environment.set_variables
|
46
|
+
end
|
47
|
+
|
48
|
+
def do_before_action
|
49
|
+
return if ENV["OPS_RUNNING"] || action.skip_hooks?("before")
|
50
|
+
|
51
|
+
# this prevents before hooks from running in ops executed by ops
|
52
|
+
ENV["OPS_RUNNING"] = "1"
|
53
|
+
hook_handler.do_hooks("before")
|
54
|
+
end
|
55
|
+
|
56
|
+
def hook_handler
|
57
|
+
@hook_handler ||= HookHandler.new(@config)
|
58
|
+
end
|
59
|
+
|
60
|
+
def builtin
|
61
|
+
@builtin ||= Builtin.class_for(name: @action_name).new(@args, @config)
|
62
|
+
rescue NameError
|
63
|
+
# this means there isn't a builtin with that name in that module
|
64
|
+
nil
|
65
|
+
end
|
66
|
+
|
67
|
+
def builtin_names
|
68
|
+
Builtins.constants.select { |c| Builtins.const_get(c).is_a? Class }.map(&:downcase)
|
69
|
+
end
|
70
|
+
|
71
|
+
def forward
|
72
|
+
@forward ||= Forwards.new(@config, @args).get(@action_name)
|
73
|
+
end
|
74
|
+
|
75
|
+
def action
|
76
|
+
return action_list.get(@action_name) if action_list.get(@action_name)
|
77
|
+
return action_list.get_by_alias(@action_name) if action_list.get_by_alias(@action_name)
|
78
|
+
end
|
79
|
+
|
80
|
+
def action_list
|
81
|
+
@action_list ||= begin
|
82
|
+
Output.warn("'ops.yml' has no 'actions' defined.") if @config.any? && @config["actions"].nil?
|
83
|
+
|
84
|
+
ActionList.new(@config["actions"], @args)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def env_vars
|
89
|
+
@config.dig("options", "environment") || {}
|
90
|
+
end
|
91
|
+
|
92
|
+
def environment
|
93
|
+
@environment ||= Environment.new(env_vars, @config_path)
|
94
|
+
end
|
95
|
+
end
|
data/ops_team.gemspec
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ops_team
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 1.2.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- nickthecook@gmail.com
|
@@ -181,6 +181,7 @@ files:
|
|
181
181
|
- lib/ops.rb
|
182
182
|
- lib/options.rb
|
183
183
|
- lib/output.rb
|
184
|
+
- lib/runner.rb
|
184
185
|
- lib/secrets.rb
|
185
186
|
- lib/version.rb
|
186
187
|
- loader.rb
|