yggdrasil 0.0.0 → 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2012 tkusukawa
1
+ Copyright (c) 2012-2013 Tomohisa Kusukawa
2
2
 
3
3
  MIT License
4
4
 
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Yggdrasil
2
2
 
3
- TODO: Write a gem description
3
+ Yggdrasil is a subversion wrapper to manage configuration files.
4
4
 
5
5
  ## Installation
6
6
 
@@ -18,12 +18,53 @@ Or install it yourself as:
18
18
 
19
19
  ## Usage
20
20
 
21
- TODO: Write usage instructions here
21
+ * Install subversion
22
+ $ sudo yum install subversion
22
23
 
23
- ## Contributing
24
+ * Prepare subversion repository and init Yggdrasil.
25
+ $ svnadmin create ~/svn-repo
26
+ $ yggdrasil init --repo file://$HOME/svn-repo
27
+ You should use svn-server if you have.
28
+ In that case, the configuration files of
29
+ all the servers can be managed on the unification.
24
30
 
25
- 1. Fork it
26
- 2. Create your feature branch (`git checkout -b my-new-feature`)
27
- 3. Commit your changes (`git commit -am 'Added some feature'`)
28
- 4. Push to the branch (`git push origin my-new-feature`)
29
- 5. Create new Pull Request
31
+ * Add configuration files
32
+ $ yggdrasil add ~/.bashrc ..etc
33
+
34
+ * Check modify and/or delete
35
+ $ yggdrasil status /
36
+
37
+ * Refer Help
38
+ $ yggdrasil help
39
+
40
+ ## Environment
41
+
42
+ * Linux
43
+ * Subversion command-line client
44
+ * Ruby
45
+ * Gem
46
+
47
+ ## License
48
+
49
+ Copyright (c) 2012-2013 Tomohisa Kusukawa
50
+
51
+ MIT License
52
+
53
+ Permission is hereby granted, free of charge, to any person obtaining
54
+ a copy of this software and associated documentation files (the
55
+ "Software"), to deal in the Software without restriction, including
56
+ without limitation the rights to use, copy, modify, merge, publish,
57
+ distribute, sublicense, and/or sell copies of the Software, and to
58
+ permit persons to whom the Software is furnished to do so, subject to
59
+ the following conditions:
60
+
61
+ The above copyright notice and this permission notice shall be
62
+ included in all copies or substantial portions of the Software.
63
+
64
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
65
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
66
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
67
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
68
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
69
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
70
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile CHANGED
@@ -1,2 +1,10 @@
1
1
  #!/usr/bin/env rake
2
2
  require "bundler/gem_tasks"
3
+
4
+ Bundler.setup
5
+ require 'rspec/core/rake_task'
6
+
7
+ desc "run spec"
8
+ RSpec::Core::RakeTask.new(:spec) do |t|
9
+ t.rspec_opts = %w{-c -fs}
10
+ end
data/bin/yggdrasil CHANGED
@@ -1,3 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- puts 'Hello World!'
3
+ require 'yggdrasil'
4
+
5
+ Yggdrasil::command(ARGV)
@@ -0,0 +1,25 @@
1
+ class Yggdrasil
2
+
3
+ # @param [Array] args
4
+ def add(args)
5
+ add_relatives = Array.new
6
+ while (arg = args.shift)
7
+ file_path = `readlink -f #{arg}`.chomp
8
+ exit $?.exitstatus unless $?.success?
9
+ unless File.exist?(file_path)
10
+ puts "no such file: #{file_path}"
11
+ next
12
+ end
13
+ mirror_path = @mirror_dir + file_path
14
+ mirror_dir = File.dirname(mirror_path)
15
+ FileUtils.mkdir_p(mirror_dir) unless File.exist?(mirror_dir)
16
+ FileUtils.copy file_path, mirror_path
17
+ add_relatives << file_path.sub(%r{^/}, '')
18
+ end
19
+ if add_relatives.size != 0
20
+ FileUtils.cd @mirror_dir do
21
+ puts system3("#@svn add --no-auth-cache --non-interactive --parents #{add_relatives.join(' ')}")
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,19 @@
1
+ class Yggdrasil
2
+
3
+ # @param [Array] args
4
+ def cleanup(args)
5
+ args, options = parse_options(args, {'--username'=>:username, '--password'=>:password})
6
+ if args.size != 0
7
+ error "invalid arguments: #{args.join(',')}"
8
+ end
9
+
10
+ options = input_user_pass(options)
11
+
12
+ system3 "rm -rf #@mirror_dir"
13
+
14
+ system3 "#@svn checkout"\
15
+ " --no-auth-cache --non-interactive"\
16
+ " --username '#{options[:username]}' --password '#{options[:password]}'"\
17
+ " #@repo #@mirror_dir"
18
+ end
19
+ end
@@ -0,0 +1,34 @@
1
+ class Yggdrasil
2
+
3
+ # @param [Array] args
4
+ def commit(args)
5
+ target_paths, options = parse_options(args,
6
+ {'--username'=>:username, '--password'=>:password,
7
+ '-m'=>:message, '--message'=>:message, '--non-interactive'=>:non_interactive?})
8
+ options = input_user_pass(options)
9
+
10
+ updates = sync_mirror(options)
11
+ matched_updates = select_updates(updates, target_paths)
12
+
13
+ confirmed_updates = confirm_updates(matched_updates,options) do |relative_path|
14
+ FileUtils.cd @mirror_dir do
15
+ puts system3("#@svn diff --no-auth-cache --non-interactive #{relative_path}")
16
+ end
17
+ end
18
+ return unless confirmed_updates
19
+ return if confirmed_updates.size == 0
20
+
21
+ until options.has_key?(:message) do
22
+ print "Input log message: "
23
+ input = $stdin.gets
24
+ options[:message] = input.chomp
25
+ end
26
+
27
+ FileUtils.cd @mirror_dir do
28
+ puts system3 "#@svn commit -m '#{options[:message]}'"\
29
+ " --no-auth-cache --non-interactive"\
30
+ " --username '#{options[:username]}' --password '#{options[:password]}'"\
31
+ " #{confirmed_updates.join(' ')}"
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,29 @@
1
+ class Yggdrasil
2
+
3
+ # @param [Array] args
4
+ def diff(args)
5
+ args, options = parse_options(args,
6
+ {'--username'=>:username, '--password'=>:password, '-r'=>:revision, '--revision'=>:revision})
7
+ options = input_user_pass(options)
8
+
9
+ sync_mirror options
10
+
11
+ paths = Array.new
12
+ if args.size == 0
13
+ paths << @current_dir.sub(%r{^/}, '')
14
+ else
15
+ args.each do |path|
16
+ path = "#@current_dir/#{path}" unless %r{^/} =~ path
17
+ paths << path.sub(%r{^/}, '')
18
+ end
19
+ end
20
+
21
+ cmd_arg = "#@svn diff --no-auth-cache --non-interactive"
22
+ cmd_arg += " --username #{options[:username]} --password #{options[:password]}"
23
+ cmd_arg += " -r #{options[:revision]}" if options.has_key?(:revision)
24
+ cmd_arg += ' '+paths.join(' ')
25
+ FileUtils.cd @mirror_dir do
26
+ puts system3(cmd_arg)
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,190 @@
1
+ class Yggdrasil
2
+
3
+ HELP_SUBCOMMANDS = <<EOS
4
+ usage: #{CMD} <subcommand> [options] [args]
5
+ Yggdrasil version #{VERSION}
6
+ Type '#{CMD} help <subcommand>' for help on a specific subcommand.
7
+
8
+ Available subcommands:
9
+ add
10
+ cleanup
11
+ commit (ci)
12
+ diff (di)
13
+ help (?, h)
14
+ init
15
+ list (ls)
16
+ log
17
+ status (stat, st)
18
+ revert
19
+ update
20
+ version
21
+
22
+ Yggdrasil is a configuration management tool by Subversion.
23
+ You should type 'yggdrasil init' at first.
24
+
25
+ EOS
26
+
27
+ # @param [Array] args
28
+ def Yggdrasil.help(args)
29
+ if args.size == 0 then
30
+ puts HELP_SUBCOMMANDS
31
+ elsif args.size != 1 then
32
+ error "too many arguments."
33
+ else
34
+ case args[0]
35
+ when 'add'
36
+ puts <<"EOS"
37
+ add: Add files to management list (add to subversion)
38
+ usage #{CMD} add [FILES...]
39
+
40
+ EOS
41
+ when 'cleanup'
42
+ puts <<"EOS"
43
+ cleanup: clean up the working copy
44
+ usage: #{CMD} cleanup [OPTIONS...]
45
+
46
+ Valid options:
47
+ --username ARG : specify a username ARG
48
+ --password ARG : specify a password ARG
49
+
50
+ EOS
51
+ when 'commit', 'ci'
52
+ puts <<"EOS"
53
+ commit (ci): Send changes from your local file to the repository.
54
+ usage: #{CMD} commit [OPTIONS...] [FILES...]
55
+
56
+ Valid options:
57
+ --username ARG : specify a username ARG
58
+ --password ARG : specify a password ARG
59
+ -m [--message] ARG : specify log message ARG
60
+ --non-interactive : do no interactive prompting
61
+
62
+ EOS
63
+ when 'diff', 'di'
64
+ puts <<"EOS"
65
+ diff (di): Display the differences between two revisions or paths.
66
+ usage: #{CMD} diff [OPTIONS...] [PATH...]
67
+
68
+ Valid options:
69
+ --username ARG : specify a username ARG
70
+ --password ARG : specify a password ARG
71
+ -r [--revision] ARG : ARG (some commands also take ARG1:ARG2 range)
72
+ A revision argument can be one of:
73
+ NUMBER revision number
74
+ '{' DATE '}' revision at start of the date
75
+ 'HEAD' latest in repository
76
+ 'BASE' base rev of item's working copy
77
+ 'COMMITTED' last commit at or before BASE
78
+ 'PREV' revision just before COMMITTED
79
+
80
+ EOS
81
+ when 'help', '?', 'h'
82
+ puts <<"EOS"
83
+ help (?,h): Describe the usage of this program or its subcommands.
84
+ usage: #{CMD} help [SUBCOMMAND]
85
+
86
+ EOS
87
+ when 'init'
88
+ puts <<"EOS"
89
+ init: Check environment and initialize configuration.
90
+ usage: #{CMD} init [OPTIONS...]
91
+
92
+ Valid options:
93
+ --repo ARG : specify svn repository
94
+ --username ARG : specify a username ARG
95
+ --password ARG : specify a password ARG
96
+
97
+ EOS
98
+ when 'list', 'ls'
99
+ puts <<"EOS"
100
+ list (ls): List directory entries in the repository.
101
+ usage: #{CMD} list [OPTIONS...] [PATH...]
102
+
103
+ Valid options:
104
+ --username ARG : specify a username ARG
105
+ --password ARG : specify a password ARG
106
+ -r [--revision] ARG : ARG (some commands also take ARG1:ARG2 range)
107
+ A revision argument can be one of:
108
+ NUMBER revision number
109
+ '{' DATE '}' revision at start of the date
110
+ 'HEAD' latest in repository
111
+ 'BASE' base rev of item's working copy
112
+ 'COMMITTED' last commit at or before BASE
113
+ 'PREV' revision just before COMMITTED
114
+ -R [--recursive] : descend recursively, same as --depth=infinity
115
+ --depth ARG : limit operation by depth ARG ('empty', 'files',
116
+ 'immediates', or 'infinity')
117
+
118
+ EOS
119
+ when 'log'
120
+ puts <<"EOS"
121
+ log: Show the log messages for a set of revision(s) and/or file(s).
122
+ usage: #{CMD} log [OPTIONS...] [PATH]
123
+
124
+ Valid options:
125
+ --username ARG : specify a username ARG
126
+ --password ARG : specify a password ARG
127
+ -r [--revision] ARG : ARG (some commands also take ARG1:ARG2 range)
128
+ A revision argument can be one of:
129
+ NUMBER revision number
130
+ '{' DATE '}' revision at start of the date
131
+ 'HEAD' latest in repository
132
+ 'BASE' base rev of item's working copy
133
+ 'COMMITTED' last commit at or before BASE
134
+ 'PREV' revision just before COMMITTED
135
+
136
+ EOS
137
+ when 'status', 'stat', 'st'
138
+ puts <<"EOS"
139
+ status (stat, st): Print the status of managed files and directories.
140
+ usage: #{CMD} status [OPTIONS...] [PATH...]
141
+
142
+ Valid options:
143
+ --username ARG : specify a username ARG
144
+ --password ARG : specify a password ARG
145
+ --depth ARG : limit operation by depth ARG ('empty', 'files',
146
+ 'immediates', or 'infinity')
147
+
148
+ EOS
149
+ when 'revert'
150
+ puts <<"EOS"
151
+ revert: Restore pristine working copy file (undo most local edits).
152
+ usage: #{CMD} revert [PATH...]
153
+
154
+ Valid options:
155
+ --username ARG : specify a username ARG
156
+ --password ARG : specify a password ARG
157
+ --non-interactive : do no interactive prompting
158
+
159
+ EOS
160
+ when 'update'
161
+ puts <<"EOS"
162
+ update (up): Bring changes from the repository into the local files.
163
+ usage: #{CMD} update [PATH...]
164
+
165
+ Valid options:
166
+ --username ARG : specify a username ARG
167
+ --password ARG : specify a password ARG
168
+ -r [--revision] ARG : ARG (some commands also take ARG1:ARG2 range)
169
+ A revision argument can be one of:
170
+ NUMBER revision number
171
+ '{' DATE '}' revision at start of the date
172
+ 'HEAD' latest in repository
173
+ 'BASE' base rev of item's working copy
174
+ 'COMMITTED' last commit at or before BASE
175
+ 'PREV' revision just before COMMITTED
176
+ --non-interactive : do no interactive prompting
177
+
178
+ EOS
179
+ when 'version', '--version'
180
+ puts <<"EOS"
181
+ version: See the program version
182
+ usage: #{CMD} version
183
+
184
+ EOS
185
+ else
186
+ error "Unknown subcommand: '#{subcommand}'"
187
+ end
188
+ end
189
+ end
190
+ end
@@ -0,0 +1,83 @@
1
+ class Yggdrasil
2
+
3
+ # @param [Array] args
4
+ def Yggdrasil.init(args)
5
+ ENV['LANG'] = 'en_US.UTF-8'
6
+
7
+ args, options = parse_options(args,
8
+ {'--repo'=>:repo, '--username'=>:username, '--password'=>:password})
9
+ if args.size != 0
10
+ error "invalid arguments: #{args.join(',')}"
11
+ end
12
+
13
+ out = system3 'which svn'
14
+ svn = out.chomp
15
+
16
+ out = system3 'svn --version'
17
+ unless /version (\d+\.\d+\.\d+) / =~ out
18
+ puts "#{CMD} error: can not find version string: svn --version"
19
+ exit 1
20
+ end
21
+ svn_version=$1
22
+
23
+ config_dir = ENV["HOME"] + '/.yggdrasil'
24
+ if File.exist?(config_dir)
25
+ puts "#{CMD} error: already exist .yggdrasil directory: #{config_dir}"
26
+ exit 1
27
+ end
28
+
29
+ until options.has_key?(:repo) do
30
+ print "Input svn repo URL: "
31
+ input = $stdin.gets
32
+
33
+ unless /^(http:|file:|svn:)/ =~ input
34
+ puts "ERROR: Invalid URL."
35
+ redo
36
+ end
37
+ options[:repo] = input
38
+ end
39
+ options[:repo].chomp!
40
+ options[:repo].chomp!('/')
41
+
42
+ options = input_user_pass(options)
43
+
44
+ puts "SVN access test..."
45
+ loop do
46
+ ret = system3 "#{svn} ls --no-auth-cache --non-interactive"\
47
+ " --username '#{options[:username]}' --password '#{options[:password]}'"\
48
+ " #{options[:repo]}", false
49
+ unless ret.nil?
50
+ puts "SVN access: OK."
51
+ break
52
+ end
53
+
54
+ ret = system3 "#{svn} mkdir --parents -m 'yggdrasil init'"\
55
+ " --no-auth-cache --non-interactive"\
56
+ " --username '#{options[:username]}' --password '#{options[:password]}'"\
57
+ " #{options[:repo]}", false
58
+ unless ret.nil?
59
+ puts "SVN mkdir: OK."
60
+ break
61
+ end
62
+
63
+ puts "SVN error: can not access to '#{options[:repo]}'."
64
+ exit 1
65
+ end
66
+
67
+ Dir.mkdir config_dir, 0755
68
+ File.write config_dir+'/config',
69
+ "path=#{ENV['PATH']}\n"\
70
+ "svn=#{svn}\n"\
71
+ "svn_version=#{svn_version}\n"\
72
+ "repo=#{options[:repo]}\n"
73
+
74
+ ret = system3 "#{svn} checkout"\
75
+ " --no-auth-cache --non-interactive"\
76
+ " --username '#{options[:username]}' --password '#{options[:password]}'"\
77
+ " #{options[:repo]} #{config_dir+'/mirror'}", false
78
+ if ret.nil?
79
+ puts "SVN checkout: error."
80
+ exit 1
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,34 @@
1
+ class Yggdrasil
2
+
3
+ # @param [Array] args
4
+ def list(args)
5
+ args, options = parse_options(args,
6
+ {'--username'=>:username, '--password'=>:password,
7
+ '-r'=>:revision, '--revision'=>:revision,
8
+ '-R'=>:recursive?, '--recursive'=>:recursive?,
9
+ '--depth'=>:depth})
10
+ options = input_user_pass(options)
11
+
12
+ sync_mirror options
13
+
14
+ repos = Array.new
15
+ if args.size == 0
16
+ repos << @repo+@current_dir
17
+ else
18
+ args.each do |path|
19
+ path = "#@current_dir/#{path}" unless %r{^/} =~ path
20
+ repos << @repo+path
21
+ end
22
+ end
23
+
24
+ cmd_arg = "#@svn list --no-auth-cache --non-interactive"
25
+ cmd_arg += " --username #{options[:username]} --password #{options[:password]}"
26
+ cmd_arg += " -r #{options[:revision]}" if options.has_key?(:revision)
27
+ cmd_arg += " -R" if options.has_key?(:recursive?)
28
+ cmd_arg += " --depth #{options[:depth]}" if options.has_key?(:depth)
29
+ cmd_arg += ' ' + repos.join(' ')
30
+ FileUtils.cd @mirror_dir do
31
+ puts system3(cmd_arg)
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,34 @@
1
+ class Yggdrasil
2
+
3
+ # @param [Array] args
4
+ def log(args)
5
+ args, options = parse_options(args,
6
+ {'--username'=>:username, '--password'=>:password,
7
+ '-r'=>:revision, '--revision'=>:revision})
8
+ options = input_user_pass(options)
9
+
10
+ if args.size == 0
11
+ dir = @mirror_dir+@current_dir
12
+ error "current directory is not managed." unless File.exist?(dir)
13
+ args << dir
14
+ else
15
+ args.collect! do |arg|
16
+ if %r{^/} =~ arg
17
+ @mirror_dir+arg
18
+ else
19
+ @mirror_dir+@current_dir+'/'+arg
20
+ end
21
+ end
22
+ end
23
+
24
+ cmd_arg = "#@svn log --verbose --no-auth-cache --non-interactive"
25
+ cmd_arg += " --username #{options[:username]} --password #{options[:password]}"
26
+ if options.has_key?(:revision)
27
+ cmd_arg += " -r #{options[:revision]}"
28
+ else
29
+ cmd_arg += " -r HEAD:1"
30
+ end
31
+ cmd_arg += ' ' + args.join(' ')
32
+ puts system3(cmd_arg)
33
+ end
34
+ end
@@ -0,0 +1,47 @@
1
+ class Yggdrasil
2
+
3
+ # @param [Array] args
4
+ def revert(args)
5
+ target_paths, options = parse_options(args,
6
+ {'--username'=>:username, '--password'=>:password,
7
+ '--non-interactive'=>:non_interactive?})
8
+ options = input_user_pass(options)
9
+
10
+ updates = sync_mirror(options)
11
+ matched_updates = select_updates(updates, target_paths)
12
+
13
+ confirmed_updates = confirm_updates(matched_updates,options) do |relative_path|
14
+ FileUtils.cd @mirror_dir do
15
+ puts system3("#@svn diff --no-auth-cache --non-interactive #{relative_path}")
16
+ end
17
+ end
18
+
19
+ return unless confirmed_updates
20
+ return if confirmed_updates.size == 0
21
+
22
+ FileUtils.cd @mirror_dir do
23
+ system3 "#@svn revert"\
24
+ " --no-auth-cache --non-interactive"\
25
+ " --username '#{options[:username]}' --password '#{options[:password]}'"\
26
+ " #{confirmed_updates.reverse.join(' ')}"
27
+
28
+ # make ls hash
29
+ out = system3("#@svn ls --no-auth-cache --non-interactive"\
30
+ " --username '#{options[:username]}' --password '#{options[:password]}'"\
31
+ " --depth infinity #@repo")
32
+ ls_hash = Hash.new
33
+ out.split(/\n/).each {|relative| ls_hash[relative]=true}
34
+
35
+ # reflect mirror to real file
36
+ confirmed_updates.each do |file|
37
+ if ls_hash.has_key?(file)
38
+ if File.file?(@mirror_dir+'/'+file)
39
+ FileUtils.copy_file @mirror_dir+'/'+file, '/'+file
40
+ end
41
+ else
42
+ system3 "rm -rf #{@mirror_dir+'/'+file}"
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,30 @@
1
+ class Yggdrasil
2
+
3
+ # @param [Array] args
4
+ def status(args)
5
+ args, options = parse_options(args,
6
+ {'--username'=>:username, '--password'=>:password,
7
+ '--depth'=>:depth})
8
+ options = input_user_pass(options)
9
+
10
+ sync_mirror options
11
+
12
+ paths = String.new
13
+ if args.size == 0
14
+ paths += ' '+@current_dir.sub(%r{^/}, '')
15
+ else
16
+ args.each do |path|
17
+ path = "#@current_dir/#{path}" unless %r{^/} =~ path
18
+ paths += ' ' + path.sub(%r{^/}, '')
19
+ end
20
+ end
21
+
22
+ cmd_arg = "#@svn status#{paths} -qu --no-auth-cache --non-interactive"
23
+ cmd_arg += " --username #{options[:username]} --password #{options[:password]}"
24
+ cmd_arg += " --depth #{options[:depth]}" if options.has_key?(:depth)
25
+ FileUtils.cd @mirror_dir do
26
+ out = system3(cmd_arg)
27
+ print out.gsub(/^Status against revision:.*\n/, '')
28
+ end
29
+ end
30
+ end