You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@buildr.apache.org by bo...@apache.org on 2012/07/19 21:25:38 UTC
svn commit: r1363492 - in /buildr/trunk: CHANGELOG lib/buildr/core/build.rb
spec/core/build_spec.rb
Author: boisvert
Date: Thu Jul 19 19:25:38 2012
New Revision: 1363492
URL: http://svn.apache.org/viewvc?rev=1363492&view=rev
Log:
BUILDR-645 Support Mercurial as a version control system (Tan Quach)
Modified:
buildr/trunk/CHANGELOG
buildr/trunk/lib/buildr/core/build.rb
buildr/trunk/spec/core/build_spec.rb
Modified: buildr/trunk/CHANGELOG
URL: http://svn.apache.org/viewvc/buildr/trunk/CHANGELOG?rev=1363492&r1=1363491&r2=1363492&view=diff
==============================================================================
--- buildr/trunk/CHANGELOG (original)
+++ buildr/trunk/CHANGELOG Thu Jul 19 19:25:38 2012
@@ -1,4 +1,5 @@
1.4.8 (Pending)
+* Added: BUILDR-645 Support Mercurial as a version control system (Tan Quach)
* Fixed: BUILDR-646 TGZ files do not keep their permissions when extracted
via Buildr::Unzip#extract
* Added: Add add_exploded_ear_artifact and add_exploded_ejb_artifact to the idea project extension.
Modified: buildr/trunk/lib/buildr/core/build.rb
URL: http://svn.apache.org/viewvc/buildr/trunk/lib/buildr/core/build.rb?rev=1363492&r1=1363491&r2=1363492&view=diff
==============================================================================
--- buildr/trunk/lib/buildr/core/build.rb (original)
+++ buildr/trunk/lib/buildr/core/build.rb Thu Jul 19 19:25:38 2012
@@ -100,6 +100,58 @@ module Buildr
end
+ module Hg #:nodoc:
+ module_function
+
+ # :call-seq:
+ # hg(*args)
+ #
+ # Executes a Mercurial (hg) command passing through the args and returns the output.
+ # Throws exception if the exit status is not zero. For example:
+ # hg 'commit'
+ # hg 'update', 'default'
+ def hg(*args)
+ cmd = "hg #{args.shift} #{args.map { |arg| arg.inspect }.join(' ')}"
+ output = `#{cmd}`
+ fail "Mercurial command \"#{cmd}\" failed with status #{$?.exitstatus}\n#{output}" unless $?.exitstatus == 0
+ return output
+ end
+
+ # Return a list of uncommitted / untracked files as reported by hg status
+ # The codes used to show the status of files are:
+ # M = modified
+ # A = added
+ # R = removed
+ # C = clean
+ # ! = missing (deleted by non-hg command, but still tracked)
+ # ? = not tracked
+ # I = ignored
+ # = origin of the previous file listed as A (added)
+ def uncommitted_files
+ `hg status`.scan(/^(A|M|R|!|\?) (\S.*)$/).map{ |match| match.last.split.last }
+ end
+
+ # Commit the given file with a message. The file should already be added to the Mercurial index.
+ def commit(file, message)
+ hg 'commit', '-m', message, file
+ end
+
+ # Update the remote branch with the local commits
+ # This will push the current remote destination and current branch.
+ def push
+ hg 'push'
+ end
+
+ # Return the name of the current local branch or nil if none.
+ def current_branch
+ hg('branch').to_s.strip
+ end
+
+ # Return the aliases (if any) of any remote repositories which the current local branch tracks
+ def remote
+ hg('paths').scan(/^(?:default|default-push)\s+=\s+(\S.*)/).map{ |match| match.last }
+ end
+ end
module Git #:nodoc:
module_function
@@ -415,6 +467,54 @@ module Buildr
end
+ class HgRelease < Release
+ class << self
+ def applies_to?
+ if File.exist? '.hg/requires'
+ true
+ else
+ curr_pwd = Dir.pwd
+ Dir.chdir('..') do
+ return false if curr_pwd == Dir.pwd # Means going up one level is not possible.
+ applies_to?
+ end
+ end
+ end
+ end
+
+ # Fails if one of these 2 conditions are not met:
+ # 1. The reository is not 'clean'; no content staged or unstaged
+ # 2. The repository is only a local repository and has no remote refs
+ def check
+ super
+ info "Working in branch '#{Hg.current_branch}'"
+ uncommitted = Hg.uncommitted_files
+ fail "Uncommitted files violate the First Principle Of Release!\n#{uncommitted.join("\n")}" unless uncommitted.empty?
+ fail "You are releasing from a local branch that does not track a remote!" if Hg.remote.empty?
+ end
+
+ # Tag this release in Mercurial
+ def tag_release(tag)
+ unless this_version == extract_version
+ info "Committing buildfile with version number #{extract_version}"
+ Hg.commit File.basename(Buildr.application.buildfile.to_s), message
+ Hg.push if Hg.remote
+ end
+ info "Tagging release #{tag}"
+ Hg.hg 'tag', tag, '-m', "[buildr] Cutting release #{tag}"
+ Hg.push if Hg.remote
+ end
+
+ # Update buildfile with next version number
+ def update_version_to_next
+ super
+ info "Current version is now #{extract_version}"
+ Hg.commit File.basename(Buildr.application.buildfile.to_s), message
+ Hg.push if Hg.remote
+ end
+ end
+
+
class GitRelease < Release
class << self
def applies_to?
@@ -430,7 +530,7 @@ module Buildr
end
end
- # Fails if one of theses 2 conditions are not met:
+ # Fails if one of these 2 conditions are not met:
# 1. the repository is clean: no content staged or unstaged
# 2. some remote repositories are defined but the current branch does not track any
def check
@@ -491,6 +591,7 @@ module Buildr
end
end
+ Release.add HgRelease
Release.add SvnRelease
Release.add GitRelease
Modified: buildr/trunk/spec/core/build_spec.rb
URL: http://svn.apache.org/viewvc/buildr/trunk/spec/core/build_spec.rb?rev=1363492&r1=1363491&r2=1363492&view=diff
==============================================================================
--- buildr/trunk/spec/core/build_spec.rb (original)
+++ buildr/trunk/spec/core/build_spec.rb Thu Jul 19 19:25:38 2012
@@ -200,6 +200,66 @@ describe Project, '#reports' do
end
+describe Hg do
+ describe '#current_branch' do
+ it 'should return the correct branch' do
+ Hg.should_receive(:hg).with('branch').and_return("default\n")
+ Hg.send(:current_branch).should == 'default'
+ end
+ end
+
+ describe '#uncommitted_files' do
+ it 'should return an array of modified files' do
+ Hg.should_receive(:`).with('hg status').and_return <<-EOF
+M abc.txt
+M xyz.txt
+R hello
+R removed
+! conflict
+A README
+? ignore.txt
+ EOF
+ Hg.uncommitted_files.should include('abc.txt', 'xyz.txt', 'hello', 'README', 'conflict', 'ignore.txt')
+ end
+ end
+
+ describe '#uncommitted_files' do
+ it 'should return an empty array on a clean repository' do
+ Hg.should_receive(:`).with('hg status').and_return "\n"
+ Hg.uncommitted_files.should be_empty
+ end
+ end
+
+ describe '#remote' do
+ it 'should return the aliases of the default remote repositories' do
+ Hg.should_receive(:hg).with('paths').and_return <<-EOF
+default = https://hg.apache.org/repo/my-repo
+ EOF
+ Hg.send(:remote).should include('https://hg.apache.org/repo/my-repo')
+ end
+
+ it 'should return the aliases of the default push remote repositories' do
+ Hg.should_receive(:hg).with('paths').and_return <<-EOF
+default-push = https://hg.apache.org/repo/my-repo
+ EOF
+ Hg.send(:remote).should include('https://hg.apache.org/repo/my-repo')
+ end
+
+ it 'should return empty array when no remote repositories found' do
+ Hg.should_receive(:hg).with('paths').and_return "\n"
+ Hg.send(:remote).should be_empty
+ end
+
+ it 'should return empty array when no default-push remote repository found' do
+ Hg.should_receive(:hg).with('paths').and_return <<-EOF
+blah = https://bitbucket.org/sample-repo
+ EOF
+ Hg.send(:remote).should be_empty
+ end
+ end
+end # end of Hg
+
+
describe Git do
describe '#uncommitted_files' do
it 'should return an empty array on a clean repository' do
@@ -346,6 +406,11 @@ end # of Buildr::Svn
describe Release do
describe 'find' do
+ it 'should return HgRelease if project uses Hg' do
+ write '.hg/requires'
+ Release.find.should be_instance_of(HgRelease)
+ end
+
it 'should return GitRelease if project uses Git' do
write '.git/config'
Release.find.should be_instance_of(GitRelease)
@@ -679,6 +744,58 @@ shared_examples_for 'a release process'
end
+describe HgRelease do
+ it_should_behave_like 'a release process'
+
+ before do
+ write 'buildfile', "VERSION_NUMBER = '1.0.0-SNAPSHOT'"
+ @release = HgRelease.new
+ Hg.stub!(:hg)
+ Hg.stub!(:remote).and_return('https://bitbucket.org/sample-repo')
+ Hg.stub!(:current_branch).and_return('default')
+ end
+
+ describe '#applies_to?' do
+ it 'should reject a non-hg repo' do
+ Dir.chdir(Dir.tmpdir) do
+ HgRelease.applies_to?.should be_false
+ end
+ end
+
+ it 'should accept a hg repo' do
+ FileUtils.mkdir '.hg'
+ FileUtils.touch File.join('.hg', 'requires')
+ HgRelease.applies_to?.should be_true
+ end
+ end
+
+ describe '#check' do
+ before do
+ @release = HgRelease.new
+ @release.send(:this_version=, '1.0.0-SNAPSHOT')
+ end
+
+ it 'should accept a clean repo' do
+ Hg.should_receive(:uncommitted_files).and_return([])
+ Hg.should_receive(:remote).and_return(["http://bitbucket.org/sample-repo"])
+ lambda { @release.check }.should_not raise_error
+ end
+
+ it 'should reject a dirty repo' do
+ Hg.should_receive(:uncommitted_files).and_return(['dirty_file.txt'])
+ lambda { @release.check }.should raise_error(RuntimeError, /uncommitted files/i)
+ end
+
+ it 'should reject a local branch not tracking a remote repo' do
+ Hg.should_receive(:uncommitted_files).and_return([])
+ Hg.should_receive(:remote).and_return([])
+ lambda{ @release.check }.should raise_error(RuntimeError,
+ "You are releasing from a local branch that does not track a remote!")
+ end
+ end
+end
+
+
describe GitRelease do
it_should_behave_like 'a release process'