gitlab_exception_notification 0.0.1 → 1.0.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: fe39c86fd3708a6ad6b2115ab9cad7f2d8cb7bee
4
- data.tar.gz: 3043cdf0626015276cb2bb694b458d142b0044fd
3
+ metadata.gz: 5605432556fe4818136d9665283a4cdc2539df00
4
+ data.tar.gz: d6e6c3db460a7b0042426ed2effe0cd4e35d41d9
5
5
  SHA512:
6
- metadata.gz: 6347f9bd84b8acba6eef8def9248b995b02b04b41b23bf889317887468b35ee1da11adaf462aae12c0471bdd9b6d21d10f88bfc47b38423790edba2827e1415f
7
- data.tar.gz: b04eb97d33d36fbb7e3654cc103d3377b63e64ac7eef231934261fd087454f81bc9d5b4d399c5a5276eccde4c6f568e89cdfbfd30118abff1b2093e8ce08c49a
6
+ metadata.gz: e39f635df1ecf5bec0c02398b1b2ed7c2f8e1a4b65c6c47ab526df220db91d8fa433961a7c6bdbfc23846050e4aa67c94f0080f2b9ce8e88ed4e21ec32f75a43
7
+ data.tar.gz: c5455fef59681269060cd1867fe423e4dcdcc5348d0ae490e2f3d89d72e66a45e5de9b94f94a2c04e1d5fc8aec1b83fb657d53ae464b1b0dfd8997ec72fdf2b2
@@ -1,2 +1,16 @@
1
1
  module GitlabExceptionNotification
2
+
3
+ require "gitlab"
4
+ require 'digest'
5
+
6
+ REJECT_HEADERS = /HTTP_COOKIE|(rack.*)|(action_dispatch.*)/
7
+ SLINE = "
8
+ "
9
+ STAB = SLINE + " "
10
+
11
+ PER_PAGE = 40
12
+
13
+ require 'gitlab_exception_notification/issue'
14
+ require 'gitlab_exception_notification/gitlab_notifier'
15
+
2
16
  end
@@ -1,139 +1,24 @@
1
1
 
2
- require "gitlab"
3
-
4
- REJECT_HEADERS = /HTTP_COOKIE|(rack.*)|(action_dispatch.*)/
5
- SLINE = "
6
- "
7
- STAB = SLINE + " "
8
-
9
- PER_PAGE = 40
10
2
 
11
3
  module ExceptionNotifier
12
4
  class GitlabNotifier
13
5
  def initialize(options)
14
- p "Booting issue notifications"
15
- @client = Gitlab.client(endpoint: 'http://gitlab.42.fr/api/v3', private_token: options[:private_token])
16
- @project_id = @client.project_search(options[:project_name]).first.id
17
- @issues = get_all_issues
18
- end
19
-
20
- def issue_exists?(exception)
21
- @issues = get_all_issues
22
- rest = @issues.select do |i|
23
- i.title == issue_title(exception) and i.description and i.description[exception.backtrace.first]
24
- end
25
- (rest.count > 0 ? rest.first.id : false)
26
- end
27
-
28
- def get_all_issues
29
- page = 1
30
- i = @client.issues(@project_id, per_page: PER_PAGE, page: page, order_by: :updated_at)
31
- @issues = i
32
- while i.count == PER_PAGE
33
- i = @client.issues(@project_id, per_page: PER_PAGE, page: page, order_by: :updated_at)
34
- @issues += i
35
- page += 1
36
- end
37
- return @issues.flatten
38
- end
39
-
40
- def update_issue(id, exception)
41
- issue = @client.issue(@project_id, id)
42
- last = issue.updated_at.to_date
43
- if last < 1.hour.ago
44
- begin
45
- @client.edit_issue(@project_id, id, {state_event: "reopen"})
46
- iss = @client.edit_issue(@project_id, id, {title: "#{issue.title}"})
47
- rescue Exception => e
48
- p "An error occured: #{e.inspect}"
49
- end
50
- else
51
- body = ":fire: This issue occured again #{Time.current}.
52
- \n#### Summary:\n
53
- #{issue_summary(exception).map { |k, v| "- #{k}: #{v}"}.join(SLINE)}
54
- "
55
- @client.reopen_issue(@project_id, id)
56
- @client.create_issue_note @project_id, id, body
57
- end
58
- end
59
-
60
-
61
- # The issue title
62
- def issue_title exception
63
- title = []
64
- title << "#{@kontroller.controller_name}##{@kontroller.action_name}" if @kontroller
65
- title << "(#{exception.class})"
66
- title << (exception.message.length > 120 ? exception.message[0..120] + "..." : exception.message)
67
- title.join(' ')
68
- end
69
-
70
- def issue_summary exception
71
- {
72
- 'URL': @request.url,
73
- 'HTTP Method': @request.request_method,
74
- 'IP address': @request.remote_ip,
75
- 'Parameters': md_hash(@request.filtered_parameters, STAB),
76
- 'Timestamp': Time.current,
77
- 'Server': Socket.gethostname,
78
- 'Rails root': (defined?(Rails) && Rails.respond_to?(:root) ? Rails.root : nil),
79
- 'Process': $$,
80
- 'session data': md_hash(@request.session.to_hash, STAB),
81
- }
6
+ @options = options
82
7
  end
83
8
 
84
- def issue_description exception
85
-
86
- # Get a 'mardowned' backtrace
87
- m_backtrace = "```#{SLINE} #{exception.backtrace.join(SLINE)}#{SLINE}```"
88
-
89
- # Get the concerned file
90
- file = exception.backtrace.first
91
-
92
- description = ["#{exception.message} #{@kontroller ? 'in controller ' + @kontroller.controller_name + '#' + @kontroller.action_name : ''}"]
93
- description << "File: #{file}"
94
- {
95
- 'Summary': issue_summary(exception).map { |k, v| "- #{k}: #{v}"}.join(SLINE),
96
- 'session id': @request.ssl? ? "[FILTERED]" : (@request.session['session_id'] || (@request.env["rack.session.options"] and @request.env["rack.session.options"][:id])).inspect,
97
- 'data': @data,
98
- 'backtrace': m_backtrace,
99
- 'request headers': md_hash(@request.headers),
100
- 'environment': md_hash(@env.reject{|k, v| (REJECT_HEADERS =~ k).nil? })
101
- }.reject{|k, v| v.nil? or v.blank?}.each do |k, v|
102
- description << "--------------------------------"
103
- description << "#### #{k.to_s.humanize}: "
104
- description << v.to_s
105
- end
106
- description.join("\n\n")
107
- end
108
-
109
- def md_hash hash, pre = ""
110
- hash.map { |k, v| "#{pre}- **#{k}**: `#{v}`"}.join(SLINE)
111
- end
112
-
113
- def create_issue(exception)
114
- @client.create_issue(@project_id, issue_title(exception), {description: issue_description(exception)})
115
- end
116
9
 
117
10
  def exception_notification(env, exception, options={})
118
- @env = env
119
- @exception = exception
120
- @options = options.reverse_merge(env['exception_notifier.options'] || {})
121
- @kontroller = env['action_controller.instance'] || MissingController.new
122
- @request = ActionDispatch::Request.new(env)
123
- @sections = @options[:sections]
124
- @data = (env['exception_notifier.exception_data'] || {}).merge(options[:data] || {})
125
- @sections = @sections + %w(data) unless @data.empty?
126
-
127
- if issue_id = issue_exists?(exception)
128
- update_issue(issue_id, exception)
11
+ issue = GitlabExceptionNotification::Issue.new(env, exception, options)
12
+ if issue_id = issue.exists?
13
+ issue.update(issue_id)
129
14
  else
130
- create_issue(exception)
15
+ issue.create
131
16
  end
132
17
  end
133
18
 
134
19
  def call(exception, options={})
135
20
  env = options[:env] || {}
136
- exception_notification(env, exception, options)
21
+ exception_notification(env, exception, @options.merge(options))
137
22
  end
138
23
  end
139
24
  end
@@ -0,0 +1,142 @@
1
+ module GitlabExceptionNotification
2
+
3
+ class Issue
4
+
5
+ def initialize(env, exception, options={})
6
+ @env = env
7
+ @exception = exception
8
+ @options = options.reverse_merge(env['exception_notifier.options'] || {})
9
+ @kontroller = env['action_controller.instance'] || MissingController.new
10
+ @request = ActionDispatch::Request.new(env)
11
+ @data = (env['exception_notifier.exception_data'] || {}).merge(options[:data] || {})
12
+ @digest = digest
13
+ @client = Gitlab.client(endpoint: 'http://gitlab.42.fr/api/v3', private_token: options[:private_token])
14
+ @project_id = @client.project_search(options[:project_name]).first.id
15
+ @issues = self.all
16
+ end
17
+
18
+ def create
19
+ @client.create_issue(@project_id, title, {description: description})
20
+ end
21
+
22
+ def update(id)
23
+ issue = @client.issue(@project_id, id)
24
+ last = issue.updated_at.to_date
25
+ if last < 1.hour.ago
26
+ begin
27
+ @client.edit_issue(@project_id, id, {state_event: "reopen"})
28
+ iss = @client.edit_issue(@project_id, id, {title: increment_title(issue)})
29
+ rescue Exception => e
30
+ p "An error occured: #{e.inspect}"
31
+ end
32
+ else
33
+ body = ":fire: This issue occured again #{Time.current}.
34
+ \n#### Summary:\n
35
+ #{summary.map { |k, v| "- #{k}: #{v}"}.join(SLINE)}
36
+ "
37
+ @client.reopen_issue(@project_id, id)
38
+ @client.create_issue_note @project_id, id, body
39
+ end
40
+ end
41
+
42
+ def is_same_exception? issue
43
+ return false if issue.nil? or issue.description.nil?
44
+ issue.description.split(SLINE).last.strip == @digest
45
+ end
46
+
47
+ def exists?
48
+ # @issues = self.all
49
+ rest = @issues.select do |i|
50
+ is_same_exception?(i)
51
+ end
52
+ (rest.count > 0 ? rest.first.id : false)
53
+ end
54
+
55
+ def all
56
+ page = 1
57
+ i = @client.issues(@project_id, per_page: PER_PAGE, page: page, order_by: :updated_at)
58
+ @issues = i
59
+ while i.count == PER_PAGE
60
+ i = @client.issues(@project_id, per_page: PER_PAGE, page: page, order_by: :updated_at)
61
+ @issues += i
62
+ page += 1
63
+ end
64
+ return @issues.flatten
65
+ end
66
+
67
+ # =====================================================================================
68
+ # Formatters
69
+ # =====================================================================================
70
+
71
+
72
+ # The issue title
73
+ def title
74
+ t = []
75
+ t << "#{@kontroller.controller_name}##{@kontroller.action_name}" if @kontroller
76
+ t << "(#{@exception.class})"
77
+ t << (@exception.message.length > 120 ? @exception.message[0..120] + "..." : @exception.message)
78
+ t.join(' ')
79
+ end
80
+
81
+ def summary
82
+ {
83
+ 'URL': @request.url,
84
+ 'HTTP Method': @request.request_method,
85
+ 'IP address': @request.remote_ip,
86
+ 'Parameters': md_hash(@request.filtered_parameters, STAB),
87
+ 'Timestamp': Time.current,
88
+ 'Server': Socket.gethostname,
89
+ 'Rails root': (defined?(Rails) && Rails.respond_to?(:root) ? Rails.root : nil),
90
+ 'Process': $$,
91
+ 'session data': md_hash(@request.session.to_hash, STAB),
92
+ }
93
+ end
94
+
95
+ def description
96
+ # Get a 'mardowned' backtrace
97
+ m_backtrace = "```#{SLINE} #{@exception.backtrace.join(SLINE)}#{SLINE}```"
98
+
99
+ # Get the concerned file
100
+ file = @exception.backtrace.first
101
+
102
+ d = ["#{@exception.message} #{@kontroller ? 'in controller ' + @kontroller.controller_name + '#' + @kontroller.action_name : ''}"]
103
+ d << "File: #{file}"
104
+ {
105
+ 'Summary': summary.map { |k, v| "- #{k}: #{v}"}.join(SLINE),
106
+ 'session id': @request.ssl? ? "[FILTERED]" : (@request.session['session_id'] || (@request.env["rack.session.options"] and @request.env["rack.session.options"][:id])).inspect,
107
+ 'data': @data,
108
+ 'backtrace': m_backtrace,
109
+ 'request headers': md_hash(@request.headers),
110
+ 'environment': md_hash(@env.reject{|k, v| (REJECT_HEADERS =~ k).nil? })
111
+ }.reject{|k, v| v.nil? or v.blank?}.each do |k, v|
112
+ d << "--------------------------------"
113
+ d << "#### #{k.to_s.humanize}: "
114
+ d << v.to_s
115
+ end
116
+ d << @digest
117
+ d.join(SLINE)
118
+ end
119
+
120
+ # =====================================================================================
121
+ # Utilities
122
+ # =====================================================================================
123
+
124
+ protected
125
+
126
+ def increment_title issue
127
+ count = ((issue.title =~ /^\([0-9]+\).*/) == 0 ? issue.title.gsub(/^\(([0-9]*)\)(.*)/,'\1').to_i + 1 : 1)
128
+ new_title = ((issue.title =~ /^\([0-9]+\).*/) == 0 ? issue.title.gsub(/^\(([0-9]*)\)(.*)/, '(' + count.to_s + ')\2') : "(#{count.to_s}) #{issue.title}")
129
+ return new_title
130
+ end
131
+
132
+ def md_hash hash, pre = ""
133
+ hash.map { |k, v| "#{pre}- **#{k}**: `#{v}`"}.join(SLINE)
134
+ end
135
+
136
+ def digest
137
+ "EXC" + Digest::SHA256.hexdigest(@exception.to_s + @exception.backtrace.first.split(":in").first.to_s)
138
+ end
139
+
140
+
141
+ end
142
+ end
@@ -1,3 +1,3 @@
1
1
  module GitlabExceptionNotification
2
- VERSION = "0.0.1"
2
+ VERSION = "1.0.0"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gitlab_exception_notification
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andre Aubin
@@ -19,7 +19,7 @@ dependencies:
19
19
  version: '4.2'
20
20
  - - ">="
21
21
  - !ruby/object:Gem::Version
22
- version: 4.2.4
22
+ version: 4.2.0
23
23
  type: :runtime
24
24
  prerelease: false
25
25
  version_requirements: !ruby/object:Gem::Requirement
@@ -29,7 +29,27 @@ dependencies:
29
29
  version: '4.2'
30
30
  - - ">="
31
31
  - !ruby/object:Gem::Version
32
- version: 4.2.4
32
+ version: 4.2.0
33
+ - !ruby/object:Gem::Dependency
34
+ name: exception_notification
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '4.0'
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ version: 4.0.1
43
+ type: :runtime
44
+ prerelease: false
45
+ version_requirements: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - "~>"
48
+ - !ruby/object:Gem::Version
49
+ version: '4.0'
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: 4.0.1
33
53
  - !ruby/object:Gem::Dependency
34
54
  name: gitlab
35
55
  requirement: !ruby/object:Gem::Requirement
@@ -62,6 +82,7 @@ files:
62
82
  - Rakefile
63
83
  - lib/gitlab_exception_notification.rb
64
84
  - lib/gitlab_exception_notification/gitlab_notifier.rb
85
+ - lib/gitlab_exception_notification/issue.rb
65
86
  - lib/gitlab_exception_notification/version.rb
66
87
  - test/dummy/README.rdoc
67
88
  - test/dummy/Rakefile