shamebot 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.
@@ -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: []