appsignal 2.11.10 → 3.0.0.beta.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (91) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +4 -1
  3. data/.semaphore/semaphore.yml +161 -70
  4. data/CHANGELOG.md +5 -18
  5. data/Rakefile +3 -13
  6. data/appsignal.gemspec +2 -24
  7. data/build_matrix.yml +20 -22
  8. data/gemfiles/capistrano2.gemfile +1 -0
  9. data/gemfiles/capistrano3.gemfile +1 -0
  10. data/gemfiles/grape.gemfile +1 -0
  11. data/gemfiles/no_dependencies.gemfile +1 -4
  12. data/gemfiles/rails-3.2.gemfile +0 -2
  13. data/gemfiles/rails-4.2.gemfile +0 -6
  14. data/gemfiles/resque-2.gemfile +4 -0
  15. data/gemfiles/sequel-435.gemfile +1 -0
  16. data/gemfiles/sequel.gemfile +1 -0
  17. data/gemfiles/sinatra.gemfile +1 -0
  18. data/lib/appsignal/auth_check.rb +2 -8
  19. data/lib/appsignal/cli.rb +1 -23
  20. data/lib/appsignal/config.rb +0 -24
  21. data/lib/appsignal/event_formatter.rb +0 -25
  22. data/lib/appsignal/extension.rb +0 -50
  23. data/lib/appsignal/hooks/action_cable.rb +5 -44
  24. data/lib/appsignal/hooks/active_support_notifications.rb +7 -86
  25. data/lib/appsignal/hooks/celluloid.rb +5 -9
  26. data/lib/appsignal/hooks/net_http.rb +2 -12
  27. data/lib/appsignal/hooks/puma.rb +3 -5
  28. data/lib/appsignal/hooks/que.rb +1 -1
  29. data/lib/appsignal/hooks/rake.rb +2 -24
  30. data/lib/appsignal/hooks/redis.rb +2 -13
  31. data/lib/appsignal/hooks/resque.rb +2 -43
  32. data/lib/appsignal/hooks/sidekiq.rb +1 -5
  33. data/lib/appsignal/hooks/unicorn.rb +3 -24
  34. data/lib/appsignal/hooks/webmachine.rb +1 -7
  35. data/lib/appsignal/hooks.rb +0 -23
  36. data/lib/appsignal/integrations/action_cable.rb +34 -0
  37. data/lib/appsignal/integrations/active_support_notifications.rb +77 -0
  38. data/lib/appsignal/integrations/net_http.rb +16 -0
  39. data/lib/appsignal/integrations/object.rb +61 -4
  40. data/lib/appsignal/integrations/padrino.rb +5 -7
  41. data/lib/appsignal/integrations/que.rb +26 -33
  42. data/lib/appsignal/integrations/railtie.rb +1 -4
  43. data/lib/appsignal/integrations/rake.rb +26 -2
  44. data/lib/appsignal/integrations/redis.rb +17 -0
  45. data/lib/appsignal/integrations/resque.rb +39 -10
  46. data/lib/appsignal/integrations/unicorn.rb +28 -0
  47. data/lib/appsignal/integrations/webmachine.rb +22 -24
  48. data/lib/appsignal/minutely.rb +0 -18
  49. data/lib/appsignal/transaction.rb +1 -1
  50. data/lib/appsignal/version.rb +1 -1
  51. data/lib/appsignal.rb +1 -27
  52. data/spec/lib/appsignal/auth_check_spec.rb +1 -24
  53. data/spec/lib/appsignal/cli_spec.rb +1 -1
  54. data/spec/lib/appsignal/config_spec.rb +0 -66
  55. data/spec/lib/appsignal/event_formatter_spec.rb +0 -37
  56. data/spec/lib/appsignal/extension_install_failure_spec.rb +7 -0
  57. data/spec/lib/appsignal/extension_spec.rb +9 -43
  58. data/spec/lib/appsignal/hooks/action_cable_spec.rb +0 -88
  59. data/spec/lib/appsignal/hooks/celluloid_spec.rb +6 -1
  60. data/spec/lib/appsignal/hooks/rake_spec.rb +1 -2
  61. data/spec/lib/appsignal/hooks/redis_spec.rb +50 -15
  62. data/spec/lib/appsignal/hooks/sidekiq_spec.rb +7 -61
  63. data/spec/lib/appsignal/hooks/unicorn_spec.rb +14 -3
  64. data/spec/lib/appsignal/hooks/webmachine_spec.rb +2 -13
  65. data/spec/lib/appsignal/hooks_spec.rb +0 -57
  66. data/spec/lib/appsignal/integrations/object_spec.rb +4 -95
  67. data/spec/lib/appsignal/integrations/padrino_spec.rb +2 -3
  68. data/spec/lib/appsignal/integrations/railtie_spec.rb +0 -45
  69. data/spec/lib/appsignal/integrations/webmachine_spec.rb +26 -8
  70. data/spec/lib/appsignal/minutely_spec.rb +0 -19
  71. data/spec/lib/appsignal/transaction_spec.rb +1 -31
  72. data/spec/lib/appsignal/transmitter_spec.rb +1 -1
  73. data/spec/lib/appsignal/utils/data_spec.rb +87 -133
  74. data/spec/lib/appsignal_spec.rb +0 -69
  75. data/spec/lib/puma/appsignal_spec.rb +0 -28
  76. data/spec/spec_helper.rb +1 -37
  77. data/spec/support/testing.rb +1 -11
  78. data/support/install_deps +0 -4
  79. metadata +10 -23
  80. data/lib/appsignal/cli/notify_of_deploy.rb +0 -131
  81. data/lib/appsignal/integrations/object_ruby_19.rb +0 -37
  82. data/lib/appsignal/integrations/object_ruby_modern.rb +0 -41
  83. data/lib/appsignal/integrations/resque_active_job.rb +0 -19
  84. data/lib/appsignal/js_exception_transaction.rb +0 -56
  85. data/lib/appsignal/rack/js_exception_catcher.rb +0 -80
  86. data/spec/lib/appsignal/cli/notify_of_deploy_spec.rb +0 -180
  87. data/spec/lib/appsignal/integrations/object_19_spec.rb +0 -266
  88. data/spec/lib/appsignal/integrations/resque_active_job_spec.rb +0 -28
  89. data/spec/lib/appsignal/integrations/resque_spec.rb +0 -28
  90. data/spec/lib/appsignal/js_exception_transaction_spec.rb +0 -128
  91. data/spec/lib/appsignal/rack/js_exception_catcher_spec.rb +0 -170
@@ -1,131 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Appsignal
4
- class CLI
5
- # Command line tool to send a "Deploy Marker" for an application to
6
- # AppSignal.
7
- #
8
- # Deploy markers are used on AppSignal.com to indicate changes in an
9
- # application, "Deploy markers" indicate a deploy of an application.
10
- #
11
- # Incidents for exceptions and performance issues will be closed and
12
- # reopened if they occur again in the new deploy.
13
- #
14
- # @note The same logic is used in the Capistrano integration. A deploy
15
- # marker is created on each deploy.
16
- #
17
- # ## Options
18
- #
19
- # - `--environment` required. The environment of the application being
20
- # deployed.
21
- # - `--user` required. User that triggered the deploy.
22
- # - `--revision` required. Git commit SHA or other identifiable revision id.
23
- # - `--name` If no "name" config can be found in a `config/appsignal.yml`
24
- # file or based on the `APPSIGNAL_APP_NAME` environment variable, this
25
- # option is required.
26
- #
27
- # ## Exit codes
28
- #
29
- # - Exits with status code `0` if the deploy marker is sent.
30
- # - Exits with status code `1` if the configuration is not valid and active.
31
- # - Exits with status code `1` if the required options aren't present.
32
- #
33
- # @example basic example
34
- # appsignal notify_of_deploy \
35
- # --user=tom \
36
- # --environment=production \
37
- # --revision=abc1234
38
- #
39
- # @example using a custom app name
40
- # appsignal notify_of_deploy \
41
- # --user=tom \
42
- # --environment=production \
43
- # --revision=abc1234 \
44
- # --name="My app"
45
- #
46
- # @example help command
47
- # appsignal notify_of_deploy --help
48
- #
49
- # @deprecated This method of sending AppSignal deploy markers is
50
- # deprecated. Use the [`revision` config option]
51
- # (https://docs.appsignal.com/ruby/configuration/options.html#app_revision-revision)
52
- # instead. For more information, please read the [deploy markers]
53
- # (https://docs.appsignal.com/application/markers/deploy-markers.html)
54
- # documentation.
55
- # @since 0.2.5
56
- # @see Appsignal::Marker Appsignal::Marker
57
- # @see http://docs.appsignal.com/ruby/command-line/notify_of_deploy.html
58
- # AppSignal notify_of_deploy documentation
59
- # @see http://docs.appsignal.com/appsignal/terminology.html#markers
60
- # Terminology: Deploy marker
61
- class NotifyOfDeploy
62
- class << self
63
- include Appsignal::Utils::DeprecationMessage
64
-
65
- # @param options [Hash]
66
- # @option options :environment [String] environment to load
67
- # configuration for.
68
- # @option options :name [String] custom name of the application.
69
- # @option options :user [String] user who triggered the deploy.
70
- # @option options :revision [String] the revision that has been
71
- # deployed. E.g. a git commit SHA.
72
- # @return [void]
73
- def run(options)
74
- config = config_for(options[:environment])
75
- config[:name] = options[:name] if options[:name]
76
-
77
- validate_active_config(config)
78
- required_config = [:revision, :user]
79
- required_config << :environment if config.env.empty?
80
- required_config << :name if !config[:name] || config[:name].empty?
81
- validate_required_options(options, required_config)
82
-
83
- Appsignal::Marker.new(
84
- {
85
- :revision => options[:revision],
86
- :user => options[:user]
87
- },
88
- config
89
- ).transmit
90
-
91
- puts
92
- message = "This command (appsignal notify_of_deploy) has been " \
93
- "deprecated in favor of the `revision` config option. Please " \
94
- "see our documentation for more information on the recommended " \
95
- "method: " \
96
- "https://docs.appsignal.com/application/markers/deploy-markers.html"
97
- deprecation_message message
98
- end
99
-
100
- private
101
-
102
- def validate_required_options(options, required_options)
103
- missing = required_options.select do |required_option|
104
- val = options[required_option]
105
- val.nil? || (val.respond_to?(:empty?) && val.empty?)
106
- end
107
- return unless missing.any?
108
-
109
- puts "Error: Missing options: #{missing.join(", ")}"
110
- exit 1
111
- end
112
-
113
- def validate_active_config(config)
114
- return if config.active?
115
-
116
- puts "Error: No valid config found."
117
- exit 1
118
- end
119
-
120
- def config_for(environment)
121
- Appsignal::Config.new(
122
- Dir.pwd,
123
- environment,
124
- {},
125
- Logger.new(StringIO.new)
126
- )
127
- end
128
- end
129
- end
130
- end
131
- end
@@ -1,37 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class Object
4
- def self.appsignal_instrument_class_method(method_name, options = {})
5
- singleton_class.send \
6
- :alias_method, "appsignal_uninstrumented_#{method_name}", method_name
7
- singleton_class.send(:define_method, method_name) do |*args, &block|
8
- name = options.fetch(:name) do
9
- "#{method_name}.class_method.#{appsignal_reverse_class_name}.other"
10
- end
11
- Appsignal.instrument name do
12
- send "appsignal_uninstrumented_#{method_name}", *args, &block
13
- end
14
- end
15
- end
16
-
17
- def self.appsignal_instrument_method(method_name, options = {})
18
- alias_method "appsignal_uninstrumented_#{method_name}", method_name
19
- define_method method_name do |*args, &block|
20
- name = options.fetch(:name) do
21
- "#{method_name}.#{appsignal_reverse_class_name}.other"
22
- end
23
- Appsignal.instrument name do
24
- send "appsignal_uninstrumented_#{method_name}", *args, &block
25
- end
26
- end
27
- end
28
-
29
- def self.appsignal_reverse_class_name
30
- return "AnonymousClass" unless name
31
- name.split("::").reverse.join(".")
32
- end
33
-
34
- def appsignal_reverse_class_name
35
- self.class.appsignal_reverse_class_name
36
- end
37
- end
@@ -1,41 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class Object
4
- def self.appsignal_instrument_class_method(method_name, options = {})
5
- singleton_class.send \
6
- :alias_method, "appsignal_uninstrumented_#{method_name}", method_name
7
- singleton_class.send(:define_method, method_name) do |*args, &block|
8
- name = options.fetch(:name) do
9
- "#{method_name}.class_method.#{appsignal_reverse_class_name}.other"
10
- end
11
- Appsignal.instrument name do
12
- send "appsignal_uninstrumented_#{method_name}", *args, &block
13
- end
14
- end
15
- if singleton_class.respond_to?(:ruby2_keywords, true)
16
- singleton_class.send(:ruby2_keywords, method_name)
17
- end
18
- end
19
-
20
- def self.appsignal_instrument_method(method_name, options = {})
21
- alias_method "appsignal_uninstrumented_#{method_name}", method_name
22
- define_method method_name do |*args, &block|
23
- name = options.fetch(:name) do
24
- "#{method_name}.#{appsignal_reverse_class_name}.other"
25
- end
26
- Appsignal.instrument name do
27
- send "appsignal_uninstrumented_#{method_name}", *args, &block
28
- end
29
- end
30
- ruby2_keywords method_name if respond_to?(:ruby2_keywords, true)
31
- end
32
-
33
- def self.appsignal_reverse_class_name
34
- return "AnonymousClass" unless name
35
- name.split("::").reverse.join(".")
36
- end
37
-
38
- def appsignal_reverse_class_name
39
- self.class.appsignal_reverse_class_name
40
- end
41
- end
@@ -1,19 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Appsignal
4
- module Integrations
5
- # @api private
6
- module ResqueActiveJobPlugin
7
- def self.included(_)
8
- callers = caller
9
- Appsignal::Utils::DeprecationMessage.message \
10
- "The AppSignal ResqueActiveJobPlugin is deprecated and does " \
11
- "nothing on extend. In this version of the AppSignal Ruby gem " \
12
- "the integration with Resque is automatic on all Resque workers. " \
13
- "Please remove the following line from this file to remove this " \
14
- "message: include Appsignal::Integrations::ResqueActiveJobPlugin\n" \
15
- "#{callers.first}"
16
- end
17
- end
18
- end
19
- end
@@ -1,56 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Appsignal
4
- class JSExceptionTransaction
5
- attr_reader :uuid, :ext
6
-
7
- def initialize(data)
8
- @data = data
9
- @uuid = SecureRandom.uuid
10
- @ext = Appsignal::Extension.start_transaction(@uuid, Appsignal::Transaction::FRONTEND, 0)
11
-
12
- set_action
13
- set_metadata
14
- set_error
15
- set_sample_data
16
- end
17
-
18
- def set_action
19
- return unless @data["action"]
20
- ext.set_action(@data["action"])
21
- end
22
-
23
- def set_metadata
24
- return unless @data["path"]
25
- ext.set_metadata("path", @data["path"])
26
- end
27
-
28
- def set_error
29
- ext.set_error(
30
- @data["name"],
31
- @data["message"] || "",
32
- Appsignal::Utils::Data.generate(@data["backtrace"] || [])
33
- )
34
- end
35
-
36
- def set_sample_data
37
- {
38
- :params => @data["params"],
39
- :session_data => @data["session_data"],
40
- :environment => @data["environment"],
41
- :tags => @data["tags"]
42
- }.each do |key, data|
43
- next unless data.is_a?(Array) || data.is_a?(Hash)
44
- ext.set_sample_data(
45
- key.to_s,
46
- Appsignal::Utils::Data.generate(data)
47
- )
48
- end
49
- end
50
-
51
- def complete!
52
- ext.finish(0)
53
- ext.complete
54
- end
55
- end
56
- end
@@ -1,80 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Appsignal
4
- # @api private
5
- module Rack
6
- # JavaScript error catching middleware.
7
- #
8
- # Listens to the endpoint specified in the `frontend_error_catching_path`
9
- # configuration option.
10
- #
11
- # This is automatically included middleware in Rails apps if the
12
- # `frontend_error_catching_path` configuration option is active.
13
- #
14
- # If AppSignal is not active in the current environment, but does have
15
- # JavaScript error catching turned on, we assume that a JavaScript script
16
- # still sends errors to this endpoint. When AppSignal is not active in this
17
- # scenario this middleware still listens to the endpoint, but won't record
18
- # the error. It will return HTTP status code 202.
19
- #
20
- # @example with a Sinatra app
21
- # Sinatra::Application.use(Appsignal::Rack::JSExceptionCatcher)
22
- #
23
- # @see http://docs.appsignal.com/front-end/error-handling.html
24
- # @api private
25
- class JSExceptionCatcher
26
- include Appsignal::Utils::DeprecationMessage
27
-
28
- def initialize(app, _options = nil)
29
- Appsignal.logger.debug \
30
- "Initializing Appsignal::Rack::JSExceptionCatcher"
31
- deprecation_message "The Appsignal::Rack::JSExceptionCatcher is " \
32
- "deprecated and will be removed in a future version. Please use " \
33
- "the official AppSignal JavaScript integration by disabling " \
34
- "`enable_frontend_error_catching` in your configuration and " \
35
- "installing AppSignal for JavaScript instead. " \
36
- "(https://docs.appsignal.com/front-end/)"
37
- @app = app
38
- end
39
-
40
- def call(env)
41
- # Ignore other paths than the error catching path.
42
- return @app.call(env) unless error_cathing_endpoint?(env)
43
-
44
- # Prevent raising a 404 not found when a non-active environment posts
45
- # to this endpoint.
46
- unless Appsignal.active?
47
- return [
48
- 202,
49
- {},
50
- ["AppSignal JavaScript error catching endpoint is not active."]
51
- ]
52
- end
53
-
54
- begin
55
- body = JSON.parse(env["rack.input"].read)
56
- rescue JSON::ParserError
57
- return [400, {}, ["Request payload is not valid JSON."]]
58
- end
59
-
60
- if body["name"].is_a?(String) && !body["name"].empty?
61
- transaction = JSExceptionTransaction.new(body)
62
- transaction.complete!
63
- code = 200
64
- else
65
- Appsignal.logger.debug \
66
- "JSExceptionCatcher: Could not send exception, 'name' is empty."
67
- code = 422
68
- end
69
-
70
- [code, {}, []]
71
- end
72
-
73
- private
74
-
75
- def error_cathing_endpoint?(env)
76
- env["PATH_INFO"] == Appsignal.config[:frontend_error_catching_path]
77
- end
78
- end
79
- end
80
- end
@@ -1,180 +0,0 @@
1
- require "appsignal/cli"
2
-
3
- describe Appsignal::CLI::NotifyOfDeploy do
4
- include CLIHelpers
5
-
6
- let(:out_stream) { std_stream }
7
- let(:output) { out_stream.read }
8
- let(:err_stream) { std_stream }
9
- let(:stderr) { err_stream.read }
10
-
11
- define :include_deploy_notification do
12
- match do |log|
13
- log.include?("Notifying AppSignal of deploy with: ") &&
14
- log.include?("AppSignal has been notified of this deploy!")
15
- end
16
- end
17
- define :include_deploy_notification_with do |options|
18
- match do |log|
19
- return false unless options
20
- values = "revision: #{options[:revision]}, user: #{options[:user]}"
21
- log.include?("Notifying AppSignal of deploy with: #{values}") &&
22
- log.include?("AppSignal has been notified of this deploy!")
23
- end
24
- end
25
- define :include_config_error do
26
- match do |log|
27
- log.include? "Error: No valid config found."
28
- end
29
- end
30
- define :include_missing_options do |options|
31
- match do |log|
32
- log.include? "Error: Missing options: #{options.join(", ")}"
33
- end
34
- end
35
-
36
- def run
37
- capture_std_streams(out_stream, err_stream) do
38
- run_cli("notify_of_deploy", options)
39
- end
40
- end
41
-
42
- context "without config" do
43
- let(:config) { Appsignal::Config.new(tmp_dir, "production") }
44
- let(:options) { {} }
45
- around do |example|
46
- Dir.chdir tmp_dir do
47
- example.run
48
- end
49
- end
50
-
51
- it "prints a config error" do
52
- expect { run }.to raise_error(SystemExit)
53
- expect(output).to include_config_error
54
- expect(output).to_not include_deploy_notification
55
- end
56
- end
57
-
58
- context "with config" do
59
- let(:config) { project_fixture_config }
60
- before do
61
- config[:name] = options[:name] if options[:name]
62
- stub_api_request config, "markers", :revision => options[:revision],
63
- :user => options[:user]
64
- end
65
- around do |example|
66
- Dir.chdir project_fixture_path do
67
- example.run
68
- end
69
- end
70
-
71
- context "without environment" do
72
- let(:options) { { :environment => "", :revision => "foo", :user => "thijs" } }
73
- before do
74
- # Makes the config "active"
75
- ENV["APPSIGNAL_PUSH_API_KEY"] = "foo"
76
- end
77
-
78
- it "requires environment option" do
79
- expect { run }.to raise_error(SystemExit)
80
- expect(output).to include_missing_options(%w[environment])
81
- expect(output).to_not include_deploy_notification
82
- end
83
- end
84
-
85
- context "without known environment" do
86
- let(:options) { { :environment => "foo" } }
87
-
88
- it "prints a config error" do
89
- expect { run }.to raise_error(SystemExit)
90
- expect(output).to include_config_error
91
- expect(output).to_not include_missing_options([])
92
- expect(output).to_not include_deploy_notification
93
- end
94
- end
95
-
96
- context "with known environment" do
97
- context "without required options" do
98
- let(:options) { { :environment => "production" } }
99
-
100
- it "prints a missing required options error" do
101
- expect { run }.to raise_error(SystemExit)
102
- expect(output).to_not include_config_error
103
- expect(output).to include_missing_options(%w[revision user])
104
- expect(output).to_not include_deploy_notification
105
- end
106
-
107
- context "with empty required option" do
108
- let(:options) { { :environment => "production", :revision => "foo", :user => "" } }
109
-
110
- it "prints a missing required option error" do
111
- expect { run }.to raise_error(SystemExit)
112
- expect(output).to_not include_config_error
113
- expect(output).to include_missing_options(%w[user])
114
- expect(output).to_not include_deploy_notification
115
- end
116
- end
117
- end
118
-
119
- context "with required options" do
120
- let(:options) { { :environment => "production", :revision => "aaaaa", :user => "thijs" } }
121
- let(:log_stream) { std_stream }
122
- let(:log) { log_contents(log_stream) }
123
- before { Appsignal.logger = test_logger(log_stream) }
124
-
125
- it "notifies of a deploy" do
126
- run
127
- expect(output).to_not include_config_error
128
- expect(output).to_not include_missing_options([])
129
- expect(output).to include_deploy_notification_with(options)
130
- end
131
-
132
- it "prints a deprecation message" do
133
- run
134
- deprecation_message = "This command (appsignal notify_of_deploy) has been deprecated"
135
- expect(stderr).to include("appsignal WARNING: #{deprecation_message}")
136
- expect(log).to contains_log :warn, deprecation_message
137
- end
138
-
139
- context "with no app name configured" do
140
- before { ENV["APPSIGNAL_APP_NAME"] = "" }
141
-
142
- context "without name option" do
143
- let(:options) { { :environment => "production", :revision => "aaaaa", :user => "thijs" } }
144
-
145
- it "requires name option" do
146
- expect { run }.to raise_error(SystemExit)
147
- expect(output).to_not include_config_error
148
- expect(output).to include_missing_options(%w[name])
149
- expect(output).to_not include_deploy_notification
150
- end
151
- end
152
-
153
- context "with name option" do
154
- let(:options) { { :environment => "production", :revision => "aaaaa", :user => "thijs", :name => "foo" } }
155
-
156
- it "notifies of a deploy with a custom name" do
157
- run
158
- expect(output).to_not include_config_error
159
- expect(output).to_not include_missing_options([])
160
- expect(output).to include_deploy_notification_with(options)
161
- end
162
- end
163
- end
164
-
165
- context "with name option" do
166
- let(:options) do
167
- { :environment => "production", :revision => "aaaaa", :user => "thijs", :name => "foo" }
168
- end
169
-
170
- it "notifies of a deploy with a custom name" do
171
- run
172
- expect(output).to_not include_config_error
173
- expect(output).to_not include_missing_options([])
174
- expect(output).to include_deploy_notification_with(options)
175
- end
176
- end
177
- end
178
- end
179
- end
180
- end