From 7d4244b35e72904e30130cad6d2258f901c16f1a Mon Sep 17 00:00:00 2001 From: Bruce Williams Date: Sat, 13 Mar 2010 11:50:28 -0800 Subject: Basic CVS support --- README.CVS.markdown | 42 ++++++++ lib/puppet/provider/vcsrepo/cvs.rb | 87 ++++++++++++++++ lib/puppet/provider/vcsrepo/svn.rb | 6 -- lib/puppet/type/vcsrepo.rb | 32 ++++-- spec/unit/puppet/provider/vcsrepo/cvs_spec.rb | 137 ++++++++++++++++++++++++++ 5 files changed, 289 insertions(+), 15 deletions(-) create mode 100644 README.CVS.markdown create mode 100644 lib/puppet/provider/vcsrepo/cvs.rb create mode 100644 spec/unit/puppet/provider/vcsrepo/cvs_spec.rb diff --git a/README.CVS.markdown b/README.CVS.markdown new file mode 100644 index 0000000..7f9647e --- /dev/null +++ b/README.CVS.markdown @@ -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 index 0000000..68d22c1 --- /dev/null +++ b/lib/puppet/provider/vcsrepo/cvs.rb @@ -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 diff --git a/lib/puppet/provider/vcsrepo/svn.rb b/lib/puppet/provider/vcsrepo/svn.rb index 226cc63..c85ffa4 100644 --- a/lib/puppet/provider/vcsrepo/svn.rb +++ b/lib/puppet/provider/vcsrepo/svn.rb @@ -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: diff --git a/lib/puppet/type/vcsrepo.rb b/lib/puppet/type/vcsrepo.rb index b093910..7616f41 100644 --- a/lib/puppet/type/vcsrepo.rb +++ b/lib/puppet/type/vcsrepo.rb @@ -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 index 0000000..c0c2dbc --- /dev/null +++ b/spec/unit/puppet/provider/vcsrepo/cvs_spec.rb @@ -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 -- cgit v1.2.3