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 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