r10k 1.1.4 → 1.2.0rc1
Sign up to get free protection for your applications and to get access to all the features.
- 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/git/tag.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'r10k/git/ref'
|
2
|
+
require 'r10k/git/repository'
|
3
|
+
|
4
|
+
# tag: A ref under refs/tags/ namespace that points to an object of an
|
5
|
+
# arbitrary type (typically a tag points to either a tag or a commit object).
|
6
|
+
# In contrast to a head, a tag is not updated by the commit command. A tag is
|
7
|
+
# most typically used to mark a particular point in the commit ancestry chain.
|
8
|
+
#
|
9
|
+
# @see https://www.kernel.org/pub/software/scm/git/docs/gitglossary.html
|
10
|
+
# @api private
|
11
|
+
class R10K::Git::Tag < R10K::Git::Ref
|
12
|
+
|
13
|
+
# @!attribute [r] tag
|
14
|
+
# @return [String] The git tag
|
15
|
+
attr_reader :tag
|
16
|
+
alias :ref :tag
|
17
|
+
|
18
|
+
def initialize(tag, repository = nil)
|
19
|
+
@tag = tag
|
20
|
+
@repository = repository
|
21
|
+
end
|
22
|
+
|
23
|
+
def fetch?
|
24
|
+
! resolvable?
|
25
|
+
end
|
26
|
+
end
|
data/lib/r10k/git/working_dir.rb
CHANGED
@@ -1,15 +1,14 @@
|
|
1
1
|
require 'forwardable'
|
2
2
|
require 'r10k/logging'
|
3
|
+
require 'r10k/git'
|
3
4
|
require 'r10k/git/cache'
|
4
5
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
# working directories only contain checked out files and all object files are
|
12
|
-
# shared.
|
6
|
+
# Implements sparse git repositories with shared objects
|
7
|
+
#
|
8
|
+
# Working directory instances use the git alternatives object store, so that
|
9
|
+
# working directories only contain checked out files and all object files are
|
10
|
+
# shared.
|
11
|
+
class R10K::Git::WorkingDir < R10K::Git::Repository
|
13
12
|
|
14
13
|
include R10K::Logging
|
15
14
|
|
@@ -20,124 +19,135 @@ class WorkingDir < R10K::Git::Repository
|
|
20
19
|
attr_reader :cache
|
21
20
|
|
22
21
|
# @!attribute [r] ref
|
23
|
-
# @return [
|
22
|
+
# @return [R10K::Git::Ref] The git reference to use check out in the given directory
|
24
23
|
attr_reader :ref
|
25
24
|
|
26
|
-
#
|
25
|
+
# @!attribute [r] remote
|
26
|
+
# @return [String] The actual remote used as an upstream for this module.
|
27
|
+
attr_reader :remote
|
28
|
+
|
29
|
+
# Create a new shallow git working directory
|
27
30
|
#
|
28
|
-
# @param [String]
|
29
|
-
# @param [String]
|
30
|
-
# @param [String]
|
31
|
-
# @param [String]
|
31
|
+
# @param ref [String, R10K::Git::Ref]
|
32
|
+
# @param remote [String]
|
33
|
+
# @param basedir [String]
|
34
|
+
# @param dirname [String]
|
32
35
|
def initialize(ref, remote, basedir, dirname = nil)
|
33
|
-
|
36
|
+
|
34
37
|
@remote = remote
|
35
38
|
@basedir = basedir
|
36
39
|
@dirname = dirname || ref
|
37
40
|
|
38
41
|
@full_path = File.join(@basedir, @dirname)
|
42
|
+
@git_dir = File.join(@full_path, '.git')
|
39
43
|
|
40
44
|
@cache = R10K::Git::Cache.generate(@remote)
|
45
|
+
|
46
|
+
if ref.is_a? String
|
47
|
+
@ref = R10K::Git::Ref.new(ref, self)
|
48
|
+
else
|
49
|
+
@ref = ref
|
50
|
+
@ref.repository = self
|
51
|
+
end
|
41
52
|
end
|
42
53
|
|
43
54
|
# Synchronize the local git repository.
|
44
55
|
def sync
|
45
|
-
|
46
|
-
@cache.sync
|
47
|
-
|
48
|
-
if cloned?
|
49
|
-
fetch
|
50
|
-
else
|
56
|
+
if not cloned?
|
51
57
|
clone
|
58
|
+
elsif fetch?
|
59
|
+
fetch_from_cache
|
60
|
+
checkout(@ref)
|
61
|
+
elsif needs_checkout?
|
62
|
+
checkout(@ref)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def update
|
67
|
+
if fetch?
|
68
|
+
fetch_from_cache
|
69
|
+
checkout(@ref)
|
70
|
+
elsif needs_checkout?
|
71
|
+
checkout(@ref)
|
52
72
|
end
|
53
|
-
reset
|
54
73
|
end
|
55
74
|
|
56
75
|
# Determine if repo has been cloned into a specific dir
|
57
76
|
#
|
58
77
|
# @return [true, false] If the repo has already been cloned
|
59
78
|
def cloned?
|
60
|
-
File.directory? git_dir
|
79
|
+
File.directory? @git_dir
|
61
80
|
end
|
81
|
+
alias :git? :cloned?
|
62
82
|
|
63
|
-
|
83
|
+
# Does a directory exist where we expect a working dir to be?
|
84
|
+
# @return [true, false]
|
85
|
+
def exist?
|
86
|
+
File.directory? @full_path
|
87
|
+
end
|
64
88
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
89
|
+
# check out the given ref
|
90
|
+
#
|
91
|
+
# @param ref [R10K::Git::Ref] The git reference to check out
|
92
|
+
def checkout(ref)
|
93
|
+
if ref.resolvable?
|
94
|
+
git ["checkout", "--force", @ref.sha1], :path => @full_path
|
70
95
|
else
|
71
|
-
|
96
|
+
raise R10K::Git::UnresolvableRefError.new(
|
97
|
+
"Cannot check out unresolvable ref '#{@ref}'",
|
98
|
+
:git_dir => @full_path)
|
72
99
|
end
|
73
100
|
end
|
74
101
|
|
75
|
-
#
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
git "remote add cache #{@cache.path}", :path => @full_path
|
81
|
-
git "checkout #{@ref}", :path => @full_path
|
102
|
+
# The currently checked out HEAD
|
103
|
+
#
|
104
|
+
# @return [R10k::Git::Head]
|
105
|
+
def current
|
106
|
+
R10K::Git::Head.new('HEAD', self)
|
82
107
|
end
|
83
108
|
|
84
|
-
def
|
85
|
-
|
86
|
-
git "fetch --prune cache", :path => @full_path
|
109
|
+
def outdated?
|
110
|
+
@ref.fetch? or needs_checkout?
|
87
111
|
end
|
88
112
|
|
89
|
-
|
90
|
-
def reset
|
91
|
-
commit = cache.rev_parse(@ref)
|
92
|
-
current = rev_parse('HEAD')
|
93
|
-
|
94
|
-
if commit == current
|
95
|
-
logger.debug1 "Git repo #{@full_path} is already at #{commit}, no need to reset"
|
96
|
-
return
|
97
|
-
end
|
113
|
+
private
|
98
114
|
|
99
|
-
|
100
|
-
|
101
|
-
rescue R10K::ExecutionFailure => e
|
102
|
-
logger.error "Unable to locate commit object #{commit} in git repo #{@full_path}"
|
103
|
-
raise
|
104
|
-
end
|
115
|
+
def fetch?
|
116
|
+
@ref.fetch?
|
105
117
|
end
|
106
118
|
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
# @return [String] The dereferenced hash of `ref`
|
112
|
-
def rev_parse(ref)
|
113
|
-
commit = git "rev-parse #{ref}^{commit}", :path => @full_path
|
114
|
-
commit.chomp
|
115
|
-
rescue R10K::ExecutionFailure => e
|
116
|
-
logger.error "Could not resolve ref #{ref.inspect} for git repo #{@full_path}"
|
117
|
-
raise
|
119
|
+
def fetch_from_cache
|
120
|
+
set_cache_remote
|
121
|
+
@cache.sync
|
122
|
+
fetch('cache')
|
118
123
|
end
|
119
124
|
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
125
|
+
def set_cache_remote
|
126
|
+
if self.remote != @cache.remote
|
127
|
+
git ["remote", "set-url", "cache", @cache.git_dir], :path => @full_path
|
128
|
+
end
|
129
|
+
end
|
124
130
|
|
125
|
-
|
131
|
+
# Perform a non-bare clone of a git repository.
|
132
|
+
def clone
|
133
|
+
@cache.sync
|
126
134
|
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
end
|
135
|
+
# We do the clone against the target repo using the `--reference` flag so
|
136
|
+
# that doing a normal `git pull` on a directory will work.
|
137
|
+
git ["clone", "--reference", @cache.git_dir, @remote, @full_path]
|
138
|
+
git ["remote", "add", "cache", @cache.git_dir], :path => @full_path
|
139
|
+
checkout(@ref)
|
140
|
+
end
|
134
141
|
|
135
|
-
|
142
|
+
def needs_fetch?
|
143
|
+
ref.fetch?
|
136
144
|
end
|
137
145
|
|
138
|
-
|
139
|
-
|
146
|
+
# Does the expected ref match the actual ref?
|
147
|
+
def needs_checkout?
|
148
|
+
expected = ref.sha1
|
149
|
+
actual = rev_parse('HEAD')
|
150
|
+
|
151
|
+
! (expected == actual)
|
140
152
|
end
|
141
153
|
end
|
142
|
-
end
|
143
|
-
end
|
data/lib/r10k/git.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
module R10K
|
2
|
+
module Git
|
3
|
+
require 'r10k/git/errors'
|
4
|
+
|
5
|
+
require 'r10k/git/ref'
|
6
|
+
require 'r10k/git/tag'
|
7
|
+
require 'r10k/git/head'
|
8
|
+
require 'r10k/git/commit'
|
9
|
+
|
10
|
+
require 'r10k/git/repository'
|
11
|
+
require 'r10k/git/cache'
|
12
|
+
require 'r10k/git/working_dir'
|
13
|
+
end
|
14
|
+
end
|
data/lib/r10k/module/forge.rb
CHANGED
@@ -1,11 +1,13 @@
|
|
1
1
|
require 'r10k/module'
|
2
2
|
require 'r10k/errors'
|
3
3
|
require 'r10k/logging'
|
4
|
+
require 'r10k/module/metadata'
|
5
|
+
require 'r10k/util/subprocess'
|
6
|
+
require 'r10k/module_repository/forge'
|
4
7
|
|
8
|
+
require 'pathname'
|
5
9
|
require 'fileutils'
|
6
|
-
require 'systemu'
|
7
10
|
require 'r10k/semver'
|
8
|
-
require 'json'
|
9
11
|
|
10
12
|
class R10K::Module::Forge < R10K::Module::Base
|
11
13
|
|
@@ -17,91 +19,156 @@ class R10K::Module::Forge < R10K::Module::Base
|
|
17
19
|
|
18
20
|
include R10K::Logging
|
19
21
|
|
20
|
-
|
22
|
+
# @!attribute [r] author
|
23
|
+
# @return [String] The Forge module author
|
24
|
+
attr_reader :author
|
21
25
|
|
22
|
-
|
23
|
-
|
26
|
+
# @deprecated
|
27
|
+
def owner
|
28
|
+
logger.warn "#{self.inspect}#owner is deprecated; use #author instead"
|
29
|
+
@author
|
30
|
+
end
|
31
|
+
|
32
|
+
# @!attribute [r] full_name
|
33
|
+
# @return [String] The fully qualified module name
|
34
|
+
attr_reader :full_name
|
35
|
+
|
36
|
+
def initialize(full_name, basedir, args)
|
37
|
+
@full_name = full_name
|
24
38
|
@basedir = basedir
|
25
39
|
|
26
|
-
@
|
40
|
+
@author, @name = full_name.split('/')
|
41
|
+
|
42
|
+
@full_path = Pathname.new(File.join(@basedir, @name))
|
43
|
+
|
44
|
+
@metadata = R10K::Module::Metadata.new(@full_path + 'metadata.json')
|
27
45
|
|
28
46
|
if args.is_a? String
|
29
|
-
@
|
47
|
+
@expected_version = R10K::SemVer.new(args)
|
48
|
+
elsif args.is_a? Symbol and args == :latest
|
49
|
+
@expected_version = args
|
30
50
|
end
|
31
51
|
end
|
32
52
|
|
33
53
|
def sync(options = {})
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
# A Pulp based puppetforge http://www.pulpproject.org/ wont support
|
42
|
-
# `puppet module install abc/xyz --version=v1.5.9` but puppetlabs forge
|
43
|
-
# will support `puppet module install abc/xyz --version=1.5.9`
|
44
|
-
#
|
45
|
-
# Removing v from the semver for constructing the command ensures
|
46
|
-
# compatibility across both
|
47
|
-
cmd = []
|
48
|
-
cmd << 'upgrade'
|
49
|
-
cmd << "--version=#{@version.to_s.sub(/^v/,'')}" if @version
|
50
|
-
cmd << "--ignore-dependencies"
|
51
|
-
cmd << @full_name
|
52
|
-
pmt cmd
|
53
|
-
else
|
54
|
-
FileUtils.mkdir @basedir unless File.directory? @basedir
|
55
|
-
#logger.debug "Module #{@full_name} is not installed"
|
56
|
-
cmd = []
|
57
|
-
cmd << 'install'
|
58
|
-
cmd << "--version=#{@version.to_s.sub(/^v/,'')}" if @version
|
59
|
-
cmd << "--ignore-dependencies"
|
60
|
-
cmd << @full_name
|
61
|
-
pmt cmd
|
54
|
+
case status
|
55
|
+
when :absent
|
56
|
+
install
|
57
|
+
when :outdated
|
58
|
+
upgrade
|
59
|
+
when :mismatched
|
60
|
+
reinstall
|
62
61
|
end
|
63
62
|
end
|
64
63
|
|
65
|
-
# @return [R10K::SemVer
|
66
|
-
def
|
67
|
-
if
|
68
|
-
|
69
|
-
else
|
70
|
-
R10K::SemVer::MIN
|
64
|
+
# @return [R10K::SemVer] The expected version that the module
|
65
|
+
def expected_version
|
66
|
+
if @expected_version == :latest
|
67
|
+
set_version_from_forge
|
71
68
|
end
|
69
|
+
@expected_version
|
72
70
|
end
|
73
71
|
|
74
|
-
|
75
|
-
|
72
|
+
|
73
|
+
# @return [R10K::SemVer] The version of the currently installed module
|
74
|
+
def current_version
|
75
|
+
@metadata.version
|
76
76
|
end
|
77
77
|
|
78
|
-
|
79
|
-
|
78
|
+
alias version current_version
|
79
|
+
|
80
|
+
def exist?
|
81
|
+
@full_path.exist?
|
80
82
|
end
|
81
83
|
|
82
|
-
def
|
83
|
-
|
84
|
+
def insync?
|
85
|
+
status == :insync
|
86
|
+
end
|
87
|
+
|
88
|
+
# Determine the status of the forge module.
|
89
|
+
#
|
90
|
+
# @return [Symbol] :absent If the directory doesn't exist
|
91
|
+
# @return [Symbol] :mismatched If the module is not a forge module, or
|
92
|
+
# isn't the right forge module
|
93
|
+
# @return [Symbol] :outdated If the installed module is older than expected
|
94
|
+
# @return [Symbol] :insync If the module is in the desired state
|
95
|
+
def status
|
96
|
+
if not self.exist?
|
97
|
+
# The module is not installed
|
98
|
+
return :absent
|
99
|
+
elsif not @metadata.exist?
|
100
|
+
# The directory exists but doesn't have a metadata file; it probably
|
101
|
+
# isn't a forge module.
|
102
|
+
return :mismatched
|
103
|
+
end
|
104
|
+
|
105
|
+
# The module is present and has a metadata file, read the metadata to
|
106
|
+
# determine the state of the module.
|
107
|
+
@metadata.read
|
108
|
+
|
109
|
+
if not @author == @metadata.author
|
110
|
+
# This is a forge module but the installed module is a different author
|
111
|
+
# than the expected author.
|
112
|
+
return :mismatched
|
113
|
+
end
|
114
|
+
|
115
|
+
if expected_version && (expected_version != @metadata.version)
|
116
|
+
return :outdated
|
117
|
+
end
|
118
|
+
|
119
|
+
return :insync
|
84
120
|
end
|
85
121
|
|
86
122
|
private
|
87
123
|
|
88
|
-
def
|
89
|
-
|
90
|
-
|
91
|
-
|
124
|
+
def install
|
125
|
+
FileUtils.mkdir @basedir unless File.directory? @basedir
|
126
|
+
cmd = []
|
127
|
+
cmd << 'install'
|
128
|
+
cmd << "--version=#{expected_version}" if expected_version
|
129
|
+
cmd << "--ignore-dependencies"
|
130
|
+
cmd << @full_name
|
131
|
+
pmt cmd
|
132
|
+
end
|
92
133
|
|
93
|
-
|
134
|
+
def upgrade
|
135
|
+
cmd = []
|
136
|
+
cmd << 'upgrade'
|
137
|
+
cmd << "--version=#{expected_version}" if expected_version
|
138
|
+
cmd << "--ignore-dependencies"
|
139
|
+
cmd << @full_name
|
140
|
+
pmt cmd
|
141
|
+
end
|
94
142
|
|
95
|
-
|
96
|
-
|
143
|
+
def uninstall
|
144
|
+
FileUtils.rm_rf full_path
|
145
|
+
end
|
97
146
|
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
147
|
+
def reinstall
|
148
|
+
uninstall
|
149
|
+
install
|
150
|
+
end
|
151
|
+
|
152
|
+
# Wrap puppet module commands
|
153
|
+
#
|
154
|
+
# @param argv [Array<String>]
|
155
|
+
#
|
156
|
+
# @return [String] The stdout from the executed command
|
157
|
+
def pmt(argv)
|
158
|
+
argv = ['puppet', 'module', '--modulepath', @basedir] + argv
|
159
|
+
|
160
|
+
subproc = R10K::Util::Subprocess.new(argv)
|
161
|
+
subproc.raise_on_fail = true
|
162
|
+
subproc.logger = self.logger
|
163
|
+
|
164
|
+
result = subproc.execute
|
165
|
+
|
166
|
+
result.stdout
|
167
|
+
end
|
168
|
+
|
169
|
+
def set_version_from_forge
|
170
|
+
repo = R10K::ModuleRepository::Forge.new
|
171
|
+
expected = repo.latest_version(@full_name)
|
172
|
+
@expected_version = R10K::SemVer.new(expected)
|
106
173
|
end
|
107
174
|
end
|