chef-relevant-tests 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 4f5e163490693818aa4ea66b915c5d42fddfc96a
4
+ data.tar.gz: 22154e143b81e92dc60cc92040d61a09fb33e468
5
+ SHA512:
6
+ metadata.gz: 51095f24d9cced208329d1640737613b5a1e24e1814fec7f4278d3357b1af9055e0e1fc95d57e7ba49f6841eb437d44458fda7ecd94f60857bad1ac5c6ee8455
7
+ data.tar.gz: 2fa43a9bb328c9c5a12f9767e9695d9f624e21c9210c964ae9b82bf6880dd4c7c09c167e5c37da8561af845fa8a9b21beaede669d25f0b164832fa03c4f7dd93
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ $:.unshift File.expand_path('../../lib', __FILE__)
3
+ require 'chef-relevant-tests'
4
+
5
+ begin
6
+ ChefRelevantTests::Cli.set_revision(ARGV[0])
7
+ ChefRelevantTests::Cli.set_expander(ARGV[1])
8
+
9
+ puts ChefRelevantTests::Cli.run.join(' ')
10
+ rescue => e
11
+ $stderr.puts(e.message)
12
+ $stderr.write(e.backtrace.join("\n"))
13
+ exit 1
14
+ end
@@ -0,0 +1,6 @@
1
+ require 'chef-relevant-tests/change-detectors/berkshelf'
2
+ require 'chef-relevant-tests/expanders/test-kitchen'
3
+ require 'chef-relevant-tests/cli'
4
+
5
+ module ChefRelevantTests
6
+ end
@@ -0,0 +1,79 @@
1
+ module ChefRelevantTests
2
+ module ChangeDetectors
3
+ class Berkshelf
4
+ def initialize(ref)
5
+ @ref = ref
6
+ end
7
+
8
+ def should_run?
9
+ load_dependencies
10
+ !!defined?(Berkshelf)
11
+ end
12
+
13
+ def changed_cookbooks
14
+ load_dependencies
15
+
16
+ changed_cookbooks = []
17
+ dependent_cookbooks = []
18
+
19
+ old, new = get_berksfiles_to_compare
20
+
21
+ # `old` and `new` are both in the format
22
+ # [["cookbook_name", "0.0.2"], ...]
23
+ #
24
+ # So, the changed cookbooks are the ones that appear in the new version but
25
+ # not the old version. This should also filter any cookbooks which no longer
26
+ # exist.
27
+ diff = berksfile_locked_versions(new) - berksfile_locked_versions(old)
28
+
29
+ changed_cookbooks = diff.map(&:first)
30
+
31
+ dependent_cookbooks = changed_cookbooks.map { |name| berksfile_dependent_upon(new, name) }.flatten
32
+
33
+ changed_cookbooks | dependent_cookbooks
34
+ end
35
+
36
+ private
37
+
38
+ def load_dependencies
39
+ begin
40
+ require 'berkshelf'
41
+ rescue LoadError
42
+ return false
43
+ end
44
+ end
45
+
46
+ # Separated for testability
47
+ def get_berksfiles_to_compare
48
+ Dir.mktmpdir do |dir|
49
+ `git show #{@ref}:Berksfile.lock > #{dir}/Berksfile.lock`
50
+ old = ::Berkshelf::Lockfile.from_file("#{dir}/Berksfile.lock")
51
+ new = ::Berkshelf::Lockfile.from_file('Berksfile.lock')
52
+
53
+ [old, new]
54
+ end
55
+ end
56
+
57
+ # The vertices in the Berkshelf-produced graph are the version-locked
58
+ # cookbooks (the part of the Berksfile.lock after "GRAPH")
59
+ #
60
+ # @param lockfile [Berkshelf::Lockfile] Lockfile to analyze
61
+ # @return [Array<Array>] for each locked cookbook, an array with two elements:
62
+ # first, cookbook name
63
+ # second, cookbook version
64
+ def berksfile_locked_versions(lockfile)
65
+ lockfile.graph.map { |g| [g.name, g.version] }
66
+ end
67
+
68
+ # Traverse the Berkshelf dependency graph to find any other cookbooks that
69
+ # include a dependency on the target cookbook.
70
+ #
71
+ # @param lockfile [Berkshelf::Lockfile]
72
+ # @param cookbook [String]
73
+ # @return [Array] Names of dependent cookbooks
74
+ def berksfile_dependent_upon(lockfile, cookbook)
75
+ lockfile.graph.select { |n| n.dependencies.include?(cookbook) }.map(&:name)
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,47 @@
1
+ require 'set'
2
+
3
+ module ChefRelevantTests
4
+ class Cli
5
+ class << self
6
+ def set_revision(rev)
7
+ @@revision = rev
8
+ end
9
+
10
+ def set_expander(exp)
11
+ @@expander = exp
12
+ end
13
+
14
+ def run
15
+ changed_cookbooks = find_cookbook_diffs(@@revision)
16
+
17
+ EXPANDER_REGISTRY[@@expander]
18
+ .new(@@revision, changed_cookbooks)
19
+ .expand
20
+ end
21
+
22
+ private
23
+
24
+ CHANGE_DETECTOR_REGISTRY = [
25
+ ChefRelevantTests::ChangeDetectors::Berkshelf,
26
+ ].freeze
27
+
28
+ EXPANDER_REGISTRY = {
29
+ 'test-kitchen' => ChefRelevantTests::Expanders::TestKitchen,
30
+ }.freeze
31
+
32
+ def find_cookbook_diffs(rev)
33
+ enabled_change_detectors = CHANGE_DETECTOR_REGISTRY.map do |klass|
34
+ detector = klass.new(rev)
35
+
36
+ detector if detector.should_run?
37
+ end.compact
38
+
39
+ raise 'Unable to find changes - try `gem install berkshelf`.' if enabled_change_detectors.none?
40
+
41
+ enabled_change_detectors.each_with_object(Set.new) do |detector, cookbooks|
42
+ cookbooks.merge(detector.changed_cookbooks)
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,76 @@
1
+ module ChefRelevantTests
2
+ module Expanders
3
+ class TestKitchen
4
+ def initialize(rev, changed_cookbooks = [])
5
+ @rev = rev
6
+ @changed_cookbooks = changed_cookbooks
7
+ end
8
+
9
+ def expand
10
+ require 'chef'
11
+ require 'chef/knife'
12
+ require 'kitchen'
13
+
14
+ changed_kitchen_instances | kitchen_run_lists.map do |instance_name, run_list|
15
+ instance_name if (@changed_cookbooks & expand_run_list(run_list)).any?
16
+ end.compact
17
+ end
18
+
19
+ private
20
+
21
+ # Separated for testability
22
+ def get_kitchen_yml_to_compare
23
+ old = nil
24
+ new = nil
25
+
26
+ Dir.mktmpdir do |dir|
27
+ `git show #{@rev}:.kitchen.yml > #{dir}/.kitchen.yml`
28
+
29
+ old = kitchen_config("#{dir}/.kitchen.yml")
30
+ new = kitchen_config('.kitchen.yml')
31
+ end
32
+
33
+ [old, new]
34
+ end
35
+
36
+ def changed_kitchen_instances
37
+ old, new = get_kitchen_yml_to_compare
38
+
39
+ new.instances.keep_if do |instance|
40
+ old_instance = old.instances.detect { |old| old.name == instance.name }
41
+
42
+ !old_instance || instance.provisioner.config_keys.any? do |config_key|
43
+ instance.provisioner[config_key] != old_instance.provisioner[config_key]
44
+ end
45
+ end.map(&:name)
46
+ end
47
+
48
+ # @return [Hash<String, Array>] Mapping of test-kitchen suite name to the run
49
+ # list specified in .kitchen.yml.
50
+ def kitchen_run_lists
51
+ c = kitchen_config('.kitchen.yml')
52
+
53
+ Hash[c.instances.map { |i| [i.name, i.provisioner[:run_list]] }]
54
+ end
55
+
56
+ # Use Chef (and Knife configuration) to expand the run list in the .kitchen.yml
57
+ # into a list of cookbooks.
58
+ #
59
+ # @return [Array] Cookbooks that have recipes that will be run
60
+ def expand_run_list(run_list, environment = 'default')
61
+ Chef::Config.from_file(Chef::Knife.locate_config_file)
62
+
63
+ Chef::RunList.new(*run_list)
64
+ .expand(environment, 'disk')
65
+ .recipes
66
+ .map { |r| Chef::Recipe.parse_recipe_name(r) }
67
+ .map { |cookbook, _recipe| cookbook.to_s }
68
+ .uniq
69
+ end
70
+
71
+ def kitchen_config(kitchen_yml)
72
+ Kitchen::Config.new(loader: Kitchen::Loader::YAML.new(:local_config => kitchen_yml))
73
+ end
74
+ end
75
+ end
76
+ end
metadata ADDED
@@ -0,0 +1,80 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: chef-relevant-tests
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Brigade Engineering
8
+ - Tom Dooner
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2014-10-27 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: chef
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: '11'
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - "~>"
26
+ - !ruby/object:Gem::Version
27
+ version: '11'
28
+ - !ruby/object:Gem::Dependency
29
+ name: rspec
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - "~>"
33
+ - !ruby/object:Gem::Version
34
+ version: '3'
35
+ type: :development
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - "~>"
40
+ - !ruby/object:Gem::Version
41
+ version: '3'
42
+ description: Only run the Chef tests that you need to run
43
+ email:
44
+ - eng@brigade.com
45
+ - tom.dooner@brigade.com
46
+ executables:
47
+ - chef-relevant-tests
48
+ extensions: []
49
+ extra_rdoc_files: []
50
+ files:
51
+ - bin/chef-relevant-tests
52
+ - lib/chef-relevant-tests.rb
53
+ - lib/chef-relevant-tests/change-detectors/berkshelf.rb
54
+ - lib/chef-relevant-tests/cli.rb
55
+ - lib/chef-relevant-tests/expanders/test-kitchen.rb
56
+ homepage: https://github.com/brigade/chef-relevant-tests
57
+ licenses:
58
+ - MIT
59
+ metadata: {}
60
+ post_install_message:
61
+ rdoc_options: []
62
+ require_paths:
63
+ - lib
64
+ required_ruby_version: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: 1.9.3
69
+ required_rubygems_version: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ version: '0'
74
+ requirements: []
75
+ rubyforge_project:
76
+ rubygems_version: 2.2.2
77
+ signing_key:
78
+ specification_version: 4
79
+ summary: Gem which looks at Chef configuration to narrow which tests you need to run
80
+ test_files: []