ops_team 1.21.1 → 2.0.0.rc3

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.
Files changed (61) hide show
  1. checksums.yaml +4 -4
  2. data/bin/ops +4 -40
  3. data/build/darwin_amd64/ops +0 -0
  4. data/build/darwin_arm64/ops +0 -0
  5. data/build/linux_amd64/ops +0 -0
  6. metadata +12 -224
  7. data/Gemfile +0 -30
  8. data/bin/benchmark +0 -185
  9. data/bin/print_config +0 -4
  10. data/bin/print_secrets +0 -4
  11. data/bin/tag +0 -16
  12. data/etc/ops.template.yml +0 -13
  13. data/etc/ruby.template.yml +0 -37
  14. data/etc/terraform.template.yml +0 -36
  15. data/lib/action.rb +0 -103
  16. data/lib/action_list.rb +0 -55
  17. data/lib/action_suggester.rb +0 -17
  18. data/lib/app_config.rb +0 -69
  19. data/lib/builtin.rb +0 -51
  20. data/lib/builtins/background.rb +0 -46
  21. data/lib/builtins/background_log.rb +0 -34
  22. data/lib/builtins/common/up_down.rb +0 -67
  23. data/lib/builtins/countdown.rb +0 -73
  24. data/lib/builtins/down.rb +0 -9
  25. data/lib/builtins/env.rb +0 -21
  26. data/lib/builtins/envdiff.rb +0 -127
  27. data/lib/builtins/exec.rb +0 -24
  28. data/lib/builtins/help.rb +0 -66
  29. data/lib/builtins/helpers/dependency_handler.rb +0 -27
  30. data/lib/builtins/helpers/enumerator.rb +0 -34
  31. data/lib/builtins/init.rb +0 -64
  32. data/lib/builtins/up.rb +0 -9
  33. data/lib/builtins/version.rb +0 -17
  34. data/lib/dependencies/apk.rb +0 -24
  35. data/lib/dependencies/apt.rb +0 -42
  36. data/lib/dependencies/brew.rb +0 -22
  37. data/lib/dependencies/cask.rb +0 -13
  38. data/lib/dependencies/custom.rb +0 -45
  39. data/lib/dependencies/dir.rb +0 -22
  40. data/lib/dependencies/docker.rb +0 -17
  41. data/lib/dependencies/gem.rb +0 -36
  42. data/lib/dependencies/helpers/apt_cache_policy.rb +0 -43
  43. data/lib/dependencies/pip.rb +0 -32
  44. data/lib/dependencies/snap.rb +0 -32
  45. data/lib/dependencies/sshkey.rb +0 -121
  46. data/lib/dependencies/versioned_dependency.rb +0 -25
  47. data/lib/dependency.rb +0 -69
  48. data/lib/environment.rb +0 -47
  49. data/lib/executor.rb +0 -20
  50. data/lib/forward.rb +0 -15
  51. data/lib/forwards.rb +0 -16
  52. data/lib/hook_handler.rb +0 -41
  53. data/lib/ops.rb +0 -129
  54. data/lib/options.rb +0 -22
  55. data/lib/output.rb +0 -71
  56. data/lib/profiler.rb +0 -47
  57. data/lib/runner.rb +0 -110
  58. data/lib/secrets.rb +0 -55
  59. data/lib/version.rb +0 -38
  60. data/loader.rb +0 -10
  61. data/ops_team.gemspec +0 -36
data/lib/action.rb DELETED
@@ -1,103 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # represents one action to be performed in the shell
4
- # can assemble a command line from a command and args
5
- class Action
6
- attr_reader :name
7
-
8
- def initialize(name, config, args)
9
- @name = name
10
- @config = config || {}
11
- @args = args
12
- end
13
-
14
- def run
15
- Output.error(Profiler.summary) if Profiler.summary
16
-
17
- if perform_shell_expansion?
18
- Kernel.exec(to_s)
19
- else
20
- Kernel.exec(*to_a)
21
- end
22
- end
23
-
24
- def to_s
25
- "#{command} #{@args.join(' ')}".strip
26
- end
27
-
28
- def alias
29
- @config["alias"] || @config["aliases"]&.first
30
- end
31
-
32
- def aliases
33
- return [@config["alias"]].compact unless @config["aliases"]
34
-
35
- ([@config["alias"]] + @config["aliases"]).compact
36
- end
37
-
38
- def command
39
- return @config if @config.is_a?(String)
40
-
41
- @config["command"]
42
- end
43
-
44
- def description
45
- @config["description"]
46
- end
47
-
48
- def skip_hooks?(name)
49
- @config["skip_#{name}_hooks"]
50
- end
51
-
52
- def config_valid?
53
- config_errors.empty?
54
- end
55
-
56
- def config_errors
57
- @config_errors ||= begin
58
- errors = []
59
-
60
- errors << "No 'command' specified in 'action'." unless command
61
-
62
- errors
63
- end
64
- end
65
-
66
- def load_secrets?
67
- @config["load_secrets"].nil? ? false : @config["load_secrets"]
68
- end
69
-
70
- def execute_in_env?(env)
71
- !skip_in_envs.include?(env)
72
- end
73
-
74
- def allowed_in_env?(env)
75
- return false if not_in_envs.include?(env)
76
-
77
- return false if in_envs.any? && !in_envs.include?(env)
78
-
79
- true
80
- end
81
-
82
- private
83
-
84
- def to_a
85
- command.split(" ").reject(&:nil?) | @args
86
- end
87
-
88
- def not_in_envs
89
- @config["not_in_envs"] || []
90
- end
91
-
92
- def in_envs
93
- @config["in_envs"] || []
94
- end
95
-
96
- def skip_in_envs
97
- @config["skip_in_envs"] || []
98
- end
99
-
100
- def perform_shell_expansion?
101
- @config["shell_expansion"].nil? ? true : @config["shell_expansion"]
102
- end
103
- end
data/lib/action_list.rb DELETED
@@ -1,55 +0,0 @@
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 actions_list
32
- @actions_list ||= []
33
- end
34
-
35
- def process_action_list
36
- @actions = {}
37
- @aliases = {}
38
-
39
- actions_list.each do |name, config|
40
- action = Action.new(name, config, @args)
41
-
42
- @actions[name] = action
43
- action.aliases.each do |aliaz|
44
- check_duplicate_alias(name, aliaz)
45
- @aliases[aliaz] = action
46
- end
47
- end
48
- end
49
-
50
- def check_duplicate_alias(name, aliaz)
51
- return if @aliases[aliaz].nil?
52
-
53
- Output.warn("Duplicate alias '#{aliaz}' detected in action '#{name}'.")
54
- end
55
- end
@@ -1,17 +0,0 @@
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
- private
13
-
14
- def spellchecker
15
- @spellchecker ||= DidYouMean::SpellChecker.new(dictionary: @dictionary)
16
- end
17
- end
data/lib/app_config.rb DELETED
@@ -1,69 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'json'
4
-
5
- class AppConfig
6
- class ParsingError < StandardError; end
7
-
8
- class << self
9
- def load
10
- new(app_config_path).load
11
- end
12
-
13
- def default_filename
14
- config_path_for(Environment.environment)
15
- end
16
-
17
- def config_path_for(env)
18
- "config/#{env}/config.json"
19
- end
20
-
21
- def app_config_path
22
- expand_path(Options.get("config.path") || default_filename)
23
- end
24
-
25
- private
26
-
27
- def expand_path(path)
28
- `echo #{path}`.chomp
29
- end
30
- end
31
-
32
- def load
33
- config['environment']&.each do |key, value|
34
- if Options.get("config.preserve_existing_env_vars") && ENV[key]
35
- Output.debug("Environment variable '$#{key}' already set; skipping...")
36
- next
37
- end
38
-
39
- ENV[key] = value.is_a?(Hash) || value.is_a?(Array) ? value.to_json : value.to_s
40
- end
41
- end
42
-
43
- private
44
-
45
- def initialize(filename = "")
46
- @filename = filename
47
- end
48
-
49
- def config
50
- @config ||= if file_contents == ""
51
- Output.warn("Config file '#{@filename}' exists but is empty.")
52
- {}
53
- elsif file_contents
54
- YAML.safe_load(file_contents)
55
- else
56
- {}
57
- end
58
- rescue YAML::SyntaxError => e
59
- raise ParsingError, "#{@filename}: #{e}"
60
- end
61
-
62
- def file_contents
63
- @file_contents ||= begin
64
- File.open(@filename).read
65
- rescue Errno::ENOENT
66
- nil
67
- end
68
- end
69
- end
data/lib/builtin.rb DELETED
@@ -1,51 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class Builtin
4
- class ArgumentError < StandardError; end
5
-
6
- attr_reader :args, :config
7
-
8
- class << self
9
- BUILTIN_DIR = "builtins"
10
-
11
- def description
12
- "no description"
13
- end
14
-
15
- def class_for(name:)
16
- file = file_for(name: name)
17
- unless File.exist?(file)
18
- require 'require_all'
19
- require_rel "builtins"
20
- end
21
-
22
- get_const(name: builtin_class_name_for(name: name))
23
- end
24
-
25
- private
26
-
27
- def get_const(name:)
28
- Builtins.const_get(name, false)
29
- rescue NameError
30
- # no such constant
31
- nil
32
- end
33
-
34
- def file_for(name:)
35
- File.join(File.dirname(__FILE__), BUILTIN_DIR, "#{name}.rb")
36
- end
37
-
38
- def builtin_class_name_for(name:)
39
- name.capitalize.to_sym
40
- end
41
- end
42
-
43
- def initialize(args, config)
44
- @args = args
45
- @config = config
46
- end
47
-
48
- def run
49
- raise NotImplementedError
50
- end
51
- end
@@ -1,46 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Builtins
4
- class Background < ::Builtin
5
- DEFAULT_LOG_FILE_PREFIX = "/tmp/ops_bglog_"
6
-
7
- class << self
8
- def description
9
- "runs the given command in a background session"
10
- end
11
-
12
- def log_filename
13
- Options.get("background.log_filename") || "#{DEFAULT_LOG_FILE_PREFIX}#{Ops.project_name}"
14
- end
15
- end
16
-
17
- def run
18
- subprocess = fork do
19
- set_bglog_file_permissions
20
- run_ops(args)
21
- end
22
-
23
- Process.detach(subprocess)
24
-
25
- true
26
- end
27
-
28
- private
29
-
30
- def set_bglog_file_permissions
31
- File.new(Background.log_filename, "w").chmod(0o600)
32
- end
33
-
34
- def run_ops(args)
35
- Output.notice("Running '#{args.join(' ')}' with stderr and stdout redirected to '#{Background.log_filename}'")
36
- $stdout.sync = $stderr.sync = true
37
- $stdout.reopen(Background.log_filename, "w")
38
- $stderr.reopen($stdout)
39
-
40
- Ops.new(args).run
41
- end
42
- end
43
-
44
- # set an alias
45
- Bg = Background
46
- end
@@ -1,34 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Builtins
4
- class BackgroundLog < Builtin
5
- class << self
6
- def description
7
- "displays the log from the current or most recent background task from this project"
8
- end
9
- end
10
-
11
- def run
12
- unless File.exist?(Background.log_filename)
13
- Output.warn("No background log found at '#{Background.log_filename}'.")
14
- return 0
15
- end
16
-
17
- Output.notice("Displaying background log '#{Background.log_filename}'...")
18
- display_file
19
- end
20
-
21
- private
22
-
23
- def display_file
24
- if args.any?
25
- exec("tail #{args.join(' ')} '#{Background.log_filename}'")
26
- else
27
- exec("cat '#{Background.log_filename}'")
28
- end
29
- end
30
- end
31
-
32
- # set an alias
33
- Bglog = BackgroundLog
34
- end
@@ -1,67 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Builtins
4
- module Common
5
- class UpDown < Builtin
6
- class << self
7
- def description
8
- "attempts to meet dependencies listed in ops.yml"
9
- end
10
- end
11
-
12
- def run
13
- meet_dependencies
14
-
15
- return true unless fail_on_error?
16
-
17
- deps_to_meet.all?(&:success?)
18
- end
19
-
20
- private
21
-
22
- def meet_dependencies
23
- deps_to_meet.each do |dependency|
24
- Output.status("[#{dependency.type}] #{dependency.name}")
25
-
26
- meet_dependency(dependency)
27
-
28
- break if dependency.failure? && exit_on_error?
29
- end
30
- end
31
-
32
- def meet_dependency(dependency)
33
- handle_dependency(dependency) if !dependency.met? || dependency.always_act?
34
-
35
- if dependency.success?
36
- Output.okay
37
- else
38
- Output.failed
39
- Output.error("Error meeting #{dependency.type} dependency '#{dependency.name}':")
40
- Output.out(dependency.output)
41
- end
42
- end
43
-
44
- def deps_to_meet
45
- @deps_to_meet ||= dependency_handler.dependencies.select(&:should_meet?)
46
- end
47
-
48
- def dependency_handler
49
- Helpers::DependencyHandler.new(dependencies)
50
- end
51
-
52
- def dependencies
53
- return @config["dependencies"] if @args.empty?
54
-
55
- @config["dependencies"].select { |dep, _names| @args.include?(dep) }
56
- end
57
-
58
- def fail_on_error?
59
- Options.get("up.fail_on_error") || false
60
- end
61
-
62
- def exit_on_error?
63
- Options.get("up.exit_on_error") || false
64
- end
65
- end
66
- end
67
- end
@@ -1,73 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'concurrent'
4
-
5
- module Builtins
6
- class Countdown < Builtin
7
- USAGE_STRING = "Usage: ops countdown <seconds>"
8
-
9
- class << self
10
- def description
11
- "Like `sleep`, but displays time remaining in terminal."
12
- end
13
- end
14
-
15
- def run
16
- check_args
17
-
18
- timer_task.execute
19
-
20
- while timer_task.running?
21
- sleep(1)
22
- timer_task.shutdown if task_complete?
23
- end
24
- Output.out("\rCountdown complete after #{sleep_seconds}s.")
25
-
26
- true
27
- end
28
-
29
- private
30
-
31
- def check_args
32
- check_arg_count
33
- check_arg_is_positive_int
34
- end
35
-
36
- def check_arg_count
37
- raise Builtin::ArgumentError, USAGE_STRING unless args.length == 1
38
- end
39
-
40
- def check_arg_is_positive_int
41
- raise Builtin::ArgumentError, USAGE_STRING unless sleep_seconds.positive?
42
- # raised when the arg is not an int
43
- rescue ::ArgumentError
44
- raise Builtin::ArgumentError, USAGE_STRING
45
- end
46
-
47
- def timer_task
48
- @timer_task ||= Concurrent::TimerTask.new(run_now: true, execution_interval: 1) do
49
- Output.print("\r \r#{seconds_left}")
50
- end
51
- end
52
-
53
- def sleep_seconds
54
- Integer(args.first)
55
- end
56
-
57
- def task_start_time
58
- @task_start_time ||= Time.now
59
- end
60
-
61
- def task_end_time
62
- @task_end_time ||= task_start_time + sleep_seconds
63
- end
64
-
65
- def task_complete?
66
- Time.now > task_end_time
67
- end
68
-
69
- def seconds_left
70
- Integer(task_end_time - Time.now + 1)
71
- end
72
- end
73
- end
data/lib/builtins/down.rb DELETED
@@ -1,9 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Builtins
4
- class Down < Common::UpDown
5
- def handle_dependency(dependency)
6
- dependency.unmeet
7
- end
8
- end
9
- end
data/lib/builtins/env.rb DELETED
@@ -1,21 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Builtins
4
- class Env < Builtin
5
- class << self
6
- def description
7
- "prints the current environment, e.g. 'dev', 'production', 'staging', etc."
8
- end
9
- end
10
-
11
- def run
12
- Output.print(environment)
13
-
14
- true
15
- end
16
-
17
- def environment
18
- ENV['environment']
19
- end
20
- end
21
- end
@@ -1,127 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Builtins
4
- class Envdiff < Builtin
5
- class << self
6
- def description
7
- "compares keys present in config and secrets between different environments"
8
- end
9
- end
10
-
11
- def run
12
- check_args
13
-
14
- if source_only_keys.empty? && dest_only_keys.empty?
15
- Output.out("Environments '#{source_env}' and '#{dest_env}' define the same #{source_keys.length} key(s).")
16
- return
17
- end
18
-
19
- output_key_summary(source_only_keys, source_env, dest_env) if source_only_keys.any?
20
- output_key_summary(dest_only_keys, dest_env, source_env) if dest_only_keys.any?
21
-
22
- true
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
- end
data/lib/builtins/exec.rb DELETED
@@ -1,24 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Builtins
4
- class Exec < Builtin
5
- class << self
6
- def description
7
- "executes the given command in the `ops` environment, i.e. with environment variables set"
8
- end
9
- end
10
-
11
- def run
12
- Secrets.load if Options.get("exec.load_secrets")
13
-
14
- if args.any?
15
- Output.error(Profiler.summary) if Profiler.summary
16
- Kernel.exec(args.join(" "))
17
- else
18
- Output.error("Usage: ops exec '<command>'")
19
-
20
- false
21
- end
22
- end
23
- end
24
- end