svnauto 1.0.0

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,108 @@
1
+ ################################################################################
2
+ #
3
+ # Copyright (C) 2006 Peter J Jones (pjones@pmade.com)
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining
6
+ # a copy of this software and associated documentation files (the
7
+ # "Software"), to deal in the Software without restriction, including
8
+ # without limitation the rights to use, copy, modify, merge, publish,
9
+ # distribute, sublicense, and/or sell copies of the Software, and to
10
+ # permit persons to whom the Software is furnished to do so, subject to
11
+ # the following conditions:
12
+ #
13
+ # The above copyright notice and this permission notice shall be
14
+ # included in all copies or substantial portions of the Software.
15
+ #
16
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
+ #
24
+ ################################################################################
25
+ module SC
26
+ ################################################################################
27
+ class Repository
28
+ ################################################################################
29
+ # The URL to the repository
30
+ attr_accessor :url
31
+
32
+ ################################################################################
33
+ # The name the user gave for this repository
34
+ attr_accessor :name
35
+
36
+ ################################################################################
37
+ # The local directory where checkouts happen
38
+ attr_accessor :workspace
39
+
40
+ ################################################################################
41
+ # Additional options to send to svn
42
+ attr_accessor :options
43
+
44
+ ################################################################################
45
+ # Prompt the user to enter the necessary attributes for a repository
46
+ def self.ask
47
+ options = {}
48
+
49
+ options[:name] = Constants::TERMINAL.ask("Repository Name (used with sc -r): ")
50
+ options[:url] = Constants::TERMINAL.ask("Repository URL: ")
51
+
52
+ url = URI.parse(options[:url])
53
+ if url.scheme.nil? and Constants::TERMINAL.agree("Repository URL is missing protocol, assume it's local? ")
54
+ url.scheme = 'file'
55
+ end
56
+
57
+ if url.scheme.downcase == 'file'
58
+ url.path = File.expand_path(url.path)
59
+
60
+ if !File.exist?(url.path) and Constants::TERMINAL.agree("Local repository does not exist, should I create it? ")
61
+ create_local(url.path)
62
+ end
63
+
64
+ options[:url] = "file://#{url.path}"
65
+ end
66
+
67
+ options[:workspace] = Constants::TERMINAL.ask("Directory where checkouts go (can be relative to ~/): ") do |q|
68
+ q.default = default_workspace(options[:name])
69
+ end
70
+
71
+ self.new(options)
72
+ end
73
+
74
+ ################################################################################
75
+ def self.create_local (path)
76
+ Dir.mkdir(path) unless File.exist?(path)
77
+ system('svnadmin', 'create', path, '--fs-type', 'fsfs') or exit 1
78
+ end
79
+
80
+ ################################################################################
81
+ def self.default_workspace (name)
82
+ "develop/#{name.gsub(/[^\w_-]+/, '_').downcase}"
83
+ end
84
+
85
+ ################################################################################
86
+ # setup the details for a repository object
87
+ def initialize (options={})
88
+ @url = options[:url]
89
+ @name = options[:name]
90
+ @workspace = options[:workspace]
91
+ @options = options[:options]
92
+ end
93
+
94
+ ################################################################################
95
+ # get the full path for the workspace
96
+ def workspace
97
+ Path.absolute_from_home(@workspace || self.class.default_workspace(@name))
98
+ end
99
+
100
+ ################################################################################
101
+ # convert the repository to a string
102
+ def to_s
103
+ "#{@name} (#{@url})"
104
+ end
105
+
106
+ end
107
+ end
108
+ ################################################################################
@@ -0,0 +1,96 @@
1
+ ################################################################################
2
+ #
3
+ # Copyright (C) 2006 Peter J Jones (pjones@pmade.com)
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining
6
+ # a copy of this software and associated documentation files (the
7
+ # "Software"), to deal in the Software without restriction, including
8
+ # without limitation the rights to use, copy, modify, merge, publish,
9
+ # distribute, sublicense, and/or sell copies of the Software, and to
10
+ # permit persons to whom the Software is furnished to do so, subject to
11
+ # the following conditions:
12
+ #
13
+ # The above copyright notice and this permission notice shall be
14
+ # included in all copies or substantial portions of the Software.
15
+ #
16
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
+ #
24
+ ################################################################################
25
+ module SC
26
+ class Svn
27
+ ################################################################################
28
+ # Run a svn subcommand
29
+ def self._svn (*args)
30
+ subcommand = caller(0)[0].sub(/^.*:in\s`([^']+).*$/, '\1')
31
+ shell_line = "#{subcommand} #{args.join(' ')}"
32
+
33
+ Constants::TERMINAL.say(Constants::TERMINAL.color("svn #{shell_line}", :yellow)) if $DEBUG
34
+
35
+ if block_given?
36
+ `svn #{shell_line} 2>&1`.each {|line| yield(line)}
37
+ else
38
+ system("svn #{shell_line}")
39
+ end
40
+
41
+ if $? != 0 and !['info', 'list', 'merge'].include?(subcommand)
42
+ raise "svn command failed: #{shell_line}"
43
+ end
44
+
45
+ $? == 0
46
+ end
47
+
48
+ ################################################################################
49
+ # Check to see if the given path exists in the repository
50
+ def self.has_path (path)
51
+ self.list(path) do |line|
52
+ return false if line.match(/^svn:/)
53
+ end
54
+
55
+ true
56
+ end
57
+
58
+ ################################################################################
59
+ # create a branch using svn copy
60
+ def self.branch (project, source, dest)
61
+ return if self.has_path(dest)
62
+
63
+ relative_dest = dest.sub("#{project.url}/", '')
64
+ dest_dirs = relative_dest.split(/\//)
65
+ dest_dirs.pop # don't need the last one
66
+ dest_path = project.url
67
+
68
+ branch_or_tag = dest.match(/branches/) ? 'branch' : 'tag'
69
+
70
+ dest_dirs.each do |dir|
71
+ dest_path << "/#{dir}"
72
+ self.mkdir('-m', "'#{Constants::ME}: creating #{branch_or_tag} path #{dest_path}'", dest_path) unless self.has_path(dest_path)
73
+ end
74
+
75
+ self.copy('-m', "'#{Constants::ME}: creating #{branch_or_tag} #{relative_dest}'", source, dest)
76
+ end
77
+
78
+ ################################################################################
79
+ # some magic to add class methods for each svn subcommand
80
+ class << self
81
+ `svn help 2>&1`.each do |line|
82
+ if line.match(/^Available subcommands/) .. line.match(/^\s*$/)
83
+ next if line.match(/^Available/) # there has to be a better way
84
+ subcommand = line.chomp.strip
85
+ subcommand.sub!(/\s.*$/, '')
86
+ next if line.match(/^\s*$/)
87
+ eval "alias #{subcommand} _svn"
88
+ end
89
+
90
+ raise "the svn command line client is not in your path" unless $? == 0
91
+ end
92
+ end
93
+
94
+ end
95
+ end
96
+ ################################################################################
@@ -0,0 +1,111 @@
1
+ ################################################################################
2
+ #
3
+ # Copyright (C) 2006 Peter J Jones (pjones@pmade.com)
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining
6
+ # a copy of this software and associated documentation files (the
7
+ # "Software"), to deal in the Software without restriction, including
8
+ # without limitation the rights to use, copy, modify, merge, publish,
9
+ # distribute, sublicense, and/or sell copies of the Software, and to
10
+ # permit persons to whom the Software is furnished to do so, subject to
11
+ # the following conditions:
12
+ #
13
+ # The above copyright notice and this permission notice shall be
14
+ # included in all copies or substantial portions of the Software.
15
+ #
16
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
+ #
24
+ ################################################################################
25
+ module SC
26
+ ################################################################################
27
+ # Help deal with version numbers. Verson numbers are made up of three parts: the major,
28
+ # minor, and macro numbers. These numbers are separated by periods:
29
+ #
30
+ # MAJOR.MINOR.MACRO as in 1.0.2
31
+ #
32
+ # In SC, release branches are created for MAJOR.MINOR release numbers. Release
33
+ # tags are used for MAJOR.MINOR.MACRO. That means that version 1.1 is open for
34
+ # bug fixes and feature enhancements, but version 1.1.4 is locked and can't be
35
+ # updated.
36
+ #
37
+ class Version
38
+ ################################################################################
39
+ # version objects can be compared
40
+ include Comparable
41
+
42
+ ################################################################################
43
+ # major release number
44
+ attr_reader :major
45
+
46
+ ################################################################################
47
+ # minor release number
48
+ attr_reader :minor
49
+
50
+ ################################################################################
51
+ # macro release number
52
+ attr_reader :macro
53
+
54
+ ################################################################################
55
+ # the encoded version number for sorting
56
+ attr_reader :encoded
57
+
58
+ ################################################################################
59
+ # create a new version object with the given version
60
+ def initialize (version_string)
61
+ parse_version(version_string)
62
+ end
63
+
64
+ ################################################################################
65
+ # convert back to a string
66
+ def to_s
67
+ str = "#{@major}.#{@minor}"
68
+ str << ".#{@macro}" unless @macro.nil?
69
+ str
70
+ end
71
+
72
+ ################################################################################
73
+ # give back just major.minor
74
+ def major_minor
75
+ "#{@major}.#{@minor}"
76
+ end
77
+
78
+ ################################################################################
79
+ # update encoding and whatnot on change
80
+ [:major, :minor, :macro].each do |part|
81
+ define_method "#{part}=", lambda { |value|
82
+ instance_variable_set("@#{part}", value)
83
+ parse_version(self.to_s)
84
+ }
85
+ end
86
+
87
+ ################################################################################
88
+ # allow comparison
89
+ def <=> (other)
90
+ @encoded <=> other.encoded
91
+ end
92
+
93
+ ################################################################################
94
+ private
95
+
96
+ ################################################################################
97
+ def parse_version (version_string)
98
+ @major, @minor, @macro = version_string.split(/\./, 3)
99
+
100
+ @encoded = [@major, @minor, @macro].inject("") do |enc, i|
101
+ part = i.dup if i
102
+ part ||= '0'
103
+ raise "malformed version string #{version_string}, part #{part}" unless part.match(/^\d+$/)
104
+ enc << part.rjust(6, '0')
105
+ end
106
+ end
107
+
108
+ end
109
+ ################################################################################
110
+ end
111
+ ################################################################################
@@ -0,0 +1,3 @@
1
+ Line One
2
+ Line Two
3
+ Line Three
@@ -0,0 +1,3 @@
1
+ Line One
2
+ Line Two
3
+ Line Three
@@ -0,0 +1,97 @@
1
+ require 'sc'
2
+ $DEBUG = true
3
+ require 'test/unit'
4
+
5
+ module SC
6
+ module Test
7
+ ################################################################################
8
+ def setup
9
+ @config_file = File.join(File.dirname(__FILE__), 'sc_config_file.yml')
10
+ ENV[SC::ConfigFile::ENV_OVERRIDE] = @config_file
11
+
12
+ @repository = File.join(File.dirname(__FILE__), 'repos')
13
+ @sandbox = File.join(File.dirname(__FILE__), 'sandbox')
14
+ @projfiles = File.join(File.dirname(__FILE__), 'projfiles')
15
+
16
+ [@repository, @sandbox].each do |dir|
17
+ system('mkdir', dir) or raise "mkdir #{dir}: failed"
18
+ end
19
+
20
+ SC::Repository.create_local(@repository)
21
+
22
+ cf = SC::ConfigFile.new
23
+ cf[:repositories] << SC::Repository.new({
24
+ :url => "file://#{File.expand_path(@repository)}",
25
+ :name => "test",
26
+ :workspace => File.expand_path(@sandbox)
27
+ })
28
+ cf.save
29
+
30
+ run_sc(%W(create -f test))
31
+ @project = SC::Project.new(:name => 'test', :repository => cf[:repositories].first)
32
+ end
33
+
34
+ ################################################################################
35
+ def teardown
36
+ File.unlink(@config_file) if File.exists?(@config_file)
37
+
38
+ [@repository, @sandbox].each do |dir|
39
+ system('rm', '-rf', dir) or raise "rm -rf #{dir}: failed"
40
+ end
41
+ end
42
+
43
+ ################################################################################
44
+ def run_sc (args)
45
+ args[1,0] = '--force' if $add_force_flag
46
+ SC::Dispatcher.new.run(%W(-rtest -ptest).concat(args))
47
+ end
48
+
49
+ ################################################################################
50
+ def add_files (to)
51
+ %W(file_one file_two).each do |file|
52
+ system('cp', File.join(@projfiles, file), to)
53
+ end
54
+
55
+ system("cd #{to}; svn add *; svn ci -m add")
56
+ end
57
+
58
+ ################################################################################
59
+ def checkout
60
+ run_sc(%W(checkout))
61
+ assert(File.exist?(File.join(@sandbox, 'test-trunk')))
62
+ end
63
+
64
+ ################################################################################
65
+ def setup_project_files
66
+ checkout
67
+ add_files(File.join(@sandbox, 'test-trunk'))
68
+ end
69
+
70
+ ################################################################################
71
+ def make_release (version, *extras)
72
+ setup_project_files
73
+ run_sc(['release', '-f', extras, version.to_s].flatten)
74
+ assert(SC::Svn.has_path(@project.release("#{version.major_minor}/file_one")))
75
+ assert(File.exist?(File.join(@sandbox, "test-rel-#{version.major_minor}")))
76
+ end
77
+
78
+ ################################################################################
79
+ def interactive
80
+ $inside_interactive ||= 0
81
+ $inside_interactive += 1
82
+
83
+ $add_force_flag = true if $inside_interactive == 1
84
+ yield
85
+ $add_force_flag = false if $inside_interactive == 1
86
+
87
+ if $inside_interactive == 1
88
+ teardown
89
+ setup
90
+ yield
91
+ end
92
+
93
+ $inside_interactive -= 1
94
+ end
95
+
96
+ end
97
+ end
@@ -0,0 +1,55 @@
1
+ require 'test/setup.rb'
2
+
3
+ class TestBug < Test::Unit::TestCase
4
+ include SC::Test
5
+
6
+ ################################################################################
7
+ def test_create
8
+ interactive do
9
+ make_release(SC::Version.new('1.0'))
10
+ run_sc(%W(bug -r 1.0 1))
11
+ assert(SC::Svn.has_path(@project.branches('bug/1/file_one')))
12
+ assert(SC::Svn.has_path(@project.tags('bug/PRE-1/file_one')))
13
+
14
+ @should_checkout_to = File.join(@sandbox, 'test-bug-1')
15
+ assert(File.exist?(@should_checkout_to))
16
+
17
+ prop = ''
18
+ SC::Svn.propget(SC::Bug::REL_BRN_PROP, @should_checkout_to) do |line|
19
+ prop << line.chomp
20
+ end
21
+
22
+ assert_equal('1.0', prop.strip)
23
+ end
24
+ end
25
+
26
+ ################################################################################
27
+ def test_good_close
28
+ interactive do
29
+ test_create
30
+
31
+ File.open(File.join(@should_checkout_to, 'file_one'), 'a') do |file|
32
+ file << "Line for Bug Fix 1"
33
+ end
34
+
35
+ Dir.chdir(@should_checkout_to) do
36
+ SC::Svn.commit('-m', "'testing merge'")
37
+ end
38
+
39
+ run_sc(%W(bug --close 1))
40
+
41
+ has_line = false
42
+ SC::Svn.cat(@project.branches('rel/1.0/file_one')) do |line|
43
+ has_line = true if line.match(/Line for Bug Fix 1/)
44
+ end
45
+
46
+ assert(has_line)
47
+
48
+ has_line = false
49
+ SC::Svn.cat("#{@project.trunk}/file_one") do |line|
50
+ has_line = true if line.match(/Line for Bug Fix 1/)
51
+ end
52
+ end
53
+ end
54
+
55
+ end