ops_team 0.14.1 → 0.16.0

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: 41782bcb93146b1b1fabc4f762bc0c9c1c2b4ae447aeb7e74a4f9b31f6a69629
4
- data.tar.gz: 8e249d5e45fd10517215864bb5a71c2d906116750205b484e8644e9b4bdd1d52
3
+ metadata.gz: 8c264a310257b2d0178a78efaaa347405c8734a527195d3546ffe635ec00633d
4
+ data.tar.gz: 9c111521daed660115eff1628d5a0390673bb57f1d1e4a245f80a23ee64bc3fe
5
5
  SHA512:
6
- metadata.gz: 979ff357afd440bfc2b52866e5f74c5a55932155b9666f5f7d88547a1d7837b1c4d75eb2559a696aac95cd42071167c68dc37dbf3f129ff944cab41b6ab591e0
7
- data.tar.gz: '07809f3717a5cff0dfa118b492fc1fef5d3280e15e4a48b7cb510052ab1724ff4289ac4f86c4d3de86254409c6794d46d06b96edb354756e8dd29fe01af1dbba'
6
+ metadata.gz: e5febca01da575199794b09fc4607f23951b194a55903093b92e2fcfe0e03722990973fa8961ab85334a19c982d69aca161d8dc11d9a4d9d0fd84cc1dddc9a63
7
+ data.tar.gz: 40022c898ba9bb313b15567bd8a768adcdead145fde33b1e88b9534af2bbdf37bf7ecc30c8604cfc34dc8f5771ac688c08b0d5bc600f6ff3c56a251ead0ec504
@@ -36,6 +36,20 @@ class Action
36
36
  @config["skip_#{name}_hooks"]
37
37
  end
38
38
 
39
+ def config_valid?
40
+ config_errors.empty?
41
+ end
42
+
43
+ def config_errors
44
+ @config_errors ||= begin
45
+ errors = []
46
+
47
+ errors << "No 'command' specified in 'action'." unless @config['command']
48
+
49
+ errors
50
+ end
51
+ end
52
+
39
53
  private
40
54
 
41
55
  def load_secrets?
@@ -8,6 +8,14 @@ class AppConfig
8
8
  new(app_config_path).load
9
9
  end
10
10
 
11
+ def default_filename
12
+ config_path_for(Environment.environment)
13
+ end
14
+
15
+ def config_path_for(env)
16
+ "config/#{env}/config.json"
17
+ end
18
+
11
19
  private
12
20
 
13
21
  def app_config_path
@@ -20,7 +28,7 @@ class AppConfig
20
28
  end
21
29
 
22
30
  def initialize(filename = "")
23
- @filename = filename.empty? ? default_filename : filename
31
+ @filename = filename.empty? ? AppConfig.default_filename : filename
24
32
  end
25
33
 
26
34
  def load
@@ -31,10 +39,6 @@ class AppConfig
31
39
 
32
40
  private
33
41
 
34
- def default_filename
35
- "config/#{environment}/config.json"
36
- end
37
-
38
42
  def config
39
43
  @config ||= file_contents ? YAML.safe_load(file_contents) : {}
40
44
  rescue YAML::SyntaxError => e
@@ -48,8 +52,4 @@ class AppConfig
48
52
  nil
49
53
  end
50
54
  end
51
-
52
- def environment
53
- ENV['environment']
54
- end
55
55
  end
@@ -1,6 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class Builtin
4
+ class ArgumentError < StandardError; end
5
+
4
6
  attr_reader :args, :config
5
7
 
6
8
  class << self
@@ -0,0 +1,129 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'builtin'
4
+
5
+ module Builtins
6
+ class EnvDiff < Builtin
7
+ class << self
8
+ def description
9
+ "compares keys present in config and secrets between different environments"
10
+ end
11
+ end
12
+
13
+ def run
14
+ check_args
15
+
16
+ if source_only_keys.empty? && dest_only_keys.empty?
17
+ Output.out("Environments '#{source_env}' and '#{dest_env}' define the same #{source_keys.length} key(s).")
18
+ return
19
+ end
20
+
21
+ output_key_summary(source_only_keys, source_env, dest_env) if source_only_keys.any?
22
+ output_key_summary(dest_only_keys, dest_env, source_env) if dest_only_keys.any?
23
+ end
24
+
25
+ private
26
+
27
+ def output_key_summary(keys, in_env, not_in_env)
28
+ Output.warn("Environment '#{in_env}' defines keys that '#{not_in_env}' does not:\n")
29
+ keys.each do |key|
30
+ Output.warn(" - #{key}")
31
+ end
32
+ Output.out("")
33
+ end
34
+
35
+ def source_only_keys
36
+ @source_only_keys ||= source_keys - dest_keys
37
+ end
38
+
39
+ def dest_only_keys
40
+ @dest_only_keys ||= dest_keys - source_keys
41
+ end
42
+
43
+ def source_keys
44
+ @source_keys ||= keys_for(source_env)
45
+ end
46
+
47
+ def dest_keys
48
+ @dest_keys ||= keys_for(dest_env)
49
+ end
50
+
51
+ def keys_for(env)
52
+ tagged_config_keys_for(env) + tagged_secrets_keys_for(env)
53
+ end
54
+
55
+ def tagged_config_keys_for(env)
56
+ config_keys_for(env).map do |key|
57
+ "[CONFIG] #{key}"
58
+ end
59
+ end
60
+
61
+ def tagged_secrets_keys_for(env)
62
+ secrets_keys_for(env).map do |key|
63
+ "[SECRET] #{key}"
64
+ end
65
+ end
66
+
67
+ def config_keys_for(env)
68
+ (config_for(env)["environment"]&.keys || []) - ignored_keys
69
+ end
70
+
71
+ def secrets_keys_for(env)
72
+ (secrets_for(env)["environment"]&.keys || []) - ignored_keys
73
+ end
74
+
75
+ def config_for(env)
76
+ YAML.load_file(config_path_for(env))
77
+ end
78
+
79
+ def secrets_for(env)
80
+ YAML.load_file(secrets_path_for(env))
81
+ end
82
+
83
+ def check_args
84
+ raise Builtin::ArgumentError, "Usage: ops envdiff <env_one> <env_two>" unless args.length == 2
85
+
86
+ check_environment(source_env)
87
+ check_environment(dest_env)
88
+ end
89
+
90
+ def source_env
91
+ args[0]
92
+ end
93
+
94
+ def dest_env
95
+ args[1]
96
+ end
97
+
98
+ def check_environment(name)
99
+ raise_missing_file_error(config_path_for(name)) unless config_file_exists?(name)
100
+ raise_missing_file_error(secrets_path_for(name)) unless secrets_file_exists?(name)
101
+ end
102
+
103
+ def raise_missing_file_error(path)
104
+ raise Builtin::ArgumentError, "File '#{path}' does not exist."
105
+ end
106
+
107
+ def config_file_exists?(env)
108
+ File.exist?(config_path_for(env))
109
+ end
110
+
111
+ def secrets_file_exists?(env)
112
+ File.exist?(secrets_path_for(env))
113
+ end
114
+
115
+ def config_path_for(env)
116
+ AppConfig.config_path_for(env)
117
+ end
118
+
119
+ def secrets_path_for(env)
120
+ Secrets.config_path_for(env)
121
+ end
122
+
123
+ def ignored_keys
124
+ Options.get("envdiff.ignored_keys") || []
125
+ end
126
+ end
127
+
128
+ Envdiff = EnvDiff
129
+ end
@@ -6,13 +6,19 @@ require 'dependencies/helpers/apt_cache_policy'
6
6
  module Dependencies
7
7
  class Apt < VersionedDependency
8
8
  def met?
9
- return apt_cache_policy.installed_version == dep_version if dep_version
10
-
11
- apt_cache_policy.installed?
9
+ if versioned?
10
+ apt_cache_policy.installed_version == dep_version
11
+ else
12
+ apt_cache_policy.installed?
13
+ end
12
14
  end
13
15
 
14
16
  def meet
15
- execute("#{sudo_string}apt-get install -y #{name}")
17
+ if versioned?
18
+ execute("#{sudo_string}apt-get install -y #{dep_name}=#{dep_version}")
19
+ else
20
+ execute("#{sudo_string}apt-get install -y #{name}")
21
+ end
16
22
  end
17
23
 
18
24
  def unmeet
@@ -1,8 +1,18 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'version'
4
+ require 'secrets'
5
+ require 'app_config'
4
6
 
5
7
  class Environment
8
+ class << self
9
+ def environment
10
+ return 'dev' if ENV['environment'].nil? || ENV['environment'].empty?
11
+
12
+ ENV['environment']
13
+ end
14
+ end
15
+
6
16
  def initialize(env_hash)
7
17
  @env_hash = env_hash
8
18
  end
@@ -13,22 +23,18 @@ class Environment
13
23
  set_configured_variables
14
24
  end
15
25
 
16
- def environment
17
- return 'dev' if ENV['environment'].nil? || ENV['environment'].empty?
18
-
19
- ENV['environment']
20
- end
21
-
22
26
  private
23
27
 
24
28
  def set_ops_variables
25
29
  ENV["OPS_YML_DIR"] = Dir.pwd
26
30
  ENV["OPS_VERSION"] = Version.version.to_s
31
+ ENV["OPS_SECRETS_FILE"] = Secrets.config_path_for(Environment.environment)
32
+ ENV["OPS_CONFIG_FILE"] = AppConfig.config_path_for(Environment.environment)
27
33
  end
28
34
 
29
35
  def set_environment_aliases
30
36
  environment_aliases.each do |alias_name|
31
- ENV[alias_name] = environment
37
+ ENV[alias_name] = Environment.environment
32
38
  end
33
39
  end
34
40
 
data/lib/ops.rb CHANGED
@@ -19,6 +19,7 @@ require_rel "builtins"
19
19
  # executes commands based on local `ops.yml`
20
20
  class Ops
21
21
  class UnknownActionError < StandardError; end
22
+ class ActionConfigError < StandardError; end
22
23
 
23
24
  CONFIG_FILE = "ops.yml"
24
25
 
@@ -26,6 +27,8 @@ class Ops
26
27
  UNKNOWN_ACTION_EXIT_CODE = 65
27
28
  ERROR_LOADING_APP_CONFIG_EXIT_CODE = 66
28
29
  MIN_VERSION_NOT_MET_EXIT_CODE = 67
30
+ ACTION_CONFIG_ERROR_EXIT_CODE = 68
31
+ BUILTIN_SYNTAX_ERROR_EXIT_CODE = 69
29
32
 
30
33
  RECOMMEND_HELP_TEXT = "Run 'ops help' for a list of builtins and actions."
31
34
 
@@ -52,6 +55,9 @@ class Ops
52
55
  Output.error(e.to_s)
53
56
  Output.out(RECOMMEND_HELP_TEXT) unless print_did_you_mean
54
57
  exit(UNKNOWN_ACTION_EXIT_CODE)
58
+ rescue ActionConfigError => e
59
+ Output.error("Error(s) running action '#{@action_name}': #{e}")
60
+ exit(ACTION_CONFIG_ERROR_EXIT_CODE)
55
61
  end
56
62
 
57
63
  private
@@ -98,9 +104,14 @@ class Ops
98
104
 
99
105
  return builtin.run if builtin
100
106
 
107
+ raise ActionConfigError, action.config_errors.join("; ") unless action.config_valid?
108
+
101
109
  do_before_action
102
110
  Output.notice("Running '#{action}' from #{CONFIG_FILE} in environment '#{ENV['environment']}'...")
103
111
  action.run
112
+ rescue Builtin::ArgumentError => e
113
+ Output.error("Error running builtin '#{@action_name}': #{e}")
114
+ exit(BUILTIN_SYNTAX_ERROR_EXIT_CODE)
104
115
  rescue AppConfig::ParsingError => e
105
116
  Output.error("Error parsing app config: #{e}")
106
117
  exit(ERROR_LOADING_APP_CONFIG_EXIT_CODE)
@@ -9,31 +9,35 @@ require 'options'
9
9
 
10
10
  class Secrets < AppConfig
11
11
  class << self
12
+ def default_filename
13
+ config_path_for(Environment.environment)
14
+ end
15
+
16
+ def config_path_for(env)
17
+ File.exist?(ejson_path_for(env)) ? ejson_path_for(env) : json_path_for(env)
18
+ end
19
+
12
20
  private
13
21
 
22
+ def ejson_path_for(env)
23
+ "config/#{env}/secrets.ejson"
24
+ end
25
+
26
+ def json_path_for(env)
27
+ "config/#{env}/secrets.json"
28
+ end
29
+
14
30
  def app_config_path
15
31
  expand_path(Options.get("secrets.path"))
16
32
  end
17
33
  end
18
34
 
19
35
  def initialize(filename = "")
20
- @filename = filename.empty? ? default_filename : actual_filename_for(filename)
36
+ @filename = filename.empty? ? Secrets.default_filename : actual_filename_for(filename)
21
37
  end
22
38
 
23
39
  private
24
40
 
25
- def default_filename
26
- File.exist?(default_ejson_filename) ? default_ejson_filename : default_json_filename
27
- end
28
-
29
- def default_ejson_filename
30
- "config/#{environment}/secrets.ejson"
31
- end
32
-
33
- def default_json_filename
34
- "config/#{environment}/secrets.json"
35
- end
36
-
37
41
  def actual_filename_for(filename)
38
42
  File.exist?(filename) ? filename : filename.sub(".ejson", ".json")
39
43
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = 'ops_team'
5
- s.version = '0.14.1'
5
+ s.version = '0.16.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.14.1
4
+ version: 0.16.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - nickthecook@gmail.com
@@ -154,6 +154,7 @@ files:
154
154
  - lib/builtins/background_log.rb
155
155
  - lib/builtins/down.rb
156
156
  - lib/builtins/env.rb
157
+ - lib/builtins/envdiff.rb
157
158
  - lib/builtins/exec.rb
158
159
  - lib/builtins/help.rb
159
160
  - lib/builtins/helpers/dependency_handler.rb