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.
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