Support different create and revision= operations depending on the revision type...
authorBruce Williams <bruce@codefluency.com>
Thu, 18 Mar 2010 21:46:12 +0000 (14:46 -0700)
committerBruce Williams <bruce@codefluency.com>
Thu, 18 Mar 2010 21:46:12 +0000 (14:46 -0700)
lib/puppet/provider/vcsrepo/git.rb
spec/fixtures/git_branch_a.txt [new file with mode: 0644]
spec/unit/puppet/provider/vcsrepo/git_spec.rb

index e511a10..952126c 100644 (file)
@@ -16,7 +16,7 @@ Puppet::Type.type(:vcsrepo).provide(:git, :parent => Puppet::Provider::Vcsrepo)
         if @resource.value(:ensure) == :bare
           notice "Ignoring revision for bare repository"
         else
-          reset(@resource.value(:revision))
+          checkout_branch_or_reset
         end
       end
       if @resource.value(:ensure) != :bare
@@ -41,9 +41,24 @@ Puppet::Type.type(:vcsrepo).provide(:git, :parent => Puppet::Provider::Vcsrepo)
 
   def revision=(desired)
     fetch
-    reset(desired)
-    unless @resource.value(:ensure) == :bare
+    if local_revision_branch?(desired)
+      at_path do
+        git('checkout', desired)
+        git('pull', 'origin')
+      end
+      update_submodules
+    elsif remote_revision_branch?(desired)
+      at_path do
+        git('checkout',
+            '-b', @resource.value(:revision),
+            '--track', "origin/#{@resource.value(:revision)}")
+      end
       update_submodules
+    else
+      reset(desired)
+      if @resource.value(:ensure) != :bare
+        update_submodules
+      end
     end
   end
 
@@ -90,6 +105,12 @@ Puppet::Type.type(:vcsrepo).provide(:git, :parent => Puppet::Provider::Vcsrepo)
     end
   end
 
+  def pull
+    at_path do
+      git('pull', 'origin')
+    end
+  end
+  
   def init_repository(path)
     if @resource.value(:ensure) == :bare && working_copy_exists?
       convert_working_copy_to_bare
@@ -150,6 +171,16 @@ Puppet::Type.type(:vcsrepo).provide(:git, :parent => Puppet::Provider::Vcsrepo)
     false
   end
 
+  def checkout_branch_or_reset
+    if remote_revision_branch?
+      at_path do
+        git('checkout', '-b', @resource.value(:revision), '--track', "origin/#{@resource.value(:revision)}")
+      end
+    else
+      reset(@resource.value(:revision))
+    end
+  end
+
   def reset(desired)
     at_path do
       git('reset', '--hard', desired)
@@ -163,4 +194,20 @@ Puppet::Type.type(:vcsrepo).provide(:git, :parent => Puppet::Provider::Vcsrepo)
     end
   end
 
+  def remote_branch_revision?(revision = @resource.value(:revision))
+    at_path do
+      branches.include?("origin/#{revision}")
+    end
+  end
+
+  def local_branch_revision?(revision = @resource.value(:revision))
+    at_path do
+      branches.include?(revision)
+    end
+  end
+
+  def branches
+    at_path { git('branch', '-a') }.gsub('*', ' ').split(/\n/).map { |line| line.strip }
+  end
+
 end
diff --git a/spec/fixtures/git_branch_a.txt b/spec/fixtures/git_branch_a.txt
new file mode 100644 (file)
index 0000000..2c99829
--- /dev/null
@@ -0,0 +1,14 @@
+  feature/foo
+  feature/bar
+  feature/baz
+  feature/quux
+  only/local
+* master
+  refactor/foo
+  origin/HEAD
+  origin/feature/foo
+  origin/feature/bar
+  origin/feature/baz
+  origin/feature/quux
+  origin/only/remote
+  origin/master
index f7864c0..4a1085a 100644 (file)
@@ -1,27 +1,34 @@
 require 'pathname'; Pathname.new(__FILE__).realpath.ascend { |x| begin; require (x + 'spec_helper.rb'); break; rescue LoadError; end }
 
 describe_provider :vcsrepo, :git, :resource => {:path => '/tmp/vcsrepo'} do
-  
+
   context 'creating' do
     resource_with :source do
       resource_with :ensure => :present do
-        resource_with :revision do
+        context "with a revision that is a remote branch", :resource => {:revision => 'only/remote'} do
+          it "should execute 'git clone' and 'git checkout -b'" do
+            provider.expects('git').with('clone', resource.value(:source), resource.value(:path))
+            expects_chdir
+            provider.expects(:remote_revision_branch?).returns(true)
+            provider.expects(:git).with('checkout', '-b', resource.value(:revision), '--track', "origin/#{resource.value(:revision)}")
+            provider.expects(:update_submodules)
+            provider.create
+          end
+        end
+        context "with a revision that is not a remote branch", :resource => {:revision => 'a-commit-or-tag'} do
           it "should execute 'git clone' and 'git reset --hard'" do
             provider.expects('git').with('clone', resource.value(:source), resource.value(:path))
             expects_chdir
+            provider.expects(:remote_revision_branch?).returns(false)
             provider.expects('git').with('reset', '--hard', resource.value(:revision))
-            provider.expects(:git).with('submodule', 'init')
-            provider.expects(:git).with('submodule', 'update')
+            provider.expects(:update_submodules)
             provider.create
           end
-        end
-        
+        end        
         resource_without :revision do
           it "should execute 'git clone' and submodule commands" do
             provider.expects(:git).with('clone', resource.value(:source), resource.value(:path))
-            expects_chdir
-            provider.expects(:git).with('submodule', 'init')
-            provider.expects(:git).with('submodule', 'update')
+            provider.expects(:update_submodules)
             provider.create
           end
         end
@@ -137,14 +144,40 @@ describe_provider :vcsrepo, :git, :resource => {:path => '/tmp/vcsrepo'} do
   end
   
   context "setting the revision property" do
-    it "should use 'git fetch' and 'git reset'" do
+    before do
       expects_chdir
       provider.expects(:git).with('fetch', 'origin')
-      provider.expects(:git).with('reset', '--hard', 'carcar')
-      provider.expects(:git).with('submodule', 'init')
-      provider.expects(:git).with('submodule', 'update')
-      provider.revision = 'carcar'
     end
+    context "when it's an existing local branch", :resource => {:revision => 'feature/foo'} do
+      it "should use 'git fetch' and 'git reset'" do
+        provider.expects(:local_revision_branch?).returns(true)
+        provider.expects(:git).with('checkout', resource.value(:revision))
+        provider.expects(:git).with('pull', 'origin')
+        provider.expects(:update_submodules)
+        provider.revision = resource.value(:revision)
+      end
+    end
+    context "when it's a remote branch", :resource => {:revision => 'only/remote'} do
+      it "should use 'git fetch' and 'git reset'" do
+        provider.expects(:local_revision_branch?).returns(false)
+        provider.expects(:remote_revision_branch?).returns(true)
+        provider.expects(:git).with('checkout',
+                                    '-b', resource.value(:revision),
+                                    '--track', "origin/#{resource.value(:revision)}")
+        provider.expects(:update_submodules)
+        provider.revision = resource.value(:revision)
+      end
+    end
+    context "when it's a commit or tag", :resource => {:revision => 'a-commit-or-tag'} do
+      it "should use 'git fetch' and 'git reset'" do
+        provider.expects(:local_revision_branch?).returns(false)
+        provider.expects(:remote_revision_branch?).returns(false)
+        provider.expects(:git).with('reset', '--hard', resource.value(:revision))
+        provider.expects(:git).with('submodule', 'init')
+        provider.expects(:git).with('submodule', 'update')
+        provider.revision = resource.value(:revision)
+      end
+    end    
   end
   
   context "updating references" do
@@ -154,5 +187,36 @@ describe_provider :vcsrepo, :git, :resource => {:path => '/tmp/vcsrepo'} do
       provider.update_references
     end
   end
+
+  context "checking if revision" do
+    before do
+      expects_chdir
+      provider.expects(:git).with('branch', '-a').returns(fixture(:git_branch_a))
+    end
+    context "is a local branch" do
+      context "when it's listed in 'git branch -a'", :resource => {:revision => 'feature/foo'} do
+        it "should return true" do
+          provider.should be_local_branch_revision
+        end
+      end
+      context "when it's not listed in 'git branch -a'" , :resource => {:revision => 'feature/notexist'}do
+        it "should return false" do
+          provider.should_not be_local_branch_revision
+        end
+      end
+    end
+    context "is a remote branch" do
+      context "when it's listed in 'git branch -a' with an 'origin/' prefix", :resource => {:revision => 'only/remote'} do
+        it "should return true" do
+          provider.should be_remote_branch_revision
+        end
+      end
+      context "when it's not listed in 'git branch -a' with an 'origin/' prefix" , :resource => {:revision => 'only/local'}do
+        it "should return false" do
+          provider.should_not be_remote_branch_revision
+        end
+      end
+    end    
+  end
   
 end