knife-spork 0.1.8 → 0.1.9

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -46,6 +46,11 @@ gist:
46
46
  in_chef: true
47
47
  chef_path: cookbooks/gist/files/default/gist
48
48
  path: /usr/bin/gist
49
+ foodcritic:
50
+ enabled: true
51
+ fail_tags: [any]
52
+ tags: [foo]
53
+ include_rules: [/home/me/myrules]
49
54
  default_environments: [ production, development ]
50
55
  ````
51
56
  ## Git
@@ -70,6 +75,18 @@ This lets you send to a graphite metric when promote --remote is performed. It s
70
75
 
71
76
  This allows you to generate an optional gist of environment changes which will be added to irccat notifications on promote --remote. It supports the https://rubygems.org/gems/gist, and contains two parameters to use a version in your chef repo, or a version installed somewhere else locally.
72
77
 
78
+ ## Foodcritic
79
+
80
+ This allows you to run a foodcritic lint check against your cookbook before spork uploading. The check only runs against the cookbook you've passed to spork-upload on the command line.
81
+
82
+ PLEASE NOTE: Due to it's many dependancies (gem dependancy Nokogiri requires libxml-devel, libxslt-devel), foodcritic is not specified as a dependency of the knife-spork gem as it won't install without other manual package installs, so if you want to use it you'll need to make sure it's installed yourself for now. This may change in a future version.
83
+
84
+ The optional attributes for this section work as follows:
85
+
86
+ fail_tags: Fail the build if any of the specified tags are matched.
87
+ tags: Only check against rules with the specified tags.
88
+ include_rules: Additional rule file path(s) to load.
89
+
73
90
  ## Default Environments
74
91
 
75
92
  This allows you to specify a default list of environments you want to promote changes to. If this option is configured and you *ommit* the environment parameter when promoting, ie knife spork promote <cookbook>, then it will promote to all environments in this list.
data/knife-spork.gemspec CHANGED
@@ -13,8 +13,8 @@ Gem::Specification.new do |s|
13
13
  ## If your rubyforge_project name is different, then edit it and comment out
14
14
  ## the sub! line in the Rakefile
15
15
  s.name = 'knife-spork'
16
- s.version = '0.1.8'
17
- s.date = '2012-02-21'
16
+ s.version = '0.1.9'
17
+ s.date = '2012-04-03'
18
18
  s.rubyforge_project = 'knife-spork'
19
19
 
20
20
  ## Make sure your summary is short. The description may be as long
@@ -83,6 +83,14 @@ module KnifeSpork
83
83
  show_usage
84
84
  exit 1
85
85
  end
86
+
87
+ if !AppConf.git.nil? && AppConf.git.enabled
88
+ if !@@gitavail
89
+ ui.msg "Git gem not available, skipping git pull.\n\n"
90
+ else
91
+ git_pull_if_repo
92
+ end
93
+ end
86
94
 
87
95
  if bump_type == "manual"
88
96
  manual_version = name_args.last
@@ -104,7 +112,6 @@ module KnifeSpork
104
112
  end
105
113
  end
106
114
 
107
-
108
115
  def patch(cookbook_path, cookbook, type)
109
116
  t = TYPE_INDEX[type]
110
117
  current_version = get_version(cookbook_path, cookbook).split(".").map{|i| i.to_i}
@@ -175,7 +182,45 @@ module KnifeSpork
175
182
  end
176
183
  end
177
184
  end
178
- end
185
+
186
+ def git_pull_if_repo
187
+ strio = StringIO.new
188
+ l = Logger.new strio
189
+ cookbook_path = config[:cookbook_path]
190
+ if cookbook_path.size > 1
191
+ ui.warn "It looks like you have multiple cookbook paths defined so I can't tell if you're running inside a git repo.\n\n"
192
+ else
193
+ begin
194
+ path = cookbook_path[0].gsub("/cookbooks","")
195
+ ui.msg "Opening git repo #{path}\n\n"
196
+ g = Git.open(path, :log => Logger.new(strio))
197
+ ui.msg "Pulling latest changes from git\n\n"
198
+ output = IO.popen ("cd #{path} && git pull 2>&1")
199
+ Process.wait
200
+ exit_code = $?
201
+ if exit_code.exitstatus == 0
202
+ ui.msg "#{output.read()}\n"
203
+ else
204
+ ui.error "#{output.read()}\n"
205
+ exit 1
206
+ end
207
+
208
+ ui.msg "Pulling latest changes from git submodules (if any)\n\n"
209
+ output = IO.popen ("cd #{path} && git submodule foreach git pull 2>&1")
210
+ Process.wait
211
+ exit_code = $?
212
+ if exit_code.exitstatus == 0
213
+ ui.msg "#{output.read()}\n"
214
+ else
215
+ ui.error "#{output.read()}\n"
216
+ exit 1
217
+ end
218
+ rescue ArgumentError => e
219
+ ui.warn "Git: The root of your chef repo doesn't look like it's a git repo. Skipping git pull...\n\n"
220
+ end
221
+ end
222
+ end
223
+ end
179
224
 
180
225
  end
181
226
 
@@ -4,7 +4,7 @@
4
4
  # License:: Apache License, Version 2.0
5
5
  #
6
6
  #
7
- # Uses code from the knife cookbook upload plugin by:
7
+ # Uses some code from the knife cookbook upload plugin by:
8
8
  #
9
9
  # Author:: Adam Jacob (<adam@opscode.com>)
10
10
  # Author:: Christopher Walters (<cw@opscode.com>)
@@ -144,7 +144,7 @@ module KnifeSpork
144
144
  ui.msg "Saving changes into #{e}.json"
145
145
  new_environment_json = pretty_print(@environment)
146
146
  save_environment_changes(e,new_environment_json)
147
-
147
+
148
148
  if config[:remote]
149
149
  ui.msg "Uploading #{e} to server"
150
150
  save_environment_changes_remote("#{e}")
@@ -180,7 +180,7 @@ module KnifeSpork
180
180
  end
181
181
  end
182
182
 
183
- def valid_version(version)
183
+ def valid_version(version)
184
184
  v = version.split(".")
185
185
  if v.size < 3 or v.size > 3
186
186
  return false
@@ -191,7 +191,7 @@ module KnifeSpork
191
191
  end
192
192
  end
193
193
  return true
194
- end
194
+ end
195
195
 
196
196
  def loader
197
197
  @loader ||= Chef::Knife::Core::ObjectLoader.new(Chef::Environment, ui)
@@ -215,7 +215,17 @@ module KnifeSpork
215
215
  if env_diff.size > 1
216
216
  ui.warn "You're about to promote changes to several cookbooks:"
217
217
  ui.warn "\n#{env_diff.collect { |k, v| "#{k}: #{v}\n" }.join}"
218
- ui.confirm("Are you sure you want to continue?")
218
+ begin
219
+ ui.confirm("Are you sure you want to continue")
220
+ rescue SystemExit => e
221
+ if e.status == 3
222
+ ui.confirm("Would you like to reset your local #{environment}.json to match the server?")
223
+ tmp_env = Chef::Environment.load(environment)
224
+ save_environment_changes(environment,pretty_print(tmp_env))
225
+ puts "#{environment}.json reset."
226
+ end
227
+ raise
228
+ end
219
229
  end
220
230
  updated.save
221
231
 
@@ -250,7 +260,37 @@ module KnifeSpork
250
260
  end
251
261
  end
252
262
 
253
-
263
+ if !AppConf.eventinator.nil? && AppConf.eventinator.enabled
264
+ metadata = {}
265
+ metadata[:promoted_cookbooks] = {}
266
+ promoted_cookbooks = []
267
+ env_diff.collect do |k,v|
268
+ v =~ /= ([\d\.]+) changed to = ([\d\.]+)/
269
+ metadata[:promoted_cookbooks][k] = { :previous_version => $1, :new_version => $2 }
270
+ promoted_cookbooks << "#{k} (#{$2})"
271
+ end
272
+ event_data = {}
273
+ event_data[:tag] = "knife"
274
+ event_data[:username] = ENV['USER']
275
+ event_data[:status] = "#{ENV['USER']} promoted #{promoted_cookbooks.join(", ")} to #{environment.gsub(".json","")}"
276
+ event_data[:metadata] = metadata.to_json
277
+ uri = URI.parse(AppConf.eventinator.url)
278
+ http = Net::HTTP.new(uri.host, uri.port)
279
+ ## TODO: should make this configurable, timeout after 5 sec
280
+ http.read_timeout = 5;
281
+ request = Net::HTTP::Post.new(uri.request_uri)
282
+ request.set_form_data(event_data)
283
+ begin
284
+ response = http.request(request)
285
+ if response.code != "200"
286
+ ui.warn("Got a #{response.code} from #{AppConf.eventinator.url} promote wasn't eventinated")
287
+ end
288
+ rescue Timeout::Error
289
+ ui.warn("Timed out connecting to #{AppConf.eventinator.url} promote wasn't eventinated")
290
+ rescue Exception => msg
291
+ ui.warn("An unhandled execption occured while eventinating: #{msg}")
292
+ end
293
+ end
254
294
  if !AppConf.graphite.nil? && AppConf.graphite.enabled
255
295
  begin
256
296
  time = Time.now
@@ -262,7 +302,6 @@ module KnifeSpork
262
302
  puts "Something went wrong with sending to graphite: (#{msg})"
263
303
  end
264
304
  end
265
-
266
305
  end
267
306
 
268
307
  def save_environment_changes(environment,envjson)
@@ -280,10 +319,16 @@ module KnifeSpork
280
319
  f2.puts envjson
281
320
  end
282
321
  end
322
+ if !AppConf.git.nil? && AppConf.git.enabled
323
+ if !@@gitavail
324
+ ui.msg "Git gem not available, skipping git add.\n\n"
325
+ else
326
+ git_add(path)
327
+ end
328
+ end
283
329
  end
284
330
 
285
331
  def promote(environment,cookbook)
286
-
287
332
  if config[:version]
288
333
  if !valid_version(config[:version])
289
334
  ui.error("#{config[:version]} isn't a valid version number.")
@@ -294,7 +339,6 @@ module KnifeSpork
294
339
  else
295
340
  @version = get_version(config[:cookbook_path], cookbook)
296
341
  end
297
-
298
342
  ui.msg "Adding version constraint #{cookbook} = #{@version}"
299
343
  return update_version_constraints(environment,cookbook,@version)
300
344
  end
@@ -306,9 +350,9 @@ module KnifeSpork
306
350
  results << c
307
351
  end
308
352
  return results
309
- end
353
+ end
310
354
 
311
- def check_cookbook_uploaded(cookbook)
355
+ def check_cookbook_uploaded(cookbook)
312
356
  if config[:version]
313
357
  if !valid_version(config[:version])
314
358
  ui.error("#{config[:version]} isn't a valid version number.")
@@ -326,11 +370,11 @@ module KnifeSpork
326
370
  ui.msg "#{cookbook} version #{@version } found on server!"
327
371
  end
328
372
 
329
- def pretty_print(environment)
373
+ def pretty_print(environment)
330
374
  return JSON.pretty_generate(JSON.parse(environment.to_json))
331
375
  end
332
376
 
333
- def git_pull_if_repo
377
+ def git_pull_if_repo
334
378
  strio = StringIO.new
335
379
  l = Logger.new strio
336
380
  cookbook_path = config[:cookbook_path]
@@ -367,10 +411,27 @@ module KnifeSpork
367
411
  end
368
412
  end
369
413
  end
370
- end
414
+
415
+ def git_add(environment)
416
+ strio = StringIO.new
417
+ l = Logger.new strio
418
+ cookbook_path = config[:cookbook_path]
419
+ begin
420
+ path = cookbook_path[0].gsub("cookbooks","")
421
+ ui.msg "Opening git repo #{path}\n\n"
422
+ g = Git.open(path, :log => Logger.new(strio))
423
+ ui.msg "Git add'ing #{environment}\n\n"
424
+ g.add("#{environment}")
425
+ rescue ArgumentError => e
426
+ ui.warn "Git: The root of your chef repo doesn't look like it's a git repo. Skipping git add...\n\n"
427
+ rescue
428
+ ui.warn "Git: Cookbook bump succeeded, but something went wrong with git add #{environment}, so you'll want to manually git add it. Dumping log info..."
429
+ ui.warn "#{strio.string}"
430
+ end
431
+ end
371
432
  end
372
-
373
- class String
433
+ end
434
+ class String
374
435
  def is_i?
375
436
  !!(self =~ /^[-+]?[0-9]+$/)
376
437
  end
@@ -3,7 +3,9 @@
3
3
  # Copyright:: Copyright (c) 2011 Jon Cowie
4
4
  # License:: Apache License, Version 2.0
5
5
  #
6
- # Modified cookbook upload to always freeze, and disable --force option
6
+ # Modified cookbook upload to always freeze, and disable --force option, some other options disabled such as
7
+ # updating environment constraints, as this is done later in the spork workflow.
8
+
7
9
  # Based on the knife cookbook upload plugin by:
8
10
  #
9
11
  # Author:: Adam Jacob (<adam@opscode.com>)
@@ -35,12 +37,18 @@ module KnifeSpork
35
37
  CHECKSUM = "checksum"
36
38
  MATCH_CHECKSUM = /[0-9a-f]{32,}/
37
39
 
40
+ @@fcavail = true
38
41
  deps do
39
42
  require 'chef/exceptions'
40
43
  require 'chef/cookbook_loader'
41
44
  require 'chef/cookbook_uploader'
45
+ begin
46
+ require "foodcritic"
47
+ rescue LoadError
48
+ @@fcavail = false
49
+ end
42
50
  end
43
-
51
+
44
52
  banner "knife spork upload [COOKBOOKS...] (options)"
45
53
 
46
54
  option :cookbook_path,
@@ -54,12 +62,6 @@ module KnifeSpork
54
62
  :description => 'Freeze this version of the cookbook so that it cannot be overwritten',
55
63
  :boolean => true
56
64
 
57
- option :environment,
58
- :short => '-E',
59
- :long => '--environment ENVIRONMENT',
60
- :description => "Set ENVIRONMENT's version dependency match the version you're uploading.",
61
- :default => nil
62
-
63
65
  option :depends,
64
66
  :short => "-d",
65
67
  :long => "--include-dependencies",
@@ -91,9 +93,7 @@ module KnifeSpork
91
93
 
92
94
  config[:cookbook_path] ||= Chef::Config[:cookbook_path]
93
95
 
94
- assert_environment_valid!
95
96
  warn_about_cookbook_shadowing
96
- version_constraints_to_update = {}
97
97
  # Get a list of cookbooks and their versions from the server
98
98
  # for checking existence of dependending cookbooks.
99
99
  @server_side_cookbooks = Chef::CookbookVersion.list
@@ -112,12 +112,20 @@ module KnifeSpork
112
112
  @name_args.push dep
113
113
  end
114
114
  end
115
+
116
+ if !AppConf.foodcritic.nil? && AppConf.foodcritic.enabled
117
+ if !@@fcavail
118
+ ui.msg "Foodcritic gem not available, skipping cookbook lint check.\n\n"
119
+ else
120
+ foodcritic_lint_check(cookbook_name)
121
+ end
122
+ end
123
+
115
124
  ui.info("Uploading and freezing #{cookbook.name.to_s.ljust(justify_width + 10)} [#{cookbook.version}]")
116
125
 
117
126
  upload(cookbook, justify_width)
118
127
  cookbook.freeze_version
119
128
  upload(cookbook, justify_width)
120
- version_constraints_to_update[cookbook_name] = cookbook.version
121
129
 
122
130
  if !AppConf.irccat.nil? && AppConf.irccat.enabled
123
131
  begin
@@ -139,6 +147,39 @@ module KnifeSpork
139
147
  end
140
148
  end
141
149
 
150
+ if !AppConf.eventinator.nil? && AppConf.eventinator.enabled
151
+ metadata = {}
152
+ metadata[:cookbook_name] = cookbook.name
153
+ metadata[:cookbook_version] = cookbook.version
154
+
155
+ event_data = {}
156
+ event_data[:tag] = "knife"
157
+ event_data[:username] = ENV['USER']
158
+ event_data[:status] = "#{ENV['USER']} uploaded and froze version #{cookbook.version} of cookbook #{cookbook_name}"
159
+ event_data[:metadata] = metadata.to_json
160
+
161
+ uri = URI.parse(AppConf.eventinator.url)
162
+
163
+ http = Net::HTTP.new(uri.host, uri.port)
164
+
165
+ ## TODO: should make this configurable, timeout after 5 sec
166
+ http.read_timeout = 5;
167
+
168
+ request = Net::HTTP::Post.new(uri.request_uri)
169
+ request.set_form_data(event_data)
170
+
171
+ begin
172
+ response = http.request(request)
173
+ if response.code != "200"
174
+ ui.warn("Got a #{response.code} from #{AppConf.eventinator.url} upload wasn't eventinated")
175
+ end
176
+ rescue Timeout::Error
177
+ ui.warn("Timed out connecting to #{AppConf.eventinator.url} upload wasn't eventinated")
178
+ rescue Exception => msg
179
+ ui.warn("An unhandled execption occured while eventinating: #{msg}")
180
+ end
181
+ end
182
+
142
183
  rescue Chef::Exceptions::CookbookNotFoundInRepo => e
143
184
  ui.error("Could not find cookbook #{cookbook_name} in your cookbook path, skipping it")
144
185
  Chef::Log.debug(e)
@@ -146,7 +187,6 @@ module KnifeSpork
146
187
  end
147
188
 
148
189
  ui.info "upload complete"
149
- update_version_constraints(version_constraints_to_update) if config[:environment]
150
190
  end
151
191
 
152
192
  def cookbook_repo
@@ -156,18 +196,6 @@ module KnifeSpork
156
196
  end
157
197
  end
158
198
 
159
- def update_version_constraints(new_version_constraints)
160
- new_version_constraints.each do |cookbook_name, version|
161
- environment.cookbook_versions[cookbook_name] = "= #{version}"
162
- end
163
- environment.save
164
- end
165
-
166
-
167
- def environment
168
- @environment ||= config[:environment] ? Chef::Environment.load(config[:environment]) : nil
169
- end
170
-
171
199
  def warn_about_cookbook_shadowing
172
200
  unless cookbook_repo.merged_cookbooks.empty?
173
201
  ui.warn "* " * 40
@@ -186,20 +214,7 @@ WARNING
186
214
 
187
215
  private
188
216
 
189
- def assert_environment_valid!
190
- environment
191
- rescue Net::HTTPServerException => e
192
- if e.response.code.to_s == "404"
193
- ui.error "The environment #{config[:environment]} does not exist on the server, aborting."
194
- Chef::Log.debug(e)
195
- exit 1
196
- else
197
- raise
198
- end
199
- end
200
-
201
217
  def upload(cookbook, justify_width)
202
-
203
218
  check_for_broken_links(cookbook)
204
219
  check_dependencies(cookbook)
205
220
  Chef::CookbookUploader.new(cookbook, config[:cookbook_path]).upload_cookbook
@@ -208,6 +223,7 @@ WARNING
208
223
  when "409"
209
224
  ui.error "Version #{cookbook.version} of cookbook #{cookbook.name} is frozen. Please bump your version number."
210
225
  Chef::Log.debug(e)
226
+ exit 1
211
227
  else
212
228
  raise
213
229
  end
@@ -269,6 +285,35 @@ WARNING
269
285
  end
270
286
  false
271
287
  end
272
-
288
+
289
+ def foodcritic_lint_check(cookbook_name)
290
+
291
+ if config[:cookbook_path].size > 1
292
+ ui.warn "It looks like you have multiple cookbook paths defined so I'm not sure where to look for this cookbook.\n\n"
293
+ ui.warn "Skipping Lint Check.\n\n"
294
+ return
295
+ end
296
+
297
+ fail_tags = []
298
+ fail_tags = AppConf.foodcritic.fail_tags unless AppConf.foodcritic.fail_tags.nil?
299
+
300
+ tags = []
301
+ tags = AppConf.foodcritic.tags unless AppConf.foodcritic.tags.nil?
302
+
303
+ include_rules = []
304
+ include_rules = AppConf.foodcritic.include_rules unless AppConf.foodcritic.include_rules.nil?
305
+
306
+ ui.msg "Lint checking #{cookbook_name}..."
307
+ options = {:fail_tags => fail_tags, :tags =>tags, :include_rules => include_rules}
308
+ review = FoodCritic::Linter.new.check("#{config[:cookbook_path][0]}/#{cookbook_name}",options)
309
+
310
+ if review.failed?
311
+ ui.error "Lint check failed. Halting upload."
312
+ ui.error "Lint check output:"
313
+ ui.error review
314
+ exit 1
315
+ end
316
+ ui.msg "Lint check passed"
317
+ end
273
318
  end
274
319
  end
data/lib/knife-spork.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module KnifeSpork
2
- VERSION = "0.1.8"
2
+ VERSION = "0.1.9"
3
3
  end
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 0
7
7
  - 1
8
- - 8
9
- version: 0.1.8
8
+ - 9
9
+ version: 0.1.9
10
10
  platform: ruby
11
11
  authors:
12
12
  - Jon Cowie
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2012-02-21 00:00:00 +00:00
17
+ date: 2012-04-03 00:00:00 +01:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency