knife-github 0.0.3 → 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -25,28 +25,14 @@ class Chef
25
25
 
26
26
  deps do
27
27
  require 'chef/knife/github_base'
28
-
29
28
  include Chef::Knife::GithubBase
29
+ require 'chef/knife/github_baselist'
30
+ include Chef::Knife::GithubBaseList
30
31
  end
31
32
 
32
33
  banner "knife github compare [COOKBOOK] (options)"
33
34
  category "github"
34
35
 
35
- option :fields,
36
- :long => "--fields 'NAME, NAME'",
37
- :description => "The fields to output, comma-separated"
38
-
39
- option :noheader,
40
- :long => "--noheader",
41
- :description => "Removes header from output",
42
- :boolean => true
43
-
44
- option :all,
45
- :short => "-a",
46
- :long => "--all",
47
- :description => "Get all cookbooks from github.",
48
- :boolean => true
49
-
50
36
  option :mismatch,
51
37
  :short => "-m",
52
38
  :long => "--mismatch",
@@ -54,7 +40,6 @@ class Chef
54
40
  :boolean => true
55
41
 
56
42
  def run
57
- # extend Chef::Mixin::ShellOut
58
43
  # validate base options from base module.
59
44
  validate_base_options
60
45
 
@@ -64,31 +49,31 @@ class Chef
64
49
  # Gather all repo information from github.
65
50
  get_all_repos = get_all_repos(@github_organizations.reverse)
66
51
 
52
+ # Get all chef cookbooks and versions.
53
+ cookbooks = rest.get_rest("/cookbooks?num_version=1")
67
54
 
68
- # Get all chef cookbooks and versions (hopefully chef does the error handeling).
69
- cb_and_ver = rest.get_rest("/cookbooks?num_version=1")
70
-
55
+ #Get the github link
56
+ git_link = get_repo_clone_link
71
57
 
72
58
  # Filter all repo information based on the tags that we can find
73
- all_repos = {}
74
- if config[:all]
75
- get_all_repos.each { |k,v|
76
- cookbook = k
77
- cb_and_ver[k].nil? || cb_and_ver[k]['versions'].nil? ? version = "" : version = cb_and_ver[k]['versions'][0]['version']
78
- ssh_url = v['ssh_url']
79
- gh_tag = v['latest_tag']
80
- all_repos[cookbook] = { 'name' => cookbook, 'latest_cb_tag' => version, 'ssh_url' => ssh_url, 'latest_gh_tag' => gh_tag }
81
- }
59
+ if config[:fields] || config[:fieldlist]
60
+ all_repos = get_all_repos
61
+ config[:fields] = "name" if config[:fields].nil? || config[:fields].empty?
82
62
  else
83
- cb_and_ver.each { |k,v|
84
- cookbook = k
85
- version = v['versions'][0]['version']
86
- get_all_repos[k].nil? || get_all_repos[k]['ssh_url'].nil? ? ssh_url = ui.color("ERROR: Cannot find cookbook!", :red) : ssh_url = get_all_repos[k]['ssh_url']
87
- get_all_repos[k].nil? || get_all_repos[k]['latest_tag'].nil? ? gh_tag = ui.color("ERROR: No tags!", :red) : gh_tag = get_all_repos[k]['latest_tag']
88
- all_repos[cookbook] = { 'name' => cookbook, 'latest_cb_tag' => version, 'ssh_url' => ssh_url, 'latest_gh_tag' => gh_tag }
89
- }
63
+ all_repos = {}
64
+ if config[:all]
65
+ get_all_repos.each { |k,v|
66
+ cookbooks[k].nil? || cookbooks[k]['versions'].nil? ? version = "" : version = cookbooks[k]['versions'][0]['version']
67
+ all_repos[k] = { 'name' => k, 'latest_cb_tag' => version, 'git_url' => v[git_link], 'latest_gh_tag' => v['latest_tag'] }
68
+ }
69
+ else
70
+ cookbooks.each { |k,v|
71
+ get_all_repos[k].nil? || get_all_repos[k][git_link].nil? ? gh_url = ui.color("ERROR: Cannot find cookbook!", :red) : gh_url = get_all_repos[k][git_link]
72
+ get_all_repos[k].nil? || get_all_repos[k]['latest_tag'].nil? ? gh_tag = ui.color("ERROR: No tags!", :red) : gh_tag = get_all_repos[k]['latest_tag']
73
+ all_repos[k] = { 'name' => k, 'latest_cb_tag' => v['versions'][0]['version'], 'git_url' => gh_url, 'latest_gh_tag' => gh_tag }
74
+ }
75
+ end
90
76
  end
91
-
92
77
 
93
78
  # Filter only on the cookbook name if its given on the command line
94
79
  @cookbook_name = name_args.first unless name_args.empty?
@@ -98,145 +83,15 @@ class Chef
98
83
  repos = all_repos
99
84
  end
100
85
 
86
+ columns = [ 'name,Name', 'latest_cb_tag,Tag', 'git_url,Link', 'latest_gh_tag,Tag' ]
87
+ match = [ 'latest_cb_tag', 'latest_gh_tag' ]
101
88
 
102
- # Displaying information based on the fields and repos
103
- if config[:fields]
104
- object_list = []
105
- config[:fields].split(',').each { |n| object_list << ui.color(("#{n}").strip, :bold) }
89
+ if repos.nil? || repos.empty?
90
+ Chef::Log.error("No repositories found.")
106
91
  else
107
- object_list = [
108
- ui.color('Cookbook', :bold),
109
- ui.color('Tag', :bold),
110
- ui.color('Github', :bold),
111
- ui.color('Tag', :bold)
112
- ]
113
- end
114
-
115
- columns = object_list.count
116
- object_list = [] if config[:noheader]
117
-
118
- repos.each do |k,r|
119
- if config[:fields]
120
- config[:fields].downcase.split(',').each { |n| object_list << ((r[("#{n}").strip]).to_s || 'n/a') }
121
- else
122
- next if config[:mismatch] && (r['latest_gh_tag'] == r['latest_cb_tag'])
123
- r['latest_gh_tag'] == r['latest_cb_tag'] ? color = :white : color = :yellow
124
- color = :white if config[:all]
125
-
126
- object_list << ui.color((r['name'] || 'n/a'), color)
127
- object_list << ui.color((r['latest_cb_tag'] || 'n/a'), color)
128
- object_list << ui.color((r['ssh_url'] || 'n/a'), color)
129
- object_list << ui.color((r['latest_gh_tag'] || 'n/a'), color)
130
- end
131
- end
132
-
133
- puts ui.list(object_list, :uneven_columns_across, columns)
134
-
135
- end
136
-
137
- def get_all_repos(orgs)
138
- # Parse every org and merge all into one hash
139
- repos = {}
140
- orgs.each do |org|
141
- get_repos(org).each { |repo| name = repo['name'] ; repos["#{name}"] = repo }
92
+ display_info(repos, columns, match)
142
93
  end
143
- repos
144
- end
145
-
146
-
147
94
 
148
- def get_repos(org)
149
- dns_name = get_dns_name(@github_url)
150
- file_cache = "#{ENV['HOME']}/.chef/.#{dns_name.downcase}_#{org.downcase}.cache"
151
- if File.exists?(file_cache)
152
- Chef::Log.debug("#{org} cache is created: " + (Time.now - File.ctime(file_cache)).to_i.to_s + " seconds ago.")
153
- if Time.now - File.ctime(file_cache) > @github_cache
154
- # update cache file
155
- create_cache_file(file_cache, org)
156
- end
157
- else
158
- create_cache_file(file_cache, org)
159
- end
160
- # use cache files
161
- JSON.parse(File.read(file_cache))
162
- end
163
-
164
- def create_cache_file(file_cache, org)
165
- Chef::Log.debug("Updating the cache file: #{file_cache}")
166
- result = get_repos_github(org)
167
- File.open(file_cache, 'w') { |file| file.write(JSON.pretty_generate(result)) }
168
- end
169
-
170
-
171
-
172
- def get_repos_github(org)
173
- # Get all repo's for the org from github
174
- arr = []
175
- page = 1
176
- url = @github_url + "/api/" + @github_api_version + "/orgs/" + org + "/repos"
177
- while true
178
- params = { 'page' => page }
179
- result = send_request(url, params)
180
- break if result.nil? || result.count < 1
181
- result.each { |key|
182
- if key['tags_url']
183
- tags = get_tags(key)
184
- key['tags'] = tags unless tags.nil? || tags.empty?
185
- key['latest_tag'] = tags.first['name'] unless tags.nil? || tags.empty?
186
- arr << key
187
- else
188
- arr << key
189
- end
190
- }
191
- page = page + 1
192
- end
193
- arr
194
- end
195
-
196
-
197
- def get_tags(repo)
198
- tags = send_request(repo['tags_url'])
199
- tags
200
- end
201
-
202
-
203
- def get_dns_name(url)
204
- url = url.downcase.gsub("http://","") if url.downcase.start_with?("http://")
205
- url = url.downcase.gsub("https://","") if url.downcase.start_with?("https://")
206
- url
207
- end
208
-
209
-
210
- def send_request(url, params = {})
211
- params['response'] = 'json'
212
-
213
- params_arr = []
214
- params.sort.each { |elem|
215
- params_arr << elem[0].to_s + '=' + CGI.escape(elem[1].to_s).gsub('+', '%20').gsub(' ','%20')
216
- }
217
- data = params_arr.join('&')
218
-
219
- if url.nil? || url.empty?
220
- puts "Error: Please specify a valid Github URL."
221
- exit 1
222
- end
223
-
224
- github_url = "#{url}?#{data}"
225
- # Chef::Log.debug("URL: #{github_url}")
226
-
227
- uri = URI.parse(github_url)
228
- req_body = Net::HTTP::Get.new(uri.request_uri)
229
- request = Chef::REST::RESTRequest.new("GET", uri, req_body, headers={})
230
-
231
- response = request.call
232
-
233
- if !response.is_a?(Net::HTTPOK) then
234
- puts "Error #{response.code}: #{response.message}"
235
- puts JSON.pretty_generate(JSON.parse(response.body))
236
- puts "URL: #{url}"
237
- exit 1
238
- end
239
- json = JSON.parse(response.body)
240
95
  end
241
96
 
242
97
  end
@@ -0,0 +1,335 @@
1
+ #
2
+ # Author:: Sander Botman (<sbotman@schubergphilis.com>)
3
+ # Copyright:: Copyright (c) 2013 Sander Botman.
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
+ # Abstract
20
+ # ---------------------------------------------------------------------------- #
21
+ # This code is specific to our company workflow
22
+ # When a cookbook is released it is tagged to a specific version
23
+ # This version should match the cookbook version (from the metadata)
24
+ # This version is then pinned against specific environments
25
+ #
26
+ # This class expects you to have pushed all your changes to github
27
+ # It will then do the rest
28
+ #
29
+ # All modes presume you have used github download to download a cookbook or
30
+ # are creating a new cookbook
31
+ #
32
+ # There are two modes of operation
33
+ # Development (default)
34
+ #
35
+ # This will take a cookbook name
36
+ # Do some basic version checks (if the current cookbook is frozen) and
37
+ # upload it
38
+ #
39
+ # If the cookbook is frozen it will force you to choose a new version
40
+ # and update the metadata accordingly
41
+ #
42
+ # Final (-f)
43
+ #
44
+ # You will be forced to select a new version.
45
+ # You can choose via the options whether to increment the Major/minor or patch
46
+ # revision numbers
47
+ # The version will be tagged
48
+ # Uploaded to the Chef server and frozen
49
+ #
50
+ # Version numbers
51
+ #
52
+ # You can choose a specific version number by specifying it on the command
53
+ # line.
54
+ #
55
+ # If you do not specify a version, the version will be the version in your
56
+ # cookbook's metadata
57
+ #
58
+ # A warning is issued if the version is lower than the version in github
59
+ # ---------------------------------------------------------------------------- #
60
+ require 'chef/knife'
61
+
62
+ class Chef
63
+ class Knife
64
+
65
+ class GithubDeploy < Knife
66
+ deps do
67
+ require 'chef/knife/github_base'
68
+ include Chef::Knife::GithubBase
69
+ require 'chef/cookbook_loader'
70
+ require 'chef/cookbook_uploader'
71
+ end
72
+
73
+ banner "knife github deploy COOKBOOK [VERSION] (options)"
74
+ category "github"
75
+
76
+ option :final,
77
+ :short => "-f",
78
+ :long => "--final",
79
+ :description => "Bump version, make git tag and freeze",
80
+ :boolean => true,
81
+ :default => false
82
+
83
+ option :major,
84
+ :short => "-M",
85
+ :long => "--major",
86
+ :description => "In final mode, increase the major version ie. X.x.x",
87
+ :boolean => true,
88
+ :default => false
89
+
90
+ option :minor,
91
+ :short => "-m",
92
+ :long => "--minor",
93
+ :description => "In final mode, increase the minor version ie. x.X.x",
94
+ :boolean => true,
95
+ :default => false
96
+
97
+ option :patch,
98
+ :short => "-p",
99
+ :long => "--patch",
100
+ :description => "In final mode, increase the minor version ie. x.x.X (Default)",
101
+ :boolean => true,
102
+ :default => true
103
+
104
+ def run
105
+
106
+ # validate base options from base module.
107
+ validate_base_options
108
+
109
+ # Display information if debug mode is on.
110
+ display_debug_info
111
+
112
+ # Gather all repo information from github.
113
+ get_all_repos = get_all_repos(@github_organizations.reverse)
114
+
115
+ # Get all chef cookbooks and versions (hopefully chef does the error handeling).
116
+ cookbooks = rest.get_rest("/cookbooks?num_version=1")
117
+
118
+ @versions = []
119
+ cookbook_version = nil
120
+ @cookbook_name = name_args.first unless name_args.empty?
121
+ cookbook_version = name_args[1] unless name_args[1].nil?
122
+
123
+ if @cookbook_name
124
+ repo = get_all_repos.select { |k,v| v["name"] == @cookbook_name }
125
+ else
126
+ Chef::Log.error("Cookbook not in git. You must add it to git to use deploy")
127
+ exit 1
128
+ end
129
+
130
+ if repo.empty?
131
+ Chef::Log.error("Cookbook not in git. You must add it to git to use deploy")
132
+ exit 1
133
+ end
134
+
135
+ # is the cookbook in the cookbook_path?
136
+ if cookbook_path_valid?(@cookbook_name, false).nil?
137
+ Chef::Log.error("Cookbook is not in cookbook path")
138
+ ui.info("HINT: knife github download #{@cookbook_name}")
139
+ exit 1
140
+ end
141
+
142
+ # ----------------------------- #
143
+ # The version can come
144
+ # 1. From the command line
145
+ # 2. From the cookbook
146
+ # ----------------------------- #
147
+ if cookbook_version.nil?
148
+ cookbook_version = get_cookbook_version()
149
+ end
150
+ # Next check to see if the version in git is way ahead
151
+ if ! get_all_repos[@cookbook_name]['latest_tag'].nil?
152
+ cb1 = Mixlib::Versioning.parse(cookbook_version)
153
+ cb2 = Mixlib::Versioning.parse(get_all_repos[@cookbook_name]['latest_tag'])
154
+ if(cb2 > cb1)
155
+ ui.confirm "Version in github #{cb2} is greater than the version you want to deploy #{cb1} - Continue"
156
+ end
157
+ end
158
+
159
+ inChef = true
160
+ isFrozen = false
161
+ if (config[:major] || config[:minor])
162
+ config[:patch] = false
163
+ end
164
+
165
+ begin
166
+ isFrozen = rest.get_rest("cookbooks/#{@cookbook_name}/#{cookbook_version}").frozen_version?
167
+ rescue
168
+ ui.warn "#{@cookbook_name} is not yet in chef"
169
+ inChef = false
170
+ end
171
+
172
+
173
+ if config[:final]
174
+ ui.info "Using Final mode"
175
+
176
+ else
177
+ ui.info "Using Development mode"
178
+ end
179
+ ui.info "Cookbook is frozen" if isFrozen
180
+
181
+ # Might be first upload so need to catch that cookbook does not exist!
182
+ get_cookbook_chef_versions() unless ! inChef
183
+
184
+ if config[:final]
185
+ cookbook_version = up_version(cookbook_version)
186
+
187
+ if repo[@cookbook_name]['tags'].select { |k| k['name'] == cookbook_version }.empty?
188
+ ui.info("Cookbook #{cookbook_version} has no tag in Git")
189
+ ui.confirm("Shall I add a tag for you?")
190
+ set_cookbook_version(cookbook_version)
191
+ add_tag(cookbook_version)
192
+ else
193
+ checkout_tag(cookbook_version)
194
+ set_cookbook_version(cookbook_version)
195
+ end
196
+
197
+ do_commit(cookbook_version, true)
198
+ end
199
+
200
+ # In Dev mode the version of the cookbook does not need to change
201
+ # If however the cookbook is frozen, then the version has to change
202
+ if ! config[:final] && isFrozen
203
+ cookbook_version = up_version(cookbook_version)
204
+ set_cookbook_version(cookbook_version)
205
+ do_commit(cookbook_version, false)
206
+ end
207
+
208
+ # If we have gotten this far we can just upload the cookbook
209
+ cookbook_upload()
210
+
211
+ end
212
+
213
+ def up_version(version)
214
+ while true do
215
+ ui.info("Trying to deploy version #{version}")
216
+ if @versions.include?(version)
217
+ ui.info("Version #{version} is already in chef")
218
+ ui.confirm("Shall I bump the version (No to Cancel)")
219
+ version = choose_version(version)
220
+ else
221
+ break
222
+ end
223
+ end
224
+ version
225
+ end
226
+
227
+ def choose_version(version)
228
+ if version =~ /(\d+)\.(\d+)\.(\d+)/
229
+ major = $1
230
+ minor = $1
231
+ patch = $3
232
+ major = major.to_i + 1 if config[:major]
233
+ minor = minor.to_i + 1 if config[:minor]
234
+ patch = patch.to_i + 1 if config[:patch]
235
+ version = "#{major}.#{minor}.#{patch}"
236
+ Chef::Log.debug("New version is #{version}")
237
+ else
238
+ Chef::Log.error("Version is in a format I cannot auto auto-update")
239
+ exit 1
240
+ end
241
+ version
242
+ end
243
+
244
+ def cookbook_upload()
245
+ # Git meuk should not be uploaded use chefignore file instead
246
+ # FileUtils.remove_entry("#{@github_tmp}/git/#{@cookbook_name}/.git")
247
+ args = ['cookbook', 'upload', @cookbook_name ]
248
+ if config[:final]
249
+ args.push "--freeze"
250
+ end
251
+ upload = Chef::Knife::CookbookUpload.new(args)
252
+ #upload.config[:cookbook_path] = "#{@github_tmp}/git"
253
+ # plugin will throw its own errors
254
+ upload.run
255
+ end
256
+
257
+ def checkout_tag(version)
258
+ ui.info "Checking out tag #{version}"
259
+ cpath = get_cookbook_path(@cookbook_name)
260
+ Dir.chdir(cpath);
261
+ `git checkout -b #{version}`
262
+ if !$?.exitstatus == 0
263
+ ui.error("Failed to checkout branch #{version} of #{@cookbook_name}")
264
+ exit 1
265
+ end
266
+ end
267
+
268
+ def get_cookbook_chef_versions ()
269
+ cookbooks = rest.get_rest("/cookbooks/#{@cookbook_name}?num_version=all")
270
+ cookbooks[@cookbook_name]['versions'].each do |v|
271
+ @versions.push v['version']
272
+ end
273
+ end
274
+
275
+ # ---------------------------------------------------------------------- #
276
+ # Get the version number in the git version of the cookbook
277
+ # ---------------------------------------------------------------------- #
278
+ def get_cookbook_version()
279
+ version = nil
280
+ cpath = get_cookbook_path(@cookbook_name)
281
+ File.foreach("#{cpath}/metadata.rb") do |line|
282
+ if line =~ /version.*"(.*)"/i
283
+ version = $1
284
+ break
285
+ end
286
+ end
287
+ if version.nil?
288
+ Chef::Log.error("Cannot get the version for cookbook #{@cookbook_name}")
289
+ exit 1
290
+ end
291
+ version
292
+ end
293
+
294
+ def get_cookbook_path(cookbook)
295
+ return cookbook_path_valid?(cookbook, false)
296
+ end
297
+
298
+ def do_commit(version, push)
299
+ cpath = get_cookbook_path(@cookbook_name)
300
+ Dir.chdir("#{cpath}")
301
+ puts cpath
302
+ output = `git commit -a -m "Deploy #{version}" 2>&1`
303
+ if $?.exitstatus != 0
304
+ if output !~ /nothing to commit/
305
+ Chef::Log.error("Could not commit #{@cookbook_name}")
306
+ puts output
307
+ exit 1
308
+ end
309
+ end
310
+ if push
311
+ output = `git push --tags 2>&1`
312
+ if $?.exitstatus != 0
313
+ Chef::Log.error("Could not push tag for: #{@cookbook_name}")
314
+ exit 1
315
+ end
316
+ output = `git push 2>&1`
317
+ end
318
+ end
319
+
320
+
321
+ def set_cookbook_version(version)
322
+ return unless get_cookbook_version() != version
323
+ contents = ''
324
+ cpath = get_cookbook_path(@cookbook_name)
325
+ File.foreach("#{cpath}/metadata.rb") do |line|
326
+ line.gsub!(/(version[\t\s]+)(.*)/i,"\\1 \"#{version}\"\n")
327
+ contents = contents << line
328
+ end
329
+ File.open("#{cpath}/metadata.rb", 'w') {|f| f.write(contents) }
330
+ return true
331
+ end
332
+
333
+ end
334
+ end
335
+ end
@@ -0,0 +1,126 @@
1
+ #
2
+ # Author:: Sander Botman (<sbotman@schubergphilis.com>)
3
+ # Copyright:: Copyright (c) 2013 Sander Botman.
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/knife'
20
+
21
+ class Chef
22
+ class Knife
23
+
24
+ class GithubDiff < Knife
25
+
26
+ deps do
27
+ require 'chef/knife/github_base'
28
+
29
+ include Chef::Knife::GithubBase
30
+ end
31
+
32
+ banner "knife github diff COOKBOOK [version] (options)"
33
+ category "github"
34
+
35
+ option :all,
36
+ :short => "-a",
37
+ :long => "--all",
38
+ :description => "Diff all cookbooks from chef against github.",
39
+ :boolean => true
40
+
41
+ def run
42
+
43
+ # validate base options from base module.
44
+ validate_base_options
45
+
46
+ # Display information if debug mode is on.
47
+ display_debug_info
48
+
49
+ # Gather all repo information from github.
50
+ get_all_repos = get_all_repos(@github_organizations.reverse)
51
+
52
+ # Get all chef cookbooks and versions (hopefully chef does the error handeling).
53
+ cookbooks = rest.get_rest("/cookbooks?num_version=1")
54
+
55
+ # Get the cookbook name from the command line
56
+ @cookbook_name = name_args.first unless name_args.empty?
57
+ cookbook_version = name_args[1] unless name_args[1].nil?
58
+ if @cookbook_name
59
+ repo = get_all_repos.select { |k,v| v["name"] == @cookbook_name }
60
+ else
61
+ #repos = all_repos
62
+ Chef::Log.error("Please specify a cookbook name")
63
+ exit 1
64
+ end
65
+
66
+ if repo.empty?
67
+ Chef::Log.error("Cannot find the repository: #{} within github")
68
+ exit 1
69
+ end
70
+
71
+ github_link = (repo[@cookbook_name][(get_repo_clone_link)])
72
+ if github_link.nil? || github_link.empty?
73
+ Chef::Log.error("Cannot find the link for the repository with the name: #{@cookbook_name}")
74
+ exit 1
75
+ end
76
+ puts github_link
77
+ get_clone(github_link, @cookbook_name)
78
+ version = get_cookbook_copy(@cookbook_name, cookbook_version)
79
+ do_diff(@cookbook_name, version)
80
+ FileUtils.remove_entry(@github_tmp)
81
+ end
82
+
83
+ def do_diff(name, version)
84
+ # Check to see if there is a tag matching the version
85
+ Dir.chdir("#{@github_tmp}/git/#{name}")
86
+ if `git tag`.split("\n").include?(version)
87
+ ui.info("Tag version #{version} found, checking that out for diff")
88
+ # Tag found so checkout that tag
89
+ `git checkout -b #{version}`
90
+ if !$?.exitstatus == 0
91
+ ui.error("Failed to checkout branch #{version}")
92
+ exit 1
93
+ end
94
+ else
95
+ ui.info("Version #{version} of #{name} has no tag, using latest for diff")
96
+ end
97
+
98
+ FileUtils.remove_entry("#{@github_tmp}/git/#{name}/.git")
99
+ output = `git diff --color #{@github_tmp}/git/#{name} #{@github_tmp}/cb/#{name}-#{version} 2>&1`
100
+ if output.length == 0
101
+ ui.info("No differences found")
102
+ else
103
+ ui.msg(output)
104
+ end
105
+ end
106
+
107
+ def get_cookbook_copy(name, version)
108
+ Dir.mkdir("#{@github_tmp}/cb")
109
+ args = ['cookbook', 'download', name ]
110
+ args.push version if version
111
+ Dir.chdir("#{@github_tmp}/cb")
112
+ download = Chef::Knife::CookbookDownload.new(args)
113
+ download.config[:download_directory] = "#{@github_tmp}/cb"
114
+ download.run
115
+
116
+ Dir.entries("#{@github_tmp}/cb").each do |d|
117
+ if d =~ /#{name}-(.*)/
118
+ version = $1
119
+ end
120
+ end
121
+ return version
122
+ end
123
+
124
+ end
125
+ end
126
+ end