knife-table 0.0.2 → 0.0.3

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.
data/CHANGELOG.md CHANGED
@@ -1,3 +1,9 @@
1
+ ## v0.0.3
2
+ * Add foodcritic support
3
+ * Add knife config support
4
+ * Add hub integration
5
+ * Add order command
6
+
1
7
  ## v0.0.2
2
8
  * Load data bag via DataBagItem class not DataBag
3
9
 
data/README.md CHANGED
@@ -1,18 +1,20 @@
1
- # ChefTable
1
+ # KnifeTable
2
2
 
3
- ChefTable is a knife plugin to aid in cookbook development
3
+ KnifeTable is a knife plugin to aid in cookbook development
4
4
  workflow. Its intention is to help automate versioning
5
5
  within environments and cookbook freezing based on a stable
6
- branch. Building off of the knife-spork plugin ChefTable
6
+ branch. Building off of the knife-spork plugin KnifeTable
7
7
  helps to provide consistency within the environment.
8
8
 
9
9
 
10
10
  ## Usage
11
11
 
12
- Currently, two helpers are available:
12
+ Currently, the supported workflow is as follows:
13
13
 
14
14
  `knife table set`
15
15
 
16
+ `knife table order`
17
+
16
18
  and
17
19
 
18
20
  `knife table serve`
@@ -25,13 +27,22 @@ of what is being added:
25
27
 
26
28
  `knife table set new feature`
27
29
 
28
- This will create a new working branch named 'WIP-new_feature'. The prefix
29
- for the branch defaults to 'WIP-' but can be modified using the `-p` option.
30
- If it is known what cookbooks will be modified, you can provide them while
31
- setting:
30
+ This will create a new working branch named 'new_feature'. A default prefix
31
+ can be added the branch name via the `-p` option. If it is known what cookbooks
32
+ will be modified, you can provide them while setting:
32
33
 
33
34
  `knife table set -c iptables,mysql new feature`
34
35
 
36
+ ### Order
37
+
38
+ Once the code has been updated, tested and is ready for review, the order
39
+ can be placed which will create a new pull request:
40
+
41
+ `knife table order`
42
+
43
+ The order option will also optionally run foodcritic and require a passing
44
+ result before proceeding with the pull request generation.
45
+
35
46
  ### Service
36
47
 
37
48
  Service works on the assumption that any new code into the stable branch (master
data/knife-table.gemspec CHANGED
@@ -1,6 +1,8 @@
1
+ $LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__)) + '/lib/'
2
+ require 'knife-table/version'
1
3
  Gem::Specification.new do |s|
2
4
  s.name = 'knife-table'
3
- s.version = '0.0.2'
5
+ s.version = KnifeTable::VERSION.version
4
6
  s.summary = 'Help chef set and serve the table'
5
7
  s.author = 'Chris Roberts'
6
8
  s.email = 'chrisroberts.code@gmail.com'
@@ -8,5 +10,7 @@ Gem::Specification.new do |s|
8
10
  s.description = "Chef's table"
9
11
  s.require_path = 'lib'
10
12
  s.files = Dir.glob('**/*')
11
- s.add_dependency 'knife-spork'
13
+ s.add_dependency 'knife-spork', '>= 0.1.11'
14
+ s.add_dependency 'hub', '>= 1.10.1'
15
+ s.add_dependency 'foodcritic', '>= 1.4.0'
12
16
  end
@@ -13,7 +13,7 @@ module KnifeTable
13
13
  banner 'knife table clear'
14
14
 
15
15
  def run
16
-
16
+ ui.fatal "Not currently supported"
17
17
  end
18
18
 
19
19
  end
@@ -0,0 +1,154 @@
1
+ require 'open3'
2
+ require 'knife-table/helpers'
3
+
4
+ module KnifeTable
5
+ class TableOrder < Chef::Knife
6
+
7
+ include KnifeTable::Helpers
8
+
9
+ deps do
10
+ require 'git'
11
+ require 'hub'
12
+ end
13
+
14
+ banner 'knife table order [BRANCH]'
15
+
16
+ option :upstream_user,
17
+ :short => '-u USER',
18
+ :long => '--upstream-user USER',
19
+ :description => 'Username for upstream github account'
20
+
21
+ option :upstream_branch,
22
+ :short => '-b BRANCH',
23
+ :long => '--upstream-branch BRANCH',
24
+ :description => 'Upstream branch name'
25
+
26
+ option :title,
27
+ :short => '-t TITLE',
28
+ :long => '--title TITLE',
29
+ :description => 'Title for pull request'
30
+
31
+ option :foodcritic,
32
+ :short => '-f',
33
+ :long => '--foodcritic',
34
+ :description => 'Pass foodcritic before generating pull request',
35
+ :boolean => true
36
+
37
+ option :foodcritic_fail_on,
38
+ :short => '-x correctness,any,~FC014',
39
+ :long => '--foodcritic-fail-on correctness,any,~FC014',
40
+ :description => 'Set what foodcritic should fail on',
41
+ :proc => lambda{|v| v.split(',').strip}
42
+
43
+
44
+ def run
45
+ ui.msg ui.highline.color("#{' ' * 10}** Knife Table: Placing Order **", [HighLine::GREEN, HighLine::BOLD])
46
+ check_config_options
47
+ if(config[:foodcritic])
48
+ fail_on = Array(config[:foodcritic_fail_on]).map{|s| "-f #{s}"}.join(' ')
49
+ cookbooks = discover_changed(:cookbooks, 'master', 'HEAD').map{|c| c.split('/').first}
50
+ pass = true
51
+ cookbooks.each do |cookbook|
52
+ res = system("foodcritic #{fail_on} #{File.join(cookbook_path, cookbook)}")
53
+ pass = res unless res
54
+ end
55
+ unless(pass)
56
+ ui.fatal "Modifications do not currently pass foodcritic!"
57
+ exit 1
58
+ end
59
+ end
60
+
61
+ # TODO: Update this to not shell out
62
+ cmd = "hub pull-request \"#{title}\" -b #{@upstream}:#{config[:upstream_branch]} -h #{local_user}:#{local_branch}"
63
+ output = ''
64
+ err = nil
65
+ unless(File.exists?(File.join(ENV['HOME'], '.config', 'hub')))
66
+ g_config = Hub::GitHubAPI::Configuration.new(nil)
67
+ g_user = g_config.prompt 'Github username'
68
+ g_pass = g_config.prompt_password 'Github', g_user
69
+ end
70
+ res = Open3.popen3(cmd) do |stdin, stdout, stderr, wait_thr|
71
+ if(g_user)
72
+ stdin.puts g_user
73
+ stdin.puts g_pass
74
+ end
75
+ output << stdout.readlines.last.to_s
76
+ err = stderr.readlines.last.to_s
77
+ wait_thr.value
78
+ end
79
+ output.strip!
80
+ if(res.success?)
81
+ ui.msg "New pull request: #{output}"
82
+ else
83
+ ui.error err.to_s.empty? ? 'Failed to create pull request' : err
84
+ end
85
+ end
86
+
87
+ private
88
+
89
+ def title
90
+ config[:title] || local_branch.gsub('_', ' ')
91
+ end
92
+
93
+ def local_branch
94
+ unless(@branch)
95
+ if(name_args.size > 0)
96
+ @branch = name_args.first
97
+ else
98
+ @branch = git.current_branch
99
+ end
100
+ end
101
+ @branch
102
+ end
103
+
104
+ def local_user
105
+ unless(@local)
106
+ l = %x{git remote -v}.split("\n").find_all{|r|
107
+ r.include?('github.com')
108
+ }.map{|r|
109
+ Array(r.scan(%r{:[^/]+}).first).first
110
+ }.compact.map{|r|
111
+ r.sub(':', '')
112
+ }.uniq.sort - [@upstream]
113
+ if(l.size > 1)
114
+ @local = ask_user_local(l)
115
+ elsif(l.size < 1)
116
+ @local = @upstream
117
+ else
118
+ @local = l.first
119
+ end
120
+ end
121
+ @local
122
+ end
123
+
124
+ def ask_user_local(locals)
125
+ l = nil
126
+ ui.msg 'Please select your local user:'
127
+ while(l.nil?)
128
+ locals.each_with_index do |name, idx|
129
+ ui.msg "#{idx + 1}. #{name}"
130
+ end
131
+ res = ui.ask_question "Enter number of local user [1-#{local.size}]:"
132
+ if(locals[res.to_i + 1])
133
+ l = locals[res.to_i + 1]
134
+ else
135
+ ui.warn "Invalid selection."
136
+ end
137
+ end
138
+ l
139
+ end
140
+
141
+ def check_config_options
142
+ %w(upstream_user upstream_branch title foodcritic foodcritic_fail_on).each do |key|
143
+ config[key.to_sym] ||= Chef::Config["table_set_#{key}".to_sym]
144
+ end
145
+ @upstream = config[:upstream_user]
146
+ unless(@upstream)
147
+ ui.fatal "Upstream user is REQUIRED"
148
+ exit 1
149
+ end
150
+ config[:foodcritic_fail_on] ||= 'correctness'
151
+ config[:upstream_branch] ||= 'master'
152
+ end
153
+ end
154
+ end
@@ -32,13 +32,11 @@ module KnifeTable
32
32
  option :git_branch,
33
33
  :short => '-b BRANCH',
34
34
  :long => '--git-branch BRANCH',
35
- :default => 'master',
36
35
  :description => 'Set working branch'
37
36
 
38
37
  option :git_remote_name,
39
38
  :short => '-r NAME',
40
39
  :long => '--git-remote-name NAME',
41
- :default => 'origin',
42
40
  :description => 'Remote repo name'
43
41
 
44
42
  option :git_autocommit,
@@ -65,19 +63,15 @@ module KnifeTable
65
63
  :boolean => true,
66
64
  :description => 'Upload any changed data bags'
67
65
 
68
- def initialize(*args)
69
- super
70
- @environments = config[:environments].to_s.split(",").map(&:strip)
71
- end
72
-
73
66
  def run
67
+ check_config_options
74
68
  sanity_checks
75
69
  cookbooks = discover_changed(:cookbooks, *determine_commit_span).map{|c|c.split('/').first}
76
70
  roles = discover_changed(:roles, *determine_commit_span) if config[:upload_roles]
77
71
  data_bags = discover_changed(:data_bags, *determine_commit_span) if config[:upload_data_bags]
78
72
 
79
73
  ui.msg ui.highline.color("#{' ' * 10}** Knife Table: Service started **", [HighLine::GREEN, HighLine::BOLD])
80
- ui.highline.say ui.highline.color("Discovered cookbooks staged for freeze: #{cookbooks.join(', ')}", HighLine::CYAN)
74
+ ui.highline.say ui.highline.color("Discovered cookbooks staged for freeze: #{cookbooks.join(', ')}", HighLine::CYAN) unless cookbooks.nil? || cookbooks.empty?
81
75
  ui.highline.say ui.highline.color("Environments staged to be updated: #{@environments.join(', ')}", HighLine::CYAN) unless @environments.empty?
82
76
  ui.highline.say ui.highline.color("Roles staged to be uploaded: #{roles.sort.map{|r|r.sub(/\.(rb|json)/, '')}.join(', ')}", HighLine::CYAN) unless roles.nil? || roles.empty?
83
77
  ui.highline.say ui.highline.color("Data Bags staged to be uploaded: #{data_bags.sort.join(', ')}", HighLine::CYAN) unless data_bags.nil? || data_bags.empty?
@@ -224,6 +218,7 @@ module KnifeTable
224
218
  def upload_roles(role)
225
219
  role_load = loader(:roles).load_from('roles', role)
226
220
  role_load.save
221
+ role_load
227
222
  end
228
223
 
229
224
  def upload_data_bags(bag)
@@ -232,6 +227,7 @@ module KnifeTable
232
227
  dbag.data_bag(bag.split('/').first)
233
228
  dbag.raw_data = data_bag_load
234
229
  dbag.save
230
+ dbag
235
231
  end
236
232
 
237
233
  private
@@ -297,23 +293,13 @@ module KnifeTable
297
293
  end
298
294
  end
299
295
 
300
- def discover_changed(type, first_commit, last_commit)
301
- changed = []
302
- git.diff(first_commit, last_commit).stats[:files].keys.each do |path|
303
- if(path.start_with?(type.to_s))
304
- changed << path.sub(/^#{type.to_s}\/?/, '')
305
- end
306
- end
307
- changed.uniq
308
- end
309
-
310
296
  def upload_changes(type, changed)
311
297
  raise "Unsupported upload change type: #{type}" unless [:roles, :data_bags].include?(type.to_sym)
312
298
  ui.highline.say "#{ui.highline.color("Uploading #{type.to_s.gsub('_', ' ')}:", HighLine::GREEN)} "
313
299
  unless(changed.empty?)
314
300
  changed.each do |item|
315
- send("upload_#{type}", item)
316
- ui.highline.say "#{File.basename(item).sub(/\.(rb|json)/, '')} "
301
+ thing = send("upload_#{type}", item)
302
+ ui.highline.say "#{thing.is_a?(Chef::Role) ? thing.name : "#{thing.data_bag}::#{thing.id}"} "
317
303
  end
318
304
  else
319
305
  ui.highline.say "no #{type.to_s.gsub('_', ' ').sub(/s$/, '')} changes detected "
@@ -331,5 +317,14 @@ module KnifeTable
331
317
  end
332
318
  end
333
319
 
320
+ def check_config_options
321
+ %w(environments git_autopush git_tag git_branch git_remote_name
322
+ git_autocommit autoproceed upload_roles upload_data_bags).each do |key|
323
+ config[key.to_sym] ||= Chef::Config["table_set_#{key}".to_sym]
324
+ end
325
+ @environments = config[:environments].to_s.split(",").map(&:strip)
326
+ config[:git_branch] ||= 'master'
327
+ config[:git_remote_name] ||= 'origin'
328
+ end
334
329
  end
335
330
  end
@@ -20,23 +20,24 @@ module KnifeTable
20
20
  option :branch_prefix,
21
21
  :short => '-p PREFIX',
22
22
  :long => '--branch-prefix PREFIX',
23
- :description => 'Set prefix for branch name',
24
- :default => 'WIP-'
23
+ :description => 'Set prefix for branch name'
25
24
 
26
25
  option :bump_type,
27
26
  :short => '-b TYPE',
28
27
  :long => '--bump-type TYPE',
29
- :description => 'Type of version bump (major, minor, patch)',
30
- :default => 'patch'
31
-
32
- def initialize(*args)
33
- super
34
- @cookbooks = config[:cookbooks].to_s.split(',').map(&:strip)
35
- end
28
+ :description => 'Type of version bump (major, minor, patch)'
36
29
 
37
30
  def run
38
31
  ui.msg ui.highline.color("#{' ' * 10}** Knife Table: New place setting **", [HighLine::GREEN, HighLine::BOLD])
32
+ if(name_args.empty?)
33
+ ui.fatal "Feature description must be provided"
34
+ exit 1
35
+ end
36
+ check_config_options
37
+ check_current_branch!
38
+ check_up_to_date!
39
39
  branch_name = "#{config[:branch_prefix]}#{name_args.join('_').downcase}"
40
+ check_branch_conflict!(branch_name)
40
41
  ui.highline.say "Creating new work branch (#{branch_name}): "
41
42
  git.branch(branch_name).create
42
43
  ui.highline.say "done"
@@ -50,5 +51,36 @@ module KnifeTable
50
51
  end
51
52
  end
52
53
 
54
+ private
55
+
56
+ def check_current_branch!
57
+ unless(git.current_branch == 'master')
58
+ ui.fatal "Set requires master branch to be checked out. Currently on: #{git.current_branch}"
59
+ exit 1
60
+ end
61
+ end
62
+
63
+ def check_branch_conflict!(name)
64
+ conflict = git.branches.map(&:full).detect do |b|
65
+ b == name || b.sub(%r{remotes/[^/]+/}, '') == name
66
+ end
67
+ if(conflict)
68
+ ui.fatal "Failed to create topic branch. Already exists: #{conflict}"
69
+ exit 1
70
+ end
71
+ end
72
+
73
+ def check_up_to_date!
74
+ # TODO: fetch/merge master to ensure up to date?
75
+ end
76
+
77
+ def check_config_options
78
+ %w(cookbooks branch_prefix bump_type).each do |key|
79
+ config[key.to_sym] ||= Chef::Config["table_set_#{key}".to_sym]
80
+ end
81
+ @cookbooks = config[:cookbooks].to_s.split(',').map(&:strip)
82
+ config[:bump_type] ||= 'patch'
83
+ end
84
+
53
85
  end
54
86
  end
@@ -7,5 +7,15 @@ module KnifeTable
7
7
  def cookbook_path
8
8
  Chef::Config[:cookbook_path].first
9
9
  end
10
+
11
+ def discover_changed(type, first_commit, last_commit)
12
+ changed = []
13
+ git.diff(first_commit, last_commit).stats[:files].keys.each do |path|
14
+ if(path.start_with?(type.to_s))
15
+ changed << path.sub(/^#{type.to_s}\/?/, '')
16
+ end
17
+ end
18
+ changed.uniq
19
+ end
10
20
  end
11
21
  end
@@ -1,3 +1,5 @@
1
1
  module KnifeTable
2
- VERSION = '0.0.2'
2
+ class Version < Gem::Version
3
+ end
4
+ VERSION = Version.new('0.0.3')
3
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: knife-table
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,32 +9,54 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-07-12 00:00:00.000000000 Z
12
+ date: 2012-07-19 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: knife-spork
16
- requirement: &20490380 !ruby/object:Gem::Requirement
16
+ requirement: &5700840 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
20
20
  - !ruby/object:Gem::Version
21
- version: '0'
21
+ version: 0.1.11
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *20490380
24
+ version_requirements: *5700840
25
+ - !ruby/object:Gem::Dependency
26
+ name: hub
27
+ requirement: &5716660 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: 1.10.1
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: *5716660
36
+ - !ruby/object:Gem::Dependency
37
+ name: foodcritic
38
+ requirement: &5716120 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: 1.4.0
44
+ type: :runtime
45
+ prerelease: false
46
+ version_requirements: *5716120
25
47
  description: Chef's table
26
48
  email: chrisroberts.code@gmail.com
27
49
  executables: []
28
50
  extensions: []
29
51
  extra_rdoc_files: []
30
52
  files:
53
+ - knife-table-0.0.3.gem
31
54
  - CHANGELOG.md
32
- - knife-table-0.0.2.gem
33
55
  - README.md
34
56
  - knife-table.gemspec
35
57
  - lib/knife-table/version.rb
36
58
  - lib/knife-table/helpers.rb
37
- - lib/chef/knife/data_holder.rb
59
+ - lib/chef/knife/table_order.rb
38
60
  - lib/chef/knife/table_serve.rb
39
61
  - lib/chef/knife/table_clear.rb
40
62
  - lib/chef/knife/table_set.rb
@@ -1,54 +0,0 @@
1
- require 'pp'
2
-
3
- class DataHolder
4
-
5
- def initialize
6
- @holder = {}
7
- @path = nil
8
- end
9
-
10
- def method_missing(name, *args)
11
- @holder[name] = args
12
- end
13
-
14
- def ==(holder)
15
- raise TypeError.new('Comparisons only allowed between DataHolder instances')
16
- self._holder == holder._holder
17
- end
18
-
19
- def _holder
20
- @holder
21
- end
22
-
23
- def _load(path)
24
- @path = path
25
- self.instance_eval(
26
- File.read(path)
27
- )
28
- self
29
- end
30
-
31
- def _output
32
- output = ''
33
- @holder.each_pair do |k,v|
34
- output << "#{k}(\n"
35
- inards = []
36
- v.each do |item|
37
- s = ''
38
- PP.pp(item, s)
39
- inards << s
40
- end
41
- output << inards.join(",\n")
42
- output << ")\n"
43
- end
44
- output
45
- File.open(@path, 'w') do |file|
46
- file.write(output)
47
- end
48
- output
49
- end
50
-
51
- def _path
52
- @path
53
- end
54
- end