microwave 0.1004.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (206) hide show
  1. data/LICENSE +201 -0
  2. data/README.rdoc +102 -0
  3. data/bin/chef-solo +25 -0
  4. data/lib/chef.rb +41 -0
  5. data/lib/chef/application.rb +147 -0
  6. data/lib/chef/application/solo.rb +181 -0
  7. data/lib/chef/applications.rb +1 -0
  8. data/lib/chef/checksum.rb +83 -0
  9. data/lib/chef/checksum_cache.rb +189 -0
  10. data/lib/chef/client.rb +325 -0
  11. data/lib/chef/config.rb +152 -0
  12. data/lib/chef/cookbook/chefignore.rb +66 -0
  13. data/lib/chef/cookbook/cookbook_collection.rb +45 -0
  14. data/lib/chef/cookbook/cookbook_version_loader.rb +173 -0
  15. data/lib/chef/cookbook/file_system_file_vendor.rb +56 -0
  16. data/lib/chef/cookbook/file_vendor.rb +48 -0
  17. data/lib/chef/cookbook/metadata.rb +629 -0
  18. data/lib/chef/cookbook/syntax_check.rb +136 -0
  19. data/lib/chef/cookbook_loader.rb +121 -0
  20. data/lib/chef/cookbook_version.rb +786 -0
  21. data/lib/chef/cookbook_version_selector.rb +151 -0
  22. data/lib/chef/environment.rb +267 -0
  23. data/lib/chef/exceptions.rb +150 -0
  24. data/lib/chef/file_access_control.rb +144 -0
  25. data/lib/chef/file_cache.rb +218 -0
  26. data/lib/chef/handler.rb +206 -0
  27. data/lib/chef/handler/error_report.rb +33 -0
  28. data/lib/chef/handler/json_file.rb +58 -0
  29. data/lib/chef/json_compat.rb +52 -0
  30. data/lib/chef/log.rb +39 -0
  31. data/lib/chef/mash.rb +211 -0
  32. data/lib/chef/mixin/check_helper.rb +31 -0
  33. data/lib/chef/mixin/checksum.rb +32 -0
  34. data/lib/chef/mixin/command.rb +221 -0
  35. data/lib/chef/mixin/command/unix.rb +215 -0
  36. data/lib/chef/mixin/command/windows.rb +76 -0
  37. data/lib/chef/mixin/convert_to_class_name.rb +63 -0
  38. data/lib/chef/mixin/create_path.rb +57 -0
  39. data/lib/chef/mixin/deep_merge.rb +225 -0
  40. data/lib/chef/mixin/deprecation.rb +65 -0
  41. data/lib/chef/mixin/from_file.rb +50 -0
  42. data/lib/chef/mixin/get_source_from_package.rb +42 -0
  43. data/lib/chef/mixin/language.rb +125 -0
  44. data/lib/chef/mixin/language_include_attribute.rb +61 -0
  45. data/lib/chef/mixin/language_include_recipe.rb +52 -0
  46. data/lib/chef/mixin/params_validate.rb +225 -0
  47. data/lib/chef/mixin/recipe_definition_dsl_core.rb +78 -0
  48. data/lib/chef/mixin/shell_out.rb +41 -0
  49. data/lib/chef/mixin/template.rb +95 -0
  50. data/lib/chef/mixin/xml_escape.rb +140 -0
  51. data/lib/chef/mixins.rb +15 -0
  52. data/lib/chef/monkey_patches/dir.rb +36 -0
  53. data/lib/chef/monkey_patches/numeric.rb +15 -0
  54. data/lib/chef/monkey_patches/object.rb +9 -0
  55. data/lib/chef/monkey_patches/regexp.rb +34 -0
  56. data/lib/chef/monkey_patches/string.rb +49 -0
  57. data/lib/chef/monkey_patches/tempfile.rb +64 -0
  58. data/lib/chef/nil_argument.rb +3 -0
  59. data/lib/chef/node.rb +469 -0
  60. data/lib/chef/node/attribute.rb +487 -0
  61. data/lib/chef/platform.rb +409 -0
  62. data/lib/chef/provider.rb +124 -0
  63. data/lib/chef/provider/breakpoint.rb +31 -0
  64. data/lib/chef/provider/cookbook_file.rb +100 -0
  65. data/lib/chef/provider/cron.rb +186 -0
  66. data/lib/chef/provider/cron/solaris.rb +195 -0
  67. data/lib/chef/provider/deploy.rb +343 -0
  68. data/lib/chef/provider/deploy/revision.rb +80 -0
  69. data/lib/chef/provider/deploy/timestamped.rb +33 -0
  70. data/lib/chef/provider/directory.rb +72 -0
  71. data/lib/chef/provider/env.rb +152 -0
  72. data/lib/chef/provider/env/windows.rb +75 -0
  73. data/lib/chef/provider/erl_call.rb +101 -0
  74. data/lib/chef/provider/execute.rb +66 -0
  75. data/lib/chef/provider/file.rb +222 -0
  76. data/lib/chef/provider/git.rb +243 -0
  77. data/lib/chef/provider/group.rb +133 -0
  78. data/lib/chef/provider/group/aix.rb +70 -0
  79. data/lib/chef/provider/group/dscl.rb +121 -0
  80. data/lib/chef/provider/group/gpasswd.rb +53 -0
  81. data/lib/chef/provider/group/groupadd.rb +81 -0
  82. data/lib/chef/provider/group/pw.rb +84 -0
  83. data/lib/chef/provider/group/suse.rb +53 -0
  84. data/lib/chef/provider/group/usermod.rb +57 -0
  85. data/lib/chef/provider/group/windows.rb +79 -0
  86. data/lib/chef/provider/ifconfig.rb +134 -0
  87. data/lib/chef/provider/link.rb +164 -0
  88. data/lib/chef/provider/log.rb +54 -0
  89. data/lib/chef/provider/mdadm.rb +91 -0
  90. data/lib/chef/provider/mount.rb +114 -0
  91. data/lib/chef/provider/mount/mount.rb +240 -0
  92. data/lib/chef/provider/mount/windows.rb +81 -0
  93. data/lib/chef/provider/ohai.rb +42 -0
  94. data/lib/chef/provider/package.rb +163 -0
  95. data/lib/chef/provider/package/apt.rb +135 -0
  96. data/lib/chef/provider/package/dpkg.rb +115 -0
  97. data/lib/chef/provider/package/easy_install.rb +136 -0
  98. data/lib/chef/provider/package/freebsd.rb +125 -0
  99. data/lib/chef/provider/package/macports.rb +105 -0
  100. data/lib/chef/provider/package/pacman.rb +101 -0
  101. data/lib/chef/provider/package/portage.rb +135 -0
  102. data/lib/chef/provider/package/rpm.rb +104 -0
  103. data/lib/chef/provider/package/rubygems.rb +465 -0
  104. data/lib/chef/provider/package/solaris.rb +130 -0
  105. data/lib/chef/provider/package/yum-dump.py +286 -0
  106. data/lib/chef/provider/package/yum.rb +1128 -0
  107. data/lib/chef/provider/package/zypper.rb +144 -0
  108. data/lib/chef/provider/remote_directory.rb +137 -0
  109. data/lib/chef/provider/route.rb +193 -0
  110. data/lib/chef/provider/ruby_block.rb +34 -0
  111. data/lib/chef/provider/script.rb +55 -0
  112. data/lib/chef/provider/service.rb +122 -0
  113. data/lib/chef/provider/service/arch.rb +116 -0
  114. data/lib/chef/provider/service/debian.rb +130 -0
  115. data/lib/chef/provider/service/freebsd.rb +154 -0
  116. data/lib/chef/provider/service/gentoo.rb +53 -0
  117. data/lib/chef/provider/service/init.rb +71 -0
  118. data/lib/chef/provider/service/insserv.rb +52 -0
  119. data/lib/chef/provider/service/redhat.rb +60 -0
  120. data/lib/chef/provider/service/simple.rb +120 -0
  121. data/lib/chef/provider/service/solaris.rb +85 -0
  122. data/lib/chef/provider/service/systemd.rb +102 -0
  123. data/lib/chef/provider/service/upstart.rb +198 -0
  124. data/lib/chef/provider/service/windows.rb +146 -0
  125. data/lib/chef/provider/subversion.rb +197 -0
  126. data/lib/chef/provider/template.rb +104 -0
  127. data/lib/chef/provider/user.rb +186 -0
  128. data/lib/chef/provider/user/dscl.rb +280 -0
  129. data/lib/chef/provider/user/pw.rb +113 -0
  130. data/lib/chef/provider/user/useradd.rb +137 -0
  131. data/lib/chef/provider/user/windows.rb +124 -0
  132. data/lib/chef/providers.rb +93 -0
  133. data/lib/chef/recipe.rb +129 -0
  134. data/lib/chef/resource.rb +584 -0
  135. data/lib/chef/resource/apt_package.rb +34 -0
  136. data/lib/chef/resource/bash.rb +33 -0
  137. data/lib/chef/resource/breakpoint.rb +35 -0
  138. data/lib/chef/resource/cookbook_file.rb +45 -0
  139. data/lib/chef/resource/cron.rb +188 -0
  140. data/lib/chef/resource/csh.rb +33 -0
  141. data/lib/chef/resource/deploy.rb +380 -0
  142. data/lib/chef/resource/deploy_revision.rb +40 -0
  143. data/lib/chef/resource/directory.rb +89 -0
  144. data/lib/chef/resource/dpkg_package.rb +34 -0
  145. data/lib/chef/resource/easy_install_package.rb +57 -0
  146. data/lib/chef/resource/env.rb +58 -0
  147. data/lib/chef/resource/erl_call.rb +83 -0
  148. data/lib/chef/resource/execute.rb +127 -0
  149. data/lib/chef/resource/file.rb +112 -0
  150. data/lib/chef/resource/freebsd_package.rb +35 -0
  151. data/lib/chef/resource/gem_package.rb +53 -0
  152. data/lib/chef/resource/git.rb +46 -0
  153. data/lib/chef/resource/group.rb +70 -0
  154. data/lib/chef/resource/ifconfig.rb +134 -0
  155. data/lib/chef/resource/link.rb +105 -0
  156. data/lib/chef/resource/log.rb +62 -0
  157. data/lib/chef/resource/macports_package.rb +29 -0
  158. data/lib/chef/resource/mdadm.rb +82 -0
  159. data/lib/chef/resource/mount.rb +134 -0
  160. data/lib/chef/resource/ohai.rb +40 -0
  161. data/lib/chef/resource/package.rb +80 -0
  162. data/lib/chef/resource/pacman_package.rb +33 -0
  163. data/lib/chef/resource/perl.rb +33 -0
  164. data/lib/chef/resource/portage_package.rb +33 -0
  165. data/lib/chef/resource/python.rb +33 -0
  166. data/lib/chef/resource/remote_directory.rb +109 -0
  167. data/lib/chef/resource/route.rb +135 -0
  168. data/lib/chef/resource/rpm_package.rb +34 -0
  169. data/lib/chef/resource/ruby.rb +33 -0
  170. data/lib/chef/resource/ruby_block.rb +40 -0
  171. data/lib/chef/resource/scm.rb +147 -0
  172. data/lib/chef/resource/script.rb +60 -0
  173. data/lib/chef/resource/service.rb +160 -0
  174. data/lib/chef/resource/solaris_package.rb +36 -0
  175. data/lib/chef/resource/subversion.rb +36 -0
  176. data/lib/chef/resource/template.rb +69 -0
  177. data/lib/chef/resource/timestamped_deploy.rb +31 -0
  178. data/lib/chef/resource/user.rb +130 -0
  179. data/lib/chef/resource/yum_package.rb +63 -0
  180. data/lib/chef/resource_collection.rb +217 -0
  181. data/lib/chef/resource_collection/stepable_iterator.rb +124 -0
  182. data/lib/chef/resource_definition.rb +67 -0
  183. data/lib/chef/resource_definition_list.rb +38 -0
  184. data/lib/chef/resources.rb +62 -0
  185. data/lib/chef/role.rb +225 -0
  186. data/lib/chef/run_context.rb +126 -0
  187. data/lib/chef/run_list.rb +161 -0
  188. data/lib/chef/run_list/run_list_expansion.rb +159 -0
  189. data/lib/chef/run_list/run_list_item.rb +92 -0
  190. data/lib/chef/run_list/versioned_recipe_list.rb +68 -0
  191. data/lib/chef/run_status.rb +121 -0
  192. data/lib/chef/runner.rb +105 -0
  193. data/lib/chef/shell_out.rb +250 -0
  194. data/lib/chef/shell_out/unix.rb +223 -0
  195. data/lib/chef/shell_out/windows.rb +98 -0
  196. data/lib/chef/tasks/chef_repo.rake +330 -0
  197. data/lib/chef/util/file_edit.rb +122 -0
  198. data/lib/chef/util/windows.rb +56 -0
  199. data/lib/chef/util/windows/net_group.rb +101 -0
  200. data/lib/chef/util/windows/net_use.rb +121 -0
  201. data/lib/chef/util/windows/net_user.rb +198 -0
  202. data/lib/chef/util/windows/volume.rb +59 -0
  203. data/lib/chef/version.rb +23 -0
  204. data/lib/chef/version_class.rb +70 -0
  205. data/lib/chef/version_constraint.rb +116 -0
  206. metadata +493 -0
@@ -0,0 +1,222 @@
1
+ #
2
+ # Author:: Adam Jacob (<adam@opscode.com>)
3
+ # Copyright:: Copyright (c) 2008 Opscode, Inc.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ require 'chef/config'
20
+ require 'chef/log'
21
+ require 'chef/resource/file'
22
+ require 'chef/mixin/checksum'
23
+ require 'chef/provider'
24
+ require 'etc'
25
+ require 'fileutils'
26
+
27
+ class Chef
28
+ class Provider
29
+ class File < Chef::Provider
30
+ include Chef::Mixin::Checksum
31
+
32
+ def negative_complement(big)
33
+ if big > 1073741823 # Fixnum max
34
+ big -= (2**32) # diminished radix wrap to negative
35
+ end
36
+ big
37
+ end
38
+
39
+ def octal_mode(mode)
40
+ ((mode.respond_to?(:oct) ? mode.oct : mode.to_i) & 007777)
41
+ end
42
+
43
+ private :negative_complement, :octal_mode
44
+
45
+ def load_current_resource
46
+ @current_resource = Chef::Resource::File.new(@new_resource.name)
47
+ @new_resource.path.gsub!(/\\/, "/") # for Windows
48
+ @current_resource.path(@new_resource.path)
49
+ if ::File.exist?(@current_resource.path) && ::File.readable?(@current_resource.path)
50
+ cstats = ::File.stat(@current_resource.path)
51
+ @current_resource.owner(cstats.uid)
52
+ @current_resource.group(cstats.gid)
53
+ @current_resource.mode(octal_mode(cstats.mode))
54
+ end
55
+ @current_resource
56
+ end
57
+
58
+ # Compare the content of a file. Returns true if they are the same, false if they are not.
59
+ def compare_content
60
+ checksum(@current_resource.path) == new_resource_content_checksum
61
+ end
62
+
63
+ # Set the content of the file, assuming it is not set correctly already.
64
+ def set_content
65
+ unless compare_content
66
+ backup @new_resource.path if ::File.exists?(@new_resource.path)
67
+ ::File.open(@new_resource.path, "w") {|f| f.write @new_resource.content }
68
+ Chef::Log.info("#{@new_resource} contents updated")
69
+ @new_resource.updated_by_last_action(true)
70
+ end
71
+ end
72
+
73
+ # Compare the ownership of a file. Returns true if they are the same, false if they are not.
74
+ def compare_owner
75
+ return false if @new_resource.owner.nil?
76
+
77
+ @set_user_id = case @new_resource.owner
78
+ when /^\d+$/, Integer
79
+ @new_resource.owner.to_i
80
+ else
81
+ # This raises an ArgumentError if you can't find the user
82
+ Etc.getpwnam(@new_resource.owner).uid
83
+ end
84
+
85
+ @set_user_id == @current_resource.owner
86
+ end
87
+
88
+ # Set the ownership on the file, assuming it is not set correctly already.
89
+ def set_owner
90
+ unless compare_owner
91
+ @set_user_id = negative_complement(@set_user_id)
92
+ ::File.chown(@set_user_id, nil, @new_resource.path)
93
+ Chef::Log.info("#{@new_resource} owner changed to #{@set_user_id}")
94
+ @new_resource.updated_by_last_action(true)
95
+ end
96
+ end
97
+
98
+ # Compares the group of a file. Returns true if they are the same, false if they are not.
99
+ def compare_group
100
+ return false if @new_resource.group.nil?
101
+
102
+ @set_group_id = case @new_resource.group
103
+ when /^\d+$/, Integer
104
+ @new_resource.group.to_i
105
+ else
106
+ Etc.getgrnam(@new_resource.group).gid
107
+ end
108
+
109
+ @set_group_id == @current_resource.group
110
+ end
111
+
112
+ def set_group
113
+ unless compare_group
114
+ @set_group_id = negative_complement(@set_group_id)
115
+ ::File.chown(nil, @set_group_id, @new_resource.path)
116
+ Chef::Log.info("#{@new_resource} group changed to #{@set_group_id}")
117
+ @new_resource.updated_by_last_action(true)
118
+ end
119
+ end
120
+
121
+ def compare_mode
122
+ case @new_resource.mode
123
+ when /^\d+$/, Integer
124
+ octal_mode(@new_resource.mode) == octal_mode(@current_resource.mode)
125
+ else
126
+ false
127
+ end
128
+ end
129
+
130
+ def set_mode
131
+ unless compare_mode && @new_resource.mode != nil
132
+ # CHEF-174, bad mojo around treating integers as octal. If a string is passed, we try to do the "right" thing
133
+ ::File.chmod(octal_mode(@new_resource.mode), @new_resource.path)
134
+ Chef::Log.info("#{@new_resource} mode changed to #{sprintf("%o" % octal_mode(@new_resource.mode))}")
135
+ @new_resource.updated_by_last_action(true)
136
+ end
137
+ end
138
+
139
+ def action_create
140
+ assert_enclosing_directory_exists!
141
+ unless ::File.exists?(@new_resource.path)
142
+ ::File.open(@new_resource.path, "w+") {|f| f.write @new_resource.content }
143
+ @new_resource.updated_by_last_action(true)
144
+ Chef::Log.info("#{@new_resource} created file #{@new_resource.path}")
145
+ else
146
+ set_content unless @new_resource.content.nil?
147
+ end
148
+ set_owner unless @new_resource.owner.nil?
149
+ set_group unless @new_resource.group.nil?
150
+ set_mode unless @new_resource.mode.nil?
151
+ end
152
+
153
+ def action_create_if_missing
154
+ action_create
155
+ end
156
+
157
+ def action_delete
158
+ if ::File.exists?(@new_resource.path)
159
+ if ::File.writable?(@new_resource.path)
160
+ backup unless ::File.symlink?(@new_resource.path)
161
+ ::File.delete(@new_resource.path)
162
+ Chef::Log.info("#{@new_resource} deleted file at #{@new_resource.path}")
163
+ @new_resource.updated_by_last_action(true)
164
+ else
165
+ raise "Cannot delete #{@new_resource} at #{@new_resource_path}!"
166
+ end
167
+ end
168
+ end
169
+
170
+ def action_touch
171
+ action_create
172
+ time = Time.now
173
+ ::File.utime(time, time, @new_resource.path)
174
+ Chef::Log.info("#{@new_resource} updated atime and mtime to #{time}")
175
+ @new_resource.updated_by_last_action(true)
176
+ end
177
+
178
+ def backup(file=nil)
179
+ file ||= @new_resource.path
180
+ if @new_resource.backup != false && @new_resource.backup > 0 && ::File.exist?(file)
181
+ time = Time.now
182
+ savetime = time.strftime("%Y%m%d%H%M%S")
183
+ backup_filename = "#{@new_resource.path}.chef-#{savetime}"
184
+ backup_filename = backup_filename.sub(/^([A-Za-z]:)/, "") #strip drive letter on Windows
185
+ # if :file_backup_path is nil, we fallback to the old behavior of
186
+ # keeping the backup in the same directory. We also need to to_s it
187
+ # so we don't get a type error around implicit to_str conversions.
188
+ prefix = Chef::Config[:file_backup_path].to_s
189
+ backup_path = ::File.join(prefix, backup_filename)
190
+ FileUtils.mkdir_p(::File.dirname(backup_path)) if Chef::Config[:file_backup_path]
191
+ FileUtils.cp(file, backup_path, :preserve => true)
192
+ Chef::Log.info("#{@new_resource} backed up to #{backup_path}")
193
+
194
+ # Clean up after the number of backups
195
+ slice_number = @new_resource.backup
196
+ backup_files = Dir[::File.join(prefix, ".#{@new_resource.path}.chef-*")].sort { |a,b| b <=> a }
197
+ if backup_files.length >= @new_resource.backup
198
+ remainder = backup_files.slice(slice_number..-1)
199
+ remainder.each do |backup_to_delete|
200
+ FileUtils.rm(backup_to_delete)
201
+ Chef::Log.info("#{@new_resource} removed backup at #{backup_to_delete}")
202
+ end
203
+ end
204
+ end
205
+ end
206
+
207
+ private
208
+
209
+ def assert_enclosing_directory_exists!
210
+ enclosing_dir = ::File.dirname(@new_resource.path)
211
+ unless ::File.directory?(enclosing_dir)
212
+ msg = "Cannot create a file at #{@new_resource.path} because the enclosing directory (#{enclosing_dir}) does not exist"
213
+ raise Chef::Exceptions::EnclosingDirectoryDoesNotExist, msg
214
+ end
215
+ end
216
+
217
+ def new_resource_content_checksum
218
+ @new_resource.content && Digest::SHA2.hexdigest(@new_resource.content)
219
+ end
220
+ end
221
+ end
222
+ end
@@ -0,0 +1,243 @@
1
+ #
2
+ # Author:: Daniel DeLeo (<dan@kallistec.com>)
3
+ # Copyright:: Copyright (c) 2008 Opscode, Inc.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+
20
+ require 'chef/log'
21
+ require 'chef/provider'
22
+ require 'chef/mixin/shell_out'
23
+ require 'fileutils'
24
+
25
+ class Chef
26
+ class Provider
27
+ class Git < Chef::Provider
28
+
29
+ include Chef::Mixin::ShellOut
30
+
31
+ def load_current_resource
32
+ @current_resource = Chef::Resource::Git.new(@new_resource.name)
33
+ if current_revision = find_current_revision
34
+ @current_resource.revision current_revision
35
+ end
36
+ end
37
+
38
+ def action_checkout
39
+ assert_target_directory_valid!
40
+
41
+ if target_dir_non_existent_or_empty?
42
+ clone
43
+ checkout
44
+ enable_submodules
45
+ add_remotes
46
+ @new_resource.updated_by_last_action(true)
47
+ else
48
+ Chef::Log.debug "#{@new_resource} checkout destination #{@new_resource.destination} already exists or is a non-empty directory"
49
+ end
50
+ end
51
+
52
+ def action_export
53
+ action_checkout
54
+ FileUtils.rm_rf(::File.join(@new_resource.destination,".git"))
55
+ @new_resource.updated_by_last_action(true)
56
+ end
57
+
58
+ def action_sync
59
+ assert_target_directory_valid!
60
+
61
+ if existing_git_clone?
62
+ current_rev = find_current_revision
63
+ Chef::Log.debug "#{@new_resource} current revision: #{current_rev} target revision: #{target_revision}"
64
+ unless current_revision_matches_target_revision?
65
+ fetch_updates
66
+ enable_submodules
67
+ Chef::Log.info "#{@new_resource} updated to revision #{target_revision}"
68
+ @new_resource.updated_by_last_action(true)
69
+ end
70
+ add_remotes
71
+ else
72
+ action_checkout
73
+ @new_resource.updated_by_last_action(true)
74
+ end
75
+ end
76
+
77
+ def assert_target_directory_valid!
78
+ target_parent_directory = ::File.dirname(@new_resource.destination)
79
+ unless ::File.directory?(target_parent_directory)
80
+ msg = "Cannot clone #{@new_resource} to #{@new_resource.destination}, the enclosing directory #{target_parent_directory} does not exist"
81
+ raise Chef::Exceptions::MissingParentDirectory, msg
82
+ end
83
+ end
84
+
85
+ def existing_git_clone?
86
+ ::File.exist?(::File.join(@new_resource.destination, ".git"))
87
+ end
88
+
89
+ def target_dir_non_existent_or_empty?
90
+ !::File.exist?(@new_resource.destination) || Dir.entries(@new_resource.destination).sort == ['.','..']
91
+ end
92
+
93
+ def find_current_revision
94
+ Chef::Log.debug("#{@new_resource} finding current git revision")
95
+ if ::File.exist?(::File.join(cwd, ".git"))
96
+ # 128 is returned when we're not in a git repo. this is fine
97
+ result = shell_out!('git rev-parse HEAD', :cwd => cwd, :returns => [0,128]).stdout.strip
98
+ end
99
+ sha_hash?(result) ? result : nil
100
+ end
101
+
102
+ def add_remotes
103
+ if (@new_resource.additional_remotes.length > 0)
104
+ @new_resource.additional_remotes.each_pair do |remote_name, remote_url|
105
+ Chef::Log.info "#{@new_resource} adding git remote #{remote_name} = #{remote_url}"
106
+ command = "git remote add #{remote_name} #{remote_url}"
107
+ if shell_out(command, run_options(:cwd => @new_resource.destination, :command_log_level => :info)).exitstatus != 0
108
+ @new_resource.updated_by_last_action(true)
109
+ end
110
+ end
111
+ end
112
+ end
113
+
114
+ def clone
115
+ remote = @new_resource.remote
116
+
117
+ args = []
118
+ args << "-o #{remote}" unless remote == 'origin'
119
+ args << "--depth #{@new_resource.depth}" if @new_resource.depth
120
+
121
+ Chef::Log.info "#{@new_resource} cloning repo #{@new_resource.repository} to #{@new_resource.destination}"
122
+
123
+ clone_cmd = "git clone #{args.join(' ')} #{@new_resource.repository} #{@new_resource.destination}"
124
+ shell_out!(clone_cmd, run_options(:command_log_level => :info))
125
+ end
126
+
127
+ def checkout
128
+ sha_ref = target_revision
129
+ # checkout into a local branch rather than a detached HEAD
130
+ shell_out!("git checkout -b deploy #{sha_ref}", run_options(:cwd => @new_resource.destination))
131
+ Chef::Log.info "#{@new_resource} checked out branch: #{@new_resource.revision} reference: #{sha_ref}"
132
+ end
133
+
134
+ def enable_submodules
135
+ if @new_resource.enable_submodules
136
+ Chef::Log.info "#{@new_resource} enabling git submodules"
137
+ command = "git submodule init && git submodule update"
138
+ shell_out!(command, run_options(:cwd => @new_resource.destination, :command_log_level => :info))
139
+ end
140
+ end
141
+
142
+ def fetch_updates
143
+ setup_remote_tracking_branches if @new_resource.remote != 'origin'
144
+
145
+ # since we're in a local branch already, just reset to specified revision rather than merge
146
+ fetch_command = "git fetch #{@new_resource.remote} && git fetch #{@new_resource.remote} --tags && git reset --hard #{target_revision}"
147
+ Chef::Log.debug "Fetching updates from #{new_resource.remote} and resetting to revison #{target_revision}"
148
+ shell_out!(fetch_command, run_options(:cwd => @new_resource.destination))
149
+ end
150
+
151
+ # Use git-config to setup a remote tracking branches. Could use
152
+ # git-remote but it complains when a remote of the same name already
153
+ # exists, git-config will just silenty overwrite the setting every
154
+ # time. This could cause wierd-ness in the remote cache if the url
155
+ # changes between calls, but as long as the repositories are all
156
+ # based from each other it should still work fine.
157
+ def setup_remote_tracking_branches
158
+ command = []
159
+
160
+ Chef::Log.debug "#{@new_resource} configuring remote tracking branches for repository #{@new_resource.repository} "+
161
+ "at remote #{@new_resource.remote}"
162
+ command << "git config remote.#{@new_resource.remote}.url #{@new_resource.repository}"
163
+ command << "git config remote.#{@new_resource.remote}.fetch +refs/heads/*:refs/remotes/#{@new_resource.remote}/*"
164
+ shell_out!(command.join(" && "), run_options(:cwd => @new_resource.destination))
165
+ end
166
+
167
+ def current_revision_matches_target_revision?
168
+ (!@current_resource.revision.nil?) && (target_revision.strip.to_i(16) == @current_resource.revision.strip.to_i(16))
169
+ end
170
+
171
+ def target_revision
172
+ @target_revision ||= begin
173
+ assert_revision_not_remote
174
+
175
+ if sha_hash?(@new_resource.revision)
176
+ @target_revision = @new_resource.revision
177
+ else
178
+ resolved_reference = remote_resolve_reference
179
+ @target_revision = extract_revision(resolved_reference)
180
+ end
181
+ end
182
+ end
183
+
184
+ alias :revision_slug :target_revision
185
+
186
+ def remote_resolve_reference
187
+ Chef::Log.debug("#{@new_resource} resolving remote reference")
188
+ command = git('ls-remote', @new_resource.repository, @new_resource.revision)
189
+ shell_out!(command, run_options).stdout
190
+ end
191
+
192
+ private
193
+
194
+ def run_options(run_opts={})
195
+ run_opts[:user] = @new_resource.user if @new_resource.user
196
+ run_opts[:group] = @new_resource.group if @new_resource.group
197
+ run_opts[:environment] = {"GIT_SSH" => @new_resource.ssh_wrapper} if @new_resource.ssh_wrapper
198
+ run_opts[:command_log_prepend] = @new_resource.to_s
199
+ run_opts[:command_log_level] ||= :debug
200
+ if run_opts[:command_log_level] == :info
201
+ if STDOUT.tty? && Chef::Log.info?
202
+ run_opts[:live_stream] = STDOUT
203
+ end
204
+ end
205
+ run_opts
206
+ end
207
+
208
+ def cwd
209
+ @new_resource.destination
210
+ end
211
+
212
+ def git(*args)
213
+ ["git", *args].compact.join(" ")
214
+ end
215
+
216
+ def sha_hash?(string)
217
+ string =~ /^[0-9a-f]{40}$/
218
+ end
219
+
220
+ def assert_revision_not_remote
221
+ if @new_resource.revision =~ /^origin\//
222
+ reference = @new_resource.revision
223
+ error_text = "Deploying remote branches is not supported. " +
224
+ "Specify the remote branch as a local branch for " +
225
+ "the git repository you're deploying from " +
226
+ "(ie: '#{reference.gsub('origin/', '')}' rather than '#{reference}')."
227
+ raise RuntimeError, error_text
228
+ end
229
+ end
230
+
231
+ def extract_revision(resolved_reference)
232
+ unless resolved_reference =~ /^([0-9a-f]{40})\s+(\S+)/
233
+ msg = "Unable to parse SHA reference for '#{@new_resource.revision}' in repository '#{@new_resource.repository}'. "
234
+ msg << "Verify your (case-sensitive) repository URL and revision.\n"
235
+ msg << "`git ls-remote` output: #{resolved_reference}"
236
+ raise Chef::Exceptions::UnresolvableGitReference, msg
237
+ end
238
+ $1
239
+ end
240
+
241
+ end
242
+ end
243
+ end