Basic CVS support
authorBruce Williams <bruce@codefluency.com>
Sat, 13 Mar 2010 19:50:28 +0000 (11:50 -0800)
committerBruce Williams <bruce@codefluency.com>
Sat, 13 Mar 2010 19:50:28 +0000 (11:50 -0800)
README.CVS.markdown [new file with mode: 0644]
lib/puppet/provider/vcsrepo/cvs.rb [new file with mode: 0644]
lib/puppet/provider/vcsrepo/svn.rb
lib/puppet/type/vcsrepo.rb
spec/unit/puppet/provider/vcsrepo/cvs_spec.rb [new file with mode: 0644]

diff --git a/README.CVS.markdown b/README.CVS.markdown
new file mode 100644 (file)
index 0000000..7f9647e
--- /dev/null
@@ -0,0 +1,42 @@
+Using vcsrepo with CVS
+======================
+
+To create a blank repository
+----------------------------
+
+Define a `vcsrepo` without a `source` or `revision`:
+
+    vcsrepo { "/path/to/repo":
+      ensure => present,
+      provider => cvs
+    }
+
+To checkout/update from a repository
+------------------------------------
+
+To get the current mainline:
+
+    vcsrepo { "/path/to/workspace":
+        ensure => present,
+        provider => cvs,
+        source => ":pserver:anonymous@example.com:/sources/myproj"
+    }
+
+You can use the `compression` parameter (it works like CVS `-z`):
+
+    vcsrepo { "/path/to/workspace":
+        ensure => present,
+        provider => cvs,
+        compression => 3,
+        source => ":pserver:anonymous@example.com:/sources/myproj"
+    }
+
+For a specific tag, use `revision`:
+
+    vcsrepo { "/path/to/workspace":
+        ensure => present,
+        provider => cvs,
+        compression => 3,
+        source => ":pserver:anonymous@example.com:/sources/myproj",
+        revision => "SOMETAG"
+    }
diff --git a/lib/puppet/provider/vcsrepo/cvs.rb b/lib/puppet/provider/vcsrepo/cvs.rb
new file mode 100644 (file)
index 0000000..68d22c1
--- /dev/null
@@ -0,0 +1,87 @@
+Puppet::Type.type(:vcsrepo).provide(:cvs) do
+  desc "Supports CVS repositories/workspaces"
+
+  commands   :cvs => 'cvs'
+  defaultfor :cvs => :exists
+  
+  def create
+    if !@resource.value(:source)
+      create_repository(@resource.value(:path))
+    else
+      checkout_repository
+    end
+  end
+
+  def exists?
+    if @resource.value(:source)
+      directory = File.join(@resource.value(:path), 'CVS')
+    else
+      directory = File.join(@resource.value(:path), 'CVSROOT')
+    end
+    File.directory?(directory)
+  end
+
+  def destroy
+    FileUtils.rm_rf(@resource.value(:path))
+  end
+
+  def revision
+    if File.exist?(tag_file)
+      contents = File.read(tag_file)
+      # Note: Doesn't differentiate between N and T entries
+      contents[1..-1]
+    else
+      'MAIN'
+    end
+  end
+
+  def revision=(desired)
+    at_path do
+      cvs('update', '-r', desired, '.')
+    end
+  end
+
+  private
+
+  def tag_file
+    File.join(@resource.value(:path), 'CVS', 'Tag')
+  end
+
+  def checkout_repository
+    dirname, basename = File.split(@resource.value(:path))
+    Dir.chdir(dirname) do
+      args = ['-d', @resource.value(:source)]
+      if @resource.value(:compression)
+        args.push('-z', @resource.value(:compression))
+      end
+      args.push('checkout', '-d', basename, module_name)
+      cvs(*args)
+    end
+    if @resource.value(:revision)
+      self.revision = @resource.value(:revision)
+    end
+  end
+
+  # When the source:
+  # * Starts with ':' (eg, :pserver:...)
+  def module_name
+    if (source = @resource.value(:source))
+      source[0, 1] == ':' ? File.basename(source) : '.'
+    end
+  end
+
+  def create_repository(path)
+    cvs('-d', path, 'init')
+  end
+
+  # Note: We don't rely on Dir.chdir's behavior of automatically returning the
+  # value of the last statement -- for easier stubbing.
+  def at_path(&block) #:nodoc:
+    value = nil
+    Dir.chdir(@resource.value(:path)) do
+      value = yield
+    end
+    value
+  end
+
+end
index 226cc63..c85ffa4 100644 (file)
@@ -56,12 +56,6 @@ Puppet::Type.type(:vcsrepo).provide(:svn) do
     svnadmin(*args)
   end
 
-  def reset(desired)
-    at_path do
-      git('reset', '--hard', desired)
-    end
-  end
-
   # Note: We don't rely on Dir.chdir's behavior of automatically returning the
   # value of the last statement -- for easier stubbing.
   def at_path(&block) #:nodoc:
index b093910..7616f41 100644 (file)
@@ -13,12 +13,20 @@ Puppet::Type.newtype(:vcsrepo) do
     def retrieve
       prov = @resource.provider
       if prov
-        if prov.respond_to?(:working_copy_exists?) && prov.working_copy_exists?
-          :present
-        elsif prov.respond_to?(:bare_exists?) && prov.bare_exists?
-          :bare
+        if prov.respond_to?(:bare_exists?)
+          if prov.respond_to?(:working_copy_exists?) && prov.working_copy_exists?
+            :present
+          elsif prov.respond_to?(:bare_exists?) && prov.bare_exists?
+            :bare
+          else
+            :absent
+          end
         else
-          :absent
+          if prov.exists?
+            :present
+          else
+            :absent
+          end
         end
       else
         :absent
@@ -39,10 +47,7 @@ Puppet::Type.newtype(:vcsrepo) do
   end
 
   newparam(:source) do
-    desc "The source URL for the repository"
-    validate do |value|
-      URI.parse(value)
-    end
+    desc "The source URI for the repository"
   end
 
   newparam(:fstype) do
@@ -54,4 +59,13 @@ Puppet::Type.newtype(:vcsrepo) do
     newvalue(/^\S+$/)
   end
 
+  newparam :compression do
+    desc "Compression level (used by CVS)"
+    validate do |amount|
+      unless Integer(amount).between?(0, 6)
+        raise ArgumentError, "Unsupported compression level: #{amount} (expected 0-6)"
+      end
+    end
+  end
+
 end
diff --git a/spec/unit/puppet/provider/vcsrepo/cvs_spec.rb b/spec/unit/puppet/provider/vcsrepo/cvs_spec.rb
new file mode 100644 (file)
index 0000000..c0c2dbc
--- /dev/null
@@ -0,0 +1,137 @@
+require 'pathname'; Pathname.new(__FILE__).realpath.ascend { |x| begin; require (x + 'spec_helper.rb'); break; rescue LoadError; end }
+
+provider_class = Puppet::Type.type(:vcsrepo).provider(:cvs)
+
+describe provider_class do
+
+  before :each do
+    @resource = stub("resource")
+    @provider = provider_class.new(@resource)
+    @path = '/tmp/vcsrepo'
+  end
+
+  describe 'when creating' do
+    before do
+      @resource.expects(:value).with(:path).returns(@path).at_least_once
+    end
+    context "when a source is given" do
+      before do
+        @source = ":pserver:anonymous@example.com:/sources/myproj"
+        @resource.expects(:value).with(:source).returns(@source).at_least_once
+        Dir.expects(:chdir).with(File.dirname(@path)).yields
+      end
+      context "and when a revision is given" do
+        before do
+          @tag = 'SOMETAG'
+          @resource.expects(:value).with(:revision).returns(@tag).at_least_once
+          @resource.expects(:value).with(:compression).returns(nil).at_least_once
+        end
+        it "should execute 'cvs checkout' and 'cvs update -r'" do
+          @provider.expects(:cvs).with('-d', @source, 'checkout', '-d', File.basename(@path), File.basename(@source))
+          Dir.expects(:chdir).with(@path).yields
+          @provider.expects(:cvs).with('update', '-r', @tag, '.')
+          @provider.create
+        end        
+      end
+      context "and when a revision is not given" do
+        before do
+          @resource.expects(:value).with(:revision).returns(nil).at_least_once
+          @resource.expects(:value).with(:compression).returns(nil).at_least_once
+        end
+        it "should just execute 'cvs checkout' without a revision" do
+          @provider.expects(:cvs).with('-d', @source, 'checkout', '-d', File.basename(@path), File.basename(@source))
+          @provider.create
+        end        
+      end
+      context "when a compression level is given" do
+        before do
+          @resource.expects(:value).with(:revision).returns(nil).at_least_once
+          @resource.expects(:value).with(:compression).returns('3').at_least_once
+        end
+        it "should just execute 'cvs checkout' without a revision" do
+          @provider.expects(:cvs).with('-d', @source, '-z', '3', 'checkout', '-d', File.basename(@path), File.basename(@source))
+          @provider.create
+        end        
+      end
+    end
+    context "when a source is not given" do
+      before do
+        @resource.expects(:value).with(:source).returns(nil)
+      end
+      it "should execute 'cvs init'" do
+        @provider.expects(:cvs).with('-d', @path, 'init')
+        @provider.create
+      end
+    end
+  end
+
+  describe 'when destroying' do
+    it "it should remove the directory" do
+      @resource.expects(:value).with(:path).returns(@path).at_least_once
+      FileUtils.expects(:rm_rf).with(@path)
+      @provider.destroy
+    end
+  end
+
+  describe "when checking existence" do
+    before do
+      @resource.expects(:value).with(:path).returns(@path)
+    end
+    context "when a source is provided" do
+      before do
+        @resource.expects(:value).with(:source).returns(":pserver:anonymous@example.com:/sources/myproj")
+      end
+      it "should check for the CVS directory" do
+        File.expects(:directory?).with(File.join(@path, 'CVS'))
+        @provider.exists?
+      end
+    end
+    context "when a source is not provided" do
+      before do
+        @resource.expects(:value).with(:source).returns(nil)        
+      end
+      it "should check for the CVSROOT directory" do
+        File.expects(:directory?).with(File.join(@path, 'CVSROOT'))
+        @provider.exists?
+      end
+    end
+  end
+
+  describe "when checking the revision property" do
+    before do
+      @resource.expects(:value).with(:path).returns(@path).at_least_once
+      @tag_file = File.join(@path, 'CVS', 'Tag')
+    end
+    context "when CVS/Tag exists" do
+      before do
+        @tag = 'HEAD'
+        File.expects(:exist?).with(@tag_file).returns(true)
+      end
+      it "should read CVS/Tag" do
+        File.expects(:read).with(@tag_file).returns("T#{@tag}")
+        @provider.revision.should == @tag
+      end
+    end
+    context "when CVS/Tag does not exist" do
+      before do
+        File.expects(:exist?).with(@tag_file).returns(false)
+      end
+      it "assumes MAIN" do
+        @provider.revision.should == 'MAIN'        
+      end
+    end
+  end
+  
+  describe "when setting the revision property" do
+    before do
+      @resource.expects(:value).with(:path).returns(@path).at_least_once
+      @tag = 'SOMETAG'
+    end
+    it "should use 'cvs update -r'" do
+      Dir.expects(:chdir).with(@path).yields
+      @provider.expects('cvs').with('update', '-r', @tag, '.')
+      @provider.revision = @tag
+    end
+  end
+
+end