hubeye 0.3.3 → 0.3.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/README.md +59 -40
- data/VERSION.rb +2 -2
- data/bin/hubeye +7 -12
- data/lib/hubeye/client/client.rb +3 -5
- data/lib/hubeye/config/environment.rb +0 -1
- data/lib/hubeye/hooks/{executer.rb → command.rb} +8 -4
- data/lib/hubeye/hooks/git.rb +19 -0
- data/lib/hubeye/log/logger.rb +1 -0
- data/lib/hubeye/server/commit.rb +17 -39
- data/lib/hubeye/server/server.rb +79 -531
- data/lib/hubeye/server/session.rb +5 -32
- data/lib/hubeye/server/strategies/add_hook.rb +32 -0
- data/lib/hubeye/server/strategies/add_repo.rb +91 -0
- data/lib/hubeye/server/strategies/decision.rb +121 -0
- data/lib/hubeye/server/strategies/exit.rb +23 -0
- data/lib/hubeye/server/strategies/list_hooks.rb +38 -0
- data/lib/hubeye/server/strategies/list_tracking.rb +27 -0
- data/lib/hubeye/server/strategies/load_hook.rb +34 -0
- data/lib/hubeye/server/strategies/load_repo.rb +35 -0
- data/lib/hubeye/server/strategies/next.rb +13 -0
- data/lib/hubeye/server/strategies/rm_repo.rb +34 -0
- data/lib/hubeye/server/strategies/save_hook.rb +31 -0
- data/lib/hubeye/server/strategies/save_repo.rb +31 -0
- data/lib/hubeye/server/strategies/shutdown.rb +23 -0
- data/lib/hubeye/server/tracker.rb +89 -0
- data/tasks/install.rb +8 -9
- data/test/helpers/server.rb +39 -0
- metadata +19 -4
- data/lib/hubeye/hooks/git_hooks.rb +0 -19
@@ -2,24 +2,21 @@ module Hubeye
|
|
2
2
|
module Server
|
3
3
|
class Session
|
4
4
|
attr_accessor :repo_name, :username, :continuous
|
5
|
-
attr_writer
|
5
|
+
attr_writer :hooks
|
6
6
|
|
7
7
|
def initialize
|
8
|
-
|
9
|
-
end
|
10
|
-
|
11
|
-
def tracker
|
12
|
-
@tracker ||= {}
|
8
|
+
defaults!
|
13
9
|
end
|
14
10
|
|
15
11
|
def hooks
|
16
12
|
@hooks ||= {}
|
17
13
|
end
|
18
14
|
|
19
|
-
def
|
15
|
+
def defaults!
|
20
16
|
reset_username
|
21
17
|
reset_repo_name
|
22
18
|
end
|
19
|
+
alias cleanup! defaults!
|
23
20
|
|
24
21
|
private
|
25
22
|
def reset_username
|
@@ -30,30 +27,6 @@ module Hubeye
|
|
30
27
|
self.repo_name = ""
|
31
28
|
end
|
32
29
|
|
33
|
-
|
34
|
-
tracker.singleton_class.class_eval do
|
35
|
-
def add_or_replace! input, new_sha=nil
|
36
|
-
if Hash === input and new_sha.nil?
|
37
|
-
repo = input.keys.first
|
38
|
-
hash = true
|
39
|
-
else
|
40
|
-
repo = input
|
41
|
-
two_args = true
|
42
|
-
end
|
43
|
-
if keys.include? repo and self[repo] == new_sha
|
44
|
-
return
|
45
|
-
elsif keys.include? repo
|
46
|
-
ret = {:replace => true}
|
47
|
-
else
|
48
|
-
ret = {:add => true}
|
49
|
-
end
|
50
|
-
two_args ? merge!(repo => new_sha) : merge!(input)
|
51
|
-
ret
|
52
|
-
end
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
end # end of Session class
|
30
|
+
end
|
57
31
|
end
|
58
32
|
end
|
59
|
-
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Hubeye
|
2
|
+
module Server
|
3
|
+
module Strategies
|
4
|
+
|
5
|
+
class AddHook
|
6
|
+
def call
|
7
|
+
cwd = File.expand_path('.')
|
8
|
+
repo_name = @matches[1]
|
9
|
+
directory = @matches[3]
|
10
|
+
command = @matches[4]
|
11
|
+
hooks = session.hooks
|
12
|
+
if repo_name.nil? and command.nil?
|
13
|
+
socket.deliver "Format: 'hook add user/repo [dir: /my/dir/repo ] cmd: some_cmd'"
|
14
|
+
return
|
15
|
+
end
|
16
|
+
directory = directory || cwd
|
17
|
+
if hooks[repo_name]
|
18
|
+
if hooks[repo_name][directory]
|
19
|
+
hooks[repo_name][directory] << command
|
20
|
+
else
|
21
|
+
hooks[repo_name][directory] = [command]
|
22
|
+
end
|
23
|
+
else
|
24
|
+
hooks[repo_name] = {directory => [command]}
|
25
|
+
end
|
26
|
+
socket.deliver "Hook added"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
3
|
+
module Hubeye
|
4
|
+
module Server
|
5
|
+
module Strategies
|
6
|
+
|
7
|
+
class AddRepo
|
8
|
+
STATES = [:added, :replaced, :unchanged, :invalid].freeze
|
9
|
+
ADDED = "%{committer} => %{message}\n".freeze
|
10
|
+
REPLACED = "New commit on %s\n".freeze
|
11
|
+
UNCHANGED = "Repository %s hasn't changed\n".freeze
|
12
|
+
INVALID = "%s isn't a valid Github repository\n".freeze
|
13
|
+
|
14
|
+
# Add the given space-separated Github repo(s) if they're valid
|
15
|
+
def call
|
16
|
+
repo_names_given = @matches[1].split
|
17
|
+
@multiple_repos_given = repo_names_given.size > 1
|
18
|
+
@unique_repo_names = Set.new
|
19
|
+
# hash of states associated with repository names.
|
20
|
+
# Ex: {:added => ['luke-gru/hubeye'], :replaced => ['rails/rails'], ...}
|
21
|
+
@states_with_repos = Hash[STATES.map {|s| [ s, [] ]}]
|
22
|
+
repo_names_given.each do |name|
|
23
|
+
full_name = server.full_repo_name(name)
|
24
|
+
@unique_repo_names << full_name
|
25
|
+
end
|
26
|
+
add_repos
|
27
|
+
gather_output
|
28
|
+
socket.deliver @output
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def add_repos
|
34
|
+
@unique_repo_names.each do |full_name|
|
35
|
+
change_state = tracker << full_name
|
36
|
+
@states_with_repos[change_state] << full_name
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def gather_output
|
41
|
+
@output = ''
|
42
|
+
STATES.each do |state|
|
43
|
+
@states_with_repos[state].each do |full_name|
|
44
|
+
@output << header(full_name) if @multiple_repos_given
|
45
|
+
case state
|
46
|
+
when :added
|
47
|
+
cmt = tracker.commit(full_name)
|
48
|
+
@output << ADDED % {:committer => cmt.committer_name, :message => cmt.message}
|
49
|
+
when :replaced
|
50
|
+
cmt = tracker.commit(full_name)
|
51
|
+
@output << REPLACED % full_name
|
52
|
+
log_change(full_name, cmt)
|
53
|
+
when :unchanged
|
54
|
+
@output << UNCHANGED % full_name
|
55
|
+
when :invalid
|
56
|
+
@output << INVALID % full_name
|
57
|
+
end
|
58
|
+
@output << "\n" if @multiple_repos_given && full_name != last_repo_outputted
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def header(full_name)
|
64
|
+
''.tap do |output|
|
65
|
+
output << full_name + "\n"
|
66
|
+
output << ('=' * full_name.length) + ("\n" * 2)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def log_change(repo_name, cmt)
|
71
|
+
if server.daemonized
|
72
|
+
Logger.log_change("#{repo_name}", cmt.message, cmt.committer_name)
|
73
|
+
else
|
74
|
+
Logger.log_change("#{repo_name}", cmt.message, cmt.committer_name,
|
75
|
+
:include_terminal => true)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def last_repo_outputted
|
80
|
+
return @last if @last
|
81
|
+
STATES.reverse_each do |state|
|
82
|
+
repo_names = @states_with_repos[state]
|
83
|
+
next if repo_names.last.nil?
|
84
|
+
return (@last = repo_names.last)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end # end of AddRepo
|
88
|
+
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,121 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
|
3
|
+
$decision_relatives = Dir.glob( File.join(File.expand_path("../", __FILE__), '*') )
|
4
|
+
$decision_relatives.each {|file| require file }
|
5
|
+
|
6
|
+
module Hubeye
|
7
|
+
module Server
|
8
|
+
module Strategies
|
9
|
+
|
10
|
+
# STRATEGIES hash
|
11
|
+
# ===============
|
12
|
+
# keys: input matches
|
13
|
+
# OR
|
14
|
+
# lambda {|input| input.something?} => SomeStrategy.new(decision)
|
15
|
+
#
|
16
|
+
# values: lambda {|decision, matches| SomeStrategy.new(decision, matches)}
|
17
|
+
STRATEGIES = {
|
18
|
+
%r{\Ashutdown\Z} => lambda {|d, m| Shutdown.new(d, m)},
|
19
|
+
%r{\Aquit|exit\Z} => lambda {|d, m| Exit.new(d, m)},
|
20
|
+
%r{\Atracking\s*\Z} => lambda {|d, m| ListTracking.new(d, m)},
|
21
|
+
%r{\Atracking\s*-d\Z} => lambda {|d, m| ListTracking.new(d, m, :details => true)},
|
22
|
+
%r{\Aadd (.*)} => lambda {|d, m| AddRepo.new(d, m)},
|
23
|
+
%r{\A\s*save hook(s?) as (.+)\Z} => lambda {|d, m| SaveHook.new(d, m)},
|
24
|
+
%r{\A\s*save repo(s?) as (.+)\Z} => lambda {|d, m| SaveRepo.new(d, m)},
|
25
|
+
%r{\A\s*load hook(s?) (.+)\Z} => lambda {|d, m| LoadHook.new(d, m)},
|
26
|
+
%r{\A\s*load repo(s?) (.+)\Z} => lambda {|d, m| LoadRepo.new(d, m)},
|
27
|
+
%r{\A\s*internal load hook(s?) (.+)\Z} => lambda {|d, m| LoadHook.new(d, m, :internal => true)},
|
28
|
+
%r{\A\s*internal load repo(s?) (.+)\Z} => lambda {|d, m| LoadRepo.new(d, m, :internal => true)},
|
29
|
+
%r{\Ahook add ([-\w]+/[-\w]+) (dir:\s?(.*))?\s*cmd:\s?(.*)\Z} => lambda {|d, m| AddHook.new(d, m)},
|
30
|
+
%r{\Ahook list\Z} => lambda {|d, m| ListHooks.new(d, m)},
|
31
|
+
%r{^\s*$} => lambda {|d, m| Next.new(d, m)},
|
32
|
+
%r{\Arm\s*-a\Z} => lambda {|d, m| RmRepo.new(d, m, :all => true)},
|
33
|
+
%r{\Arm ([-\w]+/?[-\w]*)\Z} => lambda {|d, m| RmRepo.new(d, m)},
|
34
|
+
# if all else fails, try to add the input as a repo
|
35
|
+
%r{\A(.*)} => lambda {|d, m| AddRepo.new(d, m)},
|
36
|
+
}
|
37
|
+
|
38
|
+
class Decision
|
39
|
+
extend Forwardable
|
40
|
+
attr_reader :server, :input
|
41
|
+
InvalidInput = Class.new(StandardError)
|
42
|
+
|
43
|
+
def_delegators :@server, :tracker, :session, :sockets, :socket
|
44
|
+
|
45
|
+
def initialize(server, options={})
|
46
|
+
@server = server
|
47
|
+
opts = {:internal_input => nil}.merge options
|
48
|
+
invalid_input = lambda {
|
49
|
+
@server.remote_connection = false
|
50
|
+
throw(:invalid_input)
|
51
|
+
}
|
52
|
+
|
53
|
+
if !opts[:internal_input]
|
54
|
+
begin
|
55
|
+
@input = socket.read_all
|
56
|
+
rescue => e
|
57
|
+
STDOUT.puts e
|
58
|
+
invalid_input.call
|
59
|
+
end
|
60
|
+
# check if the client pressed ^C or ^D
|
61
|
+
if @input.nil?
|
62
|
+
invalid_input.call
|
63
|
+
end
|
64
|
+
else
|
65
|
+
@input = opts[:internal_input]
|
66
|
+
end
|
67
|
+
@input = @input.strip.downcase
|
68
|
+
@input.gsub! /diiv/, '/'
|
69
|
+
end
|
70
|
+
|
71
|
+
# Get all the strategy classes from the files names in the /server/strategies/
|
72
|
+
# directory
|
73
|
+
|
74
|
+
@@strategy_classes = []
|
75
|
+
relatives = $decision_relatives.dup
|
76
|
+
relatives.delete_if {|file| file == __FILE__}
|
77
|
+
|
78
|
+
relatives.each do |file|
|
79
|
+
strat = File.basename(file)
|
80
|
+
class_name = strat.sub(/\.rb/, '').split('_').map(&:capitalize).join
|
81
|
+
@@strategy_classes << class_name
|
82
|
+
end
|
83
|
+
|
84
|
+
@@strategy_classes.each do |class_name|
|
85
|
+
klass = Hubeye::Server::Strategies.const_get(class_name)
|
86
|
+
klass.class_eval do
|
87
|
+
extend Forwardable
|
88
|
+
attr_reader :server
|
89
|
+
|
90
|
+
def_delegators :@decision, :input
|
91
|
+
def_delegators :@server, :tracker, :session, :sockets, :socket
|
92
|
+
|
93
|
+
def initialize decision, matches=nil, options={}
|
94
|
+
@decision = decision
|
95
|
+
@matches = matches
|
96
|
+
@options = options
|
97
|
+
@server = @decision.server
|
98
|
+
call
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def call_strategy
|
104
|
+
STRATEGIES.each do |inp,strat|
|
105
|
+
if inp.respond_to? :match
|
106
|
+
if m = @input.match(inp)
|
107
|
+
return strat.call(self, m)
|
108
|
+
end
|
109
|
+
elsif inp.respond_to? :call
|
110
|
+
if inp.call(@input)
|
111
|
+
return strat.call(self)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
raise InvalidInput
|
116
|
+
end
|
117
|
+
end # end of Decision
|
118
|
+
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Hubeye
|
2
|
+
module Server
|
3
|
+
module Strategies
|
4
|
+
|
5
|
+
class Exit
|
6
|
+
def call
|
7
|
+
socket.deliver "Bye!"
|
8
|
+
# mark the session as continuous to not wipe the log file
|
9
|
+
session.continuous = true
|
10
|
+
server.remote_connection = false
|
11
|
+
Logger.log "Closing connection to #{socket.peeraddr[2]}"
|
12
|
+
unless tracker.empty?
|
13
|
+
Logger.log "Tracking: #{tracker.repo_names.join ', '}"
|
14
|
+
end
|
15
|
+
Logger.log ""
|
16
|
+
sockets.delete(socket)
|
17
|
+
socket.close
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Hubeye
|
2
|
+
module Server
|
3
|
+
module Strategies
|
4
|
+
|
5
|
+
class ListHooks
|
6
|
+
def call
|
7
|
+
hooks = session.hooks
|
8
|
+
if hooks.empty?
|
9
|
+
socket.deliver "No hooks"
|
10
|
+
return
|
11
|
+
end
|
12
|
+
pwd = File.expand_path('.')
|
13
|
+
format_string = ""
|
14
|
+
hooks.each do |repo, hash|
|
15
|
+
local_dir = nil
|
16
|
+
command = nil
|
17
|
+
hash.each do |dir,cmd|
|
18
|
+
if dir.nil?
|
19
|
+
local_dir = pwd
|
20
|
+
command = cmd.join("\n" + (' ' * 8))
|
21
|
+
else
|
22
|
+
command = cmd
|
23
|
+
local_dir = dir
|
24
|
+
end
|
25
|
+
end
|
26
|
+
format_string << <<EOS
|
27
|
+
remote: #{repo}
|
28
|
+
dir: #{local_dir}
|
29
|
+
cmds: #{command}\n
|
30
|
+
EOS
|
31
|
+
end
|
32
|
+
socket.deliver format_string
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Hubeye
|
2
|
+
module Server
|
3
|
+
module Strategies
|
4
|
+
|
5
|
+
class ListTracking
|
6
|
+
def call
|
7
|
+
output = ''
|
8
|
+
if @options[:details]
|
9
|
+
commit_list = tracker.commit_list
|
10
|
+
commit_list.each do |cmt|
|
11
|
+
output << cmt.repo_name + "\n"
|
12
|
+
underline = '=' * cmt.repo_name.length
|
13
|
+
output << underline + "\n\n"
|
14
|
+
output << (cmt.committer_name + " => ") + (cmt.message + "\n")
|
15
|
+
output << "\n" unless cmt.repo_name == commit_list.last.repo_name
|
16
|
+
end
|
17
|
+
else
|
18
|
+
output << tracker.repo_names.join(', ')
|
19
|
+
end
|
20
|
+
output = "none" if output.empty?
|
21
|
+
socket.deliver output
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
module Hubeye
|
4
|
+
module Server
|
5
|
+
module Strategies
|
6
|
+
|
7
|
+
class LoadHook
|
8
|
+
def call
|
9
|
+
if _t = @options[:internal]
|
10
|
+
@silent = _t
|
11
|
+
end
|
12
|
+
hookfile = "#{ENV['HOME']}/.hubeye/hooks/#{@matches[2]}.yml"
|
13
|
+
new_hooks = nil
|
14
|
+
if File.exists?(hookfile)
|
15
|
+
File.open(hookfile) do |f|
|
16
|
+
new_hooks = YAML.load(f)
|
17
|
+
end
|
18
|
+
# need to fix this to check if there are already commands for that
|
19
|
+
# repo
|
20
|
+
session.hooks.merge!(new_hooks)
|
21
|
+
unless @silent
|
22
|
+
socket.deliver "Loaded #{@matches[1]} #{@matches[2]}"
|
23
|
+
end
|
24
|
+
else
|
25
|
+
unless @silent
|
26
|
+
socket.deliver "No #{@matches[1]} file to load from"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
module Hubeye
|
4
|
+
module Server
|
5
|
+
module Strategies
|
6
|
+
|
7
|
+
class LoadRepo
|
8
|
+
def call
|
9
|
+
if _t = @options[:internal]
|
10
|
+
@silent = _t
|
11
|
+
end
|
12
|
+
if File.exists?(repo_file = "#{ENV['HOME']}/.hubeye/repos/#{@matches[2]}.yml")
|
13
|
+
new_repos = nil
|
14
|
+
File.open(repo_file) do |f|
|
15
|
+
new_repos = YAML.load(f)
|
16
|
+
end
|
17
|
+
if !new_repos
|
18
|
+
socket.deliver "Unable to load #{@matches[2]}: empty file" unless @silent
|
19
|
+
return
|
20
|
+
end
|
21
|
+
new_repos.each do |r|
|
22
|
+
tracker << server.full_repo_name(r)
|
23
|
+
end
|
24
|
+
unless @silent
|
25
|
+
socket.deliver "Loaded #{@matches[2]}.\nTracking:\n#{tracker.repo_names.join ', '}"
|
26
|
+
end
|
27
|
+
else
|
28
|
+
socket.deliver "No file to load from" unless @silent
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|