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