yggdrasil 0.0.5 → 0.0.6
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 +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
|