yggdrasil 0.0.0 → 0.0.1

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.
@@ -0,0 +1,62 @@
1
+ class Yggdrasil
2
+
3
+ # @param [Array] args
4
+ def update(args)
5
+ target_paths, options = parse_options(args,
6
+ {'--username'=>:username, '--password'=>:password,
7
+ '-r'=>:revision, '--revision'=>:revision,
8
+ '--non-interactive'=>:non_interactive?})
9
+ options = input_user_pass(options)
10
+ sync_mirror options
11
+
12
+ updates = Array.new
13
+ FileUtils.cd @mirror_dir do
14
+ out = system3("#@svn status -qu --depth infinity --no-auth-cache --non-interactive" +
15
+ " --username '#{options[:username]}' --password '#{options[:password]}'")
16
+ out.split(/\n/).each do |line|
17
+ updates << $1 if /^.*\*.*\s(\S+)\s*$/ =~ line
18
+ end
19
+ end
20
+
21
+ matched_updates = select_updates(updates, target_paths)
22
+
23
+ confirmed_updates = confirm_updates(matched_updates,options) do |relative_path|
24
+ FileUtils.cd @mirror_dir do
25
+ cmd = "#@svn diff"
26
+ cmd += " --no-auth-cache --non-interactive"
27
+ cmd += " --username #{options[:username]} --password #{options[:password]}"
28
+ if options.has_key?(:revision)
29
+ cmd += " --old=#{relative_path} --new=#{relative_path}@#{options[:revision]}"
30
+ else
31
+ cmd += " --old=#{relative_path} --new=#{relative_path}@HEAD"
32
+ end
33
+ puts system3(cmd)
34
+ end
35
+ end
36
+ # res == 'Y' or --non-interactive
37
+
38
+ return unless confirmed_updates
39
+ return if confirmed_updates == 0 # no files to update
40
+
41
+ cmd_arg = "#@svn update --no-auth-cache --non-interactive"
42
+ cmd_arg += " --username #{options[:username]} --password #{options[:password]}"
43
+ if options.has_key?(:revision)
44
+ cmd_arg += " -r #{options[:revision]}"
45
+ else
46
+ cmd_arg += " -r HEAD"
47
+ end
48
+ cmd_arg += ' ' + confirmed_updates.join(' ')
49
+ FileUtils.cd @mirror_dir do
50
+ puts system3(cmd_arg)
51
+
52
+ # reflect mirror to real file
53
+ confirmed_updates.each do |update_file|
54
+ if File.exist?(update_file)
55
+ FileUtils.copy_file @mirror_dir+'/'+update_file, '/'+update_file
56
+ else
57
+ system3 "rm -rf /#{update_file}"
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -1,3 +1,14 @@
1
- module Yggdrasil
2
- VERSION = "0.0.0"
1
+ class Yggdrasil
2
+ VERSION = "0.0.1"
3
+ CMD = File::basename($0)
4
+
5
+ def Yggdrasil.version
6
+ puts <<"EOS"
7
+ #{CMD}, version #{VERSION}
8
+
9
+ Copyright (C) 2012-2013 Tomohisa Kusukawa.
10
+ Yggdrasil is open source software, see https://github.com/tkusukawa/yggdrasil/
11
+
12
+ EOS
13
+ end
3
14
  end
data/lib/yggdrasil.rb CHANGED
@@ -1,5 +1,245 @@
1
+ require 'fileutils'
2
+ require "open3"
1
3
  require "yggdrasil/version"
4
+ require "yggdrasil/help"
5
+ require "yggdrasil/init"
6
+ require "yggdrasil/add"
7
+ require "yggdrasil/commit"
8
+ require "yggdrasil/cleanup"
9
+ require "yggdrasil/diff"
10
+ require "yggdrasil/list"
11
+ require "yggdrasil/log"
12
+ require "yggdrasil/status"
13
+ require "yggdrasil/update"
14
+ require "yggdrasil/revert"
2
15
 
3
- module Yggdrasil
4
- # Your code goes here...
16
+ class Yggdrasil
17
+
18
+ def Yggdrasil.command(args, input = nil)
19
+ $stdin = StringIO.new(input) if input != nil
20
+ ENV['LANG'] = 'en_US.UTF-8'
21
+
22
+ if args.size == 0
23
+ Yggdrasil::help([])
24
+ return
25
+ end
26
+ case args[0]
27
+ when 'add'
28
+ new.add(args[1..-1])
29
+ when 'cleanup'
30
+ new.cleanup(args[1..-1])
31
+ when 'commit', 'ci'
32
+ new.commit(args[1..-1])
33
+ when 'diff', 'di'
34
+ new.diff(args[1..-1])
35
+ when 'help', 'h', '?'
36
+ help(args[1..-1])
37
+ when 'init'
38
+ init(args[1..-1])
39
+ when 'list', 'ls'
40
+ new.list(args[1..-1])
41
+ when 'log'
42
+ new.log(args[1..-1])
43
+ when 'status', 'stat', 'st'
44
+ new.status(args[1..-1])
45
+ when 'revert'
46
+ new.revert(args[1..-1])
47
+ when 'update'
48
+ new.update(args[1..-1])
49
+ when 'version', '--version'
50
+ version
51
+ else
52
+ error "Unknown subcommand: '#{args[0]}'"
53
+ end
54
+ end
55
+
56
+ # @param [String] cmd
57
+ def Yggdrasil.system3(cmd, err_exit=true, stdin=nil)
58
+ if stdin.nil?
59
+ out,stat = Open3.capture2e cmd
60
+ else
61
+ out,stat = Open3.capture2e cmd, :stdin_data=>stdin
62
+ end
63
+ unless stat.success?
64
+ return nil unless err_exit
65
+ $stderr.puts "#{CMD} error: command failure: #{cmd}"
66
+ $stderr.puts "command output:"
67
+ $stderr.puts out
68
+ exit stat.exitstatus
69
+ end
70
+ return out
71
+ end
72
+
73
+ protected
74
+ # @param [String] msg
75
+ def Yggdrasil.error(msg)
76
+ puts "#{CMD} error: #{msg}"
77
+ puts
78
+ exit 1
79
+ end
80
+
81
+ def Yggdrasil.parse_options(args, valid_params)
82
+ options = Hash.new
83
+ pos = 0
84
+ while args.size > pos
85
+ if valid_params.has_key?(args[pos])
86
+ option_note = args[pos]
87
+ option_key = valid_params[option_note]
88
+ args = args[0...pos]+args[pos+1..-1]
89
+ if option_key.to_s[-1] == '?'
90
+ options[option_key] = true
91
+ else
92
+ error "Not enough arguments provided: #{option_note}" unless args.size > pos
93
+ option_value = args[pos].dup
94
+ args = args[0...pos]+args[pos+1..-1]
95
+ options[option_key] = option_value
96
+ end
97
+ next
98
+ end
99
+ pos += 1
100
+ end
101
+ return args, options
102
+ end
103
+
104
+ def Yggdrasil.input_user_pass(options)
105
+ until options.has_key?(:username) do
106
+ print "Input svn username: "
107
+ input = $stdin.gets
108
+ options[:username] = input.chomp
109
+ end
110
+ until options.has_key?(:password) do
111
+ print "Input svn password: "
112
+ #input = `sh -c 'read -s hoge;echo $hoge'`
113
+ system3 'stty -echo', false
114
+ input = $stdin.gets
115
+ system3 'stty echo', false
116
+ options[:password] = input.chomp
117
+ end
118
+ return options
119
+ end
120
+
121
+ def initialize
122
+
123
+ @config = read_config
124
+ ENV["PATH"] = @config[:path]
125
+ @svn = @config[:svn]
126
+ @repo = @config[:repo]
127
+ @current_dir = `readlink -f .`.chomp
128
+ @mirror_dir = ENV["HOME"]+"/.yggdrasil/mirror"
129
+ end
130
+
131
+ # load config value from config file
132
+ def read_config
133
+ @config=Hash.new
134
+ begin
135
+ config_file = open("#{ENV['HOME']}/.yggdrasil/config")
136
+ rescue
137
+ puts "#{CMD} error: can not open config file: #{ENV['HOME']}/.yggdrasil/config"
138
+ exit 1
139
+ end
140
+ l = 0
141
+ while (line = config_file.gets)
142
+ l += 1
143
+ next if /^\s*#.*$/ =~ line # comment line
144
+ if /^\s*(\S+)\s*=\s*(\S+).*$/ =~ line
145
+ @config[$1.to_sym] = $2
146
+ else
147
+ puts "#{CMD} error: syntax error. :#{ENV['HOME']}/.yggdrasil/config(#{l})"
148
+ exit 1
149
+ end
150
+ end
151
+ config_file.close
152
+ @config
153
+ end
154
+
155
+ def sync_mirror(options)
156
+ updates = Array.new
157
+ FileUtils.cd @mirror_dir do
158
+ out = system3("#@svn ls #@repo --depth infinity --no-auth-cache --non-interactive" +
159
+ " --username '#{options[:username]}' --password '#{options[:password]}'")
160
+ files = out.split(/\n/)
161
+ out = system3("#@svn status -q --depth infinity --no-auth-cache --non-interactive" +
162
+ " --username '#{options[:username]}' --password '#{options[:password]}'")
163
+ out.split(/\n/).each do |line|
164
+ files << $1 if /^.*\s(\S+)\s*$/ =~ line
165
+ end
166
+ files.sort!
167
+ files.uniq!
168
+ files.each do |file|
169
+ if !File.exist?('/'+file)
170
+ system3 "#@svn delete #{file} --force" +
171
+ " --no-auth-cache --non-interactive"
172
+ elsif File.file?('/'+file)
173
+ if !File.exist?(@mirror_dir+'/'+file)
174
+ system3 "#@svn revert --no-auth-cache --non-interactive #{file}"
175
+ end
176
+ FileUtils.copy_file '/'+file, @mirror_dir+'/'+file
177
+ end
178
+ end
179
+ out = system3("#@svn status -q --depth infinity --no-auth-cache --non-interactive" +
180
+ " --username '#{options[:username]}' --password '#{options[:password]}'")
181
+ out.split(/\n/).each do |line|
182
+ updates << $1 if /^.*\s(\S+)\s*$/ =~ line
183
+ end
184
+ end
185
+ updates
186
+ end
187
+
188
+ def select_updates(updates, target_paths)
189
+
190
+ target_relatives = Array.new
191
+ if target_paths.size == 0
192
+ target_relatives << @current_dir.sub(%r{^/},'')
193
+ else
194
+ target_paths.each do |path|
195
+ if %r{^/} =~ path
196
+ target_relatives << path.sub(%r{^/},'') # cut first '/'
197
+ else
198
+ target_relatives << @current_dir.sub(%r{^/},'') + '/' + path
199
+ end
200
+ end
201
+ end
202
+
203
+ # search updated files in the specified dir
204
+ cond = '^'+target_relatives.join('|^') # make reg exp
205
+ matched_updates = Array.new
206
+ updates.each do |update|
207
+ matched_updates << update if update.match(cond)
208
+ end
209
+
210
+ # search parent updates of matched updates
211
+ parents = Array.new
212
+ updates.each do |update|
213
+ matched_updates.each do |matched_update|
214
+ parents << update if matched_update.match("^#{update}/")
215
+ end
216
+ end
217
+ matched_updates += parents
218
+ matched_updates.sort.uniq
219
+ end
220
+
221
+ def confirm_updates(updates, options)
222
+ until options.has_key?(:non_interactive?)
223
+ puts
224
+ (0...updates.size).each do |i|
225
+ puts "#{i}:#{updates[i]}"
226
+ end
227
+ print "OK? [Y|n|<num to diff>]:"
228
+ res = $stdin.gets
229
+ return nil unless res
230
+ res.chomp!
231
+ return nil if res == 'n'
232
+ break if res == 'Y'
233
+ next unless updates[res.to_i]
234
+ if /^\d+$/ =~ res
235
+ yield updates[res.to_i]
236
+ end
237
+ end
238
+ # res == 'Y'
239
+ updates
240
+ end
241
+
242
+ def method_missing(action, *args)
243
+ Yggdrasil.__send__ action, *args
244
+ end
5
245
  end
data/spec/add_spec.rb ADDED
@@ -0,0 +1,25 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ describe Yggdrasil, "add" do
4
+ it '-------- add' do
5
+ puts '-------- add'
6
+ prepare_environment
7
+ init_yggdrasil
8
+ end
9
+
10
+ it 'should warn: add non-exist files' do
11
+ puts '---- should warn: add non-exist files'
12
+ out = catch_stdout{Yggdrasil.command %w{add hoge}}
13
+ out.should == "no such file: #{`readlink -f hoge`}"
14
+
15
+ out = catch_stdout{Yggdrasil.command %w{add /etc/hoge}}
16
+ out.should == "no such file: /etc/hoge\n"
17
+ end
18
+
19
+ it 'should success: add exist files' do
20
+ puts '---- should success: add exist files'
21
+ Yggdrasil.command %w{add Gemfile /etc/fstab /etc/fstab}
22
+ File.exist?("/tmp/yggdrasil-test/.yggdrasil/mirror#{`readlink -f Gemfile`.chomp}").should be_true
23
+ File.exist?("/tmp/yggdrasil-test/.yggdrasil/mirror#{`readlink -f /etc/fstab`.chomp}").should be_true
24
+ end
25
+ end
@@ -0,0 +1,23 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ describe Yggdrasil, "cleanup" do
4
+ it '-------- cleanup' do
5
+ puts '-------- cleanup'
6
+ prepare_environment
7
+ init_yggdrasil
8
+ end
9
+
10
+ it 'should success cleanup' do
11
+ puts "---- should success cleanup"
12
+ puts "-- rm .svn"
13
+ `rm -rf /tmp/yggdrasil-test/.yggdrasil/mirror/.svn`
14
+
15
+ puts "-- cleanup"
16
+ Yggdrasil.command %w{cleanup --username hoge --password foo}
17
+
18
+ puts "-- check .svn"
19
+ res = File.exist?("/tmp/yggdrasil-test/.yggdrasil/mirror/.svn")
20
+ p res
21
+ res.should == true
22
+ end
23
+ end
@@ -0,0 +1,129 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ describe Yggdrasil, "commit" do
4
+ it '-------- commit' do
5
+ puts '-------- commit'
6
+ prepare_environment
7
+
8
+ puts '-- init'
9
+ Yggdrasil.command %w{init} +
10
+ %w{--repo svn://localhost/tmp/yggdrasil-test/svn-repo/mng-repo/host-name/} +
11
+ %w{--username hoge --password foo}
12
+ end
13
+
14
+ it 'should commit added files' do
15
+ puts '---- should commit added files'
16
+ `echo hoge > /tmp/yggdrasil-test/A`
17
+ `echo foo > /tmp/yggdrasil-test/B`
18
+ FileUtils.cd "/tmp/yggdrasil-test" do
19
+ puts '-- add'
20
+ Yggdrasil.command %w{add A /tmp/yggdrasil-test/B}
21
+ end
22
+
23
+ puts "-- commit"
24
+ FileUtils.cd '/tmp/yggdrasil-test' do
25
+ Yggdrasil.command %w{commit --username hoge --password foo},
26
+ "0\nY\nadd A and B\n"
27
+ end
28
+
29
+ puts "\n-- check committed file 'tmp/yggdrasil-test/A'"
30
+ res = `svn cat file:///tmp/yggdrasil-test/svn-repo/mng-repo/host-name/tmp/yggdrasil-test/A`
31
+ puts res
32
+ res.should == "hoge\n"
33
+
34
+ puts "\n-- check committed file 'tmp/yggdrasil-test/B'"
35
+ res = `svn cat file:///tmp/yggdrasil-test/svn-repo/mng-repo/host-name/tmp/yggdrasil-test/B`
36
+ puts res
37
+ res.should == "foo\n"
38
+ end
39
+
40
+ it 'should commit modified file' do
41
+ puts "---- should commit modified file"
42
+ puts "-- modify"
43
+ `echo hoge >> /tmp/yggdrasil-test/A`
44
+
45
+ puts "-- commit"
46
+ Yggdrasil.command %w{commit / --username hoge --password foo},
47
+ "0\nY\nmodify A\n"
48
+
49
+ puts "\n-- check committed file 'tmp/yggdrasil-test/A'"
50
+ res = `svn cat file:///tmp/yggdrasil-test/svn-repo/mng-repo/host-name/tmp/yggdrasil-test/A`
51
+ puts res
52
+ res.should == "hoge\nhoge\n"
53
+ end
54
+
55
+ it 'should accept password interactive' do
56
+ puts "---- should accept password interactive"
57
+ `echo A >> /tmp/yggdrasil-test/A`
58
+
59
+ Yggdrasil.command %w{commit /tmp --username hoge},
60
+ "foo\nY\nmodify A\n" # interactive input: password,Y/n, commit message
61
+
62
+ puts "\n-- check committed file 'tmp/yggdrasil-test/A'"
63
+ res = `svn cat file:///tmp/yggdrasil-test/svn-repo/mng-repo/host-name/tmp/yggdrasil-test/A`
64
+ puts res
65
+ res.should == "hoge\nhoge\nA\n"
66
+ end
67
+
68
+ it 'should commit specified file only' do
69
+ puts "---- should commit specified file only"
70
+ `echo A >> /tmp/yggdrasil-test/A`
71
+ `echo B >> /tmp/yggdrasil-test/B`
72
+
73
+ Yggdrasil.command %w{commit
74
+ --username hoge --password foo -m modify /tmp/yggdrasil-test/B},
75
+ "0\nY\n"
76
+
77
+ puts "\n-- check committed file 'tmp/yggdrasil-test/B'"
78
+ res = `svn cat file:///tmp/yggdrasil-test/svn-repo/mng-repo/host-name/tmp/yggdrasil-test/B`
79
+ puts res
80
+ res.should == "foo\nB\n"
81
+ end
82
+
83
+ it 'should not commit deleted file' do
84
+ puts "---- should not commit deleted file"
85
+ `rm -f /tmp/yggdrasil-test/A`
86
+
87
+ Yggdrasil.command %w{commit --username hoge --password foo -m delete},
88
+ "0\nn\n"
89
+ puts "\n-- check file exists on repo"
90
+ res = `svn ls file:///tmp/yggdrasil-test/svn-repo/mng-repo/host-name/tmp/yggdrasil-test`
91
+ puts res
92
+ res.should == "A\nB\n"
93
+ end
94
+
95
+ it 'should commit deleted file' do
96
+ puts "---- should commit deleted file"
97
+ `echo hoge > /tmp/yggdrasil-test/A`
98
+ `rm -f /tmp/yggdrasil-test/B`
99
+
100
+ Yggdrasil.command %w{commit -m delete /tmp/yggdrasil-test} +
101
+ %w{--username hoge --password foo},
102
+ "0\n1\nY\n"
103
+
104
+ puts "\n-- check committed delete file"
105
+ res = `svn ls file:///tmp/yggdrasil-test/svn-repo/mng-repo/host-name/tmp/yggdrasil-test`
106
+ puts res
107
+ res.should == "A\n"
108
+ end
109
+
110
+ it 'should commit all files at once' do
111
+ puts "---- should revert all files at once"
112
+
113
+ `echo HOGE >> /tmp/yggdrasil-test/A`
114
+ `rm -f /tmp/yggdrasil-test/B`
115
+ `mkdir /tmp/yggdrasil-test/c`
116
+ `echo bar > /tmp/yggdrasil-test/c/C`
117
+ Yggdrasil.command %w{add /tmp/yggdrasil-test/c/C}
118
+
119
+ Yggdrasil.command %w{commit -m delete /tmp/yggdrasil-test/c/C} +
120
+ %w{--username hoge --password foo},
121
+ "0\n1\nY\n"
122
+
123
+ puts "\n-- check committed delete file"
124
+ res = `svn ls file:///tmp/yggdrasil-test/svn-repo/mng-repo/host-name/tmp/yggdrasil-test`
125
+ puts res
126
+ res.should == "A\nc/\n"
127
+ end
128
+
129
+ end
data/spec/diff_spec.rb ADDED
@@ -0,0 +1,104 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ describe Yggdrasil, "diff" do
4
+ it '-------- diff' do
5
+ puts '-------- diff'
6
+ prepare_environment
7
+ init_yggdrasil
8
+
9
+ # modify and not commit yet
10
+ `echo HOGE >> /tmp/yggdrasil-test/A`
11
+ `echo FOO >> /tmp/yggdrasil-test/B`
12
+ end
13
+
14
+ it 'should success diff (absolute/relative path)' do
15
+ puts "---- should success diff (absolute/relative path)"
16
+ out = catch_stdout do
17
+ FileUtils.cd "/tmp/yggdrasil-test" do
18
+ Yggdrasil.command(%w{diff /tmp/yggdrasil-test/A B --username hoge --password foo})
19
+ end
20
+ end
21
+ out.should == <<"EOS"
22
+ Index: tmp/yggdrasil-test/A
23
+ ===================================================================
24
+ --- tmp/yggdrasil-test/A (revision 3)
25
+ +++ tmp/yggdrasil-test/A (working copy)
26
+ @@ -1,2 +1,3 @@
27
+ hoge
28
+ hoge
29
+ +HOGE
30
+ Index: tmp/yggdrasil-test/B
31
+ ===================================================================
32
+ --- tmp/yggdrasil-test/B (revision 3)
33
+ +++ tmp/yggdrasil-test/B (working copy)
34
+ @@ -1,2 +1,3 @@
35
+ foo
36
+ foo
37
+ +FOO
38
+ EOS
39
+ end
40
+
41
+ it 'should success (no path)' do
42
+ puts "---- should success (no path)"
43
+ out = catch_stdout do
44
+ FileUtils.cd "/tmp/yggdrasil-test" do
45
+ Yggdrasil.command %w{diff --username hoge --password foo}
46
+ end
47
+ end
48
+ out.should == <<"EOS"
49
+ Index: tmp/yggdrasil-test/A
50
+ ===================================================================
51
+ --- tmp/yggdrasil-test/A\t(revision 3)
52
+ +++ tmp/yggdrasil-test/A\t(working copy)
53
+ @@ -1,2 +1,3 @@
54
+ hoge
55
+ hoge
56
+ +HOGE
57
+ Index: tmp/yggdrasil-test/B
58
+ ===================================================================
59
+ --- tmp/yggdrasil-test/B\t(revision 3)
60
+ +++ tmp/yggdrasil-test/B\t(working copy)
61
+ @@ -1,2 +1,3 @@
62
+ foo
63
+ foo
64
+ +FOO
65
+ EOS
66
+ end
67
+
68
+ it 'should success (-r)' do
69
+ puts "---- should success (-r)"
70
+ out = catch_stdout do
71
+ FileUtils.cd "/tmp/yggdrasil-test" do
72
+ Yggdrasil.command %w{diff -r 2:3 A --username hoge --password foo}
73
+ end
74
+ end
75
+ out.should == <<"EOS"
76
+ Index: tmp/yggdrasil-test/A
77
+ ===================================================================
78
+ --- tmp/yggdrasil-test/A (revision 2)
79
+ +++ tmp/yggdrasil-test/A (revision 3)
80
+ @@ -1 +1,2 @@
81
+ hoge
82
+ +hoge
83
+ EOS
84
+ end
85
+
86
+ it 'should success (--revision)' do
87
+ puts "---- should success (--revision)"
88
+ out = catch_stdout do
89
+ FileUtils.cd "/tmp/yggdrasil-test" do
90
+ Yggdrasil.command %w{diff --revision 3 A --username hoge --password foo}
91
+ end
92
+ end
93
+ out.should == <<"EOS"
94
+ Index: tmp/yggdrasil-test/A
95
+ ===================================================================
96
+ --- tmp/yggdrasil-test/A (revision 3)
97
+ +++ tmp/yggdrasil-test/A (working copy)
98
+ @@ -1,2 +1,3 @@
99
+ hoge
100
+ hoge
101
+ +HOGE
102
+ EOS
103
+ end
104
+ end