You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@buildr.apache.org by la...@apache.org on 2008/08/28 00:04:19 UTC
svn commit: r689640 - in /incubator/buildr/trunk: CHANGELOG
lib/buildr/core/build.rb spec/build_spec.rb
Author: lacton
Date: Wed Aug 27 15:04:19 2008
New Revision: 689640
URL: http://svn.apache.org/viewvc?rev=689640&view=rev
Log:
Release task: added support for alternative SVN layout, fixed regexp for THIS_VERSION, fixed calls to Buildr.application.buildfile.
Added tests for Release task. Extracted an Svn class to make testing easier.
SVN has two official layouts. Reference: http://svnbook.red-bean.com/en/1.4/svn.reposadmin.planning.html#svn.reposadmin.projects.chooselayout
Added support for the other one.
Buildr.application.buildfile is no longer a file path, but a FileTask. This change requires to convert the Task to a file path.
Fixed a few typos and extracted a few methods.
Modified:
incubator/buildr/trunk/CHANGELOG
incubator/buildr/trunk/lib/buildr/core/build.rb
incubator/buildr/trunk/spec/build_spec.rb
Modified: incubator/buildr/trunk/CHANGELOG
URL: http://svn.apache.org/viewvc/incubator/buildr/trunk/CHANGELOG?rev=689640&r1=689639&r2=689640&view=diff
==============================================================================
--- incubator/buildr/trunk/CHANGELOG (original)
+++ incubator/buildr/trunk/CHANGELOG Wed Aug 27 15:04:19 2008
@@ -2,6 +2,8 @@
* Added: Growl notifications (OS X only).
* Added: error, info and trace methods.
* Added: BUILDR-128 Emma support
+* Added: Release task support for alternative SVN repository layout
+ (e.g., http://my.repo.org/trunk/foo).
* Change: Error reporting now shows 'buildr aborted!' (used to say rake),
more of the stack trace without running --trace, and when running
with supported terminal, error message is red.
@@ -34,6 +36,7 @@
* Fixed: BUILDR-126 Tests options are shared between unrelated projects when
using #options instead of #using (Lacton).
* Fixed: Should not display "(in `pwd`, development)" when using --quiet.
+* Fixed: Release task's regexp to find either THIS_VERSION and VERSION_NUMBER.
* Docs: BUILDR-111 Troubleshoot tip when Buildr's bin directory shows up in
RUBYLIB (Geoffrey Ruscoe).
Modified: incubator/buildr/trunk/lib/buildr/core/build.rb
URL: http://svn.apache.org/viewvc/incubator/buildr/trunk/lib/buildr/core/build.rb?rev=689640&r1=689639&r2=689640&view=diff
==============================================================================
--- incubator/buildr/trunk/lib/buildr/core/build.rb (original)
+++ incubator/buildr/trunk/lib/buildr/core/build.rb Wed Aug 27 15:04:19 2008
@@ -27,7 +27,7 @@
# Runs the build in parallel when true (defaults to false). You can force a parallel build by
# setting this option directly, or by running the parallel task ahead of the build task.
#
- # This option only affects recurvise tasks. For example:
+ # This option only affects recursive tasks. For example:
# buildr parallel package
# will run all package tasks (from the sub-projects) in parallel, but each sub-project's package
# task runs its child tasks (prepare, compile, resources, etc) in sequence.
@@ -48,7 +48,7 @@
desc 'Clean files generated during a build'
Project.local_task('clean') { |name| "Cleaning #{name}" }
- desc 'The default task it build'
+ desc 'The default task is build'
task 'default'=>'build'
end
@@ -109,9 +109,47 @@
end
+ class Svn
+
+ class << self
+ def commit file, message
+ svn 'commit', '-m', message, file
+ end
+
+ def copy dir, url, message
+ svn 'copy', dir, url, '-m', message
+ end
+
+ # Return the current SVN URL
+ def repo_url
+ url = svn('info').scan(/URL: (.*)/)[0][0]
+ end
+
+ def remove url, message
+ svn 'remove', url, '-m', message
+ end
+
+ # Status check reveals modified files, but also SVN externals which we can safely ignore.
+ def uncommitted_files
+ svn('status', '--ignore-externals').reject { |line| line =~ /^X\s/ }
+ end
+
+ # :call-seq:
+ # svn(*args)
+ #
+ # Executes SVN command and returns the output.
+ def svn(*args)
+ cmd = 'svn ' + args.map { |arg| arg[' '] ? %Q{"#{arg}"} : arg }.join(' ')
+ info cmd
+ `#{cmd}`.tap { fail 'SVN command failed' unless $?.exitstatus == 0 }
+ end
+ end
+ end
+
+
class Release
- THIS_VERSION_PATTERN = /THIS_VERSION|VERSION_NUMBER\s*=\s*(["'])(.*)\1/
+ THIS_VERSION_PATTERN = /(THIS_VERSION|VERSION_NUMBER)\s*=\s*(["'])(.*)\2/
NEXT_VERSION_PATTERN = /NEXT_VERSION\s*=\s*(["'])(.*)\1/
class << self
@@ -122,7 +160,7 @@
# Make a release.
def make()
check
- version = with_next_version do |filename, version|
+ version = with_next_version do |filename|
options = ['--buildfile', filename, 'DEBUG=no']
options << '--environment' << Buildr.environment unless Buildr.environment.to_s.empty?
sh "#{command} _#{Buildr::VERSION}_ clean upload #{options.join(' ')}"
@@ -131,23 +169,55 @@
commit version + '-SNAPSHOT'
end
- protected
-
- def command() #:nodoc:
- Config::CONFIG['arch'] =~ /dos|win32/i ? $PROGRAM_NAME.ext('cmd') : $PROGRAM_NAME
+ # :call-seq:
+ # extract_versions(buildfile) => this_version, next_version
+ #
+ # Extract the current and next version numbers from a buildfile.
+ # Raise an error if not found.
+ def extract_versions buildfile
+ begin
+ this_version = buildfile.scan(THIS_VERSION_PATTERN)[0][2]
+ rescue
+ fail 'Looking for THIS_VERSION = "..." in your Buildfile, none found'
+ end
+ begin
+ next_version = buildfile.scan(NEXT_VERSION_PATTERN)[0][1]
+ rescue
+ fail 'Looking for NEXT_VERSION = "..." in your Buildfile, none found'
+ end
+ [this_version, next_version]
end
-
+
+ # :call-seq:
+ # tag_url(svn_url, version) => tag_url
+ #
+ # Returns the SVN url for the tag.
+ # Can tag from the trunk or from branches.
+ # Can handle the two standard repository layouts.
+ # - http://my.repo/foo/trunk => http://my.repo/foo/tags/1.0.0
+ # - http://my.repo/trunk/foo => http://my.repo/tags/foo/1.0.0
+ def tag_url svn_url, version
+ trunk_or_branches = Regexp.union(%r{^(.*)/trunk(.*)$}, %r{^(.*)/branches(.*)/([^/]*)$})
+ match = trunk_or_branches.match(svn_url)
+ prefix = match[1] || match[3]
+ suffix = match[2] || match[4]
+ prefix + '/tags' + suffix + '/' + version
+ end
+
# :call-seq:
# check()
#
# Check that we don't have any local changes in the working copy. Fails if it finds anything
# in the working copy that is not checked into source control.
def check()
- fail "SVN URL must end with 'trunk' or 'branches/...'" unless svn_url =~ /(trunk)|(branches.*)$/
- # Status check reveals modified file, but also SVN externals which we can safely ignore.
- status = svn('status', '--ignore-externals').reject { |line| line =~ /^X\s/ }
- fail "Uncommitted SVN files violate the First Principle Of Release!\n#{status}" unless
- status.empty?
+ fail "SVN URL must contain 'trunk' or 'branches/...'" unless Svn.repo_url =~ /(trunk)|(branches.*)$/
+ fail "Uncommitted SVN files violate the First Principle Of Release!\n#{Svn.uncommitted_files}" unless Svn.uncommitted_files.empty?
+ end
+
+ protected
+
+ def command() #:nodoc:
+ Config::CONFIG['arch'] =~ /dos|win32/i ? $PROGRAM_NAME.ext('cmd') : $PROGRAM_NAME
end
# :call-seq:
@@ -170,7 +240,7 @@
# NEXT_VERSION = 1.2.1
# and the method will return 1.2.0.
def with_next_version()
- new_filename = Buildr.application.buildfile + '.next'
+ new_filename = Buildr.application.buildfile.to_s + '.next'
modified = change_version do |this_version, next_version|
one_after = next_version.split('.')
one_after[-1] = one_after[-1].to_i + 1
@@ -179,11 +249,11 @@
File.open(new_filename, 'w') { |file| file.write modified }
begin
yield new_filename
- mv new_filename, Buildr.application.buildfile
+ mv new_filename, Buildr.application.buildfile.to_s
ensure
rm new_filename rescue nil
end
- File.read(Buildr.application.buildfile).scan(THIS_VERSION_PATTERN)[0][1]
+ extract_versions(File.read(Buildr.application.buildfile.to_s))[0]
end
# :call-seq:
@@ -195,11 +265,8 @@
# This method yields to the block with the current (this) and next version numbers and expects
# an array with the new this and next version numbers.
def change_version()
- buildfile = File.read(Buildr.application.buildfile)
- this_version = buildfile.scan(THIS_VERSION_PATTERN)[0][1] or
- fail "Looking for THIS_VERSION = \"...\" in your Buildfile, none found"
- next_version = buildfile.scan(NEXT_VERSION_PATTERN)[0][1] or
- fail "Looking for NEXT_VERSION = \"...\" in your Buildfile, none found"
+ buildfile = File.read(Buildr.application.buildfile.to_s)
+ this_version, next_version = extract_versions buildfile
this_version, next_version = yield(this_version, next_version)
if verbose
puts 'Upgrading version numbers:'
@@ -215,9 +282,9 @@
#
# Tags the current working copy with the release version number.
def tag(version)
- url = svn_url.sub(/(trunk$)|(branches.*)$/, "tags/#{version}")
- svn 'remove', url, '-m', 'Removing old copy' rescue nil
- svn 'copy', Dir.pwd, url, '-m', "Release #{version}"
+ url = tag_url Svn.repo_url, version
+ Svn.remove url, 'Removing old copy' rescue nil
+ Svn.copy Dir.pwd, url, "Release #{version}"
end
# :call-seq:
@@ -225,30 +292,15 @@
#
# Last, we commit what we currently have in the working copy.
def commit(version)
- buildfile = File.read(Buildr.application.buildfile).
+ buildfile = File.read(Buildr.application.buildfile.to_s).
gsub(THIS_VERSION_PATTERN) { |ver| ver.sub(/(["']).*\1/, %Q{"#{version}"}) }
- File.open(Buildr.application.buildfile, 'w') { |file| file.write buildfile }
- svn 'commit', '-m', "Changed version number to #{version}", Buildr.application.buildfile
- end
-
- # :call-seq:
- # svn(*args)
- #
- # Executes SVN command and returns the output.
- def svn(*args)
- cmd = 'svn ' + args.map { |arg| arg[' '] ? %Q{"#{arg}"} : arg }.join(' ')
- info cmd
- `#{cmd}`.tap { fail 'SVN command failed' unless $?.exitstatus == 0 }
- end
-
- # Return the current SVN URL
- def svn_url
- url = svn('info').scan(/URL: (.*)/)[0][0]
+ File.open(Buildr.application.buildfile.to_s, 'w') { |file| file.write buildfile }
+ Svn.commit Buildr.application.buildfile.to_s, "Changed version number to #{version}"
end
end
-
end
+
desc 'Make a release'
task 'release' do |task|
Release.make
Modified: incubator/buildr/trunk/spec/build_spec.rb
URL: http://svn.apache.org/viewvc/incubator/buildr/trunk/spec/build_spec.rb?rev=689640&r1=689639&r2=689640&view=diff
==============================================================================
--- incubator/buildr/trunk/spec/build_spec.rb (original)
+++ incubator/buildr/trunk/spec/build_spec.rb Wed Aug 27 15:04:19 2008
@@ -191,3 +191,184 @@
@project.reports.should eql('baz')
end
end
+
+
+describe Buildr::Release, '#check' do
+ before do
+ Buildr::Svn.stub!(:uncommitted_files).and_return('')
+ end
+
+ it 'should accept to release from the trunk' do
+ Buildr::Svn.stub!(:repo_url).and_return('http://my.repo.org/foo/trunk')
+ lambda { Release.check }.should_not raise_error
+ end
+
+ it 'should accept to release from a branch' do
+ Buildr::Svn.stub!(:repo_url).and_return('http://my.repo.org/foo/branches/1.0')
+ lambda { Release.check }.should_not raise_error
+ end
+
+ it 'should reject to release from a tag' do
+ Buildr::Svn.stub!(:repo_url).and_return('http://my.repo.org/foo/tags/1.0.0')
+ lambda { Release.check }.should raise_error(RuntimeError, "SVN URL must contain 'trunk' or 'branches/...'")
+ end
+
+ it 'should reject a non standard repository layout' do
+ Buildr::Svn.stub!(:repo_url).and_return('http://my.repo.org/foo/bar')
+ lambda { Release.check }.should raise_error(RuntimeError, "SVN URL must contain 'trunk' or 'branches/...'")
+ end
+
+ it 'should reject an uncommitted file' do
+ Buildr::Svn.stub!(:repo_url).and_return('http://my.repo.org/foo/trunk')
+ Buildr::Svn.stub!(:uncommitted_files).and_return('M foo.rb')
+ lambda { Release.check }.should raise_error(RuntimeError,
+ "Uncommitted SVN files violate the First Principle Of Release!\n" +
+ "M foo.rb")
+ end
+end
+
+
+describe Buildr::Release, '#extract_versions' do
+
+ it 'should extract VERSION_NUMBER and NEXT_VERSION with single quotes' do
+ buildfile = ["VERSION_NUMBER = '1.0.0-SNAPSHOT'", "NEXT_VERSION = '1.0.1'"].join("\n")
+ Release.extract_versions(buildfile).should == ['1.0.0-SNAPSHOT', '1.0.1']
+ end
+
+ it 'should extract VERSION_NUMBER and NEXT_VERSION with double quotes' do
+ buildfile = [%{VERSION_NUMBER = "1.0.1-SNAPSHOT"}, %{NEXT_VERSION = "1.0.2"}].join("\n")
+ Release.extract_versions(buildfile).should == ['1.0.1-SNAPSHOT', '1.0.2']
+ end
+
+ it 'should extract VERSION_NUMBER and NEXT_VERSION without any spaces' do
+ buildfile = ["VERSION_NUMBER='1.0.2-SNAPSHOT'", "NEXT_VERSION='1.0.3'"].join("\n")
+ Release.extract_versions(buildfile).should == ['1.0.2-SNAPSHOT', '1.0.3']
+ end
+
+ it 'should extract THIS_VERSION as an alternative to VERSION_NUMBER' do
+ buildfile = ["THIS_VERSION = '1.0.3-SNAPSHOT'", "NEXT_VERSION = '1.0.4'"].join("\n")
+ Release.extract_versions(buildfile).should == ['1.0.3-SNAPSHOT', '1.0.4']
+ end
+
+ it 'should complain if no current version number' do
+ buildfile = "NEXT_VERSION = '1.0.1'"
+ lambda { Release.extract_versions(buildfile) }.should raise_error('Looking for THIS_VERSION = "..." in your Buildfile, none found')
+ end
+
+ it 'should complain if no next version number' do
+ buildfile = "VERSION_NUMBER = '1.0.0-SNAPSHOT'"
+ lambda { Release.extract_versions(buildfile) }.should raise_error('Looking for NEXT_VERSION = "..." in your Buildfile, none found')
+ end
+end
+
+
+describe Buildr::Svn, '#repo_url' do
+ it 'should extract the SVN URL from svn info' do
+ Svn.stub!(:svn, 'info').and_return(<<EOF)
+Path: .
+URL: http://my.repo.org/foo/trunk
+Repository Root: http://my.repo.org
+Repository UUID: 12345678-9abc-def0-1234-56789abcdef0
+Revision: 112
+Node Kind: directory
+Schedule: normal
+Last Changed Author: Lacton
+Last Changed Rev: 110
+Last Changed Date: 2008-08-19 12:00:00 +0200 (Tue, 19 Aug 2008)
+EOF
+ Svn.repo_url.should == 'http://my.repo.org/foo/trunk'
+ end
+end
+
+
+# Reference: http://svnbook.red-bean.com/en/1.4/svn.reposadmin.planning.html#svn.reposadmin.projects.chooselayout
+describe Buildr::Release, '#tag url' do
+ it 'should accept to tag foo/trunk' do
+ Release.tag_url('http://my.repo.org/foo/trunk', '1.0.0').should == 'http://my.repo.org/foo/tags/1.0.0'
+ end
+
+ it 'should accept to tag foo/branches/1.0' do
+ Release.tag_url('http://my.repo.org/foo/branches/1.0', '1.0.1').should == 'http://my.repo.org/foo/tags/1.0.1'
+ end
+
+ it 'should accept to tag trunk/foo' do
+ Release.tag_url('http://my.repo.org/trunk/foo', '1.0.0').should == 'http://my.repo.org/tags/foo/1.0.0'
+ end
+
+ it 'should accept to tag branches/foo/1.0' do
+ Release.tag_url('http://my.repo.org/branches/foo/1.0', '1.0.0').should == 'http://my.repo.org/tags/foo/1.0.0'
+ end
+end
+
+
+describe Buildr::Release, '#with_next_version' do
+ before do
+ Buildr.application.stub!(:buildfile).and_return(file('buildfile'))
+ write 'buildfile', <<-EOF
+ THIS_VERSION = '1.1.0'
+ NEXT_VERSION = '1.2.0'
+ EOF
+ end
+
+ it 'should yield the name of an updated buildfile' do
+ Release.send :with_next_version do |new_filename|
+ File.read(new_filename).should == <<-EOF
+ THIS_VERSION = "1.2.0"
+ NEXT_VERSION = "1.2.1"
+ EOF
+ end
+ end
+
+ it 'should yield a name different from the original buildfile' do
+ Release.send :with_next_version do |new_filename|
+ new_filename.should_not point_to_path('buildfile')
+ end
+ end
+
+ it 'should return the new version number' do
+ new_version = Release.send(:with_next_version) {}
+ new_version.should == '1.2.0'
+ end
+end
+
+
+describe Buildr::Release, '#tag' do
+ before do
+ Svn.stub!(:repo_url).and_return('http://my.repo.org/foo/trunk')
+ Svn.stub!(:copy)
+ end
+
+ it 'should tag the working copy' do
+ Svn.stub!(:remove)
+ Svn.should_receive(:copy).with(Dir.pwd, 'http://my.repo.org/foo/tags/1.0.1', 'Release 1.0.1')
+ Release.send :tag, '1.0.1'
+ end
+
+ it 'should remove the tag if it already exists' do
+ Svn.should_receive(:remove).with('http://my.repo.org/foo/tags/1.0.1', 'Removing old copy')
+ Release.send :tag, '1.0.1'
+ end
+
+ it 'should accept that the tag does not exist' do
+ Svn.stub!(:remove).and_raise(RuntimeError)
+ Release.send :tag, '1.0.1'
+ end
+end
+
+
+describe Buildr::Release, '#commit' do
+ before do
+ write 'buildfile', 'THIS_VERSION = "1.0.0"'
+ end
+
+ it 'should update the buildfile with the given version number' do
+ Svn.stub!(:commit)
+ Release.send :commit, '1.0.1-SNAPSHOT'
+ file('buildfile').should contain('THIS_VERSION = "1.0.1-SNAPSHOT"')
+ end
+
+ it 'should commit the new buildfile on the trunk' do
+ Svn.should_receive(:commit).with(File.expand_path('buildfile'), 'Changed version number to 1.0.1-SNAPSHOT')
+ Release.send :commit, '1.0.1-SNAPSHOT'
+ end
+end
\ No newline at end of file