yggdrasil 0.0.5 → 0.0.6
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +1 -1
- data/lib/yggdrasil.rb +97 -113
- data/lib/yggdrasil/add.rb +1 -1
- data/lib/yggdrasil/check.rb +65 -0
- data/lib/yggdrasil/cleanup.rb +2 -2
- data/lib/yggdrasil/commit.rb +19 -5
- data/lib/yggdrasil/diff.rb +2 -3
- data/lib/yggdrasil/help.rb +87 -27
- data/lib/yggdrasil/init.rb +56 -20
- data/lib/yggdrasil/list.rb +2 -3
- data/lib/yggdrasil/log.rb +2 -2
- data/lib/yggdrasil/revert.rb +4 -4
- data/lib/yggdrasil/status.rb +2 -2
- data/lib/yggdrasil/update.rb +7 -5
- data/lib/yggdrasil/version.rb +3 -4
- data/lib/yggdrasil_common.rb +102 -0
- data/lib/yggdrasil_server.rb +77 -0
- data/lib/yggdrasil_server/get_repo.rb +6 -0
- data/lib/yggdrasil_server/get_ro_id_pw.rb +9 -0
- data/lib/yggdrasil_server/init_server.rb +62 -0
- data/lib/yggdrasil_server/put_result.rb +16 -0
- data/lib/yggdrasil_server/results.rb +30 -0
- data/spec/check_spec.rb +145 -0
- data/spec/commit_spec.rb +18 -2
- data/spec/help_spec.rb +83 -6
- data/spec/init_server_spec.rb +82 -0
- data/spec/init_spec.rb +46 -1
- data/spec/list_spec.rb +5 -1
- data/spec/results_spec.rb +66 -0
- data/spec/server_spec.rb +158 -0
- metadata +21 -5
data/lib/yggdrasil/version.rb
CHANGED
@@ -1,10 +1,9 @@
|
|
1
1
|
class Yggdrasil
|
2
|
-
VERSION = "0.0.
|
3
|
-
CMD = File::basename($0)
|
2
|
+
VERSION = "0.0.6"
|
4
3
|
|
5
|
-
def
|
4
|
+
def version
|
6
5
|
puts <<"EOS"
|
7
|
-
|
6
|
+
#@base_cmd, version #{VERSION}
|
8
7
|
|
9
8
|
Copyright (C) 2012-2013 Tomohisa Kusukawa.
|
10
9
|
Yggdrasil is open source software, see https://github.com/tkusukawa/yggdrasil/
|
@@ -0,0 +1,102 @@
|
|
1
|
+
module YggdrasilCommon
|
2
|
+
|
3
|
+
=begin
|
4
|
+
def method_missing(action, *args)
|
5
|
+
self.class.send action, *args
|
6
|
+
end
|
7
|
+
=end
|
8
|
+
|
9
|
+
# load config value from config file
|
10
|
+
def read_config(config_file)
|
11
|
+
configs = Hash.new
|
12
|
+
begin
|
13
|
+
File.open(config_file) do |file|
|
14
|
+
l = 0
|
15
|
+
while (line = file.gets)
|
16
|
+
l += 1
|
17
|
+
next if /^\s*#.*$/ =~ line # comment line
|
18
|
+
if /^\s*(\S+)\s*=\s*(\S+).*$/ =~ line
|
19
|
+
key, val = $1, $2
|
20
|
+
key.gsub!(/-/, '_')
|
21
|
+
configs[key.to_sym] = val
|
22
|
+
else
|
23
|
+
error "syntax error. :#{config_file}(#{l})"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
rescue
|
28
|
+
error "can not open config file: #{config_file}"
|
29
|
+
end
|
30
|
+
configs
|
31
|
+
end
|
32
|
+
|
33
|
+
def parse_options(args, valid_params)
|
34
|
+
valid_params['--debug'] = :debug? # common
|
35
|
+
@options ||= Hash.new
|
36
|
+
pos = 0
|
37
|
+
while args.size > pos
|
38
|
+
if valid_params.has_key?(args[pos])
|
39
|
+
option_note = args[pos]
|
40
|
+
option_key = valid_params[option_note]
|
41
|
+
args = args[0...pos]+args[pos+1..-1]
|
42
|
+
if option_key.to_s[-1,1] == '?'
|
43
|
+
@options[option_key] = true
|
44
|
+
else
|
45
|
+
error "Not enough arguments provided: #{option_note}" unless args.size > pos
|
46
|
+
option_value = args[pos].dup
|
47
|
+
args = args[0...pos]+args[pos+1..-1]
|
48
|
+
@options[option_key] = option_value
|
49
|
+
end
|
50
|
+
next
|
51
|
+
end
|
52
|
+
pos += 1
|
53
|
+
end
|
54
|
+
args
|
55
|
+
end
|
56
|
+
|
57
|
+
def input_user_pass
|
58
|
+
until @options.has_key?(:username) do
|
59
|
+
error "Can't get username or password" if @options.has_key?(:non_interactive?)
|
60
|
+
print "Input svn username: "
|
61
|
+
input = $stdin.gets
|
62
|
+
error "can not input username" unless input
|
63
|
+
input.chomp!
|
64
|
+
return if input.size == 0
|
65
|
+
@options[:username] = @options[:ro_username] = input
|
66
|
+
end
|
67
|
+
until @options.has_key?(:password) do
|
68
|
+
error "Can't get username or password" if @options.has_key?(:non_interactive?)
|
69
|
+
print "Input svn password: "
|
70
|
+
#input = `sh -c 'read -s hoge;echo $hoge'`
|
71
|
+
system3 'stty -echo', false
|
72
|
+
input = $stdin.gets
|
73
|
+
system3 'stty echo', false
|
74
|
+
puts
|
75
|
+
error "can not input password" unless input
|
76
|
+
input.chomp!
|
77
|
+
@options[:password] = @options[:ro_password] = input
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
# @param [String] cmd
|
82
|
+
def system3(cmd, err_exit=true)
|
83
|
+
out = `#{cmd} 2>&1`
|
84
|
+
stat = $?
|
85
|
+
|
86
|
+
unless stat.success?
|
87
|
+
return nil unless err_exit
|
88
|
+
$stderr.puts "#@base_cmd error: command failure: #{cmd}"
|
89
|
+
$stderr.puts "command output:"
|
90
|
+
$stderr.puts out
|
91
|
+
exit stat.exitstatus
|
92
|
+
end
|
93
|
+
out
|
94
|
+
end
|
95
|
+
|
96
|
+
# @param [String] msg
|
97
|
+
def error(msg)
|
98
|
+
$stderr.puts "#@base_cmd error: #{msg}"
|
99
|
+
$stderr.puts
|
100
|
+
exit 1
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'socket'
|
2
|
+
|
3
|
+
require "yggdrasil_common"
|
4
|
+
|
5
|
+
require "yggdrasil_server/init_server"
|
6
|
+
require "yggdrasil_server/results"
|
7
|
+
|
8
|
+
require "yggdrasil_server/get_repo"
|
9
|
+
require "yggdrasil_server/get_ro_id_pw"
|
10
|
+
require "yggdrasil_server/put_result"
|
11
|
+
|
12
|
+
class YggdrasilServer
|
13
|
+
MESSAGE_QUIT = 'quit'
|
14
|
+
MESSAGES = {
|
15
|
+
:get_repo => [],
|
16
|
+
:get_ro_id_pw => [],
|
17
|
+
:put_result => [:hostname],
|
18
|
+
}
|
19
|
+
|
20
|
+
def initialize(exist_config = true)
|
21
|
+
@base_cmd = File::basename($0)
|
22
|
+
@current_dir = `readlink -f .`.chomp
|
23
|
+
@config_dir = "#{ENV["HOME"]}/.yggdrasil"
|
24
|
+
@server_config_file = "#@config_dir/server_config"
|
25
|
+
@results_dir = "#@config_dir/results"
|
26
|
+
|
27
|
+
return unless exist_config
|
28
|
+
configs = read_config(@server_config_file)
|
29
|
+
error "need 'port' in config file" unless (@port = configs[:port])
|
30
|
+
error "need 'repo' in config file" unless (@repo = configs[:repo])
|
31
|
+
@ro_username = configs[:ro_username] if configs.has_key?(:ro_username)
|
32
|
+
@ro_password = configs[:ro_password] if configs.has_key?(:ro_password)
|
33
|
+
end
|
34
|
+
|
35
|
+
def server(args)
|
36
|
+
args = parse_options(args, {'--debug'=>:debug?})
|
37
|
+
if args.size != 0
|
38
|
+
error "invalid arguments: #{args.join(',')}"
|
39
|
+
end
|
40
|
+
|
41
|
+
puts "Start: yggdrasil server (port:#@port)"
|
42
|
+
TCPServer.do_not_reverse_lookup = true
|
43
|
+
s0 = TCPServer.open(@port.to_i)
|
44
|
+
loop do
|
45
|
+
sock = s0.accept
|
46
|
+
msg = sock.gets # first line
|
47
|
+
if msg && msg.chomp! != MESSAGE_QUIT
|
48
|
+
msg.chomp!
|
49
|
+
puts "RCV: #{msg}"
|
50
|
+
msg_parts = msg.split
|
51
|
+
if msg_parts.size != 0
|
52
|
+
msg_cmd = msg_parts[0]
|
53
|
+
part_names = MESSAGES[msg_cmd.to_sym]
|
54
|
+
if (msg_parts.size - 1) == part_names.size
|
55
|
+
# make hash of args
|
56
|
+
msg_arg_hash = Hash.new
|
57
|
+
(0...part_names.size).each do |i|
|
58
|
+
msg_arg_hash[part_names[i]] = msg_parts[i+1]
|
59
|
+
end
|
60
|
+
|
61
|
+
# execute request (msg_cmd == method name)
|
62
|
+
send msg_cmd, sock, msg_arg_hash
|
63
|
+
else
|
64
|
+
puts "fail: number of arguments is mismatch: #{msg}"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
sock.close
|
69
|
+
break if @options.has_key?(:debug?) && msg == MESSAGE_QUIT
|
70
|
+
end
|
71
|
+
s0.close # MESSAGE_QUIT
|
72
|
+
end
|
73
|
+
|
74
|
+
protected
|
75
|
+
include YggdrasilCommon
|
76
|
+
end
|
77
|
+
|
@@ -0,0 +1,62 @@
|
|
1
|
+
class YggdrasilServer
|
2
|
+
|
3
|
+
# @param [Array] args
|
4
|
+
def init_server(args)
|
5
|
+
|
6
|
+
args = parse_options(args,
|
7
|
+
{'--port'=>:port, '--repo'=>:repo,
|
8
|
+
'--ro-username'=>:ro_username, '--ro-password'=>:ro_password})
|
9
|
+
if args.size != 0
|
10
|
+
error "invalid arguments: #{args.join(',')}"
|
11
|
+
end
|
12
|
+
|
13
|
+
if !@options.has_key?(:ro_username) && @options.has_key?(:ro_password)
|
14
|
+
error "--ro-password option need --ro-username, too."
|
15
|
+
end
|
16
|
+
|
17
|
+
until @options.has_key?(:port)
|
18
|
+
print "Input tcp port number: "
|
19
|
+
input = $stdin.gets
|
20
|
+
error "can not input tcp port number" unless input
|
21
|
+
|
22
|
+
input.chomp!
|
23
|
+
unless %r{\d+} =~ input && input.to_i < 0x10000
|
24
|
+
puts "ERROR: Invalid port number."
|
25
|
+
redo
|
26
|
+
end
|
27
|
+
@options[:port] = input # string
|
28
|
+
end
|
29
|
+
|
30
|
+
until @options.has_key?(:repo)
|
31
|
+
print "Input svn repo URL: "
|
32
|
+
input = $stdin.gets
|
33
|
+
error "can not input svn repo URL." unless input
|
34
|
+
|
35
|
+
unless %r{^(http://|https://|file://|svn://)} =~ input
|
36
|
+
puts "ERROR: Invalid URL."
|
37
|
+
redo
|
38
|
+
end
|
39
|
+
@options[:repo] = input
|
40
|
+
end
|
41
|
+
@options[:repo].chomp!
|
42
|
+
@options[:repo].chomp!('/')
|
43
|
+
|
44
|
+
unless @options.has_key?(:ro_password)
|
45
|
+
puts "Input read-only username/password (clients use this to read repo)."
|
46
|
+
puts "ATTENTION! username/password are stored to disk unencrypted!"
|
47
|
+
input_user_pass
|
48
|
+
end
|
49
|
+
|
50
|
+
# make config file
|
51
|
+
Dir.mkdir @config_dir, 0755 unless File.exist?(@config_dir)
|
52
|
+
File.open(@server_config_file, "w") do |f|
|
53
|
+
f.write "port=#{@options[:port]}\n"\
|
54
|
+
"repo=#{@options[:repo]}\n"
|
55
|
+
if @options.has_key?(:ro_password)
|
56
|
+
f.write "ro_username=#{@options[:ro_username]}\n"\
|
57
|
+
"ro_password=#{@options[:ro_password]}\n"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
@@ -0,0 +1,16 @@
|
|
1
|
+
class YggdrasilServer
|
2
|
+
def put_result(sock, arg_hash = {})
|
3
|
+
result_string = ""
|
4
|
+
while (line=sock.gets)
|
5
|
+
result_string << line
|
6
|
+
end
|
7
|
+
# make result file
|
8
|
+
Dir.mkdir @results_dir, 0755 unless File.exist?(@results_dir)
|
9
|
+
result_file = "#@results_dir/#{arg_hash[:hostname]}_#{sock.peeraddr[3]}"
|
10
|
+
File.delete result_file if File.exist?(result_file)
|
11
|
+
File.open(result_file, "w") do |f|
|
12
|
+
f.write result_string
|
13
|
+
end
|
14
|
+
arg_hash
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
class YggdrasilServer
|
2
|
+
|
3
|
+
# @param [Array] args
|
4
|
+
def results(args)
|
5
|
+
args = parse_options(args, {'--limit'=>:limit})
|
6
|
+
|
7
|
+
if args.size != 0
|
8
|
+
error "invalid arguments: #{args.join(',')}"
|
9
|
+
end
|
10
|
+
|
11
|
+
return unless File.exist?(@results_dir)
|
12
|
+
files = Dir.entries(@results_dir)
|
13
|
+
files.each do |file|
|
14
|
+
next if /^\./ =~ file
|
15
|
+
absolute = "#@results_dir/#{file}"
|
16
|
+
if @options.has_key?(:limit)
|
17
|
+
stat = File.stat(absolute)
|
18
|
+
if stat.mtime < (Time.now - @options[:limit].to_i * 60)
|
19
|
+
puts "######## #{file} TOO OLD: #{stat.mtime.to_s}"
|
20
|
+
next
|
21
|
+
end
|
22
|
+
end
|
23
|
+
buf = File.read("#@results_dir/#{file}")
|
24
|
+
if buf.gsub(/\s*\n/m, '') != ''
|
25
|
+
puts "######## #{file} Mismatch:"
|
26
|
+
puts buf
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
data/spec/check_spec.rb
ADDED
@@ -0,0 +1,145 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
|
3
|
+
describe Yggdrasil, "check" do
|
4
|
+
it '-------- check' do
|
5
|
+
puts '-------- check'
|
6
|
+
prepare_environment
|
7
|
+
init_yggdrasil
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'should execute checker and svn add the result' do
|
11
|
+
puts "\n---- should execute checker and svn add the result"
|
12
|
+
`rm -f /tmp/yggdrasil-test/.yggdrasil/checker/gem_list`
|
13
|
+
`echo 'echo hoge' > /tmp/yggdrasil-test/.yggdrasil/checker/hoge`
|
14
|
+
`chmod +x /tmp/yggdrasil-test/.yggdrasil/checker/hoge`
|
15
|
+
|
16
|
+
cmd = %w{check --username hoge --password foo}
|
17
|
+
out = catch_out {Yggdrasil.command(cmd)}
|
18
|
+
out.should == <<"EOS"
|
19
|
+
A 0 tmp/yggdrasil-test/.yggdrasil/checker_result
|
20
|
+
A 0 tmp/yggdrasil-test/.yggdrasil/checker_result/hoge
|
21
|
+
A 0 tmp/yggdrasil-test/.yggdrasil
|
22
|
+
|
23
|
+
Index: tmp/yggdrasil-test/.yggdrasil/checker_result/hoge
|
24
|
+
===================================================================
|
25
|
+
--- tmp/yggdrasil-test/.yggdrasil/checker_result/hoge (revision 0)
|
26
|
+
+++ tmp/yggdrasil-test/.yggdrasil/checker_result/hoge (revision 0)
|
27
|
+
@@ -0,0 +1 @@
|
28
|
+
+hoge
|
29
|
+
EOS
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'should commit the checker result' do
|
33
|
+
puts "\n---- should commit the checker result"
|
34
|
+
cmd = %w{commit / --username hoge --password foo --non-interactive -m add\ checker}
|
35
|
+
out = catch_out {Yggdrasil.command cmd}
|
36
|
+
|
37
|
+
out.should == <<"EOS"
|
38
|
+
Adding tmp/yggdrasil-test/.yggdrasil
|
39
|
+
Adding tmp/yggdrasil-test/.yggdrasil/checker_result
|
40
|
+
Adding tmp/yggdrasil-test/.yggdrasil/checker_result/hoge
|
41
|
+
Transmitting file data .
|
42
|
+
Committed revision 4.
|
43
|
+
EOS
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'should delete result if checker deleted' do
|
47
|
+
puts "\n---- should delete result if checker deleted"
|
48
|
+
`rm -f /tmp/yggdrasil-test/.yggdrasil/checker/hoge`
|
49
|
+
cmd = %w{check --username hoge --password foo}
|
50
|
+
out = catch_out {Yggdrasil.command(cmd)}
|
51
|
+
out.should == <<"EOS"
|
52
|
+
D 4 tmp/yggdrasil-test/.yggdrasil/checker_result/hoge
|
53
|
+
|
54
|
+
Index: tmp/yggdrasil-test/.yggdrasil/checker_result/hoge
|
55
|
+
===================================================================
|
56
|
+
--- tmp/yggdrasil-test/.yggdrasil/checker_result/hoge (revision 4)
|
57
|
+
+++ tmp/yggdrasil-test/.yggdrasil/checker_result/hoge (working copy)
|
58
|
+
@@ -1 +0,0 @@
|
59
|
+
-hoge
|
60
|
+
EOS
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'should commit the checker result(delete)' do
|
64
|
+
puts "\n---- should commit the checker result(delete)"
|
65
|
+
cmd = %w{commit / --username hoge --password foo --non-interactive -m delete\ checker}
|
66
|
+
out = catch_out {Yggdrasil.command cmd}
|
67
|
+
|
68
|
+
out.should == <<"EOS"
|
69
|
+
Deleting tmp/yggdrasil-test/.yggdrasil/checker_result/hoge
|
70
|
+
|
71
|
+
Committed revision 5.
|
72
|
+
EOS
|
73
|
+
end
|
74
|
+
|
75
|
+
it 'should record check result by yggdrasil server' do
|
76
|
+
puts "\n---- should record check result by yggdrasil server"
|
77
|
+
|
78
|
+
prepare_environment
|
79
|
+
|
80
|
+
sock = 0
|
81
|
+
begin
|
82
|
+
sock = TCPSocket.open("localhost", 4000)
|
83
|
+
rescue
|
84
|
+
puts "OK. no server"
|
85
|
+
else
|
86
|
+
puts "NG. zombie server. try quit"
|
87
|
+
sock.puts("quit")
|
88
|
+
sock.close
|
89
|
+
end
|
90
|
+
|
91
|
+
Yggdrasil.command %w{init-server} +
|
92
|
+
%w{--port 4000} +
|
93
|
+
%w{--repo svn://localhost/tmp/yggdrasil-test/svn-repo/servers/{HOST}/}+
|
94
|
+
%w{--ro-username hoge --ro-password foo},
|
95
|
+
"\n\n"
|
96
|
+
fork do
|
97
|
+
Yggdrasil.command %w{server --debug}
|
98
|
+
end
|
99
|
+
|
100
|
+
sleep 1
|
101
|
+
Yggdrasil.command %w{init --debug --server localhost:4000} +
|
102
|
+
%w{--username hoge --password foo},
|
103
|
+
"Y\nhoge\nfoo\n"
|
104
|
+
`rm -f /tmp/yggdrasil-test/.yggdrasil/checker/gem_list`
|
105
|
+
Yggdrasil.command %w{check}
|
106
|
+
|
107
|
+
sleep 1
|
108
|
+
File.exist?("/tmp/yggdrasil-test/.yggdrasil/results").should be_true
|
109
|
+
files = Dir.entries("/tmp/yggdrasil-test/.yggdrasil/results")
|
110
|
+
result_files = files.select{|file| %r{^#{Socket.gethostname}} =~ file}
|
111
|
+
result_files.size.should == 1
|
112
|
+
`cat /tmp/yggdrasil-test/.yggdrasil/results/#{result_files[0]}`.should == <<"EOS"
|
113
|
+
A 0 tmp/yggdrasil-test
|
114
|
+
A 0 tmp/yggdrasil-test/.yggdrasil
|
115
|
+
A 0 tmp/yggdrasil-test/.yggdrasil/checker_result
|
116
|
+
A 0 tmp
|
117
|
+
|
118
|
+
EOS
|
119
|
+
|
120
|
+
`echo hoge > /tmp/yggdrasil-test/A`
|
121
|
+
Yggdrasil.command %w{add /tmp/yggdrasil-test/A}
|
122
|
+
Yggdrasil.command %w{commit --username hoge --password foo /},
|
123
|
+
"Y\nHOGE\n"
|
124
|
+
|
125
|
+
`echo foo >> /tmp/yggdrasil-test/A`
|
126
|
+
Yggdrasil.command %w{check}
|
127
|
+
|
128
|
+
`cat /tmp/yggdrasil-test/.yggdrasil/results/#{result_files[0]}`.should == <<"EOS"
|
129
|
+
M 2 tmp/yggdrasil-test/A
|
130
|
+
|
131
|
+
Index: tmp/yggdrasil-test/A
|
132
|
+
===================================================================
|
133
|
+
--- tmp/yggdrasil-test/A (revision 2)
|
134
|
+
+++ tmp/yggdrasil-test/A (working copy)
|
135
|
+
@@ -1 +1,2 @@
|
136
|
+
hoge
|
137
|
+
+foo
|
138
|
+
EOS
|
139
|
+
|
140
|
+
sock = TCPSocket.open("localhost", 4000)
|
141
|
+
sock.puts("quit")
|
142
|
+
sock.close
|
143
|
+
Process.waitall
|
144
|
+
end
|
145
|
+
end
|