idlepattern-bullhorn 0.1.2.1
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|