shamebot 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 4b2ee07b476080c883e634f018cb592c6711bdd2
4
+ data.tar.gz: 5ada437031bfef93ae685e07b493881d320516ca
5
+ SHA512:
6
+ metadata.gz: c0197735f7035b1ba8b4e74d92da55274ac6a3044115aa0115d27f5312181720fb4a6082c576dc47c44f379f0faee22861d501d04d1239bf02eb735b86b1bc76
7
+ data.tar.gz: 4794f144c0b6b8dc9169ebee977d0f4d39de63e7b3aad568e85ead6f57a1e3f84d2ee29eaf6c52b7ff66f8063465587558807ae6a58b161a8995bdcc9a66eac3
@@ -0,0 +1,13 @@
1
+ *~
2
+ *.gem
3
+ *.out
4
+ *.pid
5
+ *.swp
6
+ *.json
7
+ *.log
8
+ .chef
9
+ .kitchen
10
+ .vagrant
11
+ .DS_Store
12
+ pkg
13
+ doc
data/Gemfile ADDED
@@ -0,0 +1,15 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ group :development do
6
+ gem 'pry'
7
+ gem 'rake'
8
+ gem 'yard'
9
+ gem 'version'
10
+ gem 'rubygems-tasks'
11
+ end
12
+
13
+ group :test do
14
+ gem 'minitest'
15
+ end
@@ -0,0 +1,58 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ shamebot (0.0.0)
5
+ gitlab (~> 3.1.0)
6
+ hipchat (~> 1)
7
+ sinatra (~> 1)
8
+ sys-proctable (~> 0)
9
+ trollop (~> 2)
10
+
11
+ GEM
12
+ remote: https://rubygems.org/
13
+ specs:
14
+ coderay (1.1.0)
15
+ gitlab (3.1.0)
16
+ httparty
17
+ terminal-table
18
+ hipchat (1.4.0)
19
+ httparty
20
+ httparty (0.13.3)
21
+ json (~> 1.8)
22
+ multi_xml (>= 0.5.2)
23
+ json (1.8.1)
24
+ method_source (0.8.2)
25
+ minitest (5.5.0)
26
+ multi_xml (0.5.5)
27
+ pry (0.10.1)
28
+ coderay (~> 1.1.0)
29
+ method_source (~> 0.8.1)
30
+ slop (~> 3.4)
31
+ rack (1.6.0)
32
+ rack-protection (1.5.3)
33
+ rack
34
+ rake (10.4.2)
35
+ rubygems-tasks (0.2.4)
36
+ sinatra (1.4.5)
37
+ rack (~> 1.4)
38
+ rack-protection (~> 1.4)
39
+ tilt (~> 1.3, >= 1.3.4)
40
+ slop (3.6.0)
41
+ sys-proctable (0.9.4)
42
+ terminal-table (1.4.5)
43
+ tilt (1.4.1)
44
+ trollop (2.0)
45
+ version (1.0.0)
46
+ yard (0.8.7.6)
47
+
48
+ PLATFORMS
49
+ ruby
50
+
51
+ DEPENDENCIES
52
+ minitest
53
+ pry
54
+ rake
55
+ rubygems-tasks
56
+ shamebot!
57
+ version
58
+ yard
@@ -0,0 +1,33 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ require 'rake'
4
+
5
+
6
+ require 'rake/testtask'
7
+ Rake::TestTask.new(:test) do |test|
8
+ test.libs << 'lib' << 'test'
9
+ test.test_files = FileList['test/test*.rb']
10
+ test.verbose = true
11
+ end
12
+
13
+ task :default => :test
14
+
15
+
16
+ require 'yard'
17
+ YARD::Rake::YardocTask.new do |t|
18
+ t.files = %w[ --readme Readme.md lib/**/*.rb - VERSION ]
19
+ end
20
+
21
+
22
+ require 'rubygems/tasks'
23
+ Gem::Tasks.new({
24
+ push: true,
25
+ sign: {}
26
+ }) do |tasks|
27
+ tasks.console.command = 'pry'
28
+ end
29
+ Gem::Tasks::Sign::Checksum.new sha2: true
30
+
31
+
32
+ require 'rake/version_task'
33
+ Rake::VersionTask.new
@@ -0,0 +1,23 @@
1
+ # Shamebot
2
+
3
+ Shame users who commit with bad messages.
4
+
5
+ $ cat config.json
6
+ {
7
+ "nick": "Shame (bot)",
8
+ "room": "YOUR_ROOM",
9
+ "gitlab": {
10
+ "endpoint": "GITLAB_V3_API_ENDPOINT",
11
+ "private_token": "GITLAB_PRIVATE_TOKEN",
12
+ "user_agent": "shamebot"
13
+ },
14
+ "hipchat": {
15
+ "auth_token": "HIPCHAT_V1_API_TOKEN",
16
+ "auth_token_v2": "HIPCHAT_V2_API_TOKEN"
17
+ },
18
+ "jira": {
19
+ "user": "JIRA_USERNAME",
20
+ "pass": "JIRA_PASSWORD"
21
+ }
22
+ }
23
+ $ shamebot config.json
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 1.0.0
@@ -0,0 +1,186 @@
1
+ #!/usr/bin/env ruby
2
+ require 'json'
3
+ require 'date'
4
+ require 'time'
5
+ require 'logger'
6
+
7
+ require 'trollop'
8
+
9
+ Opts = Trollop::options do
10
+ version Shamebot::VERSION
11
+ banner Shamebot::ART + "\n\n" + <<-EOS.gsub(/^ /, '')
12
+ #{Shamebot::SUMMARY}
13
+
14
+ Usage: shamebot [<options>]
15
+
16
+ Options:
17
+ EOS
18
+ opt :config, 'Path to configuration', type: :string, required: true, short: 'c'
19
+ opt :debug, 'Enable debugging', default: false, short: 'd'
20
+ opt :port, 'Port to listen on', default: 4567, short: 'p'
21
+ opt :env, 'Sinatra environment', default: 'production', short: 'e'
22
+ opt :bind, 'Interface to listen on', default: '0.0.0.0', short: 'b'
23
+ end
24
+
25
+ DEBUG = Opts[:debug]
26
+ CONFIG = JSON.parse File.read(Opts[:config])
27
+
28
+ unless DEBUG
29
+ at_exit do
30
+ puts "Writing back to '#{Opts[:config]}'"
31
+ File.open(Opts[:config], 'w') do |f|
32
+ f.puts JSON.pretty_generate(CONFIG)
33
+ end
34
+ end
35
+ end
36
+
37
+
38
+
39
+
40
+ # Part 1. Setup configuration from JSON file
41
+ JIRA_TAG = /\b([a-z]{2,4})\-(\d+)\b/i
42
+ MERGE_COMMIT = /\b(merge|merging)\b/i
43
+
44
+ def die message
45
+ $stderr.puts message
46
+ exit 1
47
+ end
48
+
49
+ unless CONFIG.has_key?('hipchat') && CONFIG['hipchat'].has_key?('auth_token')
50
+ die "Error: 'hipchat:auth_token' required in configuration"
51
+ end
52
+
53
+
54
+ GITLAB = {
55
+ 'endpoint' => (CONFIG['gitlab']['endpoint'] rescue die("Error: 'gitlab:endpoint' required in configuration")),
56
+ 'private_token' => (CONFIG['gitlab']['private_token'] rescue die("Error: 'gitlab:private_token' required in configuration")),
57
+ 'user_agent' => (CONFIG['gitlab']['user_agent'] rescue die("Error: 'gitlab:user_agent' required in configuration"))
58
+ }
59
+ BOTNAME = CONFIG.has_key?('nick') ? CONFIG['nick'] : 'shamebot'
60
+ ROOM = CONFIG.has_key?('room') ? CONFIG['room'] : 'test'
61
+
62
+ TEMPLATES = CONFIG.has_key?('templates') ? CONFIG['templates'] : [
63
+ '%{name}, shame on you for not including a valid JIRA tag in your commit message! %{urls}'
64
+ ]
65
+
66
+ def random_template
67
+ TEMPLATES[rand(0...TEMPLATES.length)]
68
+ end
69
+
70
+ CONFIG['shamings'] = CONFIG.has_key?('shamings') ? CONFIG['shamings'] : {}
71
+
72
+
73
+
74
+
75
+ # Part 2. Get connected to Hipchat and Gitlab
76
+ require 'hipchat'
77
+ require 'gitlab'
78
+
79
+ HIPCHAT = HipChat::Client.new CONFIG['hipchat']['auth_token']
80
+
81
+ Gitlab.configure do |config|
82
+ config.endpoint = GITLAB['endpoint']
83
+ config.private_token = GITLAB['private_token']
84
+ config.user_agent = GITLAB['user_agent']
85
+ end
86
+
87
+ def shame_user msg, args, room, hipchat, botname
88
+ hipchat[room].send(botname, msg % args, {
89
+ :notify => true,
90
+ :color => 'red',
91
+ :message_format => 'text'
92
+ })
93
+ end
94
+
95
+
96
+
97
+
98
+ # Part 3. Take requests from Gitlab WebHooks
99
+ require 'sinatra'
100
+
101
+ set :port, Opts[:port]
102
+ set :environment, Opts[:env]
103
+ set :bind, Opts[:bind]
104
+ set :raise_errors, true
105
+ set :dump_errors, true
106
+ set :show_exceptions, true
107
+ set :logging, ::Logger::DEBUG if DEBUG
108
+
109
+ get '/' do
110
+ content_type :text
111
+ "shamebot #{Shamebot::VERSION}"
112
+ end
113
+
114
+ post '/' do
115
+ now = Time.now
116
+ last_half_hour = now - 30 * 60
117
+ request.body.rewind
118
+ data = JSON.parse request.body.read
119
+
120
+ # Bad commits don't contain JIRA tags...
121
+ # And they aren't merge commits
122
+ commits = data['commits'].delete_if { |c| c['message'] =~ MERGE_COMMIT }
123
+ good_commits, bad_commits = commits.partition { |c| c['message'] =~ JIRA_TAG }
124
+
125
+ # Good commits might contain bogus JIRA tags
126
+ bad_commits += good_commits.keep_if do |c|
127
+ good_tag = false
128
+ c['message'].scan(JIRA_TAG).each do |(project, number)|
129
+ tag = '%s-%d' % [project.upcase, number.to_i]
130
+ tag_page = `curl --silent http://jira.bluejeansnet.com/browse/#{tag}` rescue ''
131
+ next if tag_page =~ /The issue you are trying to view does not exist/i
132
+ good_tag = true
133
+ break
134
+ end
135
+ !good_tag
136
+ end
137
+
138
+ # Grab user info from Gitlab
139
+ begin
140
+ user = Gitlab.user(data['user_id'].to_i)
141
+ rescue
142
+ $stderr.puts "Warning: Gitlab user for commit author not found"
143
+ return
144
+ end
145
+
146
+ # For whatever reason we get a lot of duplicate POSTs, so we need to
147
+ # keep track of which commits have already triggered shamings so we
148
+ # don't end up repeatedly shaming users for the same mistake. We also
149
+ # make sure the commits aren't too old.
150
+ new_bad_commits = []
151
+ bad_commits.each do |commit|
152
+ unless DEBUG
153
+ next if CONFIG['shamings'].has_key? commit['id']
154
+ commit_time = DateTime.strptime(commit['timestamp']).to_time
155
+ next unless commit_time >= last_half_hour
156
+ end
157
+ new_bad_commits << commit
158
+ CONFIG['shamings'][commit['id']] = true
159
+ end
160
+
161
+ # Shame the user with a random template
162
+ commit_ids = new_bad_commits.map { |c| c['id'] }
163
+ commit_urls = new_bad_commits.map { |c| c['url'] }
164
+ puts "%s: %s" % [ user.username, commit_ids.inspect ]
165
+
166
+ hipchat_users = []
167
+ 0.upto(5) do |i|
168
+ hipchat_users += HTTParty.get("https://api.hipchat.com/v2/user", :query => { 'start-index' => 100 * i, :start_index => 100 * i, 'max-results' => 100, :max_results => 1000, :auth_token => CONFIG['hipchat']['auth_token_v2'] })['items']
169
+ end
170
+
171
+ hipchat_user = hipchat_users.select { |u| u['name'] =~ /#{user.name}/i }
172
+ $stderr.puts hipchat_user
173
+
174
+ if hipchat_user.empty?
175
+ hipchat_user = user.name
176
+ else
177
+ hipchat_user = '@' + hipchat_user.first['mention_name']
178
+ end
179
+
180
+ shame_user(random_template, {
181
+ :name => hipchat_user,
182
+ :nick => user.username,
183
+ :urls => commit_urls.join(' ')
184
+ }, ROOM, HIPCHAT, BOTNAME) unless commit_ids.empty? || DEBUG
185
+ return commit_ids.inspect
186
+ end
@@ -0,0 +1 @@
1
+ require_relative 'shamebot/metadata'
@@ -0,0 +1,26 @@
1
+ module Shamebot
2
+
3
+ # We use a VERSION file to tie into our build pipeline
4
+ VERSION = File.read(File.join(File.dirname(__FILE__), '..', '..', 'VERSION')).strip
5
+
6
+ # We don't really do all that much, be humble
7
+ SUMMARY = 'Shame users who commit with bad messages'
8
+
9
+ # Your benevolent dictator for life
10
+ AUTHOR = 'Sean Clemmer'
11
+
12
+ # Turn here to strangle your dictator
13
+ EMAIL = 'sclemmer@bluejeans.com'
14
+
15
+ # Every project deserves its own ASCII art
16
+ ART = <<-'EOART' % VERSION
17
+
18
+ _____ __ __ ____ ___ ___ ___ ____ ___ ______
19
+ / ___/| | | / || | | / _]| \ / \ | |
20
+ ( \_ | | || o || _ _ | / [_ | o )| || |
21
+ \__ || _ || || \_/ || _]| || O ||_| |_|
22
+ / \ || | || _ || | || [_ | O || | | |
23
+ \ || | || | || | || || || | | |
24
+ \___||__|__||__|__||___|___||_____||_____| \___/ |__| v%s
25
+ EOART
26
+ end
@@ -0,0 +1,24 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path(File.join('..', 'lib'), __FILE__)
3
+ require 'shamebot'
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = 'shamebot'
7
+ s.version = Shamebot::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.author = Shamebot::AUTHOR
10
+ s.email = Shamebot::EMAIL
11
+ s.summary = Shamebot::SUMMARY
12
+ s.description = Shamebot::SUMMARY + '.'
13
+
14
+ s.add_runtime_dependency 'sinatra', '~> 1'
15
+ s.add_runtime_dependency 'hipchat', '~> 1'
16
+ s.add_runtime_dependency 'gitlab', '~> 3.1.0'
17
+ s.add_runtime_dependency 'trollop', '~> 2'
18
+ s.add_runtime_dependency 'sys-proctable', '~> 0'
19
+
20
+ s.files = `git ls-files`.split("\n")
21
+ s.test_files = `git ls-files -- test/*`.split("\n")
22
+ s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File::basename(f) }
23
+ s.require_paths = %w[ lib ]
24
+ end
metadata ADDED
@@ -0,0 +1,123 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: shamebot
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Sean Clemmer
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-12-23 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: sinatra
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1'
27
+ - !ruby/object:Gem::Dependency
28
+ name: hipchat
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1'
41
+ - !ruby/object:Gem::Dependency
42
+ name: gitlab
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 3.1.0
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 3.1.0
55
+ - !ruby/object:Gem::Dependency
56
+ name: trollop
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '2'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '2'
69
+ - !ruby/object:Gem::Dependency
70
+ name: sys-proctable
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ description: Shame users who commit with bad messages.
84
+ email: sclemmer@bluejeans.com
85
+ executables:
86
+ - shamebot
87
+ extensions: []
88
+ extra_rdoc_files: []
89
+ files:
90
+ - ".gitignore"
91
+ - Gemfile
92
+ - Gemfile.lock
93
+ - Rakefile
94
+ - Readme.md
95
+ - VERSION
96
+ - bin/shamebot
97
+ - lib/shamebot.rb
98
+ - lib/shamebot/metadata.rb
99
+ - shamebot.gemspec
100
+ homepage:
101
+ licenses: []
102
+ metadata: {}
103
+ post_install_message:
104
+ rdoc_options: []
105
+ require_paths:
106
+ - lib
107
+ required_ruby_version: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - ">="
110
+ - !ruby/object:Gem::Version
111
+ version: '0'
112
+ required_rubygems_version: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - ">="
115
+ - !ruby/object:Gem::Version
116
+ version: '0'
117
+ requirements: []
118
+ rubyforge_project:
119
+ rubygems_version: 2.2.2
120
+ signing_key:
121
+ specification_version: 4
122
+ summary: Shame users who commit with bad messages
123
+ test_files: []