svnauto 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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