ops_team 0.19.1 → 1.2.2

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: 78add7ded2123905f17245092c6133825009a94e16568f98cb476113335aaa93
4
- data.tar.gz: 52599bd611ee57a986b9cc2dc6c360def280b12843b9395ff6eb52e359307366
3
+ metadata.gz: 6ef9ff578bc479b6bbe53bee8143f2a0477393c32518ae15e417ab8338ac5fb4
4
+ data.tar.gz: 289a6516bda02ad14993a4529358a671a7ea500269ac9710f42a960cc5a3e524
5
5
  SHA512:
6
- metadata.gz: 390c1581cacc1a290cb4e165665e00799502fea8315aac6eac43a32e8bc987c2e986bb7550c90bd75c6cef25f87d41b1f4227c087907b182277b5403ef681e84
7
- data.tar.gz: c35a9d8d7e70d9a901ca5686ead93a19182b23b7becce3d5fda3df4069b03bda45ad8869680b2ca54a0b3ac30031ed368a6be1d91d6f830891fa4c3963a4ee5c
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
- require_relative "../loader"
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
@@ -17,9 +17,11 @@ class Action
17
17
  raise NotAllowedInEnvError, "Action not allowed in #{Environment.environment} environment."
18
18
  end
19
19
 
20
- Secrets.load if load_secrets?
21
-
22
- Kernel.exec(to_s)
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 load_secrets?
62
- @config["load_secrets"]
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
@@ -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 actions"
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>-35s %<desc>s", name: name.downcase.to_s.yellow, desc: klass.description)
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>-40s %<desc>s",
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
  )
@@ -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"] = Dir.pwd
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)
@@ -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 'action_list'
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
- run_action
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
- if @action_name.nil?
69
- Output.error("Usage: ops <action>")
70
- Output.out(RECOMMEND_HELP_TEXT)
71
- false
72
- else
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
- suggestions = did_you_mean.check(@action_name)
79
+ Output.out("Did you mean '#{runner.suggestions.join(", ")}'?") if runner.suggestions.any?
79
80
 
80
- Output.out("Did you mean '#{suggestions.join(", ")}'?") if suggestions.any?
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 run_action
105
- return forward.run if forward
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 File.exist?(CONFIG_FILE)
177
- YAML.load_file(CONFIG_FILE)
105
+ if config_file_exists?
106
+ parsed_config_contents
178
107
  else
179
- Output.warn("File '#{CONFIG_FILE}' does not exist.") unless @action_name == "init"
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 env_vars
189
- config.dig("options", "environment") || {}
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 environment
193
- @environment ||= Environment.new(env_vars)
125
+ def config_file_absolute_path
126
+ File.expand_path(@config_file)
194
127
  end
195
128
  end
196
129
 
@@ -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
@@ -2,7 +2,7 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = 'ops_team'
5
- s.version = '0.19.1'
5
+ s.version = '1.2.2'
6
6
  s.authors = [
7
7
  'nickthecook@gmail.com'
8
8
  ]
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: 0.19.1
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