hubeye 0.1.0 → 0.2.5

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.
@@ -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