hubeye 0.1.0 → 0.2.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,764 +1,752 @@
1
- module Server
2
- # standard lib.
3
- require 'socket'
4
- require 'yaml'
5
-
6
- # vendor
7
- begin
8
- require 'octopi'
9
- rescue LoadError
10
- if require 'rubygems'
11
- retry
12
- else
13
- abort 'Octopi is needed to run hubeye. Gem install octopi'
1
+ class Hubeye
2
+
3
+ # simple interface to Github's api v3 for commits
4
+ class Commit
5
+ attr_reader :raw_input, :repo, :only_sha, :latest
6
+ def initialize(input)
7
+ @raw_input = input
8
+ @repo = input.keys.first
9
+ if Hash === input
10
+ if input[@repo].keys == ["sha"]
11
+ @only_sha = true
12
+ else
13
+ @latest = true
14
+ end
15
+ else
16
+ raise ArgumentError.new "input must be a kind of hash"
17
+ end
14
18
  end
15
- end
16
- include Octopi
17
-
18
- # hubeye
19
- require "config/parser"
20
- require "log/logger"
21
- require "notification/notification"
22
- require "hooks/git_hooks"
23
- require "hooks/executer"
24
- require "helpers/time"
25
- include Helpers::Time
26
-
27
- CONFIG_FILE = ENV['HOME'] + "/.hubeye/hubeyerc"
28
- CONFIG = {}
29
- # find Desktop notification system
30
-
31
- # CONFIG options: defined in ~/.hubeye/hubeyerc
32
- #
33
- # Option overview:
34
- #
35
- # CONFIG[:oncearound]: 60 (seconds) is the default amount of time for looking
36
- # for changes in every single repository. If tracking lots of repos,
37
- # it might be a good idea to increase the value, or hubeye will cry
38
- # due to overwork, fatigue and general anhedonia.
39
- #
40
- # hubeyerc format => oncearound: 1000
41
- #
42
- # CONFIG[:username] is the username used when not specified.
43
- # hubeyerc format => username: 'hansolo'
44
- # when set to 'hansolo'
45
- # >rails
46
- # would track https://www.github.com/hansolo/rails
47
- # but a full URI path won't use CONFIG[:username]
48
- # >rails/rails
49
- # would track https://www.github.com/rails/rails
50
- ::Hubeye::Config::Parser.new(CONFIG_FILE) do |c|
51
- CONFIG[:username] = c.username || 'john_doe'
52
- CONFIG[:oncearound] = c.oncearound || 60
53
- CONFIG[:load_repos] = c.load_repos || []
54
- CONFIG[:load_hooks] = c.load_hooks || []
55
- CONFIG[:default_track] = c.default_track || nil
56
- # returns true or false if defined in hubeyerc
57
- CONFIG[:notification_wanted] = case c.notification_wanted
58
- when false
59
- false
60
- when true
61
- true
62
- when nil
63
- # default is true if not defined in hubeyerc
64
- true
65
- end
66
- end
67
-
68
- CONFIG[:desktop_notification] = (CONFIG[:notification_wanted] ?
69
- Notification::Finder.find_notify : nil)
70
19
 
71
- class InputError < StandardError; end
20
+ def sha
21
+ @sha ||= @raw_input[@repo]['sha']
22
+ end
72
23
 
73
- def start(port, options={})
74
- listen(port)
75
- setup_env(options)
76
- loop do
77
- catch(:next) do
78
- not_connected() unless @remote_connection
79
- get_input(@socket)
80
- puts @input if @debug
81
- parse_input()
82
- parse_doc()
83
- @username = CONFIG[:username]
24
+ def commit_message
25
+ if @only_sha
26
+ return
27
+ elsif @latest
28
+ @commit_message ||= @raw_input[@repo]['commit']['message']
29
+ else
30
+ raise
84
31
  end
85
32
  end
86
- end
87
33
 
88
- # Listen on port (2000 is the default)
89
- def listen(port)
90
- @server = TCPServer.open(port)
34
+ def committer_name
35
+ if @only_sha
36
+ return
37
+ elsif @latest
38
+ @committer_name ||= @raw_input[@repo]['commit']['committer']['name']
39
+ else
40
+ raise
41
+ end
42
+ end
91
43
  end
92
44
 
93
-
94
- def setup_env(options={})
95
- @daemonized = options[:daemon]
96
- @sockets = [@server] # An array of sockets we'll monitor
97
- if CONFIG[:default_track].nil?
98
- # will be array of 2-element arrays that contain the
99
- # tracked repo name [0] and hash of latest sha and latest commit object
100
- # for that repo [1]
101
- # Example:
102
- # [ ['luke-gru/hubeye', {:sha1 => 90j93r0rf389, :commit => <#Commit Object>}], [..., ...] ]
103
- @hubeye_tracker = []
104
- else
105
- # default tracking arrays (hubeyerc configurations)
106
- @hubeye_tracker = CONFIG[:default_track]
107
- end
108
- setup_hubeye_singleton_methods
109
-
110
- if CONFIG[:load_hooks].empty?
111
- # do nothing (the hooks hash is only assigned when needed)
112
- else
113
- hooks_ary = CONFIG[:load_hooks].dup
114
- load_hooks_or_repos :internal_input => hooks_ary,
115
- :internal_input_hooks => true
45
+ module Server
46
+ attr_accessor :remote_connection
47
+ attr_reader :socket, :sockets, :session, :daemonized
48
+
49
+ # standard lib.
50
+ require 'socket'
51
+ require 'yaml'
52
+ require 'json'
53
+ require 'open-uri'
54
+ # hubeye
55
+ require "config/parser"
56
+ require "log/logger"
57
+ require "notification/notification"
58
+ require "hooks/git_hooks"
59
+ require "hooks/executer"
60
+ require "helpers/time"
61
+ include Helpers::Time
62
+
63
+ CONFIG_FILE = ENV['HOME'] + "/.hubeye/hubeyerc"
64
+ CONFIG = {}
65
+ # find Desktop notification system
66
+
67
+ # CONFIG options: defined in ~/.hubeye/hubeyerc
68
+ #
69
+ # Option overview:
70
+ #
71
+ # CONFIG[:oncearound]: 60 (seconds) is the default amount of time for looking
72
+ # for changes in every single repository. If tracking lots of repos,
73
+ # it might be a good idea to increase the value, or hubeye will cry
74
+ # due to overwork, fatigue and general anhedonia.
75
+ #
76
+ # hubeyerc format => oncearound: 1000
77
+ #
78
+ # CONFIG[:username] is the username used when not specified.
79
+ # hubeyerc format => username: 'hansolo'
80
+ # when set to 'hansolo'
81
+ # >rails
82
+ # would track https://www.github.com/hansolo/rails
83
+ # but a full URI path won't use CONFIG[:username]
84
+ # >rails/rails
85
+ # would track https://www.github.com/rails/rails
86
+ Config::Parser.new(CONFIG_FILE) do |c|
87
+ CONFIG[:username] = c.username ||
88
+ `git config --get-regexp github`.split(' ').last
89
+ CONFIG[:oncearound] = c.oncearound || 60
90
+ CONFIG[:load_repos] = c.load_repos || []
91
+ CONFIG[:load_hooks] = c.load_hooks || []
92
+ CONFIG[:default_track] = c.default_track || nil
93
+
94
+ CONFIG[:notification_wanted] = if c.notification_wanted.nil?
95
+ true
96
+ else
97
+ c.notification_wanted
98
+ end
116
99
  end
117
100
 
118
- if CONFIG[:load_repos].empty?
119
- # do nothing
120
- else
121
- repos_ary = CONFIG[:load_repos].dup
122
- load_hooks_or_repos :internal_input => repos_ary,
123
- :internal_input_repos => true
124
- end
125
- # @username changes if input includes a '/' when removing and adding
126
- # tracked repos.
127
- @username = CONFIG[:username]
128
- @remote_connection = false
129
- end
101
+ CONFIG[:desktop_notification] = Notification::Finder.find_notify if
102
+ CONFIG[:notification_wanted]
130
103
 
131
- class ::Array
132
- include Octopi
133
- class NotTrackerElementError < TypeError ; end
104
+ class Strategy
105
+ UnknownStrategy = Class.new(StandardError)
106
+ attr_reader :server, :input
134
107
 
135
- def extract_old_and_new
136
- if length != 2
137
- raise NotTrackerElementError.new "#{self} is not a @hubeye_tracker element"
138
- else
139
- tracked_repo, tracked_sha = self[0], self[1][:sha1]
140
- username, repo_name = tracked_repo.split '/'
141
- begin
142
- gh_user = User.find(username)
143
- rescue Octopi::NotFound
144
- # fall back to using default username if not found
145
- gh_user = @username
108
+ def initialize(server, options={})
109
+ @server = server
110
+ opts = {:internal_input => nil}.merge options
111
+ if !opts[:internal_input]
112
+ @input = server.socket.gets
113
+ @input.chop!
114
+ else
115
+ @input = opts[:internal_input]
146
116
  end
147
- begin
148
- repo = gh_user.repository repo_name
149
- rescue Octopi::NotFound
150
- raise
117
+ unless @input
118
+ Logger.log "Client on #{@server.socket.peeraddr[2]} disconnected."
119
+ @server.sockets.delete(socket)
120
+ @server.socket.close
121
+ return
151
122
  end
123
+ @input = @input.strip.downcase
124
+ @input.gsub! /diiv/, '/'
152
125
  end
153
- [tracked_sha, repo]
154
- end
155
- end
156
126
 
157
- def setup_hubeye_singleton_methods
158
- def_try_append_or_replace!
159
- def_eyeing
160
- def_rm_repo
161
- end
127
+ STRATEGY_CLASSES = [ "Shutdown", "Exit", "SaveHook", "SaveRepo",
128
+ "LoadHook", "LoadRepo", "AddHook", "ListHooks", "ListTracking",
129
+ "Next", "RmRepo", "AddRepo" ]
130
+
131
+
132
+ STRATEGY_CLASSES.each do |klass_str|
133
+ binding.eval "class #{klass_str}; end"
134
+ klass = const_get klass_str.intern
135
+ klass.class_eval do
136
+ def initialize matches, strategy, options={}
137
+ @options = options
138
+ @matches = matches
139
+ @server = strategy.server
140
+ @input = strategy.input
141
+ @socket = @server.socket
142
+ @sockets = @server.sockets
143
+ @session = @server.session
144
+ call
145
+ end
146
+ end
147
+ end
162
148
 
163
- def def_try_append_or_replace!
164
- @hubeye_tracker.singleton_class.class_eval do
165
- define_method :try_append_or_replace! do |repo_name, new_commit_obj|
166
- status = {:replace => nil, :add => nil, :same => true}
167
- match = nil
168
- map! do |e|
169
- if e[0] == repo_name
170
- match = true
171
- if e[1][:sha1] == new_commit_obj.id
172
- e
173
- else
174
- e[1][:sha1] = new_commit_obj.id
175
- e[1][:commit] = new_commit_obj
176
- status = status.merge :replace => true, :same => false
149
+ # strategy classes
150
+ class Exit
151
+ def call
152
+ @socket.puts "Bye!"
153
+ # mark the session as continuous to not wipe the log file
154
+ @session.continuous = true
155
+ Logger.log "Closing connection to #{@socket.peeraddr[2]}"
156
+ @server.remote_connection = false
157
+ if !@session.tracker.empty?
158
+ Logger.log "Tracking: #{@session.tracker.keys.join ', '}"
159
+ end
160
+ # to look pretty when multiple connections per loop
161
+ Logger.log ""
162
+ @sockets.delete(@socket)
163
+ @socket.close
164
+ end
165
+ end
166
+
167
+ class Shutdown
168
+ def call
169
+ Logger.log "Closing connection to #{@socket.peeraddr[2]}"
170
+ Logger.log "Shutting down... (#{::Hubeye::Server::NOW})"
171
+ Logger.log ""
172
+ Logger.log ""
173
+ @socket.puts("Shutting down server")
174
+ @sockets.delete(@socket)
175
+ @socket.close
176
+ unless @server.daemonized
177
+ STDOUT.puts "Shutdown gracefully."
178
+ end
179
+ exit 0
180
+ end
181
+ end
182
+
183
+ class SaveHook
184
+ def call
185
+ hooks = @session.hooks
186
+ if !hooks.empty?
187
+ file = "#{ENV['HOME']}/.hubeye/hooks/#{@matches[2]}.yml"
188
+ if File.exists? file
189
+ override?
177
190
  end
191
+ File.open(file, "w") do |f_out|
192
+ ::YAML.dump(hooks, f_out)
193
+ end
194
+ @socket.puts("Saved hook#{@matches[1]} as #{@matches[2]}")
178
195
  else
179
- e
196
+ @socket.puts("No hook#{@matches[1]} to save")
180
197
  end
181
- end # end of map!
182
- if match
183
- return status
184
- else
185
- self << [repo_name, {:sha1 => new_commit_obj.id,
186
- :commit => new_commit_obj}]
187
- return status.merge :add => true, :same => false
198
+ end
199
+
200
+ private
201
+ def override?
188
202
  end
189
203
  end
190
- end
191
- end
192
204
 
193
- def def_eyeing
194
- @hubeye_tracker.singleton_class.class_eval do
195
- define_method :eyeing do
196
- (ary = []).tap do
197
- each do |e|
198
- ary << e[0]
205
+ class SaveRepo
206
+ def call
207
+ if !@session.tracker.empty?
208
+ file = "#{ENV['HOME']}/.hubeye/repos/#{@matches[2]}.yml"
209
+ if File.exists? file
210
+ override?
211
+ end
212
+ # dump only the repository names, not the shas
213
+ File.open(file, "w") do |f_out|
214
+ ::YAML.dump(@session.tracker.keys, f_out)
215
+ end
216
+ @socket.puts("Saved repo#{@matches[1]} as #{@matches[2]}")
217
+ else
218
+ @socket.puts("No remote repos are being tracked")
199
219
  end
200
220
  end
201
- end
202
- end
203
- end
204
221
 
205
- def def_rm_repo
206
- @hubeye_tracker.singleton_class.class_eval do
207
- define_method :rm_repo do |repo_name|
208
- match = nil
209
- reject! do |e|
210
- e[0] == repo_name ? match = true : nil
222
+ private
223
+ def override?
211
224
  end
212
- match
213
225
  end
214
- end
215
- end
216
226
 
227
+ class LoadHook
228
+ def call
229
+ if @options[:internal]
230
+ @silent = @options[:internal]
231
+ end
232
+ hookfile = "#{ENV['HOME']}/.hubeye/hooks/#{@matches[2]}.yml"
233
+ new_hooks = nil
234
+ if File.exists?(hookfile)
235
+ File.open(hookfile) do |f|
236
+ new_hooks = ::YAML.load(f)
237
+ end
238
+ @session.hooks.merge!(new_hooks)
239
+ unless @silent
240
+ @socket.puts("Loaded #{@matches[1]} #{@matches[2]}")
241
+ end
242
+ else
243
+ unless @silent
244
+ @socket.puts("No #{@matches[1]} file to load from")
245
+ end
246
+ end
247
+ end
248
+ end
217
249
 
218
- def not_connected
219
- # if no client is connected, but the commits array contains repos
220
- if @sockets.size == 1 and !@hubeye_tracker.empty?
221
-
222
- while @remote_connection == false
223
- sleep_amt = CONFIG[:oncearound] / @hubeye_tracker.length
224
- @hubeye_tracker.each do |ary|
225
- # this method does api calls, takes a couple of seconds to
226
- # complete right now (TODO: see what's taking so long)
227
- start_time = Time.now
228
- old_sha, new_repo = ary.extract_old_and_new
229
- api_time = (Time.now - start_time).to_i
230
- new_commit = new_repo.commits.first
231
- if new_commit.id == old_sha
232
- (sleep_amt - api_time).times do
233
- sleep 1
234
- @remote_connection = client_ready(@sockets) ? true : false
235
- break if @remote_connection
250
+ class LoadRepo
251
+ def call
252
+ if @options[:internal]
253
+ @silent = @options[:internal]
254
+ end
255
+ if File.exists?(repo_file = "#{ENV['HOME']}/.hubeye/repos/#{@matches[2]}.yml")
256
+ new_repos = nil
257
+ File.open(repo_file) do |f|
258
+ new_repos = ::YAML.load(f)
259
+ end
260
+ if !new_repos
261
+ @socket.puts "Unable to load #{@matches[2]}: empty file" unless @silent
262
+ return
263
+ end
264
+ new_repos.each do |r|
265
+ # add the repo name to the hubeye tracker
266
+ commit = @server.track(r)
267
+ @session.tracker.add_or_replace!(commit.repo, commit.sha)
268
+ end
269
+ unless @silent
270
+ @socket.puts "Loaded #{@matches[2]}.\nTracking:\n#{@session.tracker.keys.join ', '}"
236
271
  end
237
272
  else
238
- # There was a change to a tracked repository.
239
- full_repo_name = ary[0]
240
- commit_msg = new_commit.message
241
- committer = new_commit.author['name']
242
- # notify of change to repository
243
- # if they have a Desktop notification
244
- # library installed
245
- change_msg = "Repo #{full_repo_name} has changed\nNew commit: " +
246
- "#{commit_msg}\n=> #{committer}"
247
- case CONFIG[:desktop_notification]
248
- when "libnotify"
249
- Autotest::GnomeNotify.notify("Hubeye", change_msg)
250
- Logger.log_change(full_repo_name, commit_msg, committer)
251
- when "growl"
252
- Autotest::Growl.growl("Hubeye", change_msg)
253
- Logger.log_change(full_repo_name, commit_msg, committer)
254
- when nil
255
- if @daemonized
256
- Logger.log_change(full_repo_name, commit_msg, committer)
273
+ @socket.puts("No file to load from") unless @silent
274
+ end
275
+ end
276
+ end
277
+
278
+ class AddHook
279
+ def call
280
+ cwd = File.expand_path('.')
281
+ repo = @matches[1]
282
+ dir = @matches[3]
283
+ cmd = @matches[4]
284
+ if repo != nil and cmd != nil
285
+ if @session.hooks[repo]
286
+ if dir
287
+ if @session.hooks[repo][dir]
288
+ @session.hooks[repo][dir] << cmd
289
+ else
290
+ @session.hooks[repo][dir] = [cmd]
291
+ end
257
292
  else
258
- Logger.log_change(full_repo_name, commit_msg, committer, :include_terminal => true)
293
+ if @session.hooks[repo][cwd]
294
+ @session.hooks[repo][cwd] << cmd
295
+ else
296
+ @session.hooks[repo][cwd] = [cmd]
297
+ end
259
298
  end
260
- end
261
- # execute any hooks for that repository
262
- unless @hook_cmds.nil? || @hook_cmds.empty?
263
- if @hook_cmds[full_repo_name]
264
- hook_cmds = @hook_cmds[full_repo_name].dup
265
- dir = (hook_cmds.include?('/') ? hook_cmds.shift : nil)
266
-
267
- # execute() takes [commands], {options} where
268
- # options = :directory and :repo
269
- Hooks::Command.execute(hook_cmds, :directory => dir, :repo => repo)
299
+ else
300
+ if dir
301
+ @session.hooks[repo] = {dir => [cmd]}
302
+ else
303
+ @session.hooks[repo] = {cwd => [cmd]}
270
304
  end
271
305
  end
272
- @hubeye_tracker.try_append_or_replace!(full_repo_name, new_commit)
306
+ @socket.puts("Hook added")
307
+ else
308
+ @socket.puts("Format: 'hook add user/repo [dir: /my/dir/repo ] cmd: git pull origin'")
273
309
  end
274
310
  end
275
- redo unless @remote_connection
276
- end # end of (while remote_connection == false)
277
- end
278
- client_connect(@sockets)
279
- end
280
-
311
+ end
281
312
 
282
- def client_ready(sockets)
283
- select(sockets, nil, nil, 2)
284
- end
285
- private :client_ready
286
-
287
-
288
- def client_connect(sockets)
289
- ready = select(sockets)
290
- readable = ready[0] # These sockets are readable
291
- readable.each do |socket| # Loop through readable sockets
292
- if socket == @server # If the server socket is ready
293
- client = @server.accept # Accept a new client
294
- @socket = client # From here on in referred to as @socket
295
- sockets << @socket # Add it to the set of sockets
296
- # Inform the client of connection
297
- basic_inform = "Hubeye running on #{Socket.gethostname} as #{@username}"
298
- if !@hubeye_tracker.empty?
299
- @socket.puts "#{basic_inform}\nTracking:#{@hubeye_tracker.eyeing.join ', '}"
300
- else
301
- @socket.puts basic_inform
313
+ class ListHooks
314
+ def call
315
+ unless @session.hooks.empty?
316
+ pwd = File.expand_path('.')
317
+ format_string = ""
318
+ @session.hooks.each do |repo, hash|
319
+ local_dir = nil
320
+ command = nil
321
+ hash.each do |dir,cmd|
322
+ if dir.nil?
323
+ local_dir = pwd
324
+ command = cmd.join("\n" + (' ' * 8))
325
+ else
326
+ command = cmd
327
+ local_dir = dir
328
+ end
329
+ end
330
+ format_string << <<EOS
331
+ remote: #{repo}
332
+ dir: #{local_dir}
333
+ cmds: #{command}\n
334
+ EOS
335
+ end
336
+ @socket.puts(format_string)
337
+ else
338
+ @socket.puts("No hooks")
339
+ end
302
340
  end
341
+ end
303
342
 
304
- if !@daemonized
305
- puts "Client connected at #{NOW}"
343
+ class ListTracking
344
+ def call
345
+ output = ''
346
+ if @options[:details]
347
+ commit_list = []
348
+ @session.tracker.keys.each do |repo|
349
+ commit = @server.track(repo, :list => true)
350
+ commit_list << commit
351
+ end
352
+ commit_list.each do |c|
353
+ output << c.repo + "\n"
354
+ underline = '=' * c.repo.length
355
+ output << underline + "\n"
356
+ output << c.commit_message + "\n=> " +
357
+ c.committer_name + "\n"
358
+ output << "\n" unless c.repo == commit_list.last.repo
359
+ end
360
+ else
361
+ output << @session.tracker.keys.join(', ')
362
+ end
363
+ output = "none" if output.empty?
364
+ @socket.puts(output)
306
365
  end
366
+ end
307
367
 
308
- @socket.flush
309
- # And log the fact that the client connected
310
- if @still_logging == true
311
- # if the client quit, do not wipe the log file
312
- Logger.log "Accepted connection from #{@socket.peeraddr[2]} (#{NOW})"
313
- else
314
- # wipe the log file and start anew
315
- Logger.relog "Accepted connection from #{@socket.peeraddr[2]} (#{NOW})"
368
+ class Next
369
+ def call
370
+ @socket.puts("")
316
371
  end
317
- Logger.log "local: #{@socket.addr}"
318
- Logger.log "peer : #{@socket.peeraddr}"
319
372
  end
320
- end
321
- end
322
373
 
374
+ class RmRepo
375
+ def call
376
+ if @matches[1].include?("/")
377
+ @session.username, @session.repo_name = @matches[1].split('/')
378
+ else
379
+ @session.repo_name = @matches[1]
380
+ end
381
+ rm = @session.tracker.delete("#{@session.username}/#{@session.repo_name}")
382
+ if rm
383
+ @socket.puts("Stopped watching repository #{@session.username}/#{@session.repo_name}")
384
+ else
385
+ @socket.puts("Repository #{@session.username}/#{@session.repo_name} not currently being watched")
386
+ end
387
+ end
388
+ end
323
389
 
324
- def get_input(socket)
325
- @input = socket.gets # Read input from the client
326
- @input.chop! unless @input.nil? # Trim client's input
327
- # If no input, the client has disconnected
328
- if !@input
329
- Logger.log "Client on #{socket.peeraddr[2]} disconnected."
330
- @sockets.delete(socket) # Stop monitoring this socket
331
- socket.close # Close it
332
- throw(:next) # And go on to the next
333
- end
334
- end
335
- private :get_input
336
-
337
-
338
- def parse_input
339
- @input.strip!; @input.downcase!
340
- if parse_quit()
341
- elsif parse_shutdown()
342
- elsif save_hooks_or_repos()
343
- elsif load_hooks_or_repos()
344
- # parse_hook must be before parse_fullpath_add for the moment
345
- elsif parse_hook()
346
- elsif hook_list()
347
- elsif tracking_list()
348
- elsif parse_pwd()
349
- elsif parse_empty()
350
- elsif parse_remove()
351
- elsif parse_fullpath_add()
352
- elsif parse_add()
353
- else
354
- raise InputError "Invalid input #{@input}"
355
- end
356
- end
390
+ class AddRepo
391
+ def call
392
+ if @options and @options[:pwd]
393
+ @session.repo_name = File.dirname(File.expand_path('.'))
394
+ elsif @options and @options[:fullpath]
395
+ @session.username, @session.repo_name = @input.split('/')
396
+ else
397
+ @session.repo_name = @input
398
+ end
399
+ add_repo
400
+ end
357
401
 
402
+ private
403
+ def add_repo
404
+ full_repo_name = "#{@session.username}/#{@session.repo_name}"
405
+ commit = @server.track(full_repo_name, :latest => true)
406
+ new_sha = commit.sha
407
+ commit_msg = commit.commit_message
408
+ committer = commit.committer_name
409
+ msg = "#{commit_msg}\n=> #{committer}"
410
+ change = @session.tracker.add_or_replace!(full_repo_name, new_sha)
411
+ # new repo to track
412
+ if !change
413
+ @socket.puts("Repository #{full_repo_name} has not changed")
414
+ return
415
+ elsif change[:add]
416
+ # log the fact that the user added a repo to be tracked
417
+ Logger.log("Added to tracker: #{full_repo_name} (#{::Hubeye::Server::NOW})")
418
+ # show the user, via the client, the info and commit msg for the commit
419
+ @socket.puts(msg)
420
+ elsif change[:replace]
421
+ change_msg = "New commit on #{full_repo_name}\n"
422
+ change_msg << msg
423
+ @socket.puts(change_msg)
424
+ if @server.daemonized
425
+ Logger.log_change(full_repo_name, commit_msg, committer)
426
+ else
427
+ Logger.log_change(full_repo_name, commit_msg, committer,
428
+ :include_terminal => true)
429
+ end
430
+ end
431
+ end
432
+ end
358
433
 
359
- def parse_quit
360
- if @input =~ /\Aquit|exit\Z/ # If the client asks to quit
361
- @socket.puts("Bye!") # Say goodbye
362
- Logger.log "Closing connection to #{@socket.peeraddr[2]}"
363
- @remote_connection = false
364
- if !@hubeye_tracker.empty?
365
- Logger.log "Tracking: #{@hubeye_tracker.eyeing.join ', '}"
366
- end
367
- Logger.log "" # to look pretty when multiple connections per loop
368
- @sockets.delete(@socket) # Stop monitoring the socket
369
- @socket.close # Terminate the session
370
- # still_logging makes the server not wipe the log file
371
- @still_logging = true
372
- else
373
- return
434
+ # STRATEGIES hash
435
+ # ===============
436
+ # keys: input matches
437
+ # OR
438
+ # lambda {|input| input.something?} => value
439
+ #
440
+ # values: lambda {|matchdata, basestrategy| SomeStrategy.new(matchdata, basestrategy)}
441
+ STRATEGIES = {
442
+ %r{\Ashutdown\Z} => lambda {|m, s| Shutdown.new(m, s)},
443
+ %r{\Aquit|exit\Z} => lambda {|m, s| Exit.new(m, s)},
444
+ %r{\A\s*save hook(s?) as (.+)\Z} => lambda {|m, s| SaveHook.new(m, s)},
445
+ %r{\A\s*save repo(s?) as (.+)\Z} => lambda {|m, s| SaveRepo.new(m, s)},
446
+ %r{\A\s*load hook(s?) (.+)\Z} => lambda {|m, s| LoadHook.new(m, s)},
447
+ %r{\A\s*load repo(s?) (.+)\Z} => lambda {|m, s| LoadRepo.new(m, s)},
448
+ %r{\A\s*internal load hook(s?) (.+)\Z} => lambda {|m, s| LoadHook.new(m, s, :internal => true)},
449
+ %r{\A\s*internal load repo(s?) (.+)\Z} => lambda {|m, s| LoadRepo.new(m, s, :internal => true)},
450
+ %r{\Ahook add ([-\w]+/[-\w]+) (dir:\s?(.*))?\s*cmd:\s?(.*)\Z} => lambda {|m, s| AddHook.new(m, s)},
451
+ %r{\Ahook list\Z} => lambda {|m, s| ListHooks.new(m, s)},
452
+ %r{\Atracking\s*\Z} => lambda {|m, s| ListTracking.new(m, s)},
453
+ %r{\Atracking\s*-d\Z} => lambda {|m, s| ListTracking.new(m, s, :details => true)},
454
+ %r{^pwd} => lambda {|m, s| AddRepo.new(m, s, :pwd => true)},
455
+ %r{^\s*$} => lambda {|m, s| Next.new(m, s)},
456
+ %r{\Arm ([-\w]+/?[-\w]*)\Z} => lambda {|m, s| RmRepo.new(m, s)},
457
+ lambda {|inp| inp.include? '/'} => lambda {|m, s| AddRepo.new(m, s, :fullpath => true)},
458
+ lambda {|inp| not inp.nil?} => lambda {|m, s| AddRepo.new(m, s)}
459
+ }
460
+
461
+ def call
462
+ STRATEGIES.each do |inp,strat|
463
+ if inp.respond_to? :match
464
+ if m = @input.match(inp)
465
+ return strat.call(m, self)
466
+ end
467
+ elsif inp.respond_to? :call
468
+ if m = inp.call(@input)
469
+ return strat.call(m, self)
470
+ end
471
+ end
472
+ end
473
+ raise UnknownStrategy
474
+ end
374
475
  end
375
- throw(:next)
376
- end
377
476
 
477
+ class Session
478
+ attr_accessor :repo_name, :username, :continuous
479
+ attr_writer :tracker, :hooks
378
480
 
379
- def parse_shutdown
380
- if @input == "shutdown"
381
- # local
382
- Logger.log "Closing connection to #{@socket.peeraddr[2]}"
383
- Logger.log "Shutting down... (#{NOW})"
384
- Logger.log ""
385
- Logger.log ""
386
- # peer
387
- @socket.puts("Shutting down server")
388
- else
389
- return
390
- end
391
- shutdown
392
- end
481
+ def initialize
482
+ setup_singleton_methods
483
+ end
393
484
 
485
+ def tracker
486
+ @tracker ||= {}
487
+ end
394
488
 
395
- def shutdown
396
- @sockets.delete(@socket)
397
- @socket.close
398
- exit
399
- end
489
+ def hooks
490
+ @hooks ||= {}
491
+ end
400
492
 
493
+ def cleanup
494
+ reset_username
495
+ reset_repo_name
496
+ end
401
497
 
402
- def parse_hook
403
- if %r{hook add} =~ @input
404
- hook_add
405
- else
406
- return
407
- end
408
- end
498
+ private
499
+ def reset_username
500
+ self.username = CONFIG[:username]
501
+ end
409
502
 
503
+ def reset_repo_name
504
+ self.repo_name = ""
505
+ end
410
506
 
411
- # @hook_cmds:
412
- # repo is the key, value is array of directory and commands. First element
413
- # of array is the local directory for that remote repo, rest are commands
414
- # related to hooks called on change of commit message (with plans to change
415
- # that to commit SHA reference) of the remote repo
416
- def hook_add
417
- @input.gsub!(/diiv/, '/')
418
- # make match-$globals parse input
419
- @input =~ /add ([^\/]+\/\w+) (dir: (\S*) )?cmd: (.*)\Z/
420
- @hook_cmds ||= {}
421
- if $1 != nil && $4 != nil
422
- if @hook_cmds[$1]
423
- @hook_cmds[$1] << $4
424
- elsif $2 != nil
425
- @hook_cmds[$1] = [$3, $4]
426
- else
427
- @hook_cmds[$1] = [$4]
507
+ def setup_singleton_methods
508
+ tracker.singleton_class.class_eval do
509
+ def add_or_replace! repo_name, new_sha=nil
510
+ if Hash === repo_name
511
+ merge! repo_name
512
+ return true
513
+ else
514
+ if keys.include? repo_name and self[repo_name] == new_sha
515
+ return
516
+ elsif keys.include? repo_name
517
+ ret = {:replace => true}
518
+ else
519
+ ret = {:add => true}
520
+ end
521
+ end
522
+ merge! repo_name => new_sha
523
+ ret
524
+ end
525
+ end
526
+ end
527
+ end # end of Session class
528
+
529
+ def start(port, options={})
530
+ listen(port)
531
+ setup_env(options)
532
+ loop do
533
+ waiting = catch(:connect) do
534
+ look_for_changes unless @remote_connection
535
+ end
536
+ client_connect(@sockets) if waiting
537
+ strategy = Strategy.new(self)
538
+ strategy.call
539
+ @session.cleanup
428
540
  end
429
- @socket.puts("Hook added")
430
- else
431
- @socket.puts("Format: 'hook add user/repo [dir: /my/dir/repo ] cmd: git pull origin'")
432
541
  end
433
- throw(:next)
434
- end
435
- private :hook_add
436
542
 
543
+ # The track method closes over a list variable to store recent info on
544
+ # tracked repositories.
545
+ # Options: :sha, :latest, :full, :list (all boolean).
546
+ # The :list => true option gives back the commit object from the list.
437
547
 
438
- def save_hooks_or_repos
439
- if @input =~ %r{\A\s*save hook(s?) as (.+)\Z}
440
- if !@hook_cmds.nil? && !@hook_cmds.empty?
441
- File.open("#{ENV['HOME']}/.hubeye/hooks/#{$2}.yml", "w") do |f_out|
442
- ::YAML.dump(@hook_cmds, f_out)
443
- end
444
- @socket.puts("Saved hook#{$1} as #{$2}")
548
+ list = []
549
+
550
+ define_method :track do |repo, options={}|
551
+ if repo.include? '/'
552
+ username, repo_name = repo.split '/'
553
+ full_repo_name = repo
445
554
  else
446
- @socket.puts("No hook#{$1} to save")
555
+ username, repo_name = @session.username, repo
556
+ full_repo_name = "#{username}/#{repo_name}"
447
557
  end
448
- throw(:next)
449
- elsif @input =~ %r{\A\s*save repo(s?) as (.+)\Z}
450
- if !@hubeye_tracker.empty?
451
- File.open("#{ENV['HOME']}/.hubeye/repos/#{$2}.yml", "w") do |f_out|
452
- ::YAML.dump(@hubeye_tracker.eyeing, f_out)
558
+ unless options[:list]
559
+ hist = nil
560
+ begin
561
+ open "https://api.github.com/repos/#{username}/" \
562
+ "#{repo_name}/commits" do |f|
563
+ hist = JSON.parse f.read
564
+ end
565
+ rescue
566
+ @socket.puts "Not a Github repository name"
567
+ return
453
568
  end
454
- @socket.puts("Saved repo#{$1} as #{$2}")
569
+ new_info =
570
+ {full_repo_name =>
571
+ {'sha' => hist.first['sha'],
572
+ 'commit' =>
573
+ {'message' => hist.first['commit']['message'],
574
+ 'committer' => {'name' => hist.first['commit']['committer']['name']}
575
+ }
576
+ }
577
+ }
578
+ commit = Commit.new(new_info)
579
+ # update the list
580
+ list.reject! {|cmt| cmt.repo == full_repo_name}
581
+ list << commit
582
+ end
583
+ if options[:full]
584
+ # unsupported so far
585
+ raise ArgumentError.new
586
+ elsif options[:latest]
587
+ commit.dup
588
+ elsif options[:list]
589
+ list.each {|c| return c if c.repo == full_repo_name}
590
+ nil
455
591
  else
456
- @socket.puts("No remote repos are being tracked")
592
+ # default
593
+ Commit.new full_repo_name => {'sha' => hist.first['sha']}
457
594
  end
458
- throw(:next)
459
- else
460
- return
461
595
  end
462
- end
463
596
 
464
-
465
- # options are internal input: can be true or falselike.
466
- # When falselike (nil is default), it outputs to the client socket.
467
- # When truelike (such as when used internally), it outputs nothing.
468
- def load_hooks_or_repos(options={})
469
- opts = {:internal_input => nil, :internal_input_hooks => nil,
470
- :internal_input_repos => nil}.merge options
471
- if opts[:internal_input].nil?
472
- load_hooks_repos_from_terminal_input
473
- elsif opts[:internal_input]
474
- input = opts[:internal_input]
475
- if opts[:internal_input_hooks]
476
- load_hooks_from_internal_input(input)
477
- elsif opts[:internal_input_repos]
478
- load_repos_from_internal_input(input)
479
- end
597
+ private
598
+ def listen(port)
599
+ @server = TCPServer.open(port)
480
600
  end
481
- end
482
601
 
483
- def load_hooks_repos_from_terminal_input
484
- if @input =~ %r{\A\s*load hook(s?) (.+)\Z}
485
- hookfile = "#{ENV['HOME']}/.hubeye/hooks/#{$2}.yml"
486
-
487
- # establish non block-local scope
488
- newhooks = nil
489
-
490
- if File.exists?(hookfile)
491
- File.open(hookfile) do |f|
492
- newhooks = ::YAML.load(f)
493
- end
494
- @hook_cmds ||= {}
495
- @hook_cmds = newhooks.merge(@hook_cmds)
496
- @socket.puts("Loaded #{$1} #{$2}")
497
- else
498
- @socket.puts("No #{$1} file to load from")
499
- end
500
- throw(:next)
501
- elsif @input =~ %r{\A\s*load repo(s)? (.+)\Z}
502
- if File.exists? repo_file = "#{ENV['HOME']}/.hubeye/repos/#{$2}.yml"
503
- newrepos = nil
504
- File.open(repo_file) do |f|
505
- newrepos = ::YAML.load(f)
602
+ def setup_env(options={})
603
+ @daemonized = options[:daemon]
604
+ @sockets = [@server] # An array of sockets we'll monitor
605
+ @session = Session.new
606
+ unless CONFIG[:default_track].nil?
607
+ CONFIG[:default_track].each do |repo|
608
+ commit = track(repo)
609
+ @session.tracker.merge! commit.repo => commit.sha
506
610
  end
611
+ end
612
+ unless CONFIG[:load_hooks].empty?
613
+ hooks = CONFIG[:load_hooks].dup
614
+ session_load :hooks => hooks
615
+ end
616
+ unless CONFIG[:load_repos].empty?
617
+ repos = CONFIG[:load_repos].dup
618
+ session_load :repos => repos
619
+ end
620
+ @session.username = CONFIG[:username]
621
+ @remote_connection = false
622
+ end
507
623
 
508
- if !newrepos
509
- @socket.puts "Unable to load #{$2}: empty file"
510
- throw(:next)
624
+ def session_load options={}
625
+ opts = {:hooks => nil, :repos => nil}.merge options
626
+ if hooks = opts[:hooks]
627
+ hooks.each do |h|
628
+ strat = Strategy.new(self, :internal_input => "internal load hook #{h}")
629
+ strat.call
511
630
  end
512
- # newrepos is an array of repos to be tracked
513
- newrepos.each do |e|
514
- # append the repo name and the commit hash to the hubeye tracker
515
- # array, then inform the client of the newest commit message
516
- username, repo = e.split '/'
517
- gh_user = User.find(username)
518
- gh_repo = gh_user.repository repo
519
- new_commit = gh_repo.commits.first
520
- @hubeye_tracker.try_append_or_replace!(e, new_commit)
631
+ elsif repos = opts[:repos]
632
+ repos.each do |r|
633
+ strat = Strategy.new(self, :internal_input => "internal load repo #{r}")
634
+ strat.call
521
635
  end
522
- @socket.puts "Loaded #{$2}.\nTracking:\n#{@hubeye_tracker.eyeing.join ', '}"
523
636
  else
524
- # no repo file with that name
525
- @socket.puts("No file to load from")
637
+ raise ArgumentError.new "Must load either hooks or repos"
526
638
  end
527
- throw(:next)
528
639
  end
529
- return
530
- end
531
640
 
532
- def load_hooks_from_internal_input(input)
533
- if input.respond_to? :to_a
534
- input = input.to_a
535
- input.each do |hook|
536
- hookfile = "#{ENV['HOME']}/.hubeye/hooks/#{hook}.yml"
537
- newhook = nil
538
- if File.exists?(hookfile)
539
- File.open(hookfile) do |f|
540
- newhook = ::YAML.load(f)
641
+ #TODO: refactor this ugly, long method into a new class
642
+ def look_for_changes
643
+ # if no client is connected, but the commits array contains repos
644
+ if @sockets.size == 1 and !@session.tracker.empty?
645
+
646
+ loop do
647
+ sleep_amt = CONFIG[:oncearound] / @session.tracker.length
648
+ @session.tracker.each do |repo,sha|
649
+ start_time = Time.now
650
+ commit = track(repo, :latest => true)
651
+ api_time = (Time.now - start_time).to_i
652
+ if commit.sha == sha
653
+ (sleep_amt - api_time).times do
654
+ sleep 1
655
+ @remote_connection = client_ready(@sockets) ? true : false
656
+ throw(:connect, true) if @remote_connection
657
+ end
658
+ else
659
+ # There was a change to a tracked repository.
660
+ full_repo_name = commit.repo
661
+ commit_msg = commit.commit_message
662
+ committer = commit.committer_name
663
+ new_sha = commit.sha
664
+ change_msg = "Repo #{full_repo_name} has changed\nNew commit: " \
665
+ "#{commit_msg}\n=> #{committer}"
666
+ case CONFIG[:desktop_notification]
667
+ when "libnotify"
668
+ Autotest::GnomeNotify.notify("Hubeye", change_msg)
669
+ Logger.log_change(full_repo_name, commit_msg, committer)
670
+ when "growl"
671
+ Autotest::Growl.growl("Hubeye", change_msg)
672
+ Logger.log_change(full_repo_name, commit_msg, committer)
673
+ when nil
674
+ if @daemonized
675
+ Logger.log_change(full_repo_name, commit_msg, committer)
676
+ else
677
+ Logger.log_change(full_repo_name, commit_msg, committer, :include_terminal => true)
678
+ end
679
+ end
680
+ # execute any hooks for that repository
681
+ unless @session.hooks.nil? || @session.hooks.empty?
682
+ if hooks = @session.hooks[full_repo_name]
683
+ hooks = hooks.dup
684
+ hooks.each do |dir,cmds|
685
+ # execute() takes [commands], {options} where
686
+ # options = :directory and :repo
687
+ Hooks::Command.execute(cmds, :directory => dir, :repo => full_repo_name)
688
+ end
689
+ end
690
+ end
691
+ @session.tracker.add_or_replace!(full_repo_name, new_sha)
692
+ end
541
693
  end
542
- @hook_cmds ||= {}
543
- @hook_cmds = newhook.merge(@hook_cmds)
544
- else
545
- # do nothing because of no extra processing after this, newhook
546
- # can stay nil if the hook file doesn't exist
547
- end
694
+ end # end of (while remote_connection == false)
695
+ else
696
+ @remote_connection = client_ready(@sockets, :block => true) ? true : false
697
+ throw(:connect, true) if @remote_connection
548
698
  end
549
- else
550
- raise ArgumentError.new "#{input} must be array-like"
551
699
  end
552
- end
553
700
 
554
- def load_repos_from_internal_input(input)
555
- if input.respond_to? :to_a
556
- input = input.to_a
557
- newrepos = []
558
- input.each do |repo|
559
- repofile = "#{ENV['HOME']}/.hubeye/repos/#{repo}.yml"
560
- newrepo = nil
561
- if File.exists?(repofile)
562
- File.open(repofile) do |f|
563
- newrepo = ::YAML.load(f)
564
- end
565
- # empty repo file, go to next repo file in the array
566
- if !newrepo
567
- next
568
- else
569
- # append the newrepo array to the newrepos array
570
- newrepos << newrepo
571
- end
572
- else
573
- # file doesn't exist, next repo file
574
- next
575
- end
576
- end # end of input#each
577
- # flatten the newrepos array because it contains arrays
578
- newrepos.flatten!
579
- newrepos.each do |e|
580
- username, repo = e.split '/'
581
- gh_user = User.find(username)
582
- gh_repo = gh_user.repository repo
583
- new_commit = gh_repo.commits.first
584
- @hubeye_tracker.try_append_or_replace!(e, new_commit)
585
- end
586
- else
587
- raise ArgumentError.new "#{input} must be array-like"
701
+ def client_ready(sockets, options={})
702
+ if options[:block]
703
+ select(sockets, nil, nil)
704
+ else
705
+ select(sockets, nil, nil, 1)
706
+ end
588
707
  end
589
- end
590
708
 
591
- def hook_list
592
- if @input =~ %r{hook list}
593
- unless @hook_cmds.nil? || @hook_cmds.empty?
594
- format_string = ""
595
- @hook_cmds.each do |repo, ary|
596
- remote = repo
597
- if ary.first.include? '/'
598
- local = ary.first
599
- cmds = ary[1..-1]
709
+ def client_connect(sockets)
710
+ ready = select(sockets)
711
+ readable = ready[0]
712
+ readable.each do |socket|
713
+ if socket == @server
714
+ @socket = @server.accept
715
+ sockets << @socket
716
+ # Inform the client of connection
717
+ basic_inform = "Hubeye running on #{Socket.gethostname} as #{@session.username}"
718
+ if !@session.tracker.empty?
719
+ @socket.puts "#{basic_inform}\nTracking: #{@session.tracker.keys.join ', '}"
600
720
  else
601
- cmds = ary
602
- local = "N/A"
721
+ @socket.puts basic_inform
603
722
  end
604
- format_string += <<-EOS
605
- remote: #{remote}
606
- dir : #{local}
607
- cmds: #{cmds.each {|cmd| print cmd + ' ' }} \n
608
- EOS
723
+ if !@daemonized
724
+ puts "Client connected at #{NOW}"
725
+ end
726
+ @socket.flush
727
+ # And log the fact that the client connected
728
+ # if the client quit, do not wipe the log file
729
+ if @session.continuous
730
+ Logger.log "Accepted connection from #{@socket.peeraddr[2]} (#{NOW})"
731
+ else
732
+ # wipe the log file and start anew
733
+ Logger.relog "Accepted connection from #{@socket.peeraddr[2]} (#{NOW})"
734
+ end
735
+ Logger.log "local: #{@socket.addr}"
736
+ Logger.log "peer : #{@socket.peeraddr}"
609
737
  end
610
- @socket.puts(format_string)
611
- @socketspoke = true
612
- end
613
- else
614
- return
615
- end
616
- @socket.puts("No hooks") unless @socketspoke
617
- @socketspoke = nil
618
- throw(:next)
619
- end
620
-
621
-
622
- # show the client what repos (with commit messages)
623
- # they're tracking
624
- def tracking_list
625
- if @input =~ /\Atracking\s*\Z/
626
- list = @hubeye_tracker.eyeing.join ', '
627
- @socket.puts(list)
628
- throw(:next)
629
- else
630
- return
631
- end
632
- end
633
-
634
-
635
- # This means the user pressed '.' in the client,
636
- # wanting to track the pwd repo. The period is replaced
637
- # by 'pwd' in the client application and sent to @input because of
638
- # problems with the period getting stripped in TCP transit. The name
639
- # of the client's present working directory comes right after the 'pwd'.
640
- # Typing '.' in the client only works (ie: begins tracking the remote repo)
641
- # if the root directory of the git repository has the same name as one of
642
- # the user's github repositories.
643
- def parse_pwd
644
- if @input.match(/^pwd/)
645
- @repo_name = @input[3..-1]
646
- else
647
- return
648
- end
649
- return true
650
- end
651
-
652
-
653
- def parse_empty
654
- if @input == ''
655
- @socket.puts("")
656
- throw(:next)
657
- else
658
- return
659
- end
660
- end
661
-
662
-
663
- # Like the method parse_pwd, in which the client application replaces
664
- # the '.' with 'pwd' and sends that to this server instead, parse_remove
665
- # does pretty much the same thing with '/'. This is because of the slash
666
- # getting stripped in TCP transit. Here, the slash is replaced with 'diiv',
667
- # as this is unlikely to be included anywhere in a real username/repository
668
- # combination (or is it... duh duh DUUUUHH)
669
- # p.s. no it isn't
670
- def parse_remove
671
- if %r{\Arm ([\w-](diiv)?[\w-]*)\Z} =~ @input
672
- if $1.include?("diiv")
673
- @username, @repo_name = $1.split('diiv')
674
- else
675
- @username, @repo_name = "#{@username}/#{$1}".split('/')
676
738
  end
677
- rm = @hubeye_tracker.rm_repo("#{@username}/#{@repo_name}")
678
- if rm
679
- @socket.puts("Stopped watching repository #{@username}/#{@repo_name}")
680
- sleep 0.5
681
- throw(:next)
682
- else
683
- @socket.puts("Repository #{@username}/#{@repo_name} not currently being watched")
684
- throw(:next)
685
- end
686
- else
687
- return
688
739
  end
689
- end
690
740
 
741
+ end # of Server module
691
742
 
692
- def parse_fullpath_add
693
- if @input.include?('diiv')
694
- # includes a '/', such as rails/rails, but in the adding to tracker context
695
- @username, @repo_name = @input.split('diiv')
696
- else
697
- return
698
- end
699
- return true
700
- end
743
+ class HubeyeServer
744
+ include Server
701
745
 
702
-
703
- # if the input is not the above special scenarios
704
- def parse_add
705
- @repo_name = @input
706
- end
707
-
708
- def parse_doc
709
- @full_repo_name = "#{@username}/#{@repo_name}"
710
- begin
711
- gh_user = User.find(@username)
712
- gh_repo = gh_user.repository @repo_name
713
- rescue ArgumentError, Octopi::NotFound
714
- #Octopi library's User.find ArgumentError
715
- @socket.puts "Not a Github repository name"
716
- throw :next
746
+ def initialize(debug=true)
747
+ @debug = debug
717
748
  end
718
- new_commit = gh_repo.commits.first
719
- change_status = @hubeye_tracker.try_append_or_replace!(@full_repo_name, new_commit)
720
- # get commit info
721
- commit_msg = new_commit.message
722
- committer = new_commit.author['name']
723
- url = "https://www.github.com#{new_commit.url[0..-30]}"
724
- msg = "#{commit_msg}\n=> #{committer}"
725
- # new repo to track
726
- if change_status[:add]
727
- # log the fact that the user added a repo to be tracked
728
- Logger.log("Added to tracker: #{@full_repo_name} (#{NOW})")
729
- # show the user, via the client, the info and commit msg for the commit
730
- @socket.puts("#{msg}\n#{url}")
731
-
732
- # new commit to tracked repo
733
- elsif change_status[:replace]
734
- change_msg = "New commit on #{@full_repo_name}\n"
735
- change_msg << "#{msg}\n#{url}"
736
- @socket.puts(change_msg)
737
- begin
738
- # log to the logfile and tell the client
739
- if @daemonized
740
- Logger.log_change(@full_repo_name, commit_msg, committer)
741
- else
742
- Logger.log_change(@full_repo_name, commit_msg, committer,
743
- :include_terminal => true)
744
- end
745
- rescue
746
- throw :next
747
- end
748
- else
749
- # no change to the tracked repo
750
- @socket.puts("Repository #{@full_repo_name} has not changed")
751
- end
752
- end
753
749
 
754
- end # of Server module
755
-
756
- class HubeyeServer
757
- include Server
758
-
759
- def initialize(debug=true)
760
- @debug = debug
761
750
  end
762
-
763
751
  end
764
752