diff options
-rw-r--r-- | .travis.yml | 25 | ||||
-rw-r--r-- | Gemfile | 6 | ||||
-rw-r--r-- | Gemfile.lock | 14 | ||||
-rw-r--r-- | Modulefile | 4 | ||||
-rw-r--r-- | README.GIT.markdown | 12 | ||||
-rw-r--r-- | README.HG.markdown | 22 | ||||
-rw-r--r-- | README.markdown | 2 | ||||
-rw-r--r-- | lib/puppet/provider/vcsrepo/bzr.rb | 20 | ||||
-rw-r--r-- | lib/puppet/provider/vcsrepo/git.rb | 37 | ||||
-rw-r--r-- | lib/puppet/provider/vcsrepo/hg.rb | 32 | ||||
-rw-r--r-- | lib/puppet/provider/vcsrepo/svn.rb | 7 | ||||
-rw-r--r-- | lib/puppet/type/vcsrepo.rb | 22 | ||||
-rw-r--r-- | spec/unit/puppet/provider/vcsrepo/bzr_spec.rb | 6 | ||||
-rw-r--r-- | spec/unit/puppet/provider/vcsrepo/git_spec.rb | 47 |
14 files changed, 207 insertions, 49 deletions
diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..f349b8d --- /dev/null +++ b/.travis.yml @@ -0,0 +1,25 @@ +--- +branches: + only: + - master +notifications: + email: false +language: ruby +script: 'bundle exec rake spec' +after_success: +- git clone -q git://github.com/puppetlabs/ghpublisher.git .forge-releng +- .forge-releng/publish +rvm: +- 1.8.7 +- 1.9.3 +env: + matrix: + - PUPPET_VERSION=2.6.18 + - PUPPET_VERSION=2.7.21 + - PUPPET_VERSION=3.1.1 + global: + - PUBLISHER_LOGIN=puppetlabs + - secure: |- + ZiIkYd9+CdPzpwSjFPnVkCx1FIlipxpbdyD33q94h2Tj5zXjNb1GXizVy0NR + kVxGhU5Ld8y9z8DTqKRgCI1Yymg3H//OU++PKLOQj/X5juWVR4URBNPeBOzu + IJBDl1MADKA4i1+jAZPpz4mTvTtKS4pWKErgCSmhSfsY1hs7n6c= @@ -1,5 +1,5 @@ -source :rubygems +source 'https://rubygems.org' gem 'rake', '~> 0.8.7' -gem 'rspec', '~> 1.2.9' -gem 'mocha', '~> 0.12.7', :require => false +gem 'rspec', '~> 1.3' +gem 'mocha', '~> 0.12.9', :require => false gem 'puppet', '~> 2.7' diff --git a/Gemfile.lock b/Gemfile.lock index 210a2dd..ce22804 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,20 +1,20 @@ GEM - remote: http://rubygems.org/ + remote: https://rubygems.org/ specs: - facter (1.6.13) + facter (1.7.2) metaclass (0.0.1) - mocha (0.12.7) + mocha (0.12.10) metaclass (~> 0.0.1) - puppet (2.7.19) + puppet (2.7.22) facter (~> 1.5) rake (0.8.7) - rspec (1.2.9) + rspec (1.3.2) PLATFORMS ruby DEPENDENCIES - mocha (~> 0.12.7) + mocha (~> 0.12.9) puppet (~> 2.7) rake (~> 0.8.7) - rspec (~> 1.2.9) + rspec (~> 1.3) @@ -1,2 +1,4 @@ name 'puppetlabs/vcsrepo' -version '0.1.1' +version '0.1.2' +summary 'Manage repositories from various version control systems' +description 'Manage repositories from various version control systems' diff --git a/README.GIT.markdown b/README.GIT.markdown index d6b8afe..846bdcc 100644 --- a/README.GIT.markdown +++ b/README.GIT.markdown @@ -76,9 +76,15 @@ Keep the repository at the latest revision (note: this will always overwrite loc For sources that use SSH (eg, `username@server:...`) ---------------------------------------------------- -Manage your SSH keys with Puppet and use `require` in your `vcsrepo` -to ensure they are present. For more information, see the `require` -metaparameter documentation[1]. +If your SSH key is associated with a user, simply fill the `user` parameter to use his keys. + +Example: + + user => 'toto' # will use toto's $HOME/.ssh setup + + +Otherwise, manage your SSH keys with Puppet and use `require` in your `vcsrepo` to ensure they are present. +For more information, see the `require` metaparameter documentation[1]. More Examples ------------- diff --git a/README.HG.markdown b/README.HG.markdown index b1680c8..55ceef4 100644 --- a/README.HG.markdown +++ b/README.HG.markdown @@ -27,7 +27,7 @@ For a specific changeset, use `revision`: vcsrepo { "/path/to/repo": ensure => present, provider => hg, - source => "http://hg.example.com/myrepo" + source => "http://hg.example.com/myrepo", revision => '21ea4598c962' } @@ -36,10 +36,28 @@ You can also set `revision` to a tag: vcsrepo { "/path/to/repo": ensure => present, provider => hg, - source => "http://hg.example.com/myrepo" + source => "http://hg.example.com/myrepo", revision => '1.1.2' } +Check out as a user: + + vcsrepo { "/path/to/repo": + ensure => present, + provider => hg, + source => "http://hg.example.com/myrepo", + user => 'user' + } + +Specify an SSH identity key: + + vcsrepo { "/path/to/repo": + ensure => present, + provider => hg, + source => "ssh://hg@hg.example.com/myrepo", + identity => "/home/user/.ssh/id_dsa, + } + For sources that use SSH (eg, `ssh://...`) ------------------------------------------ diff --git a/README.markdown b/README.markdown index 823e044..8487256 100644 --- a/README.markdown +++ b/README.markdown @@ -1,6 +1,8 @@ vcsrepo ======= +[](https://travis-ci.org/puppetlabs/puppetlabs-vcsrepo) + Purpose ------- diff --git a/lib/puppet/provider/vcsrepo/bzr.rb b/lib/puppet/provider/vcsrepo/bzr.rb index 6169929..6688ce8 100644 --- a/lib/puppet/provider/vcsrepo/bzr.rb +++ b/lib/puppet/provider/vcsrepo/bzr.rb @@ -45,7 +45,25 @@ Puppet::Type.type(:vcsrepo).provide(:bzr, :parent => Puppet::Provider::Vcsrepo) end def revision=(desired) - bzr('update', '-r', desired, @resource.value(:path)) + at_path do + begin + bzr('update', '-r', desired) + rescue Puppet::ExecutionFailure + bzr('update', '-r', desired, ':parent') + end + end + end + + def latest + at_path do + bzr('version-info', ':parent')[/^revision-id:\s+(\S+)/, 1] + end + end + + def latest? + at_path do + return self.revision == self.latest + end end private diff --git a/lib/puppet/provider/vcsrepo/git.rb b/lib/puppet/provider/vcsrepo/git.rb index 66c4d07..d971e90 100644 --- a/lib/puppet/provider/vcsrepo/git.rb +++ b/lib/puppet/provider/vcsrepo/git.rb @@ -5,7 +5,7 @@ Puppet::Type.type(:vcsrepo).provide(:git, :parent => Puppet::Provider::Vcsrepo) ##TODO modify the commands below so that the su - is included optional_commands :git => 'git', - :su => 'su' + :su => 'su' has_features :bare_repositories, :reference_tracking, :ssh_identity, :multiple_remotes, :user def create @@ -54,9 +54,22 @@ Puppet::Type.type(:vcsrepo).provide(:git, :parent => Puppet::Provider::Vcsrepo) return current unless @resource.value(:revision) if tag_revision?(@resource.value(:revision)) - canonical = at_path { git_with_identity('show', @resource.value(:revision)).scan(/commit (.*)/).to_s } + canonical = at_path { git_with_identity('show', @resource.value(:revision)).scan(/^commit (.*)/).to_s } else - canonical = at_path { git_with_identity('rev-parse', @resource.value(:revision)).chomp } + # if it's not a tag, look for it as a local ref + canonical = at_path { git_with_identity('rev-parse', '--revs-only', @resource.value(:revision)).chomp } + if canonical.empty? + # git rev-parse executed properly but didn't find the ref; + # look for it in the remote + remote_ref = at_path { git_with_identity('ls-remote', '--heads', '--tags', @resource.value(:remote), @resource.value(:revision)).chomp } + if remote_ref.empty? + fail("#{@resource.value(:revision)} is not a local or remote ref") + end + + # $ git ls-remote --heads --tags origin feature/cvs + # 7d4244b35e72904e30130cad6d2258f901c16f1a refs/heads/feature/cvs + canonical = remote_ref.split.first + end end if current == canonical @@ -93,8 +106,18 @@ Puppet::Type.type(:vcsrepo).provide(:git, :parent => Puppet::Provider::Vcsrepo) working_copy_exists? || bare_exists? end + def update_remote_origin_url + current = git_with_identity('config', 'remote.origin.url') + unless @resource.value(:source).nil? + if current.nil? or current.strip != @resource.value(:source) + git_with_identity('config', 'remote.origin.url', @resource.value(:source)) + end + end + end + def update_references at_path do + update_remote_origin_url git_with_identity('fetch', @resource.value(:remote)) git_with_identity('fetch', '--tags', @resource.value(:remote)) update_owner_and_excludes @@ -115,7 +138,9 @@ Puppet::Type.type(:vcsrepo).provide(:git, :parent => Puppet::Provider::Vcsrepo) end if !File.exist?(File.join(@resource.value(:path), '.git')) args.push(source, path) - git_with_identity(*args) + Dir.chdir("/") do + git_with_identity(*args) + end else notice "Repo has already been cloned" end @@ -141,6 +166,7 @@ Puppet::Type.type(:vcsrepo).provide(:git, :parent => Puppet::Provider::Vcsrepo) else # normal init FileUtils.mkdir(@resource.value(:path)) + FileUtils.chown(@resource.value(:user), nil, @resource.value(:path)) if @resource.value(:user) args = ['init'] if @resource.value(:ensure) == :bare args << '--bare' @@ -247,6 +273,7 @@ Puppet::Type.type(:vcsrepo).provide(:git, :parent => Puppet::Provider::Vcsrepo) create end at_path do + update_remote_origin_url git_with_identity('fetch', @resource.value(:remote)) git_with_identity('fetch', '--tags', @resource.value(:remote)) end @@ -276,7 +303,7 @@ Puppet::Type.type(:vcsrepo).provide(:git, :parent => Puppet::Provider::Vcsrepo) if @resource.value(:identity) Tempfile.open('git-helper') do |f| f.puts '#!/bin/sh' - f.puts "exec ssh -oStrictHostKeyChecking=no -oPasswordAuthentication=no -oKbdInteractiveAuthentication=no -oChallengeResponseAuthentication=no -i #{@resource.value(:identity)} $*" + f.puts "exec ssh -oStrictHostKeyChecking=no -oPasswordAuthentication=no -oKbdInteractiveAuthentication=no -oChallengeResponseAuthentication=no -oConnectTimeout=120 -i #{@resource.value(:identity)} $*" f.close FileUtils.chmod(0755, f.path) diff --git a/lib/puppet/provider/vcsrepo/hg.rb b/lib/puppet/provider/vcsrepo/hg.rb index 67a2a82..4886b7a 100644 --- a/lib/puppet/provider/vcsrepo/hg.rb +++ b/lib/puppet/provider/vcsrepo/hg.rb @@ -3,8 +3,9 @@ require File.join(File.dirname(__FILE__), '..', 'vcsrepo') Puppet::Type.type(:vcsrepo).provide(:hg, :parent => Puppet::Provider::Vcsrepo) do desc "Supports Mercurial repositories" - optional_commands :hg => 'hg' - has_features :reference_tracking + optional_commands :hg => 'hg', + :su => 'su' + has_features :reference_tracking, :ssh_identity, :user def create if !@resource.value(:source) @@ -36,7 +37,7 @@ Puppet::Type.type(:vcsrepo).provide(:hg, :parent => Puppet::Provider::Vcsrepo) d def latest at_path do begin - hg('incoming', '--branch', '.', '--newest-first', '--limit', '1')[/^changeset:\s+(?:-?\d+):(\S+)/m, 1] + hg_wrapper('incoming', '--branch', '.', '--newest-first', '--limit', '1')[/^changeset:\s+(?:-?\d+):(\S+)/m, 1] rescue Puppet::ExecutionFailure # If there are no new changesets, return the current nodeid self.revision @@ -46,11 +47,11 @@ Puppet::Type.type(:vcsrepo).provide(:hg, :parent => Puppet::Provider::Vcsrepo) d def revision at_path do - current = hg('parents')[/^changeset:\s+(?:-?\d+):(\S+)/m, 1] + current = hg_wrapper('parents')[/^changeset:\s+(?:-?\d+):(\S+)/m, 1] desired = @resource.value(:revision) if desired # Return the tag name if it maps to the current nodeid - mapped = hg('tags')[/^#{Regexp.quote(desired)}\s+\d+:(\S+)/m, 1] + mapped = hg_wrapper('tags')[/^#{Regexp.quote(desired)}\s+\d+:(\S+)/m, 1] if current == mapped desired else @@ -65,15 +66,15 @@ Puppet::Type.type(:vcsrepo).provide(:hg, :parent => Puppet::Provider::Vcsrepo) d def revision=(desired) at_path do begin - hg('pull') + hg_wrapper('pull') rescue end begin - hg('merge') + hg_wrapper('merge') rescue Puppet::ExecutionFailure # If there's nothing to merge, just skip end - hg('update', '--clean', '-r', desired) + hg_wrapper('update', '--clean', '-r', desired) end update_owner end @@ -81,7 +82,7 @@ Puppet::Type.type(:vcsrepo).provide(:hg, :parent => Puppet::Provider::Vcsrepo) d private def create_repository(path) - hg('init', path) + hg_wrapper('init', path) end def clone_repository(revision) @@ -91,7 +92,7 @@ Puppet::Type.type(:vcsrepo).provide(:hg, :parent => Puppet::Provider::Vcsrepo) d end args.push(@resource.value(:source), @resource.value(:path)) - hg(*args) + hg_wrapper(*args) end def update_owner @@ -100,4 +101,15 @@ Puppet::Type.type(:vcsrepo).provide(:hg, :parent => Puppet::Provider::Vcsrepo) d end end + def hg_wrapper(*args) + if @resource.value(:identity) + args += ["--ssh", "ssh -oStrictHostKeyChecking=no -oPasswordAuthentication=no -oKbdInteractiveAuthentication=no -oChallengeResponseAuthentication=no -i #{@resource.value(:identity)}"] + end + if @resource.value(:user) + args.map! { |a| if a =~ /\s/ then "'#{a}'" else a end } # Adds quotes to arguments with whitespaces. + su(@resource.value(:user), '-c', "hg #{args.join(' ')}") + else + hg(*args) + end + end end diff --git a/lib/puppet/provider/vcsrepo/svn.rb b/lib/puppet/provider/vcsrepo/svn.rb index b2a8880..2dc0fd1 100644 --- a/lib/puppet/provider/vcsrepo/svn.rb +++ b/lib/puppet/provider/vcsrepo/svn.rb @@ -4,7 +4,7 @@ Puppet::Type.type(:vcsrepo).provide(:svn, :parent => Puppet::Provider::Vcsrepo) desc "Supports Subversion repositories" optional_commands :svn => 'svn', - :svnadmin => 'svnadmin' + :svnadmin => 'svnadmin' has_features :filesystem_types, :reference_tracking, :basic_auth @@ -48,6 +48,11 @@ Puppet::Type.type(:vcsrepo).provide(:svn, :parent => Puppet::Provider::Vcsrepo) args.push('--password', @resource.value(:basic_auth_password)) args.push('--no-auth-cache') end + + if @resource.value(:force) + args.push('--force') + end + return args end diff --git a/lib/puppet/type/vcsrepo.rb b/lib/puppet/type/vcsrepo.rb index 4b53235..45ac455 100644 --- a/lib/puppet/type/vcsrepo.rb +++ b/lib/puppet/type/vcsrepo.rb @@ -38,16 +38,16 @@ Puppet::Type.newtype(:vcsrepo) do @should ||= [] case should - when :present - return true unless [:absent, :purged, :held].include?(is) - when :latest - if is == :latest - return true - else - return false - end - when :bare - return is == :bare + when :present + return true unless [:absent, :purged, :held].include?(is) + when :latest + if is == :latest + return true + else + return false + end + when :bare + return is == :bare end end @@ -57,7 +57,7 @@ Puppet::Type.newtype(:vcsrepo) do end newvalue :bare, :required_features => [:bare_repositories] do - if !provider.exists? + if !provider.exists? provider.create end end diff --git a/spec/unit/puppet/provider/vcsrepo/bzr_spec.rb b/spec/unit/puppet/provider/vcsrepo/bzr_spec.rb index e1804de..c0231e9 100644 --- a/spec/unit/puppet/provider/vcsrepo/bzr_spec.rb +++ b/spec/unit/puppet/provider/vcsrepo/bzr_spec.rb @@ -81,9 +81,9 @@ describe_provider :vcsrepo, :bzr, :resource => {:path => '/tmp/vcsrepo'} do describe "setting the revision property" do it "should use 'bzr update -r' with the revision" do - revision = 'somerev' - provider.expects(:bzr).with('update', '-r', revision, resource.value(:path)) - provider.revision = revision + expects_chdir + provider.expects(:bzr).with('update', '-r', 'somerev') + provider.revision = 'somerev' end end diff --git a/spec/unit/puppet/provider/vcsrepo/git_spec.rb b/spec/unit/puppet/provider/vcsrepo/git_spec.rb index c878941..68b6c0a 100644 --- a/spec/unit/puppet/provider/vcsrepo/git_spec.rb +++ b/spec/unit/puppet/provider/vcsrepo/git_spec.rb @@ -8,6 +8,7 @@ describe_provider :vcsrepo, :git, :resource => {:path => '/tmp/vcsrepo'} 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('/') expects_chdir provider.expects(:update_submodules) provider.expects(:git).with('branch', '-a').returns(resource.value(:revision)) @@ -18,6 +19,7 @@ describe_provider :vcsrepo, :git, :resource => {:path => '/tmp/vcsrepo'} do 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('/') expects_chdir provider.expects(:update_submodules) provider.expects(:git).with('branch', '-a').returns(resource.value(:revision)) @@ -127,9 +129,10 @@ describe_provider :vcsrepo, :git, :resource => {:path => '/tmp/vcsrepo'} do context "when its SHA is not different than the current SHA" do it "should return the ref" do + provider.expects(:git).with('config', 'remote.origin.url').returns('') provider.expects(:git).with('fetch', 'origin') # FIXME provider.expects(:git).with('fetch', '--tags', 'origin') - provider.expects(:git).with('rev-parse', resource.value(:revision)).returns('currentsha') + provider.expects(:git).with('rev-parse', '--revs-only', resource.value(:revision)).returns('currentsha') provider.expects(:git).with('tag', '-l').returns("Hello") provider.revision.should == resource.value(:revision) end @@ -137,13 +140,52 @@ describe_provider :vcsrepo, :git, :resource => {:path => '/tmp/vcsrepo'} do context "when its SHA is different than the current SHA" do it "should return the current SHA" do + provider.expects(:git).with('config', 'remote.origin.url').returns('') provider.expects(:git).with('fetch', 'origin') # FIXME provider.expects(:git).with('fetch', '--tags', 'origin') - provider.expects(:git).with('rev-parse', resource.value(:revision)).returns('othersha') + provider.expects(:git).with('rev-parse', '--revs-only', resource.value(:revision)).returns('othersha') provider.expects(:git).with('tag', '-l').returns("Hello") provider.revision.should == 'currentsha' end end + + context "when its a ref to a remote head" do + it "should return the revision" do + provider.expects(:git).with('config', 'remote.origin.url').returns('') + provider.expects(:git).with('fetch', 'origin') # FIXME + provider.expects(:git).with('fetch', '--tags', 'origin') + provider.expects(:git).with('tag', '-l').returns("Hello") + provider.expects(:git).with('rev-parse', '--revs-only', resource.value(:revision)).returns('') + provider.expects(:git).with('ls-remote', '--heads', '--tags', 'origin', resource.value(:revision)).returns("newsha refs/heads/#{resource.value(:revision)}") + provider.revision.should == 'currentsha' + end + end + + context "when its a ref to non existant remote head" do + it "should fail" do + provider.expects(:git).with('config', 'remote.origin.url').returns('') + provider.expects(:git).with('fetch', 'origin') # FIXME + provider.expects(:git).with('fetch', '--tags', 'origin') + provider.expects(:git).with('tag', '-l').returns("Hello") + provider.expects(:git).with('rev-parse', '--revs-only', resource.value(:revision)).returns('') + provider.expects(:git).with('ls-remote', '--heads', '--tags', 'origin', resource.value(:revision)).returns('') + expect { provider.revision }.should raise_error(Puppet::Error, /not a local or remote ref$/) + end + end + + context "when the source is modified" do + resource_with :source => 'git://git@foo.com/bar.git' do + it "should update the origin url" do + provider.expects(:git).with('config', 'remote.origin.url').returns('old') + provider.expects(:git).with('config', 'remote.origin.url', 'git://git@foo.com/bar.git') + provider.expects(:git).with('fetch', 'origin') # FIXME + provider.expects(:git).with('fetch', '--tags', 'origin') + provider.expects(:git).with('rev-parse', '--revs-only', resource.value(:revision)).returns('currentsha') + provider.expects(:git).with('tag', '-l').returns("Hello") + provider.revision.should == resource.value(:revision) + end + end + end end end @@ -186,6 +228,7 @@ describe_provider :vcsrepo, :git, :resource => {:path => '/tmp/vcsrepo'} do context "updating references" do it "should use 'git fetch --tags'" do expects_chdir + provider.expects(:git).with('config', 'remote.origin.url').returns('') provider.expects(:git).with('fetch', 'origin') provider.expects(:git).with('fetch', '--tags', 'origin') provider.update_references |