294c2a977319617513ad11d8d83066a7e1bc42f7
[puppet_vcsrepo.git] / lib / puppet / provider / vcsrepo / hg.rb
1 require File.join(File.dirname(__FILE__), '..', 'vcsrepo')
2
3 Puppet::Type.type(:vcsrepo).provide(:hg, :parent => Puppet::Provider::Vcsrepo) do
4   desc "Supports Mercurial repositories"
5
6   commands :hg => 'hg'
7
8   has_features :reference_tracking, :ssh_identity, :user, :basic_auth
9
10   def create
11     if !@resource.value(:source)
12       create_repository(@resource.value(:path))
13     else
14       clone_repository(@resource.value(:revision))
15     end
16     update_owner
17   end
18
19   def working_copy_exists?
20     File.directory?(File.join(@resource.value(:path), '.hg'))
21   end
22
23   def exists?
24     working_copy_exists?
25   end
26
27   def destroy
28     FileUtils.rm_rf(@resource.value(:path))
29   end
30
31   def latest?
32     at_path do
33       return self.revision == self.latest
34     end
35   end
36
37   def latest
38     at_path do
39       begin
40         hg_wrapper('incoming', '--branch', '.', '--newest-first', '--limit', '1', { :remote => true })[/^changeset:\s+(?:-?\d+):(\S+)/m, 1]
41       rescue Puppet::ExecutionFailure
42         # If there are no new changesets, return the current nodeid
43         self.revision
44       end
45     end
46   end
47
48   def revision
49     at_path do
50       current = hg_wrapper('parents')[/^changeset:\s+(?:-?\d+):(\S+)/m, 1]
51       desired = @resource.value(:revision)
52       if desired
53         # Return the tag name if it maps to the current nodeid
54         mapped = hg_wrapper('tags')[/^#{Regexp.quote(desired)}\s+\d+:(\S+)/m, 1]
55         if current == mapped
56           desired
57         else
58           current
59         end
60       else
61         current
62       end
63     end
64   end
65
66   def revision=(desired)
67     at_path do
68       begin
69         hg_wrapper('pull', { :remote => true })
70       rescue
71       end
72       begin
73         hg_wrapper('merge')
74       rescue Puppet::ExecutionFailure
75         # If there's nothing to merge, just skip
76       end
77       hg_wrapper('update', '--clean', '-r', desired)
78     end
79     update_owner
80   end
81
82   private
83
84   def create_repository(path)
85     hg_wrapper('init', path)
86   end
87
88   def clone_repository(revision)
89     args = ['clone']
90     if revision
91       args.push('-u', revision)
92     end
93     args.push(@resource.value(:source),
94               @resource.value(:path))
95     args.push({ :remote => true })
96     hg_wrapper(*args)
97   end
98
99   def update_owner
100     if @resource.value(:owner) or @resource.value(:group)
101       set_ownership
102     end
103   end
104
105   def hg_wrapper(*args)
106     options = { :remote => false }
107     if args.length > 0 and args[-1].is_a? Hash
108       options.merge!(args.pop)
109     end
110
111     if @resource.value(:basic_auth_username) && @resource.value(:basic_auth_password)
112       args += [
113         "--config", "\"auth.x.prefix=#{@resource.value(:source)}\"",
114         "--config", "\"auth.x.username=#{@resource.value(:basic_auth_username)}\"",
115         "--config", "\"auth.x.password=#{@resource.value(:basic_auth_password)}\"",
116         "--config", "\"auth.x.schemes=http https\""
117       ]
118     end
119
120     if options[:remote] and @resource.value(:identity)
121       args += ["--ssh", "ssh -oStrictHostKeyChecking=no -oPasswordAuthentication=no -oKbdInteractiveAuthentication=no -oChallengeResponseAuthentication=no -i #{@resource.value(:identity)}"]
122     end
123     if @resource.value(:user) and @resource.value(:user) != Facter['id'].value
124       args.map! { |a| if a =~ /\s/ then "'#{a}'" else a end }  # Adds quotes to arguments with whitespaces.
125       Puppet::Util::Execution.execute("hg #{args.join(' ')}", :uid => @resource.value(:user), :failonfail => true)
126     else
127       hg(*args)
128     end
129   end
130 end