ops_team 0.11.0.pre2 → 0.13.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: 0a5f98b47649c277f4c0b29228d9d9ced79c985301bde206ec9a9a1cdad1990c
4
- data.tar.gz: 6a5f36ef9a54b6a31dc745922888996f2420959a1a414f95318e6374fa162cc0
3
+ metadata.gz: 9656333af7b4f103645f9f27af758b4cc11e749c0e9770474ec249af8db85983
4
+ data.tar.gz: d1a69bbc44b1626bbcd754fa8484547f2bfa3beede615d8ebcd951e00432008a
5
5
  SHA512:
6
- metadata.gz: a0869b884b7c8bda06c1ae664c3a056610776326ca7a0f87adfa64c9f45f4d4deb53d5e64000ffcdd0528865153da7b0a4155a92d20c4939cd38a8224ac3aa7f
7
- data.tar.gz: 444234f4870cc90c6d56233763a69973de5f6f78c92eada37324bb5da354d09ae4493a49b04f15bf09f5d471f4669595f558e9de52e7f75ee287f9b1a05964fd
6
+ metadata.gz: 73e177e194c2f3e7415612bd23aea779502182929fbb5264c00a1516a52869056bb045e75ff5aaca4c546b92f03481f5346eb24958f4a6ccdd247d0d3cdc06dc
7
+ data.tar.gz: 53c224dd9162d269c0154c842ec36d4e748dd59b401efb1a959199332e3ee99489a1de4cf88d3e2d2bff7c04a37906c687d542ac2bc4852d7fa38a0f4e0ad9c6
data/bin/ops CHANGED
@@ -5,4 +5,6 @@ require_relative "../loader"
5
5
 
6
6
  require 'ops'
7
7
 
8
+ require 'pry-byebug' if ENV['environment'] == 'dev' || ENV['environment'].nil?
9
+
8
10
  Ops.new(ARGV).run
@@ -32,6 +32,10 @@ class Action
32
32
  @config["description"]
33
33
  end
34
34
 
35
+ def skip_hooks?(name)
36
+ @config["skip_#{name}_hooks"]
37
+ end
38
+
35
39
  private
36
40
 
37
41
  def load_secrets?
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ class ActionList
4
+ class UnknownActionError < StandardError; end
5
+
6
+ def initialize(actions_list, args)
7
+ @actions_list = actions_list
8
+ @args = args
9
+
10
+ process_action_list
11
+ end
12
+
13
+ def get(name)
14
+ @actions[name]
15
+ end
16
+
17
+ def get_by_alias(name)
18
+ @aliases[name]
19
+ end
20
+
21
+ def names
22
+ @actions.keys
23
+ end
24
+
25
+ def aliases
26
+ @aliases.keys
27
+ end
28
+
29
+ private
30
+
31
+ def process_action_list
32
+ @actions = {}
33
+ @aliases = {}
34
+
35
+ @actions_list.each do |name, config|
36
+ action = Action.new(config, @args)
37
+
38
+ @actions[name] = action
39
+ @aliases[action.alias] = action
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ class ActionSuggester
4
+ def initialize(dictionary)
5
+ @dictionary = dictionary
6
+ end
7
+
8
+ def check(word)
9
+ spellchecker.correct(word)
10
+ end
11
+
12
+ def spellchecker
13
+ @spellchecker ||= DidYouMean::SpellChecker.new(dictionary: @dictionary)
14
+ end
15
+ end
@@ -1,13 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "rubygems"
4
-
5
3
  require "output"
4
+ require "version"
6
5
 
7
6
  module Builtins
8
7
  class Version < Builtin
9
- GEMSPEC_FILE = "#{__dir__}/../../ops_team.gemspec"
10
-
11
8
  class << self
12
9
  def description
13
10
  "prints the version of ops that is running"
@@ -15,18 +12,7 @@ module Builtins
15
12
  end
16
13
 
17
14
  def run
18
- unless gemspec
19
- Output.error("Unable to load gemspec at '#{GEMSPEC_FILE}")
20
- return false
21
- end
22
-
23
- Output.out(gemspec.version)
24
- end
25
-
26
- private
27
-
28
- def gemspec
29
- @gemspec ||= Gem::Specification.load(GEMSPEC_FILE)
15
+ Output.out(::Version.version)
30
16
  end
31
17
  end
32
18
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'version'
4
+
3
5
  class Environment
4
6
  def initialize(env_hash)
5
7
  @env_hash = env_hash
@@ -21,6 +23,7 @@ class Environment
21
23
 
22
24
  def set_ops_variables
23
25
  ENV["OPS_YML_DIR"] = Dir.pwd
26
+ ENV["OPS_VERSION"] = Version.version.to_s
24
27
  end
25
28
 
26
29
  def set_environment_aliases
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'output'
4
+
3
5
  class HookHandler
4
6
  class HookConfigError < StandardError; end
5
7
  class HookExecError < StandardError; end
@@ -22,6 +24,7 @@ class HookHandler
22
24
 
23
25
  def execute_hooks(name)
24
26
  hooks(name).each do |hook|
27
+ Output.notice("Running #{name} hook: #{hook}")
25
28
  output, exit_code = Executor.execute(hook)
26
29
 
27
30
  next if exit_code.zero?
data/lib/ops.rb CHANGED
@@ -3,12 +3,17 @@
3
3
 
4
4
  require 'yaml'
5
5
  require 'require_all'
6
+ require "rubygems"
6
7
 
7
8
  require 'hook_handler'
8
9
  require 'action'
9
10
  require 'output'
10
11
  require 'options'
11
12
  require 'environment'
13
+ require 'version'
14
+ require 'action_list'
15
+ require 'action_suggester'
16
+
12
17
  require_rel "builtins"
13
18
 
14
19
  # executes commands based on local `ops.yml`
@@ -20,6 +25,7 @@ class Ops
20
25
  INVALID_SYNTAX_EXIT_CODE = 64
21
26
  UNKNOWN_ACTION_EXIT_CODE = 65
22
27
  ERROR_LOADING_APP_CONFIG_EXIT_CODE = 66
28
+ MIN_VERSION_NOT_MET_EXIT_CODE = 67
23
29
 
24
30
  class << self
25
31
  def project_name
@@ -35,11 +41,14 @@ class Ops
35
41
  end
36
42
 
37
43
  def run
44
+ # "return" is here to allow specs to stub "exit" without executing everything after it
38
45
  return exit(INVALID_SYNTAX_EXIT_CODE) unless syntax_valid?
46
+ return exit(MIN_VERSION_NOT_MET_EXIT_CODE) unless min_version_met?
39
47
 
40
48
  run_action
41
49
  rescue UnknownActionError => e
42
- Output.error("Error: #{e}")
50
+ Output.error(e.to_s)
51
+ print_did_you_mean
43
52
  exit(UNKNOWN_ACTION_EXIT_CODE)
44
53
  end
45
54
 
@@ -54,11 +63,37 @@ class Ops
54
63
  end
55
64
  end
56
65
 
66
+ def print_did_you_mean
67
+ suggestions = did_you_mean.check(@action_name)
68
+
69
+ Output.out("Did you mean '#{suggestions.join(", ")}'?") if suggestions.any?
70
+ end
71
+
72
+ def did_you_mean
73
+ ActionSuggester.new(action_list.names + action_list.aliases + builtin_names)
74
+ end
75
+
76
+ def min_version_met?
77
+ return true unless min_version
78
+
79
+ if Version.min_version_met?(min_version)
80
+ true
81
+ else
82
+ Output.error("ops.yml specifies minimum version of #{min_version}, but ops version is #{Version.version}")
83
+ false
84
+ end
85
+ end
86
+
87
+ def min_version
88
+ config["min_version"]
89
+ end
90
+
57
91
  def run_action
58
- do_before_run_action
92
+ do_before_all
59
93
 
60
94
  return builtin.run if builtin
61
95
 
96
+ do_before_action
62
97
  Output.notice("Running '#{action}' from #{CONFIG_FILE} in environment '#{ENV['environment']}'...")
63
98
  action.run
64
99
  rescue AppConfig::ParsingError => e
@@ -66,10 +101,16 @@ class Ops
66
101
  exit(ERROR_LOADING_APP_CONFIG_EXIT_CODE)
67
102
  end
68
103
 
69
- def do_before_run_action
104
+ def do_before_all
70
105
  environment.set_variables
71
106
  AppConfig.load
72
- hook_handler.do_hooks("before")
107
+ end
108
+
109
+ def do_before_action
110
+ hook_handler.do_hooks("before") unless ENV["OPS_RUNNING"] || action.skip_hooks?("before")
111
+
112
+ # this prevents before hooks from running in ops executed by ops
113
+ ENV["OPS_RUNNING"] = "1"
73
114
  end
74
115
 
75
116
  def hook_handler
@@ -83,24 +124,22 @@ class Ops
83
124
  nil
84
125
  end
85
126
 
127
+ def builtin_names
128
+ Builtins.constants.select { |c| Builtins.const_get(c).is_a? Class }.map(&:downcase)
129
+ end
130
+
86
131
  def action
87
- return actions[@action_name] if actions[@action_name]
88
- return aliases[@action_name] if aliases[@action_name]
132
+ return action_list.get(@action_name) if action_list.get(@action_name)
133
+ return action_list.get_by_alias(@action_name) if action_list.get_by_alias(@action_name)
89
134
 
90
135
  raise UnknownActionError, "Unknown action: #{@action_name}"
91
136
  end
92
137
 
93
- def actions
94
- @actions ||= begin
95
- if config["actions"]
96
- config["actions"]&.transform_values do |config|
97
- Action.new(config, @args)
98
- end
99
- else
100
- # only print this error if ops.yml had something in it
101
- Output.warn("'ops.yml' has no 'actions' defined.") if config.any?
102
- {}
103
- end
138
+ def action_list
139
+ @action_list ||= begin
140
+ Output.warn("'ops.yml' has no 'actions' defined.") if config.any? && config["actions"].nil?
141
+
142
+ ActionList.new(config["actions"], @args)
104
143
  end
105
144
  end
106
145
 
@@ -114,14 +153,6 @@ class Ops
114
153
  end
115
154
  end
116
155
 
117
- def aliases
118
- @aliases ||= begin
119
- actions.each_with_object({}) do |(_name, action), alias_hash|
120
- alias_hash[action.alias] = action if action.alias
121
- end
122
- end
123
- end
124
-
125
156
  def env_vars
126
157
  @config.dig("options", "environment") || {}
127
158
  end
@@ -129,10 +160,6 @@ class Ops
129
160
  def environment
130
161
  @environment ||= Environment.new(env_vars)
131
162
  end
132
-
133
- def app_config
134
- @app_config ||= AppConfig.new
135
- end
136
163
  end
137
164
 
138
165
  Ops.new(ARGV).run if $PROGRAM_NAME == __FILE__
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rubygems"
4
+
5
+ class Version
6
+ GEMSPEC_FILE = "#{__dir__}/../ops_team.gemspec"
7
+
8
+ class << self
9
+ # these class methods exist because I don't want to have to call `Version.new.version` elsewhere
10
+ def version
11
+ new.send(:version)
12
+ end
13
+
14
+ def min_version_met?(min_version)
15
+ new.send(:min_version_met?, min_version)
16
+ end
17
+ end
18
+
19
+ private
20
+
21
+ # these instance methods exist so that I don't have to clear class variables between tests
22
+ def version
23
+ unless gemspec
24
+ Output.error("Unable to load gemspec at '#{GEMSPEC_FILE}")
25
+ return nil
26
+ end
27
+
28
+ gemspec.version
29
+ end
30
+
31
+ def min_version_met?(min_version)
32
+ Gem::Version.new(version) >= Gem::Version.new(min_version)
33
+ end
34
+
35
+ def gemspec
36
+ @gemspec ||= Gem::Specification.load(GEMSPEC_FILE)
37
+ end
38
+ end
@@ -2,7 +2,7 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = 'ops_team'
5
- s.version = '0.11.0.pre2'
5
+ s.version = '0.13.0'
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.11.0.pre2
4
+ version: 0.13.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - nickthecook@gmail.com
@@ -146,6 +146,8 @@ files:
146
146
  - etc/ruby.template.yml
147
147
  - etc/terraform.template.yml
148
148
  - lib/action.rb
149
+ - lib/action_list.rb
150
+ - lib/action_suggester.rb
149
151
  - lib/app_config.rb
150
152
  - lib/builtin.rb
151
153
  - lib/builtins/background.rb
@@ -176,6 +178,7 @@ files:
176
178
  - lib/options.rb
177
179
  - lib/output.rb
178
180
  - lib/secrets.rb
181
+ - lib/version.rb
179
182
  - loader.rb
180
183
  - ops_team.gemspec
181
184
  homepage: https://github.com/nickthecook/ops
@@ -193,9 +196,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
193
196
  version: '2.5'
194
197
  required_rubygems_version: !ruby/object:Gem::Requirement
195
198
  requirements:
196
- - - ">"
199
+ - - ">="
197
200
  - !ruby/object:Gem::Version
198
- version: 1.3.1
201
+ version: '0'
199
202
  requirements: []
200
203
  rubygems_version: 3.0.3
201
204
  signing_key: