airbrake-ruby 2.11.0 → 2.12.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
  SHA1:
3
- metadata.gz: 0a6cbbc0e744591a711d5452e6bae85c3c261607
4
- data.tar.gz: 5b35acf12d2e1f8d6fb59ce953cf93c1611953b9
3
+ metadata.gz: 3f92ef10b01c37b9374a6662bedba21c1593ef34
4
+ data.tar.gz: 957da6a830307c3d3cd9f583f1eaa9bf4a2df96f
5
5
  SHA512:
6
- metadata.gz: 85e6610457fb10e5b7247a6fdf20e694abff4b5ab94d27484078c04b8fc0f4fdf350b8da76412e55f0348a80bdca6169cc9a97c3f5e32bbf529916e02252c7e8
7
- data.tar.gz: 80a7f9beddf36c304a264edbfe2044732654f9026f6a7a82feffdba95d6859c1746cb631bd09f7400a630008794daf61f53f84ac3d9d62ea0db5b48237798a18
6
+ metadata.gz: b0ca6c8f84dfc39758f22203519765820a25c4bddd8e7aae90928d46b1f201ee7ab283679fdc16020043cd222c3a6f3e09def0642d1428d005b3022b8dfc59ee
7
+ data.tar.gz: 46f263d46199864c89c70c721ecb1c50f13262a13b9b3603b31260cd0f6ea44536fa2ceb419b3efaee1bd82b7877f53246c754288f433054c43ca3b0af3bba58
data/lib/airbrake-ruby.rb CHANGED
@@ -27,6 +27,8 @@ require 'airbrake-ruby/filters/context_filter'
27
27
  require 'airbrake-ruby/filters/exception_attributes_filter'
28
28
  require 'airbrake-ruby/filters/dependency_filter'
29
29
  require 'airbrake-ruby/filters/git_revision_filter'
30
+ require 'airbrake-ruby/filters/git_repository_filter'
31
+ require 'airbrake-ruby/filters/git_last_checkout_filter'
30
32
  require 'airbrake-ruby/filter_chain'
31
33
  require 'airbrake-ruby/notifier'
32
34
  require 'airbrake-ruby/code_hunk'
@@ -98,7 +98,7 @@ module Airbrake
98
98
 
99
99
  self.project_id = user_config[:project_id]
100
100
  self.project_key = user_config[:project_key]
101
- self.host = 'https://airbrake.io'
101
+ self.host = 'https://api.airbrake.io'
102
102
 
103
103
  self.ignore_environments = []
104
104
 
@@ -0,0 +1,88 @@
1
+ module Airbrake
2
+ module Filters
3
+ # Attaches git checkout info to `context`. The info includes:
4
+ # * username
5
+ # * email
6
+ # * revision
7
+ # * time
8
+ #
9
+ # This information is used to track deploys automatically.
10
+ #
11
+ # @api private
12
+ # @since v2.12.0
13
+ class GitLastCheckoutFilter
14
+ # @return [Integer]
15
+ attr_reader :weight
16
+
17
+ # @return [Integer] least possible amount of columns in git's `logs/HEAD`
18
+ # file (checkout information is omitted)
19
+ MIN_HEAD_COLS = 6
20
+
21
+ # @param [Logger] logger
22
+ # @param [String] root_directory
23
+ def initialize(logger, root_directory)
24
+ @git_path = File.join(root_directory, '.git')
25
+ @logger = logger
26
+ @weight = 116
27
+ @last_checkout = nil
28
+ end
29
+
30
+ # @macro call_filter
31
+ def call(notice)
32
+ return if notice[:context].key?(:lastCheckout)
33
+
34
+ if @last_checkout
35
+ notice[:context][:lastCheckout] = @last_checkout
36
+ return
37
+ end
38
+
39
+ return unless File.exist?(@git_path)
40
+ return unless (checkout = last_checkout)
41
+ notice[:context][:lastCheckout] = checkout
42
+ end
43
+
44
+ private
45
+
46
+ def last_checkout
47
+ return unless (line = last_checkout_line)
48
+
49
+ parts = line.chomp.split("\t").first.split(' ')
50
+ if parts.size < MIN_HEAD_COLS
51
+ @logger.error(
52
+ "#{LOG_LABEL} Airbrake::#{self.class.name}: can't parse line: #{line}"
53
+ )
54
+ return
55
+ end
56
+
57
+ author = parts[2..-4]
58
+ @last_checkout = {
59
+ username: author[0..1].join(' '),
60
+ email: parts[-3][1..-2],
61
+ revision: parts[1],
62
+ time: timestamp(parts[-2].to_i)
63
+ }
64
+ end
65
+
66
+ def last_checkout_line
67
+ head_path = File.join(@git_path, 'logs', 'HEAD')
68
+ return unless File.exist?(head_path)
69
+
70
+ last_line = nil
71
+ IO.foreach(head_path) do |line|
72
+ last_line = line if checkout_line?(line)
73
+ end
74
+ last_line
75
+ end
76
+
77
+ def checkout_line?(line)
78
+ line.include?("\tclone:") ||
79
+ line.include?("\tpull:") ||
80
+ line.include?("\tcheckout:")
81
+ end
82
+
83
+ def timestamp(utime)
84
+ Time.at(utime).to_datetime.rfc3339
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,34 @@
1
+ module Airbrake
2
+ module Filters
3
+ # Attaches git repository URL to `context`.
4
+ # @api private
5
+ # @since v2.12.0
6
+ class GitRepositoryFilter
7
+ # @return [Integer]
8
+ attr_reader :weight
9
+
10
+ # @param [String] root_directory
11
+ def initialize(root_directory)
12
+ @git_path = File.join(root_directory, '.git')
13
+ @repository = nil
14
+ @weight = 116
15
+ end
16
+
17
+ # @macro call_filter
18
+ def call(notice)
19
+ return if notice[:context].key?(:repository)
20
+
21
+ if @repository
22
+ notice[:context][:repository] = @repository
23
+ return
24
+ end
25
+
26
+ return unless File.exist?(@git_path)
27
+
28
+ @repository = `cd #{@git_path} && git remote get-url origin`.chomp
29
+ return unless @repository
30
+ notice[:context][:repository] = @repository
31
+ end
32
+ end
33
+ end
34
+ end
@@ -13,16 +13,24 @@ module Airbrake
13
13
  # @param [String] root_directory
14
14
  def initialize(root_directory)
15
15
  @git_path = File.join(root_directory, '.git')
16
+ @revision = nil
16
17
  @weight = 116
17
18
  end
18
19
 
19
20
  # @macro call_filter
20
21
  def call(notice)
21
22
  return if notice[:context].key?(:revision)
23
+
24
+ if @revision
25
+ notice[:context][:revision] = @revision
26
+ return
27
+ end
28
+
22
29
  return unless File.exist?(@git_path)
23
30
 
24
- revision = find_revision
25
- notice[:context][:revision] = revision if revision
31
+ @revision = find_revision
32
+ return unless @revision
33
+ notice[:context][:revision] = @revision
26
34
  end
27
35
 
28
36
  private
@@ -76,9 +76,12 @@ module Airbrake
76
76
  # @macro see_public_api_method
77
77
  def create_deploy(deploy_params)
78
78
  deploy_params[:environment] ||= @config.environment
79
- path = "api/v4/projects/#{@config.project_id}/deploys?key=#{@config.project_key}"
80
79
  promise = Airbrake::Promise.new
81
- @sync_sender.send(deploy_params, promise, URI.join(@config.host, path))
80
+ @sync_sender.send(
81
+ deploy_params,
82
+ promise,
83
+ URI.join(@config.host, "api/v4/projects/#{@config.project_id}/deploys")
84
+ )
82
85
  promise
83
86
  end
84
87
 
@@ -115,8 +118,8 @@ module Airbrake
115
118
  end
116
119
 
117
120
  notice = build_notice(exception, params)
121
+ yield notice if block_given?
118
122
  @filter_chain.refine(notice)
119
- yield notice if block_given? && !notice.ignored?
120
123
 
121
124
  return promise.reject("#{notice} was marked as ignored") if notice.ignored?
122
125
 
@@ -163,12 +166,16 @@ module Airbrake
163
166
  )
164
167
 
165
168
  return unless (root_directory = @config.root_directory)
166
- @filter_chain.add_filter(
167
- Airbrake::Filters::RootDirectoryFilter.new(root_directory)
168
- )
169
+ [
170
+ Airbrake::Filters::RootDirectoryFilter,
171
+ Airbrake::Filters::GitRevisionFilter,
172
+ Airbrake::Filters::GitRepositoryFilter
173
+ ].each do |filter|
174
+ @filter_chain.add_filter(filter.new(root_directory))
175
+ end
169
176
 
170
177
  @filter_chain.add_filter(
171
- Airbrake::Filters::GitRevisionFilter.new(root_directory)
178
+ Airbrake::Filters::GitLastCheckoutFilter.new(@config.logger, root_directory)
172
179
  )
173
180
  end
174
181
  # rubocop:enable Metrics/AbcSize
@@ -2,5 +2,5 @@
2
2
  # More information: http://semver.org/
3
3
  module Airbrake
4
4
  # @return [String] the library version
5
- AIRBRAKE_RUBY_VERSION = '2.11.0'.freeze
5
+ AIRBRAKE_RUBY_VERSION = '2.12.0'.freeze
6
6
  end
data/spec/config_spec.rb CHANGED
@@ -30,7 +30,7 @@ RSpec.describe Airbrake::Config do
30
30
  end
31
31
 
32
32
  it "sets the default host" do
33
- expect(config.host).to eq('https://airbrake.io')
33
+ expect(config.host).to eq('https://api.airbrake.io')
34
34
  end
35
35
 
36
36
  it "sets the default endpoint" do
@@ -0,0 +1,48 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Airbrake::Filters::GitLastCheckoutFilter do
4
+ subject { described_class.new(Logger.new(STDOUT), '.') }
5
+
6
+ let(:notice) do
7
+ Airbrake::Notice.new(Airbrake::Config.new, AirbrakeTestError.new)
8
+ end
9
+
10
+ context "when context/lastCheckout is defined" do
11
+ it "doesn't attach anything to context/lastCheckout" do
12
+ notice[:context][:lastCheckout] = '123'
13
+ subject.call(notice)
14
+ expect(notice[:context][:lastCheckout]).to eq('123')
15
+ end
16
+ end
17
+
18
+ context "when .git directory doesn't exist" do
19
+ subject { described_class.new(Logger.new(STDOUT), 'root/dir') }
20
+
21
+ it "doesn't attach anything to context/lastCheckout" do
22
+ subject.call(notice)
23
+ expect(notice[:context][:lastCheckout]).to be_nil
24
+ end
25
+ end
26
+
27
+ context "when .git directory exists" do
28
+ before { subject.call(notice) }
29
+
30
+ it "attaches last checkouted username" do
31
+ expect(notice[:context][:lastCheckout][:username]).not_to be_empty
32
+ end
33
+
34
+ it "attaches last checkouted email" do
35
+ expect(notice[:context][:lastCheckout][:email]).to match(/\A\w+@\w+\.?\w+?\z/)
36
+ end
37
+
38
+ it "attaches last checkouted revision" do
39
+ expect(notice[:context][:lastCheckout][:revision]).not_to be_empty
40
+ expect(notice[:context][:lastCheckout][:revision].size).to eq(40)
41
+ end
42
+
43
+ it "attaches last checkouted time" do
44
+ expect(notice[:context][:lastCheckout][:time]).not_to be_empty
45
+ expect(notice[:context][:lastCheckout][:time].size).to eq(25)
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,35 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Airbrake::Filters::GitRepositoryFilter do
4
+ subject { described_class.new('.') }
5
+
6
+ let(:notice) do
7
+ Airbrake::Notice.new(Airbrake::Config.new, AirbrakeTestError.new)
8
+ end
9
+
10
+ context "when context/repository is defined" do
11
+ it "doesn't attach anything to context/repository" do
12
+ notice[:context][:repository] = 'git@github.com:kyrylo/test.git'
13
+ subject.call(notice)
14
+ expect(notice[:context][:repository]).to eq('git@github.com:kyrylo/test.git')
15
+ end
16
+ end
17
+
18
+ context "when .git directory doesn't exist" do
19
+ subject { described_class.new('root/dir') }
20
+
21
+ it "doesn't attach anything to context/repository" do
22
+ subject.call(notice)
23
+ expect(notice[:context][:repository]).to be_nil
24
+ end
25
+ end
26
+
27
+ context "when .git directory exists" do
28
+ it "attaches context/repository" do
29
+ subject.call(notice)
30
+ expect(notice[:context][:repository]).to eq(
31
+ 'ssh://git@github.com/airbrake/airbrake-ruby.git'
32
+ )
33
+ end
34
+ end
35
+ end
@@ -95,7 +95,7 @@ RSpec.describe Airbrake::Notifier do
95
95
  end
96
96
 
97
97
  describe "#notify" do
98
- let(:endpoint) { 'https://airbrake.io/api/v3/projects/1/notices' }
98
+ let(:endpoint) { 'https://api.airbrake.io/api/v3/projects/1/notices' }
99
99
 
100
100
  subject { described_class.new(user_params) }
101
101
 
@@ -129,12 +129,12 @@ RSpec.describe Airbrake::Notifier do
129
129
  end
130
130
  end
131
131
 
132
- context "when a notice is ignored" do
132
+ context "when a notice is ignored via a filter" do
133
133
  before { subject.add_filter(&:ignore!) }
134
134
 
135
- it "doesn't yield the notice" do
135
+ it "yields the notice" do
136
136
  expect { |b| subject.notify('ex', &b) }
137
- .not_to yield_with_args(Airbrake::Notice)
137
+ .to yield_with_args(Airbrake::Notice)
138
138
  end
139
139
 
140
140
  it "returns a rejected promise" do
@@ -143,6 +143,14 @@ RSpec.describe Airbrake::Notifier do
143
143
  end
144
144
  end
145
145
 
146
+ context "when a notice is ignored via an inline filter" do
147
+ before { subject.add_filter { raise AirbrakeTestError } }
148
+
149
+ it "doesn't invoke regular filters" do
150
+ expect { subject.notify('ex', &:ignore!) }.not_to raise_error
151
+ end
152
+ end
153
+
146
154
  context "when async sender has workers" do
147
155
  it "sends an exception asynchronously" do
148
156
  expect_any_instance_of(Airbrake::AsyncSender).to receive(:send)
@@ -179,7 +187,7 @@ RSpec.describe Airbrake::Notifier do
179
187
  end
180
188
 
181
189
  describe "#notify_sync" do
182
- let(:endpoint) { 'https://airbrake.io/api/v3/projects/1/notices' }
190
+ let(:endpoint) { 'https://api.airbrake.io/api/v3/projects/1/notices' }
183
191
 
184
192
  let(:user_params) do
185
193
  { project_id: 1, project_key: 'abc', logger: Logger.new('/dev/null') }
@@ -221,12 +229,12 @@ RSpec.describe Airbrake::Notifier do
221
229
  end
222
230
  end
223
231
 
224
- context "when a notice is ignored" do
232
+ context "when a notice is ignored via a filter" do
225
233
  before { subject.add_filter(&:ignore!) }
226
234
 
227
- it "doesn't yield the notice" do
235
+ it "yields the notice" do
228
236
  expect { |b| subject.notify_sync('ex', &b) }
229
- .not_to yield_with_args(Airbrake::Notice)
237
+ .to yield_with_args(Airbrake::Notice)
230
238
  end
231
239
 
232
240
  it "returns an error hash" do
@@ -235,6 +243,14 @@ RSpec.describe Airbrake::Notifier do
235
243
  end
236
244
  end
237
245
 
246
+ context "when a notice is ignored via an inline filter" do
247
+ before { subject.add_filter { raise AirbrakeTestError } }
248
+
249
+ it "doesn't invoke regular filters" do
250
+ expect { subject.notify('ex', &:ignore!) }.not_to raise_error
251
+ end
252
+ end
253
+
238
254
  context "when the provided environment is ignored" do
239
255
  subject do
240
256
  described_class.new(
@@ -393,7 +409,7 @@ RSpec.describe Airbrake::Notifier do
393
409
 
394
410
  describe "#create_deploy" do
395
411
  it "returns a promise" do
396
- stub_request(:post, "https://airbrake.io/api/v4/projects/1/deploys?key=abc")
412
+ stub_request(:post, 'https://api.airbrake.io/api/v4/projects/1/deploys')
397
413
  .to_return(status: 201, body: '')
398
414
  expect(subject.create_deploy({})).to be_an(Airbrake::Promise)
399
415
  end
@@ -403,7 +419,7 @@ RSpec.describe Airbrake::Notifier do
403
419
  expect_any_instance_of(Airbrake::SyncSender).to receive(:send).with(
404
420
  { environment: 'barenv' },
405
421
  instance_of(Airbrake::Promise),
406
- URI('https://airbrake.io/api/v4/projects/1/deploys?key=abc')
422
+ URI('https://api.airbrake.io/api/v4/projects/1/deploys')
407
423
  )
408
424
  described_class.new(
409
425
  user_params.merge(environment: 'fooenv')
@@ -416,7 +432,7 @@ RSpec.describe Airbrake::Notifier do
416
432
  expect_any_instance_of(Airbrake::SyncSender).to receive(:send).with(
417
433
  { environment: 'fooenv' },
418
434
  instance_of(Airbrake::Promise),
419
- URI('https://airbrake.io/api/v4/projects/1/deploys?key=abc')
435
+ URI('https://api.airbrake.io/api/v4/projects/1/deploys')
420
436
  )
421
437
  subject.create_deploy(environment: 'fooenv')
422
438
  end
@@ -10,7 +10,7 @@ RSpec.describe Airbrake::Notifier do
10
10
  let(:localhost) { 'http://localhost:8080' }
11
11
 
12
12
  let(:endpoint) do
13
- "https://airbrake.io/api/v3/projects/#{project_id}/notices"
13
+ "https://api.airbrake.io/api/v3/projects/#{project_id}/notices"
14
14
  end
15
15
 
16
16
  let(:airbrake_params) do
@@ -25,7 +25,7 @@ RSpec.describe Airbrake::SyncSender do
25
25
 
26
26
  let(:sender) { described_class.new(config) }
27
27
  let(:notice) { Airbrake::Notice.new(config, AirbrakeTestError.new) }
28
- let(:endpoint) { 'https://airbrake.io/api/v3/projects/1/notices' }
28
+ let(:endpoint) { 'https://api.airbrake.io/api/v3/projects/1/notices' }
29
29
 
30
30
  before { stub_request(:post, endpoint).to_return(body: '{}') }
31
31
 
@@ -90,7 +90,7 @@ RSpec.describe Airbrake::SyncSender do
90
90
  end
91
91
 
92
92
  context "when IP is rate limited" do
93
- let(:endpoint) { %r{https://airbrake.io/api/v3/projects/1/notices} }
93
+ let(:endpoint) { %r{https://api.airbrake.io/api/v3/projects/1/notices} }
94
94
 
95
95
  before do
96
96
  stub_request(:post, endpoint).to_return(
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: airbrake-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.11.0
4
+ version: 2.12.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Airbrake Technologies, Inc.
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-06-27 00:00:00.000000000 Z
11
+ date: 2018-10-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec
@@ -127,6 +127,8 @@ files:
127
127
  - lib/airbrake-ruby/filters/dependency_filter.rb
128
128
  - lib/airbrake-ruby/filters/exception_attributes_filter.rb
129
129
  - lib/airbrake-ruby/filters/gem_root_filter.rb
130
+ - lib/airbrake-ruby/filters/git_last_checkout_filter.rb
131
+ - lib/airbrake-ruby/filters/git_repository_filter.rb
130
132
  - lib/airbrake-ruby/filters/git_revision_filter.rb
131
133
  - lib/airbrake-ruby/filters/keys_blacklist.rb
132
134
  - lib/airbrake-ruby/filters/keys_filter.rb
@@ -154,6 +156,8 @@ files:
154
156
  - spec/filters/dependency_filter_spec.rb
155
157
  - spec/filters/exception_attributes_filter_spec.rb
156
158
  - spec/filters/gem_root_filter_spec.rb
159
+ - spec/filters/git_last_checkout_filter_spec.rb
160
+ - spec/filters/git_repository_filter.rb
157
161
  - spec/filters/git_revision_filter_spec.rb
158
162
  - spec/filters/keys_blacklist_spec.rb
159
163
  - spec/filters/keys_whitelist_spec.rb
@@ -210,9 +214,11 @@ test_files:
210
214
  - spec/filters/thread_filter_spec.rb
211
215
  - spec/filters/dependency_filter_spec.rb
212
216
  - spec/filters/context_filter_spec.rb
217
+ - spec/filters/git_last_checkout_filter_spec.rb
213
218
  - spec/filters/git_revision_filter_spec.rb
214
219
  - spec/filters/keys_blacklist_spec.rb
215
220
  - spec/filters/gem_root_filter_spec.rb
221
+ - spec/filters/git_repository_filter.rb
216
222
  - spec/spec_helper.rb
217
223
  - spec/notice_spec.rb
218
224
  - spec/config_spec.rb