hubeye 0.1.0 → 0.2.5
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +1 -0
- data/VERSION.rb +2 -2
- data/bin/hubeye +14 -36
- data/lib/client/hubeye_client.rb +56 -51
- data/lib/config/parser.rb +17 -21
- data/lib/hooks/executer.rb +1 -1
- data/lib/hooks/git_hooks.rb +0 -4
- data/lib/log/logger.rb +3 -3
- data/lib/server/hubeye_server.rb +654 -666
- data/tasks/install.rb +16 -24
- data/test/config_parser.rb +62 -35
- metadata +15 -17
data/lib/server/hubeye_server.rb
CHANGED
@@ -1,764 +1,752 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
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
|
-
|
20
|
+
def sha
|
21
|
+
@sha ||= @raw_input[@repo]['sha']
|
22
|
+
end
|
72
23
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
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
|
-
|
89
|
-
|
90
|
-
|
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
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
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
|
-
|
119
|
-
|
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
|
-
|
132
|
-
|
133
|
-
|
104
|
+
class Strategy
|
105
|
+
UnknownStrategy = Class.new(StandardError)
|
106
|
+
attr_reader :server, :input
|
134
107
|
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
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
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
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
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
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
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
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
|
-
|
196
|
+
@socket.puts("No hook#{@matches[1]} to save")
|
180
197
|
end
|
181
|
-
end
|
182
|
-
|
183
|
-
|
184
|
-
|
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
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
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
|
-
|
206
|
-
|
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
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
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
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
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
|
-
|
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
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
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
|
-
@
|
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
|
-
|
276
|
-
end # end of (while remote_connection == false)
|
277
|
-
end
|
278
|
-
client_connect(@sockets)
|
279
|
-
end
|
280
|
-
|
311
|
+
end
|
281
312
|
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
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
|
-
|
305
|
-
|
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
|
-
|
309
|
-
|
310
|
-
|
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
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
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
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
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
|
-
|
380
|
-
|
381
|
-
|
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
|
-
|
396
|
-
|
397
|
-
|
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
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
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
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
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
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
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
|
-
@
|
555
|
+
username, repo_name = @session.username, repo
|
556
|
+
full_repo_name = "#{username}/#{repo_name}"
|
447
557
|
end
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
466
|
-
|
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
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
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
|
-
|
509
|
-
|
510
|
-
|
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
|
-
|
513
|
-
|
514
|
-
|
515
|
-
|
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
|
-
|
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
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
|
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
|
-
|
543
|
-
|
544
|
-
|
545
|
-
|
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
|
-
|
555
|
-
|
556
|
-
|
557
|
-
|
558
|
-
|
559
|
-
|
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
|
-
|
592
|
-
|
593
|
-
|
594
|
-
|
595
|
-
|
596
|
-
|
597
|
-
|
598
|
-
|
599
|
-
|
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
|
-
|
602
|
-
local = "N/A"
|
721
|
+
@socket.puts basic_inform
|
603
722
|
end
|
604
|
-
|
605
|
-
|
606
|
-
|
607
|
-
|
608
|
-
|
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
|
-
|
693
|
-
|
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
|
-
|
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
|
|