airbrake-ruby 2.11.0 → 2.12.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
  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