chef-relevant-tests 1.0.0

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.
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: []