r10k 1.1.4 → 1.2.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/.gitignore +1 -0
- data/.nodeset.yml +7 -0
- data/.rspec +1 -0
- data/.travis.yml +2 -1
- data/CHANGELOG +17 -12
- data/Gemfile +8 -0
- data/README.markdown +4 -2
- data/Rakefile +1 -0
- data/doc/dynamic-environments.markdown +206 -0
- data/doc/puppetfile.markdown +87 -0
- data/lib/r10k/cli.rb +1 -1
- data/lib/r10k/errors.rb +30 -3
- data/lib/r10k/execution.rb +5 -2
- data/lib/r10k/git/cache.rb +26 -42
- data/lib/r10k/git/commit.rb +22 -0
- data/lib/r10k/git/errors.rb +31 -22
- data/lib/r10k/git/head.rb +33 -0
- data/lib/r10k/git/ref.rb +63 -0
- data/lib/r10k/git/repository.rb +65 -36
- data/lib/r10k/git/tag.rb +26 -0
- data/lib/r10k/git/working_dir.rb +93 -83
- data/lib/r10k/git.rb +14 -0
- data/lib/r10k/module/forge.rb +129 -62
- data/lib/r10k/module/git.rb +72 -6
- data/lib/r10k/module/metadata.rb +47 -0
- data/lib/r10k/module/svn.rb +99 -0
- data/lib/r10k/module.rb +1 -0
- data/lib/r10k/module_repository/forge.rb +64 -0
- data/lib/r10k/module_repository.rb +8 -0
- data/lib/r10k/semver.rb +1 -1
- data/lib/r10k/svn/working_dir.rb +76 -0
- data/lib/r10k/task/deployment.rb +21 -28
- data/lib/r10k/util/subprocess/io.rb +12 -0
- data/lib/r10k/util/subprocess/result.rb +36 -0
- data/lib/r10k/util/subprocess/runner.rb +88 -0
- data/lib/r10k/util/subprocess.rb +107 -0
- data/lib/r10k/version.rb +1 -1
- data/r10k.gemspec +11 -1
- data/spec/fixtures/vcr/cassettes/R10K_ModuleRepository_Forge/and_the_expected_version_is_latest/can_fetch_all_versions_of_a_given_module.yml +42 -0
- data/spec/fixtures/vcr/cassettes/R10K_ModuleRepository_Forge/and_the_expected_version_is_latest/can_fetch_the_latest_version_of_a_given_module.yml +42 -0
- data/spec/fixtures/vcr/cassettes/R10K_ModuleRepository_Forge/looking_up_versions/can_fetch_all_versions_of_a_given_module.yml +42 -0
- data/spec/fixtures/vcr/cassettes/R10K_ModuleRepository_Forge/looking_up_versions/can_fetch_the_latest_version_of_a_given_module.yml +42 -0
- data/spec/fixtures/vcr/cassettes/R10K_ModuleRepository_Forge/looking_up_versions.yml +42 -0
- data/spec/fixtures/vcr/cassettes/R10K_Module_Forge/and_the_expected_version_is_latest/sets_the_expected_version_based_on_the_latest_forge_version.yml +42 -0
- data/spec/rspec-system-r10k/puppetfile.rb +24 -0
- data/spec/rspec-system-r10k/tmpdir.rb +32 -0
- data/spec/shared-examples/git-ref.rb +49 -0
- data/spec/spec_helper.rb +23 -0
- data/spec/system/module/forge/install_spec.rb +51 -0
- data/spec/system/module/git/install_spec.rb +117 -0
- data/spec/system/module/svn/install_spec.rb +51 -0
- data/spec/system/module/svn/update_spec.rb +38 -0
- data/spec/system/spec_helper.rb +60 -0
- data/spec/system/system-helpers.rb +4 -0
- data/spec/system/version_spec.rb +7 -0
- data/spec/system-provisioning/el.rb +38 -0
- data/spec/unit/deployment/source_spec.rb +1 -1
- data/spec/unit/git/cache_spec.rb +38 -0
- data/spec/unit/git/commit_spec.rb +33 -0
- data/spec/unit/git/head_spec.rb +27 -0
- data/spec/unit/git/ref_spec.rb +68 -0
- data/spec/unit/git/tag_spec.rb +31 -0
- data/spec/unit/module/forge_spec.rb +157 -37
- data/spec/unit/module/git_spec.rb +49 -0
- data/spec/unit/module/metadata_spec.rb +68 -0
- data/spec/unit/module/svn_spec.rb +146 -0
- data/spec/unit/module_repository/forge_spec.rb +32 -0
- metadata +151 -8
data/lib/r10k/module/git.rb
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
require 'r10k/module'
|
2
|
-
require 'r10k/git
|
3
|
-
require 'forwardable'
|
2
|
+
require 'r10k/git'
|
4
3
|
|
5
4
|
class R10K::Module::Git < R10K::Module::Base
|
6
5
|
|
@@ -12,14 +11,17 @@ class R10K::Module::Git < R10K::Module::Base
|
|
12
11
|
false
|
13
12
|
end
|
14
13
|
|
15
|
-
|
16
|
-
|
14
|
+
# @!attribute [r] working_dir
|
15
|
+
# @api private
|
16
|
+
# @return [R10K::Git::WorkingDir]
|
17
|
+
attr_reader :working_dir
|
17
18
|
|
18
19
|
def initialize(name, basedir, args)
|
19
20
|
@name, @basedir, @args = name, basedir, args
|
20
21
|
|
21
|
-
|
22
|
-
|
22
|
+
parse_options(args)
|
23
|
+
|
24
|
+
@full_path = Pathname.new(File.join(basedir, name))
|
23
25
|
|
24
26
|
@working_dir = R10K::Git::WorkingDir.new(@ref, @remote, @basedir, @name)
|
25
27
|
end
|
@@ -27,4 +29,68 @@ class R10K::Module::Git < R10K::Module::Base
|
|
27
29
|
def version
|
28
30
|
@ref
|
29
31
|
end
|
32
|
+
|
33
|
+
def sync
|
34
|
+
case status
|
35
|
+
when :absent
|
36
|
+
install
|
37
|
+
when :mismatched
|
38
|
+
uninstall
|
39
|
+
install
|
40
|
+
when :outdated
|
41
|
+
@working_dir.sync
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def status
|
46
|
+
if not @working_dir.exist?
|
47
|
+
return :absent
|
48
|
+
elsif not @working_dir.git?
|
49
|
+
return :mismatched
|
50
|
+
elsif not @remote == @working_dir.remote
|
51
|
+
return :mismatched
|
52
|
+
end
|
53
|
+
|
54
|
+
if @working_dir.outdated?
|
55
|
+
return :outdated
|
56
|
+
end
|
57
|
+
|
58
|
+
return :insync
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
def install
|
64
|
+
@working_dir.sync
|
65
|
+
end
|
66
|
+
|
67
|
+
def uninstall
|
68
|
+
@full_path.rmtree
|
69
|
+
end
|
70
|
+
|
71
|
+
def parse_options(options)
|
72
|
+
@remote = options.delete(:git)
|
73
|
+
|
74
|
+
if options[:branch]
|
75
|
+
@ref = R10K::Git::Head.new(options.delete(:branch))
|
76
|
+
end
|
77
|
+
|
78
|
+
if options[:tag]
|
79
|
+
@ref = R10K::Git::Tag.new(options.delete(:tag))
|
80
|
+
end
|
81
|
+
|
82
|
+
if options[:commit]
|
83
|
+
@ref = R10K::Git::Commit.new(options.delete(:commit))
|
84
|
+
end
|
85
|
+
|
86
|
+
if options[:ref]
|
87
|
+
@ref = R10K::Git::Ref.new(options.delete(:ref))
|
88
|
+
end
|
89
|
+
|
90
|
+
@ref ||= R10K::Git::Ref.new('master')
|
91
|
+
|
92
|
+
unless options.empty?
|
93
|
+
raise ArgumentError, "Unhandled options #{options.keys.inspect} specified for #{self.class}"
|
94
|
+
end
|
95
|
+
end
|
30
96
|
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'r10k/module'
|
2
|
+
require 'r10k/semver'
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
class R10K::Module::Metadata
|
6
|
+
|
7
|
+
# @!attribute [r] name
|
8
|
+
# @return [String] The module name
|
9
|
+
attr_reader :name
|
10
|
+
|
11
|
+
# @!attribute [r] author
|
12
|
+
# @return [String] The module author username
|
13
|
+
attr_reader :author
|
14
|
+
|
15
|
+
# @!attribute [r] version
|
16
|
+
# @return [R10K::SemVer] The module version
|
17
|
+
attr_reader :version
|
18
|
+
|
19
|
+
# @param metadata_path [Pathname] The file path to the metadata
|
20
|
+
def initialize(metadata_path)
|
21
|
+
@metadata_path = metadata_path
|
22
|
+
|
23
|
+
@version = R10K::SemVer::MIN
|
24
|
+
end
|
25
|
+
|
26
|
+
# Does the metadata file itself exist?
|
27
|
+
def exist?
|
28
|
+
@metadata_path.file? and @metadata_path.readable?
|
29
|
+
end
|
30
|
+
|
31
|
+
# Attempt to read the metadata file
|
32
|
+
def read
|
33
|
+
if self.exist?
|
34
|
+
hash = JSON.parse(@metadata_path.read)
|
35
|
+
attributes_from_hash(hash)
|
36
|
+
end
|
37
|
+
rescue JSON::ParserError
|
38
|
+
false
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def attributes_from_hash(json)
|
44
|
+
@author, _, @name = json['name'].partition('-')
|
45
|
+
@version = R10K::SemVer.new(json['version'])
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
require 'r10k/module'
|
2
|
+
require 'r10k/execution'
|
3
|
+
require 'r10k/logging'
|
4
|
+
require 'r10k/svn/working_dir'
|
5
|
+
|
6
|
+
class R10K::Module::SVN < R10K::Module::Base
|
7
|
+
|
8
|
+
R10K::Module.register(self)
|
9
|
+
|
10
|
+
def self.implement?(name, args)
|
11
|
+
args.is_a? Hash and args.has_key? :svn
|
12
|
+
end
|
13
|
+
|
14
|
+
# @!attribute [r] expected_revision
|
15
|
+
# @return [String] The SVN revision that the repo should have checked out
|
16
|
+
attr_reader :expected_revision
|
17
|
+
alias expected_version expected_revision
|
18
|
+
|
19
|
+
# @!attribute [r] svn_path
|
20
|
+
# @return [String] The path inside of the SVN repository to have checked out
|
21
|
+
attr_reader :svn_path
|
22
|
+
|
23
|
+
# @!attribute [r] full_path
|
24
|
+
# @return [Pathname] The filesystem path to the SVN repo
|
25
|
+
attr_reader :full_path
|
26
|
+
|
27
|
+
def initialize(name, basedir, args)
|
28
|
+
@name = name
|
29
|
+
@basedir = basedir
|
30
|
+
|
31
|
+
parse_options(args)
|
32
|
+
|
33
|
+
@full_path = Pathname.new(File.join(@basedir, @name))
|
34
|
+
@working_dir = R10K::SVN::WorkingDir.new(@full_path)
|
35
|
+
end
|
36
|
+
|
37
|
+
def status
|
38
|
+
if not self.exist?
|
39
|
+
:absent
|
40
|
+
elsif not @working_dir.is_svn?
|
41
|
+
:mismatched
|
42
|
+
elsif not @url == @working_dir.url
|
43
|
+
:mismatched
|
44
|
+
elsif not @expected_revision == @working_dir.revision
|
45
|
+
:outdated
|
46
|
+
else
|
47
|
+
:insync
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def sync
|
52
|
+
case status
|
53
|
+
when :absent
|
54
|
+
install
|
55
|
+
when :mismatched
|
56
|
+
reinstall
|
57
|
+
when :outdated
|
58
|
+
update
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def exist?
|
63
|
+
@full_path.exist?
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
def install
|
69
|
+
FileUtils.mkdir @basedir unless File.directory? @basedir
|
70
|
+
|
71
|
+
@working_dir.checkout(@url, @expected_revision)
|
72
|
+
end
|
73
|
+
|
74
|
+
def uninstall
|
75
|
+
@full_path.rmtree
|
76
|
+
end
|
77
|
+
|
78
|
+
def reinstall
|
79
|
+
uninstall
|
80
|
+
install
|
81
|
+
end
|
82
|
+
|
83
|
+
def update
|
84
|
+
@working_dir.update(@expected_revision)
|
85
|
+
end
|
86
|
+
|
87
|
+
def parse_options(hash)
|
88
|
+
hash.each_pair do |key, value|
|
89
|
+
case key
|
90
|
+
when :svn
|
91
|
+
@url = value
|
92
|
+
when :rev, :revision
|
93
|
+
@expected_revision = value
|
94
|
+
when :svn_path
|
95
|
+
@svn_path = value
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
data/lib/r10k/module.rb
CHANGED
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'r10k/module_repository'
|
2
|
+
require 'r10k/version'
|
3
|
+
|
4
|
+
require 'faraday'
|
5
|
+
require 'faraday_middleware/multi_json'
|
6
|
+
require 'faraday_middleware'
|
7
|
+
|
8
|
+
class R10K::ModuleRepository::Forge
|
9
|
+
|
10
|
+
# @!attribute [r] forge
|
11
|
+
# @return [String] The forge hostname to use for requests
|
12
|
+
attr_reader :forge
|
13
|
+
|
14
|
+
# @!attribute [r] :conn
|
15
|
+
# @api private
|
16
|
+
# @return [Faraday]
|
17
|
+
attr_reader :conn
|
18
|
+
|
19
|
+
def initialize(forge = 'forge.puppetlabs.com')
|
20
|
+
@forge = forge
|
21
|
+
|
22
|
+
@conn = Faraday.new(
|
23
|
+
:url => "https://#{@forge}",
|
24
|
+
:user_agent => "Ruby/r10k #{R10K::VERSION}"
|
25
|
+
) do |builder|
|
26
|
+
builder.request :multi_json
|
27
|
+
builder.response :multi_json
|
28
|
+
|
29
|
+
# This needs to be _after_ request/response configuration for testing
|
30
|
+
# purposes. This comment is the result of much consternation.
|
31
|
+
builder.adapter Faraday.default_adapter
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# Query for all published versions of a module
|
36
|
+
#
|
37
|
+
# @example
|
38
|
+
# forge = R10K::ModuleRepository::Forge.new
|
39
|
+
# forge.versions('adrien/boolean')
|
40
|
+
# #=> ["0.9.0-rc1", "0.9.0", "1.0.0", "1.0.1"]
|
41
|
+
#
|
42
|
+
# @param module_name [String] The fully qualified module name
|
43
|
+
# @return [Array<String>] All published versions of the given module
|
44
|
+
def versions(module_name)
|
45
|
+
response = @conn.get("/api/v1/releases.json", {'module' => module_name})
|
46
|
+
|
47
|
+
response.body[module_name].map do |version_info|
|
48
|
+
version_info['version']
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# Query for the newest published version of a module
|
53
|
+
#
|
54
|
+
# @example
|
55
|
+
# forge = R10K::ModuleRepository::Forge.new
|
56
|
+
# forge.latest_version('adrien/boolean')
|
57
|
+
# #=> "1.0.1"
|
58
|
+
#
|
59
|
+
# @param module_name [String] The fully qualified module name
|
60
|
+
# @return [String] The latest published version of the given module
|
61
|
+
def latest_version(module_name)
|
62
|
+
versions(module_name).last
|
63
|
+
end
|
64
|
+
end
|
data/lib/r10k/semver.rb
CHANGED
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'r10k/logging'
|
2
|
+
require 'r10k/util/subprocess'
|
3
|
+
|
4
|
+
module R10K
|
5
|
+
module SVN
|
6
|
+
class WorkingDir
|
7
|
+
|
8
|
+
# @param full_path [Pathname]
|
9
|
+
def initialize(full_path)
|
10
|
+
@full_path = full_path
|
11
|
+
end
|
12
|
+
|
13
|
+
def is_svn?
|
14
|
+
dot_svn = @full_path + '.svn'
|
15
|
+
dot_svn.exist?
|
16
|
+
end
|
17
|
+
|
18
|
+
def revision
|
19
|
+
info.slice(/^Revision: (\d+)$/, 1)
|
20
|
+
end
|
21
|
+
|
22
|
+
def url
|
23
|
+
info.slice(/^URL: (.*)$/, 1)
|
24
|
+
end
|
25
|
+
|
26
|
+
def root
|
27
|
+
info.slice(/^Repository Root: (.*)$/, 1)
|
28
|
+
end
|
29
|
+
|
30
|
+
def update(revision = nil)
|
31
|
+
argv = %w[update]
|
32
|
+
argv << '-r' << revision if revision
|
33
|
+
|
34
|
+
svn argv, :cwd => @full_path
|
35
|
+
end
|
36
|
+
|
37
|
+
def checkout(url, revision = nil)
|
38
|
+
argv = ['checkout', url]
|
39
|
+
argv << '-r' << revision if revision
|
40
|
+
argv << @full_path.basename.to_s
|
41
|
+
|
42
|
+
svn argv, :cwd => @full_path.parent
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def info
|
48
|
+
svn ["info"], :cwd => @full_path
|
49
|
+
end
|
50
|
+
|
51
|
+
include R10K::Execution
|
52
|
+
include R10K::Logging
|
53
|
+
|
54
|
+
# Wrap SVN commands
|
55
|
+
#
|
56
|
+
# @param argv [Array<String>]
|
57
|
+
# @param opts [Hash]
|
58
|
+
#
|
59
|
+
# @option opts [Pathname] :cwd The directory to run the command in
|
60
|
+
#
|
61
|
+
# @return [String] The stdout from the given command
|
62
|
+
def svn(argv, opts = {})
|
63
|
+
argv.unshift('svn')
|
64
|
+
|
65
|
+
subproc = R10K::Util::Subprocess.new(argv)
|
66
|
+
subproc.raise_on_fail = true
|
67
|
+
subproc.logger = self.logger
|
68
|
+
|
69
|
+
subproc.cwd = opts[:cwd]
|
70
|
+
result = subproc.execute
|
71
|
+
|
72
|
+
result.stdout
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
data/lib/r10k/task/deployment.rb
CHANGED
@@ -11,39 +11,32 @@ module Deployment
|
|
11
11
|
|
12
12
|
private
|
13
13
|
|
14
|
-
def
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
all_environments = @deployment.environments
|
19
|
-
if names.empty?
|
20
|
-
active = all_environments
|
21
|
-
else
|
22
|
-
# This has average case O(N^2) but N should remain relatively small, so
|
23
|
-
# while this should be optimized that optimization can wait a while.
|
24
|
-
names.each do |env_name|
|
25
|
-
|
26
|
-
matching = all_environments.select do |env|
|
27
|
-
env.dirname == env_name
|
28
|
-
end
|
29
|
-
|
30
|
-
if matching.empty?
|
31
|
-
logger.warn "Environment #{env_name} not found in any source"
|
32
|
-
task_runner.succeeded = false
|
33
|
-
else
|
34
|
-
active.concat(matching)
|
35
|
-
end
|
36
|
-
end
|
14
|
+
def load_environments!
|
15
|
+
@environments = @deployment.environments.inject({}) do |hash, env|
|
16
|
+
hash[env.dirname] = env
|
17
|
+
hash
|
37
18
|
end
|
38
|
-
|
39
|
-
active
|
40
19
|
end
|
41
20
|
|
42
21
|
# @param [Array<String>] names The list of environments to deploy.
|
43
22
|
#
|
44
|
-
def with_environments(names = [])
|
45
|
-
|
46
|
-
|
23
|
+
def with_environments(names = [], &block)
|
24
|
+
load_environments!
|
25
|
+
|
26
|
+
# If an explicit list of environments were not given, deploy everything
|
27
|
+
if names.size > 0
|
28
|
+
to_deploy = names
|
29
|
+
else
|
30
|
+
to_deploy = @environments.keys
|
31
|
+
end
|
32
|
+
|
33
|
+
to_deploy.reverse.each do |env_name|
|
34
|
+
if (env = @environments[env_name])
|
35
|
+
yield env
|
36
|
+
else
|
37
|
+
logger.warn "Environment #{env_name} not found in any source"
|
38
|
+
task_runner.succeeded = false
|
39
|
+
end
|
47
40
|
end
|
48
41
|
end
|
49
42
|
end
|