r10k 1.2.4 → 1.3.0rc1
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 +8 -8
- data/{CHANGELOG → CHANGELOG.mkd} +51 -41
- data/doc/dynamic-environments/configuration.mkd +1 -1
- data/doc/dynamic-environments/git-environments.markdown +19 -0
- data/doc/dynamic-environments/usage.mkd +6 -0
- data/lib/r10k/cli/deploy.rb +15 -0
- data/lib/r10k/cli/ext/logging.rb +0 -1
- data/lib/r10k/cli/module/deploy.rb +0 -1
- data/lib/r10k/cli/puppetfile.rb +2 -2
- data/lib/r10k/cli.rb +2 -16
- data/lib/r10k/deployment/environment.rb +9 -79
- data/lib/r10k/deployment/source.rb +15 -89
- data/lib/r10k/deployment.rb +13 -14
- data/lib/r10k/environment/base.rb +42 -0
- data/lib/r10k/environment/git.rb +79 -0
- data/lib/r10k/environment/svn.rb +73 -0
- data/lib/r10k/environment.rb +7 -0
- data/lib/r10k/execution.rb +0 -1
- data/lib/r10k/git/cache.rb +11 -5
- data/lib/r10k/git/repository.rb +1 -8
- data/lib/r10k/git/working_dir.rb +11 -34
- data/lib/r10k/git.rb +0 -1
- data/lib/r10k/instance_cache.rb +32 -0
- data/lib/r10k/keyed_factory.rb +39 -0
- data/lib/r10k/module/forge.rb +2 -3
- data/lib/r10k/module/svn.rb +0 -1
- data/lib/r10k/puppetfile.rb +0 -1
- data/lib/r10k/registry.rb +3 -31
- data/lib/r10k/source/base.rb +60 -0
- data/lib/r10k/source/git.rb +195 -0
- data/lib/r10k/source/svn.rb +140 -0
- data/lib/r10k/source.rb +39 -0
- data/lib/r10k/svn/remote.rb +48 -0
- data/lib/r10k/svn/working_dir.rb +0 -2
- data/lib/r10k/svn.rb +6 -0
- data/lib/r10k/task/deployment.rb +1 -2
- data/lib/r10k/task.rb +0 -2
- data/lib/r10k/task_runner.rb +0 -1
- data/lib/r10k/util/core_ext/hash_ext.rb +19 -0
- data/lib/r10k/util/subprocess.rb +0 -1
- data/lib/r10k/version.rb +1 -1
- data/lib/r10k.rb +1 -0
- data/spec/unit/deployment/environment_spec.rb +16 -15
- data/spec/unit/environment/git_spec.rb +81 -0
- data/spec/unit/environment/svn_spec.rb +76 -0
- data/spec/unit/git/repository_spec.rb +0 -10
- data/spec/unit/git/working_dir_spec.rb +1 -110
- data/spec/unit/{registry_spec.rb → instance_cache_spec.rb} +3 -3
- data/spec/unit/keyed_factory_spec.rb +51 -0
- data/spec/unit/source/git_spec.rb +274 -0
- data/spec/unit/source/svn_spec.rb +102 -0
- data/spec/unit/source_spec.rb +10 -0
- data/spec/unit/svn/remote_spec.rb +21 -0
- data/spec/unit/util/core_ext/hash_ext_spec.rb +63 -0
- metadata +36 -10
- data/lib/r10k/git/alternates.rb +0 -49
- data/spec/unit/git/alternates_spec.rb +0 -90
@@ -0,0 +1,79 @@
|
|
1
|
+
require 'r10k/logging'
|
2
|
+
require 'r10k/puppetfile'
|
3
|
+
require 'r10k/git/working_dir'
|
4
|
+
|
5
|
+
# This class implements an environment based on a Git branch.
|
6
|
+
#
|
7
|
+
# @since 1.3.0
|
8
|
+
class R10K::Environment::Git < R10K::Environment::Base
|
9
|
+
|
10
|
+
include R10K::Logging
|
11
|
+
|
12
|
+
# @!attribute [r] remote
|
13
|
+
# @return [String] The URL to the remote git repository
|
14
|
+
attr_reader :remote
|
15
|
+
|
16
|
+
# @!attribute [r] ref
|
17
|
+
# @return [String] The git reference to use for this environment
|
18
|
+
attr_reader :ref
|
19
|
+
|
20
|
+
# @!attribute [r] working_dir
|
21
|
+
# @api private
|
22
|
+
# @return [R10K::Git::WorkingDir] The git working directory backing this environment
|
23
|
+
attr_reader :working_dir
|
24
|
+
|
25
|
+
# @!attribute [r] puppetfile
|
26
|
+
# @api public
|
27
|
+
# @return [R10K::Puppetfile] The puppetfile instance associated with this environment
|
28
|
+
attr_reader :puppetfile
|
29
|
+
|
30
|
+
# Initialize the given SVN environment.
|
31
|
+
#
|
32
|
+
# @param name [String] The unique name describing this environment.
|
33
|
+
# @param basedir [String] The base directory where this environment will be created.
|
34
|
+
# @param dirname [String] The directory name for this environment.
|
35
|
+
# @param options [Hash] An additional set of options for this environment.
|
36
|
+
#
|
37
|
+
# @param options [String] :remote The URL to the remote git repository
|
38
|
+
# @param options [String] :ref The git reference to use for this environment
|
39
|
+
def initialize(name, basedir, dirname, options = {})
|
40
|
+
super
|
41
|
+
@remote = options[:remote]
|
42
|
+
@ref = options[:ref]
|
43
|
+
|
44
|
+
@working_dir = R10K::Git::WorkingDir.new(@ref, @remote, @basedir, @dirname)
|
45
|
+
@puppetfile = R10K::Puppetfile.new(@full_path)
|
46
|
+
end
|
47
|
+
|
48
|
+
# Clone or update the given Git environment.
|
49
|
+
#
|
50
|
+
# If the environment is being created for the first time, it will
|
51
|
+
# automatically update all modules to ensure that the environment is complete.
|
52
|
+
#
|
53
|
+
# @api public
|
54
|
+
# @return [void]
|
55
|
+
def sync
|
56
|
+
recursive_needed = !(@working_dir.cloned?)
|
57
|
+
@working_dir.sync
|
58
|
+
|
59
|
+
if recursive_needed
|
60
|
+
logger.debug "Environment #{@full_path} is a fresh clone; automatically updating modules."
|
61
|
+
sync_modules
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# @api private
|
66
|
+
def sync_modules
|
67
|
+
modules.each do |mod|
|
68
|
+
logger.debug "Deploying module #{mod.name}"
|
69
|
+
mod.sync
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
# @return [Array<R10K::Module::Base>] All modules defined in the Puppetfile
|
74
|
+
# associated with this environment.
|
75
|
+
def modules
|
76
|
+
@puppetfile.load
|
77
|
+
@puppetfile.modules
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'r10k/puppetfile'
|
2
|
+
require 'r10k/svn/working_dir'
|
3
|
+
|
4
|
+
# This class implements an environment based on an SVN branch.
|
5
|
+
#
|
6
|
+
# @since 1.3.0
|
7
|
+
class R10K::Environment::SVN < R10K::Environment::Base
|
8
|
+
|
9
|
+
include R10K::Logging
|
10
|
+
|
11
|
+
# @!attribute [r] remote
|
12
|
+
# @return [String] The URL to the remote SVN branch to check out
|
13
|
+
attr_reader :remote
|
14
|
+
|
15
|
+
# @!attribute [r] working_dir
|
16
|
+
# @api private
|
17
|
+
# @return [R10K::SVN::WorkingDir] The SVN working directory backing this environment
|
18
|
+
attr_reader :working_dir
|
19
|
+
|
20
|
+
# @!attribute [r] puppetfile
|
21
|
+
# @api public
|
22
|
+
# @return [R10K::Puppetfile] The puppetfile instance associated with this environment
|
23
|
+
attr_reader :puppetfile
|
24
|
+
|
25
|
+
# Initialize the given SVN environment.
|
26
|
+
#
|
27
|
+
# @param name [String] The unique name describing this environment.
|
28
|
+
# @param basedir [String] The base directory where this environment will be created.
|
29
|
+
# @param dirname [String] The directory name for this environment.
|
30
|
+
# @param options [Hash] An additional set of options for this environment.
|
31
|
+
#
|
32
|
+
# @param options [String] :remote The URL to the remote SVN branch to check out
|
33
|
+
def initialize(name, basedir, dirname, options = {})
|
34
|
+
super
|
35
|
+
|
36
|
+
@remote = options[:remote]
|
37
|
+
|
38
|
+
@working_dir = R10K::SVN::WorkingDir.new(Pathname.new(@full_path))
|
39
|
+
@puppetfile = R10K::Puppetfile.new(@full_path)
|
40
|
+
end
|
41
|
+
|
42
|
+
# Perform an initial checkout of the SVN repository or update the repository.
|
43
|
+
#
|
44
|
+
# If the environment is being created for the first time, it will
|
45
|
+
# automatically update all modules to ensure that the environment is complete.
|
46
|
+
#
|
47
|
+
# @api public
|
48
|
+
# @return [void]
|
49
|
+
def sync
|
50
|
+
if @working_dir.is_svn?
|
51
|
+
@working_dir.update
|
52
|
+
else
|
53
|
+
@working_dir.checkout(@remote)
|
54
|
+
logger.debug "Environment #{@full_path} is a fresh clone; automatically updating modules."
|
55
|
+
sync_modules
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# @return [Array<R10K::Module::Base>] All modules defined in the Puppetfile
|
60
|
+
# associated with this environment.
|
61
|
+
def modules
|
62
|
+
@puppetfile.load
|
63
|
+
@puppetfile.modules
|
64
|
+
end
|
65
|
+
|
66
|
+
# @api private
|
67
|
+
def sync_modules
|
68
|
+
modules.each do |mod|
|
69
|
+
logger.debug "Deploying module #{mod.name}"
|
70
|
+
mod.sync
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
data/lib/r10k/execution.rb
CHANGED
data/lib/r10k/git/cache.rb
CHANGED
@@ -1,5 +1,3 @@
|
|
1
|
-
require 'r10k/logging'
|
2
|
-
|
3
1
|
require 'r10k/git'
|
4
2
|
require 'r10k/git/repository'
|
5
3
|
|
@@ -15,12 +13,20 @@ class R10K::Git::Cache < R10K::Git::Repository
|
|
15
13
|
|
16
14
|
def_setting_attr :cache_root, File.expand_path(ENV['HOME'] ? '~/.r10k/git': '/root/.r10k/git')
|
17
15
|
|
18
|
-
|
19
|
-
|
16
|
+
# Lazily construct an instance cache for R10K::Git::Cache objects
|
17
|
+
# @api private
|
18
|
+
def self.instance_cache
|
19
|
+
@instance_cache ||= R10K::InstanceCache.new(self)
|
20
20
|
end
|
21
21
|
|
22
|
+
# Generate a new instance with the given remote or return an existing object
|
23
|
+
# with the given remote. This should be used over R10K::Git::Cache.new.
|
24
|
+
#
|
25
|
+
# @api public
|
26
|
+
# @param remote [String] The git remote to cache
|
27
|
+
# @return [R10K::Git::Cache] The requested cache object.
|
22
28
|
def self.generate(remote)
|
23
|
-
|
29
|
+
instance_cache.generate(remote)
|
24
30
|
end
|
25
31
|
|
26
32
|
include R10K::Logging
|
data/lib/r10k/git/repository.rb
CHANGED
@@ -84,7 +84,7 @@ class R10K::Git::Repository
|
|
84
84
|
|
85
85
|
ret = {}
|
86
86
|
output.stdout.each_line do |line|
|
87
|
-
next if line.match
|
87
|
+
next if line.match /\(push\)/
|
88
88
|
name, url, _ = line.split(/\s+/)
|
89
89
|
ret[name] = url
|
90
90
|
end
|
@@ -92,13 +92,6 @@ class R10K::Git::Repository
|
|
92
92
|
ret
|
93
93
|
end
|
94
94
|
|
95
|
-
def tags
|
96
|
-
entries = []
|
97
|
-
output = git(['tag', '-l'], :git_dir => @git_dir).stdout
|
98
|
-
output.each_line { |line| entries << line.chomp }
|
99
|
-
entries
|
100
|
-
end
|
101
|
-
|
102
95
|
private
|
103
96
|
|
104
97
|
# Fetch objects and refs from the given git remote
|
data/lib/r10k/git/working_dir.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
require 'forwardable'
|
2
|
-
require 'r10k/logging'
|
3
2
|
require 'r10k/git'
|
4
3
|
require 'r10k/git/cache'
|
5
4
|
|
@@ -41,8 +40,7 @@ class R10K::Git::WorkingDir < R10K::Git::Repository
|
|
41
40
|
@full_path = File.join(@basedir, @dirname)
|
42
41
|
@git_dir = File.join(@full_path, '.git')
|
43
42
|
|
44
|
-
@
|
45
|
-
@cache = R10K::Git::Cache.generate(@remote)
|
43
|
+
@cache = R10K::Git::Cache.generate(@remote)
|
46
44
|
|
47
45
|
if ref.is_a? String
|
48
46
|
@ref = R10K::Git::Ref.new(ref, self)
|
@@ -62,9 +60,7 @@ class R10K::Git::WorkingDir < R10K::Git::Repository
|
|
62
60
|
end
|
63
61
|
|
64
62
|
def update
|
65
|
-
|
66
|
-
|
67
|
-
if ref_needs_fetch?
|
63
|
+
if fetch?
|
68
64
|
fetch_from_cache
|
69
65
|
checkout(@ref)
|
70
66
|
elsif needs_checkout?
|
@@ -117,17 +113,22 @@ class R10K::Git::WorkingDir < R10K::Git::Repository
|
|
117
113
|
|
118
114
|
private
|
119
115
|
|
120
|
-
|
121
|
-
# @return [true, false]
|
122
|
-
def ref_needs_fetch?
|
116
|
+
def fetch?
|
123
117
|
@ref.fetch?
|
124
118
|
end
|
125
119
|
|
126
120
|
def fetch_from_cache
|
121
|
+
set_cache_remote
|
127
122
|
@cache.sync
|
128
123
|
fetch('cache')
|
129
124
|
end
|
130
125
|
|
126
|
+
def set_cache_remote
|
127
|
+
if self.remote != @cache.remote
|
128
|
+
git ["remote", "set-url", "cache", @cache.git_dir], :path => @full_path
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
131
132
|
# Perform a non-bare clone of a git repository.
|
132
133
|
def clone
|
133
134
|
@cache.sync
|
@@ -149,30 +150,6 @@ class R10K::Git::WorkingDir < R10K::Git::Repository
|
|
149
150
|
expected = ref.sha1
|
150
151
|
actual = rev_parse('HEAD')
|
151
152
|
|
152
|
-
!(expected == actual)
|
153
|
-
end
|
154
|
-
|
155
|
-
def update_remotes?
|
156
|
-
real_remotes = remotes
|
157
|
-
|
158
|
-
expected_origin = @remote
|
159
|
-
expected_cache = @cache.git_dir
|
160
|
-
|
161
|
-
!(expected_origin == real_remotes['origin'] and expected_cache == real_remotes['cache'])
|
162
|
-
end
|
163
|
-
|
164
|
-
def update_remotes
|
165
|
-
# todo: remove all existing refs as they may belong to the old remote
|
166
|
-
git ['remote', 'set-url', 'origin', remote], :path => @full_path
|
167
|
-
git ['remote', 'set-url', 'cache', @cache.git_dir], :path => @full_path
|
168
|
-
@alternates << File.join(@cache.git_dir, 'objects')
|
169
|
-
logger.debug("Removing stale git tags from #{@full_path}")
|
170
|
-
remove_tags
|
171
|
-
end
|
172
|
-
|
173
|
-
def remove_tags
|
174
|
-
tags.each do |tag|
|
175
|
-
git ['tag', '-d', tag], :path => @full_path
|
176
|
-
end
|
153
|
+
! (expected == actual)
|
177
154
|
end
|
178
155
|
end
|
data/lib/r10k/git.rb
CHANGED
@@ -0,0 +1,32 @@
|
|
1
|
+
module R10K
|
2
|
+
|
3
|
+
# This class implements a generic object memoization container. It caches
|
4
|
+
# new objects and returns cached objects based on the instantiation arguments.
|
5
|
+
class InstanceCache
|
6
|
+
|
7
|
+
# Initialize a new registry with a given class
|
8
|
+
#
|
9
|
+
# @param klass [Class] The class to memoize
|
10
|
+
# @param method [Symbol] The method name to use when creating objects.
|
11
|
+
# Defaults to :new.
|
12
|
+
def initialize(klass, method = :new)
|
13
|
+
@klass = klass
|
14
|
+
@method = method
|
15
|
+
@instances = {}
|
16
|
+
end
|
17
|
+
|
18
|
+
# Create a new object, or return a memoized object.
|
19
|
+
#
|
20
|
+
# @param args [*Object] The arguments to pass to the initialize method
|
21
|
+
#
|
22
|
+
# @return [Object] A memoized instance of the registered class
|
23
|
+
def generate(*args)
|
24
|
+
@instances[args] ||= @klass.send(@method, *args)
|
25
|
+
end
|
26
|
+
|
27
|
+
# Clear all memoized objects
|
28
|
+
def clear!
|
29
|
+
@instances = {}
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module R10K
|
2
|
+
|
3
|
+
# This implements a factory by storing classes indexed with a given key and
|
4
|
+
# creates objects based on that key.
|
5
|
+
class KeyedFactory
|
6
|
+
|
7
|
+
# @!attribute [r] implementations
|
8
|
+
# @return [Hash<Object, Class>] A hash of keys and the associated
|
9
|
+
# implementations that this factory can generate.
|
10
|
+
attr_reader :implementations
|
11
|
+
|
12
|
+
def initialize
|
13
|
+
@implementations = {}
|
14
|
+
end
|
15
|
+
|
16
|
+
def register(key, klass)
|
17
|
+
if @implementations.has_key?(key)
|
18
|
+
raise DuplicateImplementationError, "Class already registered for #{key}"
|
19
|
+
else
|
20
|
+
@implementations[key] = klass
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def retrieve(key)
|
25
|
+
@implementations[key]
|
26
|
+
end
|
27
|
+
|
28
|
+
def generate(key, *args)
|
29
|
+
if (impl = @implementations[key])
|
30
|
+
impl.new(*args)
|
31
|
+
else
|
32
|
+
raise UnknownImplementationError, "No class registered for #{key}"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
class DuplicateImplementationError < StandardError; end
|
37
|
+
class UnknownImplementationError < StandardError; end
|
38
|
+
end
|
39
|
+
end
|
data/lib/r10k/module/forge.rb
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
require 'r10k/module'
|
2
2
|
require 'r10k/errors'
|
3
|
-
require 'r10k/logging'
|
4
3
|
require 'r10k/module/metadata'
|
5
4
|
require 'r10k/util/subprocess'
|
6
5
|
require 'r10k/module_repository/forge'
|
@@ -126,7 +125,7 @@ class R10K::Module::Forge < R10K::Module::Base
|
|
126
125
|
cmd = []
|
127
126
|
cmd << 'install'
|
128
127
|
cmd << "--version=#{expected_version}" if expected_version
|
129
|
-
cmd << "--
|
128
|
+
cmd << "--ignore-dependencies"
|
130
129
|
cmd << @full_name
|
131
130
|
pmt cmd
|
132
131
|
end
|
@@ -135,7 +134,7 @@ class R10K::Module::Forge < R10K::Module::Base
|
|
135
134
|
cmd = []
|
136
135
|
cmd << 'upgrade'
|
137
136
|
cmd << "--version=#{expected_version}" if expected_version
|
138
|
-
cmd << "--
|
137
|
+
cmd << "--ignore-dependencies"
|
139
138
|
cmd << @full_name
|
140
139
|
pmt cmd
|
141
140
|
end
|
data/lib/r10k/module/svn.rb
CHANGED
data/lib/r10k/puppetfile.rb
CHANGED
data/lib/r10k/registry.rb
CHANGED
@@ -1,32 +1,4 @@
|
|
1
|
-
|
1
|
+
require 'r10k/instance_cache'
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
class Registry
|
6
|
-
|
7
|
-
# Initialize a new registry with a given class
|
8
|
-
#
|
9
|
-
# @param klass [Class] The class to memoize
|
10
|
-
# @param method [Symbol] The method name to use when creating objects.
|
11
|
-
# Defaults to :new.
|
12
|
-
def initialize(klass, method = :new)
|
13
|
-
@klass = klass
|
14
|
-
@method = method
|
15
|
-
@instances = {}
|
16
|
-
end
|
17
|
-
|
18
|
-
# Create a new object, or return a memoized object.
|
19
|
-
#
|
20
|
-
# @param args [*Object] The arguments to pass to the initialize method
|
21
|
-
#
|
22
|
-
# @return [Object] A memoized instance of the registered class
|
23
|
-
def generate(*args)
|
24
|
-
@instances[args] ||= @klass.send(@method, *args)
|
25
|
-
end
|
26
|
-
|
27
|
-
# Clear all memoized objects
|
28
|
-
def clear!
|
29
|
-
@instances = {}
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
3
|
+
# @deprecated
|
4
|
+
R10K::Registry = R10K::InstanceCache
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# This class defines a common interface for source implementations.
|
2
|
+
#
|
3
|
+
# @since 1.3.0
|
4
|
+
class R10K::Source::Base
|
5
|
+
|
6
|
+
# @!attribute [r] basedir
|
7
|
+
# @return [String] The path this source will place environments in
|
8
|
+
attr_reader :basedir
|
9
|
+
|
10
|
+
# @!attribute [r] name
|
11
|
+
# @return [String] The short name for this environment source
|
12
|
+
attr_reader :name
|
13
|
+
|
14
|
+
# @!attribute [r] prefix
|
15
|
+
# @return [true, false] Whether the source name should be prefixed to each
|
16
|
+
# environment basedir. Defaults to false
|
17
|
+
attr_reader :prefix
|
18
|
+
|
19
|
+
# Initialize the given source.
|
20
|
+
#
|
21
|
+
# @param name [String] The identifier for this source.
|
22
|
+
# @param basedir [String] The base directory where the generated environments will be created.
|
23
|
+
# @param options [Hash] An additional set of options for this source. The
|
24
|
+
# semantics of this hash may depend on the source implementation.
|
25
|
+
#
|
26
|
+
# @option options [Boolean] :prefix Whether to prefix the source name to the
|
27
|
+
# environment directory names. All sources should respect this option.
|
28
|
+
# Defaults to false.
|
29
|
+
def initialize(name, basedir, options = {})
|
30
|
+
@name = name
|
31
|
+
@basedir = basedir
|
32
|
+
@prefix = options.delete(:prefix)
|
33
|
+
@options = options
|
34
|
+
end
|
35
|
+
|
36
|
+
# Perform any actions needed for loading environments that may have side
|
37
|
+
# effects.
|
38
|
+
#
|
39
|
+
# Actions done during preloading may include things like updating caches or
|
40
|
+
# performing network queries. If an environment has not been preloaded but
|
41
|
+
# {#environments} is invoked, it should return the best known state of
|
42
|
+
# environments or return an empty list.
|
43
|
+
#
|
44
|
+
# @api public
|
45
|
+
# @abstract
|
46
|
+
# @return [void]
|
47
|
+
def preload!
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
# Enumerate the environments associated with this SVN source.
|
52
|
+
#
|
53
|
+
# @api public
|
54
|
+
# @abstract
|
55
|
+
# @return [Array<R10K::Environment::Base>] An array of environments created
|
56
|
+
# from this source.
|
57
|
+
def environments
|
58
|
+
raise NotImplementedError, "#{self.class} has not implemented method #{__method__}"
|
59
|
+
end
|
60
|
+
end
|