controlrepo 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +1 -0
- data/README.md +274 -0
- data/controlrepo.gemspec +26 -0
- data/lib/controlrepo.rb +323 -0
- data/lib/controlrepo/beaker.rb +185 -0
- data/lib/controlrepo/class.rb +28 -0
- data/lib/controlrepo/group.rb +61 -0
- data/lib/controlrepo/node.rb +43 -0
- data/lib/controlrepo/rake_tasks.rb +150 -0
- data/lib/controlrepo/test.rb +124 -0
- data/lib/controlrepo/testconfig.rb +136 -0
- data/templates/.fixtures.yml.erb +24 -0
- data/templates/Gemfile.erb +5 -0
- data/templates/Rakefile.erb +1 -0
- data/templates/acceptance_test_spec.rb.erb +18 -0
- data/templates/nodeset.yaml.erb +9 -0
- data/templates/spec_helper.rb.erb +7 -0
- data/templates/spec_helper_acceptance.rb.erb +24 -0
- data/templates/test_spec.rb.erb +13 -0
- metadata +162 -0
@@ -0,0 +1,124 @@
|
|
1
|
+
class Controlrepo
|
2
|
+
class Test
|
3
|
+
@@all =[]
|
4
|
+
|
5
|
+
attr_accessor :nodes
|
6
|
+
attr_accessor :classes
|
7
|
+
attr_accessor :options
|
8
|
+
|
9
|
+
# This can accept a bunch of stuff. It can accept nodes, classes or groups anywhere
|
10
|
+
# it will then detect them and expand them out into their respective objects so that
|
11
|
+
# we just end up with a list of nodes and classes
|
12
|
+
def initialize(on_these,test_this,options = {})
|
13
|
+
@nodes = []
|
14
|
+
@classes = []
|
15
|
+
@options = options
|
16
|
+
|
17
|
+
# Get the nodes we are working on
|
18
|
+
if Controlrepo::Group.find(on_these)
|
19
|
+
@nodes << Controlrepo::Group.find(on_these).members
|
20
|
+
elsif Controlrepo::Node.find(on_these)
|
21
|
+
@nodes << Controlrepo::Node.find(on_these)
|
22
|
+
else
|
23
|
+
raise "#{on_these} was not found in the list of nodes or groups!"
|
24
|
+
end
|
25
|
+
|
26
|
+
@nodes.flatten!
|
27
|
+
|
28
|
+
# Check that our nodes list contains only nodes
|
29
|
+
raise "#{@nodes} contained a non-node object." unless @nodes.all? { |item| item.is_a?(Controlrepo::Node) }
|
30
|
+
|
31
|
+
if test_this.is_a?(String)
|
32
|
+
# If we have just given a string then grab all the classes it corresponds to
|
33
|
+
if Controlrepo::Group.find(test_this)
|
34
|
+
@classes << Controlrepo::Group.find(test_this).members
|
35
|
+
elsif Controlrepo::Class.find(test_this)
|
36
|
+
@classes << Controlrepo::Class.find(test_this)
|
37
|
+
else
|
38
|
+
raise "#{test_this} was not found in the list of classes or groups!"
|
39
|
+
end
|
40
|
+
@classes.flatten!
|
41
|
+
elsif test_this.is_a?(Hash)
|
42
|
+
# If it is a hash we need to get creative
|
43
|
+
|
44
|
+
# Get all of the included classes and add them
|
45
|
+
if Controlrepo::Group.find(test_this['include'])
|
46
|
+
@classes << Controlrepo::Group.find(test_this['include']).members
|
47
|
+
elsif Controlrepo::Class.find(test_this['include'])
|
48
|
+
@classes << Controlrepo::Class.find(test_this['include'])
|
49
|
+
else
|
50
|
+
raise "#{test_this['include']} was not found in the list of classes or groups!"
|
51
|
+
end
|
52
|
+
@classes.flatten!
|
53
|
+
|
54
|
+
# Then remove any excluded ones
|
55
|
+
if Controlrepo::Group.find(test_this['exclude'])
|
56
|
+
Controlrepo::Group.find(test_this['exclude']).members.each do |clarse|
|
57
|
+
@classes.delete(clarse)
|
58
|
+
end
|
59
|
+
elsif Controlrepo::Class.find(test_this['exclude'])
|
60
|
+
@classes.delete(Controlrepo::Class.find(test_this['exclude']))
|
61
|
+
else
|
62
|
+
raise "#{test_this['exclude']} was not found in the list of classes or groups!"
|
63
|
+
end
|
64
|
+
elsif test_this.is_a?(Controlrepo::Class)
|
65
|
+
@classes << test_this
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def eql?(other)
|
70
|
+
(@nodes.sort.eql?(other.nodes.sort)) and (@classes.sort.eql?(other.classes.sort))
|
71
|
+
end
|
72
|
+
|
73
|
+
def to_s
|
74
|
+
class_msg = ""
|
75
|
+
node_msg = ""
|
76
|
+
if classes.count > 1
|
77
|
+
class_msg = "#{classes.count}_classes"
|
78
|
+
else
|
79
|
+
class_msg = classes[0].name
|
80
|
+
end
|
81
|
+
|
82
|
+
if nodes.count > 1
|
83
|
+
node_msg = "#{nodes.count}_nodes"
|
84
|
+
else
|
85
|
+
node_msg = nodes[0].name
|
86
|
+
end
|
87
|
+
|
88
|
+
"#{class_msg}_on_#{node_msg}"
|
89
|
+
end
|
90
|
+
|
91
|
+
def self.deduplicate(tests)
|
92
|
+
# This should take an array of tests and remove any duplicates from them
|
93
|
+
|
94
|
+
# this will be an array of arrays, or maybe hashes
|
95
|
+
combinations = []
|
96
|
+
new_tests = []
|
97
|
+
tests.each do |test|
|
98
|
+
test.nodes.each do |node|
|
99
|
+
test.classes.each do |cls|
|
100
|
+
combo = {node => cls}
|
101
|
+
unless combinations.member?(combo)
|
102
|
+
combinations << combo
|
103
|
+
new_tests << Controlrepo::Test.new(node,cls,test.options)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
# The array that this returns should be ephemeral, it does not
|
110
|
+
# represent anything defined in a controlrepo and should just
|
111
|
+
# be passed into the thing doing the testing and then killed,
|
112
|
+
# we don't want too many copies of the same shit going around
|
113
|
+
#
|
114
|
+
# Actually based on the way things are written I don't think this
|
115
|
+
# will duplicated node or class objects, just test objects,
|
116
|
+
# everything else is passed by reference
|
117
|
+
new_tests
|
118
|
+
end
|
119
|
+
|
120
|
+
def self.all
|
121
|
+
@@all
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
@@ -0,0 +1,136 @@
|
|
1
|
+
require 'controlrepo/class'
|
2
|
+
require 'controlrepo/node'
|
3
|
+
require 'controlrepo/group'
|
4
|
+
require 'controlrepo/test'
|
5
|
+
|
6
|
+
class Controlrepo
|
7
|
+
class TestConfig
|
8
|
+
require 'yaml'
|
9
|
+
|
10
|
+
attr_accessor :classes
|
11
|
+
attr_accessor :nodes
|
12
|
+
attr_accessor :groups
|
13
|
+
attr_accessor :tests
|
14
|
+
attr_accessor :environment
|
15
|
+
|
16
|
+
def initialize(file, environment = 'production')
|
17
|
+
begin
|
18
|
+
config = YAML.load(File.read(file))
|
19
|
+
rescue YAML::ParserError
|
20
|
+
raise "Could not parse the YAML file, check that it is valid YAML and that the encoding is correct"
|
21
|
+
end
|
22
|
+
|
23
|
+
@environment = environment
|
24
|
+
@classes = []
|
25
|
+
@nodes = []
|
26
|
+
@groups = []
|
27
|
+
@tests = []
|
28
|
+
|
29
|
+
config['classes'].each { |clarse| @classes << Controlrepo::Class.new(clarse) }
|
30
|
+
config['nodes'].each { |node| @nodes << Controlrepo::Node.new(node) }
|
31
|
+
config['groups'].each { |name, members| @groups << Controlrepo::Group.new(name, members) }
|
32
|
+
|
33
|
+
# Add the 'all_classes' and 'all_nodes' default groups
|
34
|
+
@groups << Controlrepo::Group.new('all_nodes',@nodes)
|
35
|
+
@groups << Controlrepo::Group.new('all_classes',@classes)
|
36
|
+
|
37
|
+
config['test_matrix'].each do |machines, roles|
|
38
|
+
# TODO: Work out some way to set per-test options like idempotency
|
39
|
+
@tests << Controlrepo::Test.new(machines,roles)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def r10k_deploy_local(repo = Controlrepo.new)
|
44
|
+
require 'controlrepo'
|
45
|
+
tempdir = Dir.mktmpdir('r10k')
|
46
|
+
repo.tempdir = tempdir
|
47
|
+
|
48
|
+
# Read in the config and change all the directories, then create them
|
49
|
+
r10k_config = repo.r10k_config
|
50
|
+
r10k_config[:cachedir] = "#{tempdir}#{r10k_config[:cachedir]}"
|
51
|
+
FileUtils::mkdir_p(r10k_config[:cachedir])
|
52
|
+
r10k_config[:sources].map do |name,source_settings|
|
53
|
+
source_settings["basedir"] = "#{tempdir}#{source_settings["basedir"]}"
|
54
|
+
FileUtils::mkdir_p(source_settings["basedir"])
|
55
|
+
# Yes, I realise this is going to set it many times
|
56
|
+
repo.temp_environmentpath = source_settings["basedir"]
|
57
|
+
end
|
58
|
+
File.write("#{tempdir}/r10k.yaml",r10k_config.to_yaml)
|
59
|
+
|
60
|
+
# Pull the trigger!
|
61
|
+
Dir.chdir(tempdir) do
|
62
|
+
`r10k deploy environment #{@environment} -p --color --config #{tempdir}/r10k.yaml --verbose`
|
63
|
+
end
|
64
|
+
|
65
|
+
# Return tempdir for use
|
66
|
+
tempdir
|
67
|
+
end
|
68
|
+
|
69
|
+
def write_spec_test(location, test)
|
70
|
+
# Use an ERB template to write a spec test
|
71
|
+
template_dir = File.expand_path('../../templates',File.dirname(__FILE__))
|
72
|
+
spec_template = File.read(File.expand_path('./test_spec.rb.erb',template_dir))
|
73
|
+
randomness = (0...6).map { (65 + rand(26)).chr }.join
|
74
|
+
File.write("#{location}/#{randomness}_#{test.to_s}_spec.rb",ERB.new(spec_template, nil, '-').result(binding))
|
75
|
+
end
|
76
|
+
|
77
|
+
def write_acceptance_test(location, test)
|
78
|
+
template_dir = File.expand_path('../../templates',File.dirname(__FILE__))
|
79
|
+
acc_test_template = File.read(File.expand_path('./acceptance_test_spec.rb.erb',template_dir))
|
80
|
+
raise 'We only support writing acceptance tests for one node at the moment' unless test.nodes.count == 1
|
81
|
+
randomness = (0...6).map { (65 + rand(26)).chr }.join
|
82
|
+
File.write("#{location}/#{randomness}_#{test.to_s}_spec.rb",ERB.new(acc_test_template, nil, '-').result(binding))
|
83
|
+
end
|
84
|
+
|
85
|
+
def write_spec_helper_acceptance(location, repo)
|
86
|
+
template_dir = File.expand_path('../../templates',File.dirname(__FILE__))
|
87
|
+
spec_heler_acc_template = File.read(File.expand_path('./spec_helper_acceptance.rb.erb',template_dir))
|
88
|
+
File.write("#{location}/spec_helper_acceptance.rb",ERB.new(spec_heler_acc_template, nil, '-').result(binding))
|
89
|
+
end
|
90
|
+
|
91
|
+
def write_rakefile(location, pattern)
|
92
|
+
template_dir = File.expand_path('../../templates',File.dirname(__FILE__))
|
93
|
+
rakefile_template = File.read(File.expand_path('./Rakefile.erb',template_dir))
|
94
|
+
File.write("#{location}/Rakefile",ERB.new(rakefile_template, nil, '-').result(binding))
|
95
|
+
end
|
96
|
+
|
97
|
+
def write_gemfile(location)
|
98
|
+
template_dir = File.expand_path('../../templates',File.dirname(__FILE__))
|
99
|
+
gemfile_template = File.read(File.expand_path('./Gemfile.erb',template_dir))
|
100
|
+
File.write("#{location}/Gemfile",ERB.new(gemfile_template, nil, '-').result(binding))
|
101
|
+
end
|
102
|
+
|
103
|
+
def write_spec_helper(location, repo)
|
104
|
+
environmentpath = repo.temp_environmentpath
|
105
|
+
modulepath = repo.config['modulepath']
|
106
|
+
modulepath.delete("$basemodulepath")
|
107
|
+
modulepath.map! do |path|
|
108
|
+
"#{environmentpath}/#{@environment}/#{path}"
|
109
|
+
end
|
110
|
+
modulepath = modulepath.join(":")
|
111
|
+
repo.temp_modulepath = modulepath
|
112
|
+
|
113
|
+
# Use an ERB template to write a spec test
|
114
|
+
template_dir = File.expand_path('../../templates',File.dirname(__FILE__))
|
115
|
+
spec_helper_template = File.read(File.expand_path('./spec_helper.rb.erb',template_dir))
|
116
|
+
File.write("#{location}/spec_helper.rb",ERB.new(spec_helper_template, nil, '-').result(binding))
|
117
|
+
end
|
118
|
+
|
119
|
+
def create_fixtures_symlinks(repo)
|
120
|
+
FileUtils.mkdir_p("#{repo.tempdir}/spec/fixtures/modules")
|
121
|
+
repo.temp_modulepath.split(':').each do |path|
|
122
|
+
Dir["#{path}/*"].each do |mod|
|
123
|
+
modulename = File.basename(mod)
|
124
|
+
FileUtils.ln_s(mod, "#{repo.tempdir}/spec/fixtures/modules/#{modulename}")
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
# TODO: Work out the best way to format the output
|
130
|
+
# TODO: Look into bundling bundler into the temp dir
|
131
|
+
# TODO: Write task for beaker tests *brace yourself* Dont forget about the beaker file you have
|
132
|
+
# TODO: Compare the outlout of the beaker helper that I wrote
|
133
|
+
# with the output from the templated tests, us ethe better one
|
134
|
+
# bearing in minf that beaker has logger options that could help
|
135
|
+
end
|
136
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
---
|
2
|
+
fixtures:
|
3
|
+
<% if symlinks.any? then -%>
|
4
|
+
symlinks:
|
5
|
+
<% symlinks.each do |link| -%>
|
6
|
+
<%= link['name'] %>: <%= link['dir'] %>
|
7
|
+
<% end -%>
|
8
|
+
<% end -%>
|
9
|
+
<% if repositories.any? then -%>
|
10
|
+
repositories:
|
11
|
+
<% repositories.each do |repo| -%>
|
12
|
+
<%= repo['name'] %>:
|
13
|
+
repo: <%= repo['repo'] %>
|
14
|
+
ref: <%= repo['ref'] %>
|
15
|
+
<% end -%>
|
16
|
+
<% end -%>
|
17
|
+
<% if forge_modules.any? then -%>
|
18
|
+
forge_modules:
|
19
|
+
<% forge_modules.each do |mod| -%>
|
20
|
+
<%= mod['name'] %>:
|
21
|
+
repo: <%= mod['repo'] %>
|
22
|
+
ref: <%= mod['ref'] %>
|
23
|
+
<% end -%>
|
24
|
+
<% end -%>
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'puppetlabs_spec_helper/rake_tasks'
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'spec_helper_acceptance'
|
2
|
+
|
3
|
+
<% test.nodes.each do |node| -%>
|
4
|
+
describe '<%= node.name %>' do
|
5
|
+
<% test.classes.each do |cls| %>
|
6
|
+
context 'when classified with <%= cls.name %>' do
|
7
|
+
role = '<%= cls.name %>'
|
8
|
+
apply_manifest(role, :catch_failures => true)
|
9
|
+
<% if test.options[:runs_for_idempotency] -%>
|
10
|
+
<% (test.options[:runs_for_idempotency] - 1).times do -%>
|
11
|
+
apply_manifest(role, :catch_failures => true)
|
12
|
+
<% end -%>
|
13
|
+
<% end -%>
|
14
|
+
apply_manifest(role, :catch_changes => true)
|
15
|
+
end
|
16
|
+
<% end %>
|
17
|
+
end
|
18
|
+
<% end %>
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'beaker-rspec'
|
2
|
+
|
3
|
+
scp_to hosts, '<%= repo.tempdir %>/etc', '/etc' # Check that this wil work recursively
|
4
|
+
|
5
|
+
RSpec.configure do |c|
|
6
|
+
# Project root
|
7
|
+
proj_root = File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
8
|
+
|
9
|
+
# Readable test descriptions
|
10
|
+
c.formatter = :documentation
|
11
|
+
|
12
|
+
# Configure all nodes in nodeset
|
13
|
+
#c.before :suite do
|
14
|
+
# # Install module
|
15
|
+
# puppet_module_install(:source => proj_root, :module_name => 'virtualbox')
|
16
|
+
# hosts.each do |host|
|
17
|
+
# on host, puppet('module','install','puppetlabs-stdlib'), { :acceptable_exit_codes => [0,1] }
|
18
|
+
# on host, puppet('module','install','puppetlabs-apt'), { :acceptable_exit_codes => [0,1] }
|
19
|
+
# on host, puppet('module','install','stahnma-epel'), { :acceptable_exit_codes => [0,1] }
|
20
|
+
# on host, puppet('module','install','camptocamp-archive'), { :acceptable_exit_codes => [0,1] }
|
21
|
+
# on host, puppet('module','install','darin-zypprepo'), { :acceptable_exit_codes => [0,1] }
|
22
|
+
# end
|
23
|
+
#end
|
24
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
<% test.classes.each do |cls| -%>
|
4
|
+
describe "<%= cls.name %>" do
|
5
|
+
<% test.nodes.each do |node| -%>
|
6
|
+
context "using fact set <%= node.name %>" do
|
7
|
+
let(:facts) { <%= node.fact_set %> }
|
8
|
+
it { should compile }
|
9
|
+
end
|
10
|
+
<% end -%>
|
11
|
+
end
|
12
|
+
|
13
|
+
<% end -%>
|
metadata
ADDED
@@ -0,0 +1,162 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: controlrepo
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Dylan Ratcliffe
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-10-19 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rake
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: json
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: beaker-rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rspec-puppet
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rspec
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: bundler
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: puppet
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :runtime
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
description: ''
|
112
|
+
email:
|
113
|
+
- dylan.ratcliffe@puppetlabs.com
|
114
|
+
executables: []
|
115
|
+
extensions: []
|
116
|
+
extra_rdoc_files: []
|
117
|
+
files:
|
118
|
+
- ".gitignore"
|
119
|
+
- README.md
|
120
|
+
- controlrepo.gemspec
|
121
|
+
- lib/controlrepo.rb
|
122
|
+
- lib/controlrepo/beaker.rb
|
123
|
+
- lib/controlrepo/class.rb
|
124
|
+
- lib/controlrepo/group.rb
|
125
|
+
- lib/controlrepo/node.rb
|
126
|
+
- lib/controlrepo/rake_tasks.rb
|
127
|
+
- lib/controlrepo/test.rb
|
128
|
+
- lib/controlrepo/testconfig.rb
|
129
|
+
- templates/.fixtures.yml.erb
|
130
|
+
- templates/Gemfile.erb
|
131
|
+
- templates/Rakefile.erb
|
132
|
+
- templates/acceptance_test_spec.rb.erb
|
133
|
+
- templates/nodeset.yaml.erb
|
134
|
+
- templates/spec_helper.rb.erb
|
135
|
+
- templates/spec_helper_acceptance.rb.erb
|
136
|
+
- templates/test_spec.rb.erb
|
137
|
+
homepage: ''
|
138
|
+
licenses:
|
139
|
+
- Apache-2.0
|
140
|
+
metadata: {}
|
141
|
+
post_install_message:
|
142
|
+
rdoc_options: []
|
143
|
+
require_paths:
|
144
|
+
- lib
|
145
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
146
|
+
requirements:
|
147
|
+
- - ">="
|
148
|
+
- !ruby/object:Gem::Version
|
149
|
+
version: '0'
|
150
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
151
|
+
requirements:
|
152
|
+
- - ">="
|
153
|
+
- !ruby/object:Gem::Version
|
154
|
+
version: '0'
|
155
|
+
requirements: []
|
156
|
+
rubyforge_project:
|
157
|
+
rubygems_version: 2.4.7
|
158
|
+
signing_key:
|
159
|
+
specification_version: 4
|
160
|
+
summary: ''
|
161
|
+
test_files: []
|
162
|
+
has_rdoc:
|