idlepattern-bullhorn 0.1.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (4) hide show
  1. checksums.yaml +7 -0
  2. data/bin/bullhorn-client +94 -0
  3. data/bin/bullhorn-server +144 -0
  4. 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
@@ -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
@@ -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: []