idlepattern-bullhorn 0.1.2.1
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 +7 -0
- data/bin/bullhorn-client +94 -0
- data/bin/bullhorn-server +144 -0
- metadata +188 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 0e5c89e7de833de68c8545311336d7664ba64090
|
4
|
+
data.tar.gz: be9c01f568b51b0155cb500b90b9d7f29d39f0bb
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: c5f7a3b0e6f4716eaeae4c9645f67199b77b7f32f769735313f8e35665c1f0e3f632034b58e06c4b0d973991321c83fd17355a9d0e238da0613784147b346460
|
7
|
+
data.tar.gz: c1884531d2314e50c168cfe36fa3e8317f6ebf0205a5b8c486e587ebac56be4b86f5e9ef549416263432ce61aa62df575ddfb817db5ca8fbee61e05c97b804c7
|
data/bin/bullhorn-client
ADDED
@@ -0,0 +1,94 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'eventmachine'
|
5
|
+
require 'em-eventsource'
|
6
|
+
require 'json'
|
7
|
+
require 'daemons'
|
8
|
+
require 'socket'
|
9
|
+
require 'yaml'
|
10
|
+
require 'uri'
|
11
|
+
|
12
|
+
DEFAULT_OPTIONS = {
|
13
|
+
'bullhorn' => 'http://localhost:9292',
|
14
|
+
'repos' => [
|
15
|
+
]
|
16
|
+
}
|
17
|
+
|
18
|
+
HOSTNAME = Socket.gethostname
|
19
|
+
|
20
|
+
def load_config
|
21
|
+
cfg_file = '/etc/bullhorn.yml' if File.exists? '/etc/bullhorn.yml'
|
22
|
+
cfg_file = File.expand_path('~/.bullhorn.yml') if File.exists? File.expand_path('~/.bullhorn.yml')
|
23
|
+
cfg_file = 'bullhorn.yml' if File.exists? 'bullhorn.yml'
|
24
|
+
raise 'No configuration file found' unless cfg_file
|
25
|
+
|
26
|
+
config = YAML.load_file(cfg_file) rescue nil
|
27
|
+
@options = DEFAULT_OPTIONS.update(config)
|
28
|
+
raise 'No repositories defined' unless @options['repos'].length > 0
|
29
|
+
|
30
|
+
@options
|
31
|
+
end
|
32
|
+
|
33
|
+
def broadcast_url(repo_name)
|
34
|
+
File.join(@options['bullhorn'], 'broadcast', repo_name) + "?hostname=#{HOSTNAME}"
|
35
|
+
end
|
36
|
+
|
37
|
+
def notify_url(params = {})
|
38
|
+
raise unless params[:repo] and params[:branch] and params[:stage]
|
39
|
+
File.join(@options['bullhorn'], 'update', params[:repo], params[:branch])
|
40
|
+
end
|
41
|
+
|
42
|
+
def notify(params = {})
|
43
|
+
raise unless params[:retval] and params[:response]
|
44
|
+
|
45
|
+
url = notify_url :repo => params[:repo], :branch => params[:branch], :stage => params[:stage]
|
46
|
+
|
47
|
+
EM::HttpRequest.new(url).post(
|
48
|
+
:query => { 'hostname' => HOSTNAME },
|
49
|
+
:body => {
|
50
|
+
:stage => params[:stage],
|
51
|
+
:retval => params[:retval],
|
52
|
+
:response => params[:response]
|
53
|
+
}
|
54
|
+
)
|
55
|
+
|
56
|
+
params[:retval] == 0
|
57
|
+
end
|
58
|
+
|
59
|
+
sources = {}
|
60
|
+
@options = load_config
|
61
|
+
|
62
|
+
Daemons.daemonize :app_name => 'bullhorn-client'
|
63
|
+
|
64
|
+
EM.run do
|
65
|
+
@options['repos'].each do |repo|
|
66
|
+
sources[repo[:name]] = EM::EventSource.new broadcast_url(repo['name'])
|
67
|
+
source = sources[repo[:name]]
|
68
|
+
|
69
|
+
source.on "commit/#{repo['name']}/#{repo['branch']}" do |message|
|
70
|
+
msg = JSON.parse message
|
71
|
+
|
72
|
+
if File.exists? "#{repo['destination']}/.git"
|
73
|
+
git_resp = %x[ cd #{repo['destination']} && git checkout #{msg['branch']} && git reset --hard && git pull ]
|
74
|
+
else
|
75
|
+
uri = URI.parse msg['repo']
|
76
|
+
uri.user = msg['token']
|
77
|
+
uri.password = 'x-oauth-token'
|
78
|
+
|
79
|
+
git_resp = %x[ git clone #{uri.to_s} #{repo['destination']} ]
|
80
|
+
end
|
81
|
+
next unless notify :repo => repo['name'], :branch => repo['branch'], :stage => 'git', :retval => $?.exitstatus, :response => git_resp
|
82
|
+
|
83
|
+
if repo['lint_cmd']
|
84
|
+
lint_resp = %x[ cd #{repo['destination']} && #{repo['lint_cmd']} ]
|
85
|
+
next unless notify :repo => repo['name'], :branch => repo['branch'], :stage => 'lint', :retval => $?.exitstatus, :response => lint_resp
|
86
|
+
end
|
87
|
+
|
88
|
+
update_resp = %x[ cd #{repo['destination']} && #{repo['post_update_cmd']} ]
|
89
|
+
next unless notify :repo => repo['name'], :branch => repo['branch'], :stage => 'update', :retval => $?.exitstatus, :response => update_resp
|
90
|
+
end
|
91
|
+
|
92
|
+
source.start
|
93
|
+
end
|
94
|
+
end
|
data/bin/bullhorn-server
ADDED
@@ -0,0 +1,144 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'sinatra'
|
5
|
+
require 'vegas'
|
6
|
+
require 'em-hiredis'
|
7
|
+
require 'hipchat'
|
8
|
+
require 'json'
|
9
|
+
require 'uuidtools'
|
10
|
+
|
11
|
+
DEFAULT_OPTIONS = {
|
12
|
+
'topic' => 'commit-and-deploy::broadcast',
|
13
|
+
'hipchat_v1_token' => nil,
|
14
|
+
'hipchat_room' => 'Development',
|
15
|
+
'redis' => 'redis://localhost:6379'
|
16
|
+
}
|
17
|
+
|
18
|
+
class Bullhorn < Sinatra::Application
|
19
|
+
set :subscribers, {}
|
20
|
+
|
21
|
+
def initialize
|
22
|
+
# read our options
|
23
|
+
load_config
|
24
|
+
|
25
|
+
# note: this connect is deferred until we get a broadcast subscriber
|
26
|
+
connect_to_redis ENV['REDIS_URL'] || @options['redis'] || 'redis://localhost:6379'
|
27
|
+
|
28
|
+
# create empty hash for our lifeline
|
29
|
+
@lifeline = {}
|
30
|
+
|
31
|
+
# connect to hipchat
|
32
|
+
@hipchat = HipChat::Client.new @options['hipchat_v1_token']
|
33
|
+
|
34
|
+
super
|
35
|
+
end
|
36
|
+
|
37
|
+
def connect_to_redis(redis_url)
|
38
|
+
@publisher = EM::Hiredis.connect redis_url
|
39
|
+
@publisher.errback { |err| raise err }
|
40
|
+
end
|
41
|
+
|
42
|
+
def load_config
|
43
|
+
cfg_file = File.exists?('/etc/bullhorn.yml') ? '/etc/bullhorn.yml' : File.expand_path('~/.bullhorn.yml')
|
44
|
+
config = YAML.load_file(cfg_file) rescue nil
|
45
|
+
@options = config ? DEFAULT_OPTIONS.update(config) : DEFAULT_OPTIONS
|
46
|
+
end
|
47
|
+
|
48
|
+
get '/' do
|
49
|
+
redirect 'http://www.idlepattern.com'
|
50
|
+
end
|
51
|
+
|
52
|
+
get '/favicon.ico' do
|
53
|
+
end
|
54
|
+
|
55
|
+
get '/healthcheck' do
|
56
|
+
"OK\n"
|
57
|
+
end
|
58
|
+
|
59
|
+
post '/commit' do
|
60
|
+
halt 500, 'Need push parameters!' unless params[:payload]
|
61
|
+
|
62
|
+
# parse the incoming json from github
|
63
|
+
push = JSON.parse params[:payload] rescue halt 500, 'Could not parse payload!'
|
64
|
+
|
65
|
+
# generate outbound notify message
|
66
|
+
notify = {
|
67
|
+
:uuid => UUIDTools::UUID.random_create.to_s,
|
68
|
+
:name => push['repository']['name'],
|
69
|
+
:repo => push['repository']['url'],
|
70
|
+
:branch => File.basename(push['ref']),
|
71
|
+
:token => 'githubtokengoeshere'
|
72
|
+
}
|
73
|
+
|
74
|
+
# notify hipchat and all subscribers of this new github push
|
75
|
+
@hipchat[@options['hipchat_room']].send 'Bullhorn', "Notifying all subscribers of recent push to #{notify[:branch]} @ #{notify[:repo]}"
|
76
|
+
@publisher.publish File.join(@options['topic'], notify[:name]), notify.to_json
|
77
|
+
end
|
78
|
+
|
79
|
+
get '/lifeline' do
|
80
|
+
content_type 'application/json'
|
81
|
+
JSON.pretty_generate @lifeline
|
82
|
+
end
|
83
|
+
|
84
|
+
get '/broadcast/:repo' do |repo|
|
85
|
+
# set content type to event stream
|
86
|
+
content_type 'text/event-stream'
|
87
|
+
|
88
|
+
stream :keep_open do |connection|
|
89
|
+
# put new connection in our subscribers hash
|
90
|
+
oid = connection.object_id
|
91
|
+
settings.subscribers[oid] = { :connection => connection }
|
92
|
+
subscriber = settings.subscribers[oid][:subscriber]
|
93
|
+
|
94
|
+
connection.callback do
|
95
|
+
# close subscriber redis handle if it exists, delete from subscribers hash
|
96
|
+
subscriber.close_connection if subscriber
|
97
|
+
settings.subscribers.delete oid
|
98
|
+
|
99
|
+
# delete entry from lifeline on disconnect?
|
100
|
+
@lifeline[repo].delete oid if @lifeline[repo]
|
101
|
+
|
102
|
+
# stdout logging, hah
|
103
|
+
puts "Unsubscribed \##{oid} @ #{request.ip} from #{repo}"
|
104
|
+
end
|
105
|
+
|
106
|
+
# track our lifeline
|
107
|
+
@lifeline[repo] ||= {}
|
108
|
+
@lifeline[repo][oid] = {
|
109
|
+
:hostname => params[:hostname],
|
110
|
+
:ip => request.ip,
|
111
|
+
:heartbeat => nil
|
112
|
+
}
|
113
|
+
|
114
|
+
# send heartbeat message every 5 seconds
|
115
|
+
EM.add_periodic_timer(5) do
|
116
|
+
if @lifeline[repo][oid]
|
117
|
+
@lifeline[repo][oid][:heartbeat] = Time.now
|
118
|
+
connection << "event: heartbeat\ndata: #{@lifeline[repo][oid][:heartbeat].to_s}\n\n"
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
# new redis subscriber
|
123
|
+
subscriber = EM::Hiredis.connect
|
124
|
+
subscriber.errback { |err| p err; raise err }
|
125
|
+
|
126
|
+
# subscribe to topic, and send every received message to connection
|
127
|
+
subscriber.subscribe File.join(@options['topic'], repo)
|
128
|
+
subscriber.on :message do |topic, msg|
|
129
|
+
notify = JSON.parse msg
|
130
|
+
connection << "event: commit/#{notify['name']}/#{notify['branch']}\ndata: #{msg}\n\n"
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
post '/update/:repo/:branch' do |repo, branch|
|
136
|
+
halt 403, 'Forbidden' unless params[:hostname] and params[:stage] and params[:retval] and params[:response]
|
137
|
+
return "OK\n" if params[:retval].to_i == 0
|
138
|
+
|
139
|
+
@hipchat[@options['hipchat_room']].send 'Bullhorn', "Client #{params[:hostname]} has reported a failure on deploy (stage: #{params[:stage] || '*unknown*'}, return code: #{params[:retval].to_i})", :notify => true
|
140
|
+
@hipchat[@options['hipchat_room']].send 'Bullhorn', "Response:<br>\n<pre>#{params[:response]}</pre>", :notify => true
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
Vegas::Runner.new Bullhorn, 'bullhorn', :port => 9292, :skip_launch => true
|
metadata
ADDED
@@ -0,0 +1,188 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: idlepattern-bullhorn
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.2.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Omachonu Ogali
|
8
|
+
- Adam Greenfield
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2014-08-31 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: daemons
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - '>='
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: '0'
|
21
|
+
type: :runtime
|
22
|
+
prerelease: false
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - '>='
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: '0'
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: em-eventsource
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - '>='
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: '0'
|
35
|
+
type: :runtime
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - '>='
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '0'
|
42
|
+
- !ruby/object:Gem::Dependency
|
43
|
+
name: em-hiredis
|
44
|
+
requirement: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - '>='
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '0'
|
49
|
+
type: :runtime
|
50
|
+
prerelease: false
|
51
|
+
version_requirements: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - '>='
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '0'
|
56
|
+
- !ruby/object:Gem::Dependency
|
57
|
+
name: hipchat
|
58
|
+
requirement: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - '>='
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '0'
|
63
|
+
type: :runtime
|
64
|
+
prerelease: false
|
65
|
+
version_requirements: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
- !ruby/object:Gem::Dependency
|
71
|
+
name: iconv
|
72
|
+
requirement: !ruby/object:Gem::Requirement
|
73
|
+
requirements:
|
74
|
+
- - '>='
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '0'
|
77
|
+
type: :runtime
|
78
|
+
prerelease: false
|
79
|
+
version_requirements: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - '>='
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: '0'
|
84
|
+
- !ruby/object:Gem::Dependency
|
85
|
+
name: json
|
86
|
+
requirement: !ruby/object:Gem::Requirement
|
87
|
+
requirements:
|
88
|
+
- - '>='
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: '0'
|
91
|
+
type: :runtime
|
92
|
+
prerelease: false
|
93
|
+
version_requirements: !ruby/object:Gem::Requirement
|
94
|
+
requirements:
|
95
|
+
- - '>='
|
96
|
+
- !ruby/object:Gem::Version
|
97
|
+
version: '0'
|
98
|
+
- !ruby/object:Gem::Dependency
|
99
|
+
name: sinatra
|
100
|
+
requirement: !ruby/object:Gem::Requirement
|
101
|
+
requirements:
|
102
|
+
- - '>='
|
103
|
+
- !ruby/object:Gem::Version
|
104
|
+
version: '0'
|
105
|
+
type: :runtime
|
106
|
+
prerelease: false
|
107
|
+
version_requirements: !ruby/object:Gem::Requirement
|
108
|
+
requirements:
|
109
|
+
- - '>='
|
110
|
+
- !ruby/object:Gem::Version
|
111
|
+
version: '0'
|
112
|
+
- !ruby/object:Gem::Dependency
|
113
|
+
name: thin
|
114
|
+
requirement: !ruby/object:Gem::Requirement
|
115
|
+
requirements:
|
116
|
+
- - '>='
|
117
|
+
- !ruby/object:Gem::Version
|
118
|
+
version: '0'
|
119
|
+
type: :runtime
|
120
|
+
prerelease: false
|
121
|
+
version_requirements: !ruby/object:Gem::Requirement
|
122
|
+
requirements:
|
123
|
+
- - '>='
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '0'
|
126
|
+
- !ruby/object:Gem::Dependency
|
127
|
+
name: uuidtools
|
128
|
+
requirement: !ruby/object:Gem::Requirement
|
129
|
+
requirements:
|
130
|
+
- - '>='
|
131
|
+
- !ruby/object:Gem::Version
|
132
|
+
version: '0'
|
133
|
+
type: :runtime
|
134
|
+
prerelease: false
|
135
|
+
version_requirements: !ruby/object:Gem::Requirement
|
136
|
+
requirements:
|
137
|
+
- - '>='
|
138
|
+
- !ruby/object:Gem::Version
|
139
|
+
version: '0'
|
140
|
+
- !ruby/object:Gem::Dependency
|
141
|
+
name: vegas
|
142
|
+
requirement: !ruby/object:Gem::Requirement
|
143
|
+
requirements:
|
144
|
+
- - '>='
|
145
|
+
- !ruby/object:Gem::Version
|
146
|
+
version: '0'
|
147
|
+
type: :runtime
|
148
|
+
prerelease: false
|
149
|
+
version_requirements: !ruby/object:Gem::Requirement
|
150
|
+
requirements:
|
151
|
+
- - '>='
|
152
|
+
- !ruby/object:Gem::Version
|
153
|
+
version: '0'
|
154
|
+
description: '...'
|
155
|
+
email: hello@idlepattern.com
|
156
|
+
executables:
|
157
|
+
- bullhorn-client
|
158
|
+
- bullhorn-server
|
159
|
+
extensions: []
|
160
|
+
extra_rdoc_files: []
|
161
|
+
files:
|
162
|
+
- bin/bullhorn-client
|
163
|
+
- bin/bullhorn-server
|
164
|
+
homepage: http://idlepattern.com
|
165
|
+
licenses:
|
166
|
+
- BSD
|
167
|
+
metadata: {}
|
168
|
+
post_install_message:
|
169
|
+
rdoc_options: []
|
170
|
+
require_paths:
|
171
|
+
- lib
|
172
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
173
|
+
requirements:
|
174
|
+
- - '>='
|
175
|
+
- !ruby/object:Gem::Version
|
176
|
+
version: '0'
|
177
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
178
|
+
requirements:
|
179
|
+
- - '>='
|
180
|
+
- !ruby/object:Gem::Version
|
181
|
+
version: '0'
|
182
|
+
requirements: []
|
183
|
+
rubyforge_project:
|
184
|
+
rubygems_version: 2.0.14
|
185
|
+
signing_key:
|
186
|
+
specification_version: 4
|
187
|
+
summary: Trigger actions based on Github deploys
|
188
|
+
test_files: []
|