r10k 3.9.1 → 3.11.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 +4 -4
- data/.github/workflows/rspec_tests.yml +1 -1
- data/.travis.yml +0 -10
- data/CHANGELOG.mkd +28 -0
- data/README.mkd +6 -0
- data/doc/dynamic-environments/configuration.mkd +21 -0
- data/doc/puppetfile.mkd +15 -1
- data/integration/Rakefile +3 -1
- data/integration/tests/user_scenario/basic_workflow/negative/neg_specify_deleted_forge_module.rb +3 -9
- data/integration/tests/user_scenario/basic_workflow/single_env_purge_unmanaged_modules.rb +21 -25
- data/integration/tests/user_scenario/complex_workflow/multi_env_add_change_remove.rb +3 -3
- data/integration/tests/user_scenario/complex_workflow/multi_env_remove_re-add.rb +3 -3
- data/integration/tests/user_scenario/complex_workflow/multi_env_unamanaged.rb +3 -3
- data/lib/r10k/action/base.rb +6 -3
- data/lib/r10k/action/deploy/display.rb +6 -3
- data/lib/r10k/action/deploy/environment.rb +15 -4
- data/lib/r10k/action/deploy/module.rb +37 -8
- data/lib/r10k/action/runner.rb +45 -10
- data/lib/r10k/cli/deploy.rb +4 -0
- data/lib/r10k/git.rb +3 -0
- data/lib/r10k/git/cache.rb +1 -1
- data/lib/r10k/git/rugged/credentials.rb +77 -0
- data/lib/r10k/git/stateful_repository.rb +1 -0
- data/lib/r10k/initializers.rb +10 -0
- data/lib/r10k/module/base.rb +37 -0
- data/lib/r10k/module/forge.rb +7 -2
- data/lib/r10k/module/git.rb +2 -1
- data/lib/r10k/module/svn.rb +2 -1
- data/lib/r10k/module_loader/puppetfile.rb +206 -0
- data/lib/r10k/module_loader/puppetfile/dsl.rb +37 -0
- data/lib/r10k/puppetfile.rb +83 -160
- data/lib/r10k/settings.rb +47 -2
- data/lib/r10k/settings/definition.rb +1 -1
- data/lib/r10k/source/base.rb +10 -0
- data/lib/r10k/source/git.rb +5 -0
- data/lib/r10k/source/svn.rb +4 -0
- data/lib/r10k/util/purgeable.rb +70 -8
- data/lib/r10k/version.rb +1 -1
- data/locales/r10k.pot +129 -57
- data/r10k.gemspec +2 -0
- data/spec/fixtures/unit/action/r10k_forge_auth.yaml +4 -0
- data/spec/fixtures/unit/action/r10k_forge_auth_no_url.yaml +3 -0
- data/spec/fixtures/unit/util/purgeable/managed_one/managed_subdir_1/managed_subdir_2/ignored_1 +0 -0
- data/spec/fixtures/unit/util/purgeable/managed_two/.hidden/unmanaged_3 +0 -0
- data/spec/unit/action/deploy/environment_spec.rb +25 -0
- data/spec/unit/action/deploy/module_spec.rb +216 -14
- data/spec/unit/action/runner_spec.rb +129 -25
- data/spec/unit/git/cache_spec.rb +14 -0
- data/spec/unit/git/rugged/credentials_spec.rb +29 -0
- data/spec/unit/git/stateful_repository_spec.rb +5 -0
- data/spec/unit/module/base_spec.rb +46 -0
- data/spec/unit/module/forge_spec.rb +27 -1
- data/spec/unit/module/git_spec.rb +17 -8
- data/spec/unit/module/svn_spec.rb +18 -0
- data/spec/unit/module_loader/puppetfile_spec.rb +343 -0
- data/spec/unit/module_spec.rb +28 -0
- data/spec/unit/puppetfile_spec.rb +127 -191
- data/spec/unit/settings_spec.rb +24 -2
- data/spec/unit/util/purgeable_spec.rb +38 -6
- metadata +23 -2
@@ -18,14 +18,18 @@ module R10K
|
|
18
18
|
# @param opts [Hash] A hash of options defined in #allowed_initialized_opts
|
19
19
|
# and managed by the SetOps mixin within the Action::Base class.
|
20
20
|
# Corresponds to the CLI flags and options.
|
21
|
-
# @param argv [CRI::ArgumentList
|
22
|
-
# arguments to the CLI invocation (after
|
21
|
+
# @param argv [Enumerable] Typically CRI::ArgumentList or Array. A list-like
|
22
|
+
# collection of the remaining arguments to the CLI invocation (after
|
23
|
+
# removing flags and options).
|
23
24
|
# @param settings [Hash] A hash of configuration loaded from the relevant
|
24
25
|
# config (r10k.yaml).
|
25
|
-
|
26
|
+
#
|
27
|
+
# @note All arguments will be required in the next major version
|
28
|
+
def initialize(opts, argv, settings = {})
|
26
29
|
super
|
27
30
|
|
28
31
|
requested_env = @opts[:environment] ? [@opts[:environment].gsub(/\W/, '_')] : []
|
32
|
+
@modified_envs = []
|
29
33
|
|
30
34
|
@settings = @settings.merge({
|
31
35
|
overrides: {
|
@@ -34,6 +38,7 @@ module R10K
|
|
34
38
|
generate_types: @generate_types
|
35
39
|
},
|
36
40
|
modules: {
|
41
|
+
exclude_spec: settings.dig(:deploy, :exclude_spec),
|
37
42
|
requested_modules: @argv.map.to_a,
|
38
43
|
# force here is used to make it easier to reason about
|
39
44
|
force: !@no_force
|
@@ -66,6 +71,21 @@ module R10K
|
|
66
71
|
|
67
72
|
def visit_deployment(deployment)
|
68
73
|
yield
|
74
|
+
ensure
|
75
|
+
if (postcmd = @settings[:postrun])
|
76
|
+
if @modified_envs.any?
|
77
|
+
if postcmd.grep('$modifiedenvs').any?
|
78
|
+
envs_to_run = @modified_envs.join(' ')
|
79
|
+
logger.debug "Running postrun command for environments #{envs_to_run}."
|
80
|
+
postcmd = postcmd.map { |e| e.gsub('$modifiedenvs', envs_to_run) }
|
81
|
+
end
|
82
|
+
subproc = R10K::Util::Subprocess.new(postcmd)
|
83
|
+
subproc.logger = logger
|
84
|
+
subproc.execute
|
85
|
+
else
|
86
|
+
logger.debug "No environments were modified, not executing postrun command."
|
87
|
+
end
|
88
|
+
end
|
69
89
|
end
|
70
90
|
|
71
91
|
def visit_source(source)
|
@@ -82,10 +102,15 @@ module R10K
|
|
82
102
|
environment.deploy
|
83
103
|
|
84
104
|
requested_mods = @settings.dig(:overrides, :modules, :requested_modules) || []
|
85
|
-
|
86
|
-
if
|
87
|
-
|
88
|
-
environment.
|
105
|
+
# We actually synced a module in this env
|
106
|
+
if !((environment.modules.map(&:name) & requested_mods).empty?)
|
107
|
+
# Record modified environment for postrun command
|
108
|
+
@modified_envs << environment.dirname
|
109
|
+
|
110
|
+
if generate_types = @settings.dig(:overrides, :environments, :generate_types)
|
111
|
+
logger.debug("Generating puppet types for environment '#{environment.dirname}'...")
|
112
|
+
environment.generate_types!
|
113
|
+
end
|
89
114
|
end
|
90
115
|
end
|
91
116
|
end
|
@@ -93,12 +118,16 @@ module R10K
|
|
93
118
|
def allowed_initialize_opts
|
94
119
|
super.merge(environment: true,
|
95
120
|
cachedir: :self,
|
121
|
+
'exclude-spec': :self,
|
96
122
|
'no-force': :self,
|
97
123
|
'generate-types': :self,
|
98
124
|
'puppet-path': :self,
|
99
125
|
'puppet-conf': :self,
|
100
126
|
'private-key': :self,
|
101
|
-
'oauth-token': :self
|
127
|
+
'oauth-token': :self,
|
128
|
+
'github-app-id': :self,
|
129
|
+
'github-app-key': :self,
|
130
|
+
'github-app-ttl': :self)
|
102
131
|
end
|
103
132
|
end
|
104
133
|
end
|
data/lib/r10k/action/runner.rb
CHANGED
@@ -44,10 +44,13 @@ module R10K
|
|
44
44
|
|
45
45
|
overrides = {}
|
46
46
|
overrides[:cachedir] = @opts[:cachedir] if @opts.key?(:cachedir)
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
47
|
+
if @opts.key?(:'puppet-path') || @opts.key?(:'generate-types') || @opts.key?(:'exclude-spec') || @opts.key?(:'puppet-conf')
|
48
|
+
overrides[:deploy] = {}
|
49
|
+
overrides[:deploy][:puppet_path] = @opts[:'puppet-path'] if @opts.key?(:'puppet-path')
|
50
|
+
overrides[:deploy][:puppet_conf] = @opts[:'puppet-conf'] if @opts.key?(:'puppet-conf')
|
51
|
+
overrides[:deploy][:generate_types] = @opts[:'generate-types'] if @opts.key?(:'generate-types')
|
52
|
+
overrides[:deploy][:exclude_spec] = @opts[:'exclude-spec'] if @opts.key?(:'exclude-spec')
|
53
|
+
end
|
51
54
|
|
52
55
|
with_overrides = config_settings.merge(overrides) do |key, oldval, newval|
|
53
56
|
newval = oldval.merge(newval) if oldval.is_a? Hash
|
@@ -67,15 +70,20 @@ module R10K
|
|
67
70
|
exit(8)
|
68
71
|
end
|
69
72
|
|
73
|
+
# Set up authorization from license file if it wasn't
|
74
|
+
# already set via the config
|
70
75
|
def setup_authorization
|
71
|
-
|
72
|
-
|
76
|
+
if PuppetForge::Connection.authorization.nil?
|
77
|
+
begin
|
78
|
+
license = R10K::Util::License.load
|
73
79
|
|
74
|
-
|
75
|
-
|
80
|
+
if license.respond_to?(:authorization_token)
|
81
|
+
logger.debug "Using token from license to connect to the Forge."
|
82
|
+
PuppetForge::Connection.authorization = license.authorization_token
|
83
|
+
end
|
84
|
+
rescue R10K::Error => e
|
85
|
+
logger.warn e.message
|
76
86
|
end
|
77
|
-
rescue R10K::Error => e
|
78
|
-
logger.warn e.message
|
79
87
|
end
|
80
88
|
end
|
81
89
|
|
@@ -100,11 +108,26 @@ module R10K
|
|
100
108
|
def add_credential_overrides(overrides)
|
101
109
|
sshkey_path = @opts[:'private-key']
|
102
110
|
token_path = @opts[:'oauth-token']
|
111
|
+
app_id = @opts[:'github-app-id']
|
112
|
+
app_private_key_path = @opts[:'github-app-key']
|
113
|
+
app_ttl = @opts[:'github-app-ttl']
|
103
114
|
|
104
115
|
if sshkey_path && token_path
|
105
116
|
raise R10K::Error, "Cannot specify both an SSH key and a token to use with this deploy."
|
106
117
|
end
|
107
118
|
|
119
|
+
if sshkey_path && (app_private_key_path || app_id)
|
120
|
+
raise R10K::Error, "Cannot specify both an SSH key and an SSL key or Github App id to use with this deploy."
|
121
|
+
end
|
122
|
+
|
123
|
+
if token_path && (app_private_key_path || app_id)
|
124
|
+
raise R10K::Error, "Cannot specify both an OAuth token and an SSL key or Github App id to use with this deploy."
|
125
|
+
end
|
126
|
+
|
127
|
+
if app_id && ! app_private_key_path || app_private_key_path && ! app_id
|
128
|
+
raise R10K::Error, "Must specify both id and SSL private key to use Github App for this deploy."
|
129
|
+
end
|
130
|
+
|
108
131
|
if sshkey_path
|
109
132
|
overrides[:git] ||= {}
|
110
133
|
overrides[:git][:private_key] = sshkey_path
|
@@ -121,6 +144,18 @@ module R10K
|
|
121
144
|
repo[:oauth_token] = token_path
|
122
145
|
end
|
123
146
|
end
|
147
|
+
elsif app_id
|
148
|
+
overrides[:git] ||= {}
|
149
|
+
overrides[:git][:github_app_id] = app_id
|
150
|
+
overrides[:git][:github_app_key] = app_private_key_path
|
151
|
+
overrides[:git][:github_app_ttl] = app_ttl
|
152
|
+
if repo_settings = overrides[:git][:repositories]
|
153
|
+
repo_settings.each do |repo|
|
154
|
+
repo[:github_app_id] = app_id
|
155
|
+
repo[:github_app_key] = app_private_key_path
|
156
|
+
repo[:github_app_ttl] = app_ttl
|
157
|
+
end
|
158
|
+
end
|
124
159
|
end
|
125
160
|
|
126
161
|
overrides
|
data/lib/r10k/cli/deploy.rb
CHANGED
@@ -24,6 +24,7 @@ module R10K::CLI
|
|
24
24
|
option nil, :cachedir, 'Specify a cachedir, overriding the value in config', argument: :required
|
25
25
|
flag nil, :'no-force', 'Prevent the overwriting of local module modifications'
|
26
26
|
flag nil, :'generate-types', 'Run `puppet generate types` after updating an environment'
|
27
|
+
flag nil, :'exclude-spec', 'Exclude the module\'s spec dir from deployment'
|
27
28
|
option nil, :'puppet-path', 'Path to puppet executable', argument: :required do |value, cmd|
|
28
29
|
unless File.executable? value
|
29
30
|
$stderr.puts "The specified puppet executable #{value} is not executable."
|
@@ -34,6 +35,9 @@ module R10K::CLI
|
|
34
35
|
option nil, :'puppet-conf', 'Path to puppet.conf', argument: :required
|
35
36
|
option nil, :'private-key', 'Path to SSH key to use when cloning. Only valid with rugged provider', argument: :required
|
36
37
|
option nil, :'oauth-token', 'Path to OAuth token to use when cloning. Only valid with rugged provider', argument: :required
|
38
|
+
option nil, :'github-app-id', 'Github App id. Only valid with rugged provider', argument: :required
|
39
|
+
option nil, :'github-app-key', 'Github App private key. Only valid with rugged provider', argument: :required
|
40
|
+
option nil, :'github-app-ttl', 'Github App token expiration, in seconds. Only valid with rugged provider', default: "120", argument: :optional
|
37
41
|
|
38
42
|
run do |opts, args, cmd|
|
39
43
|
puts cmd.help(:verbose => opts[:verbose])
|
data/lib/r10k/git.rb
CHANGED
@@ -135,6 +135,9 @@ module R10K
|
|
135
135
|
|
136
136
|
def_setting_attr :private_key
|
137
137
|
def_setting_attr :oauth_token
|
138
|
+
def_setting_attr :github_app_id
|
139
|
+
def_setting_attr :github_app_key
|
140
|
+
def_setting_attr :github_app_ttl
|
138
141
|
def_setting_attr :proxy
|
139
142
|
def_setting_attr :username
|
140
143
|
def_setting_attr :repositories, {}
|
data/lib/r10k/git/cache.rb
CHANGED
@@ -111,6 +111,6 @@ class R10K::Git::Cache
|
|
111
111
|
|
112
112
|
# Reformat the remote name into something that can be used as a directory
|
113
113
|
def sanitized_dirname
|
114
|
-
@sanitized_dirname ||= @remote.gsub(/[^@\w\.-]/, '-')
|
114
|
+
@sanitized_dirname ||= @remote.gsub(/(\w+:\/\/)(.*)(@)/, '\1').gsub(/[^@\w\.-]/, '-')
|
115
115
|
end
|
116
116
|
end
|
@@ -1,6 +1,10 @@
|
|
1
1
|
require 'r10k/git/rugged'
|
2
2
|
require 'r10k/git/errors'
|
3
3
|
require 'r10k/logging'
|
4
|
+
require 'json'
|
5
|
+
require 'jwt'
|
6
|
+
require 'net/http'
|
7
|
+
require 'openssl'
|
4
8
|
|
5
9
|
# Generate credentials for secured remote connections.
|
6
10
|
#
|
@@ -62,15 +66,29 @@ class R10K::Git::Rugged::Credentials
|
|
62
66
|
|
63
67
|
def get_plaintext_credentials(url, username_from_url)
|
64
68
|
per_repo_oauth_token = nil
|
69
|
+
per_repo_github_app_id = nil
|
70
|
+
per_repo_github_app_key = nil
|
71
|
+
per_repo_github_app_ttl = nil
|
72
|
+
|
65
73
|
if per_repo_settings = R10K::Git.get_repo_settings(url)
|
66
74
|
per_repo_oauth_token = per_repo_settings[:oauth_token]
|
75
|
+
per_repo_github_app_id = per_repo_settings[:github_app_id]
|
76
|
+
per_repo_github_app_key = per_repo_settings[:github_app_key]
|
77
|
+
per_repo_github_app_ttl = per_repo_settings[:github_app_ttl]
|
67
78
|
end
|
68
79
|
|
80
|
+
app_id = per_repo_github_app_id || R10K::Git.settings[:github_app_id]
|
81
|
+
app_key = per_repo_github_app_key || R10K::Git.settings[:github_app_key]
|
82
|
+
app_ttl = per_repo_github_app_ttl || R10K::Git.settings[:github_app_ttl]
|
83
|
+
|
69
84
|
if token_path = per_repo_oauth_token || R10K::Git.settings[:oauth_token]
|
70
85
|
@oauth_token ||= extract_token(token_path, url)
|
71
86
|
|
72
87
|
user = 'x-oauth-token'
|
73
88
|
password = @oauth_token
|
89
|
+
elsif app_id && app_key && app_ttl
|
90
|
+
user = 'x-access-token'
|
91
|
+
password = github_app_token(app_id, app_key, app_ttl)
|
74
92
|
else
|
75
93
|
user = get_git_username(url, username_from_url)
|
76
94
|
password = URI.parse(url).password || ''
|
@@ -125,4 +143,63 @@ class R10K::Git::Rugged::Credentials
|
|
125
143
|
|
126
144
|
user
|
127
145
|
end
|
146
|
+
|
147
|
+
def github_app_token(app_id, private_key, ttl)
|
148
|
+
raise R10K::Git::GitError, _('Github App id contains invalid characters.') unless app_id =~ /^\d+$/
|
149
|
+
raise R10K::Git::GitError, _('Github App token ttl contains invalid characters.') unless ttl =~ /^\d+$/
|
150
|
+
raise R10K::Git::GitError, _('Github App key is missing or unreadable') unless File.readable?(private_key)
|
151
|
+
|
152
|
+
begin
|
153
|
+
ssl_key = OpenSSL::PKey::RSA.new(File.read(private_key).strip)
|
154
|
+
unless ssl_key.private?
|
155
|
+
raise R10K::Git::GitError, _('Github App key is not a valid SSL private key')
|
156
|
+
end
|
157
|
+
rescue OpenSSL::PKey::RSAError
|
158
|
+
raise R10K::Git::GitError, _('Github App key is not a valid SSL key')
|
159
|
+
end
|
160
|
+
|
161
|
+
logger.debug2 _("Using Github App id %{app_id} with SSL key from %{key_path}") % { key_path: private_key, app_id: app_id }
|
162
|
+
|
163
|
+
jwt_issue_time = Time.now.to_i - 60
|
164
|
+
jwt_exp_time = (jwt_issue_time + 60) + ttl.to_i
|
165
|
+
payload = { iat: jwt_issue_time, exp: jwt_exp_time, iss: app_id }
|
166
|
+
jwt = JWT.encode(payload, ssl_key, "RS256")
|
167
|
+
|
168
|
+
get = URI.parse("https://api.github.com/app/installations")
|
169
|
+
get_request = Net::HTTP::Get.new(get)
|
170
|
+
get_request["Authorization"] = "Bearer #{jwt}"
|
171
|
+
get_request["Accept"] = "application/vnd.github.v3+json"
|
172
|
+
get_req_options = { use_ssl: get.scheme == "https", }
|
173
|
+
get_response = Net::HTTP.start(get.hostname, get.port, get_req_options) do |http|
|
174
|
+
http.request(get_request)
|
175
|
+
end
|
176
|
+
|
177
|
+
unless (get_response.class < Net::HTTPSuccess)
|
178
|
+
logger.debug2 _("Unexpected response code: #{get_response.code}\nResponse body: #{get_response.body}")
|
179
|
+
raise R10K::Git::GitError, _("Error using private key to get Github App access token from url")
|
180
|
+
end
|
181
|
+
|
182
|
+
access_tokens_url = JSON.parse(get_response.body)[0]['access_tokens_url']
|
183
|
+
|
184
|
+
post = URI.parse(access_tokens_url)
|
185
|
+
post_request = Net::HTTP::Post.new(post)
|
186
|
+
post_request["Authorization"] = "Bearer #{jwt}"
|
187
|
+
post_request["Accept"] = "application/vnd.github.v3+json"
|
188
|
+
post_req_options = { use_ssl: post.scheme == "https", }
|
189
|
+
post_response = Net::HTTP.start(post.hostname, post.port, post_req_options) do |http|
|
190
|
+
http.request(post_request)
|
191
|
+
end
|
192
|
+
|
193
|
+
unless (post_response.class < Net::HTTPSuccess)
|
194
|
+
logger.debug2 _("Unexpected response code: #{post_response.code}\nResponse body: #{post_response.body}")
|
195
|
+
raise R10K::Git::GitError, _("Error using private key to generate access token from #{access_token_url}")
|
196
|
+
end
|
197
|
+
|
198
|
+
token = JSON.parse(post_response.body)['token']
|
199
|
+
|
200
|
+
raise R10K::Git::GitError, _("Github App token contains invalid characters.") unless valid_token?(token)
|
201
|
+
|
202
|
+
logger.debug2 _("Github App token generated, expires at: %{expire}") % {expire: JSON.parse(post_response.body)['expires_at']}
|
203
|
+
token
|
204
|
+
end
|
128
205
|
end
|
data/lib/r10k/initializers.rb
CHANGED
@@ -56,6 +56,9 @@ module R10K
|
|
56
56
|
with_setting(:proxy) { |value| R10K::Git.settings[:proxy] = value }
|
57
57
|
with_setting(:repositories) { |value| R10K::Git.settings[:repositories] = value }
|
58
58
|
with_setting(:oauth_token) { |value| R10K::Git.settings[:oauth_token] = value }
|
59
|
+
with_setting(:github_app_id) { |value| R10K::Git.settings[:github_app_id] = value }
|
60
|
+
with_setting(:github_app_key) { |value| R10K::Git.settings[:github_app_key] = value }
|
61
|
+
with_setting(:github_app_ttl) { |value| R10K::Git.settings[:github_app_ttl] = value }
|
59
62
|
end
|
60
63
|
end
|
61
64
|
|
@@ -63,6 +66,13 @@ module R10K
|
|
63
66
|
def call
|
64
67
|
with_setting(:baseurl) { |value| PuppetForge.host = value }
|
65
68
|
with_setting(:proxy) { |value| PuppetForge::Connection.proxy = value }
|
69
|
+
with_setting(:authorization_token) { |value|
|
70
|
+
if @settings[:baseurl]
|
71
|
+
PuppetForge::Connection.authorization = value
|
72
|
+
else
|
73
|
+
raise R10K::Error, "Cannot specify a Forge authorization token without configuring a custom baseurl."
|
74
|
+
end
|
75
|
+
}
|
66
76
|
end
|
67
77
|
end
|
68
78
|
end
|
data/lib/r10k/module/base.rb
CHANGED
@@ -35,6 +35,10 @@ class R10K::Module::Base
|
|
35
35
|
# @return [String] Where the module was sourced from. E.g., "Puppetfile"
|
36
36
|
attr_accessor :origin
|
37
37
|
|
38
|
+
# @!attribute [rw] spec_deletable
|
39
|
+
# @return [Boolean] set this to true if the spec dir can be safely removed, ie in the moduledir
|
40
|
+
attr_accessor :spec_deletable
|
41
|
+
|
38
42
|
# There's been some churn over `author` vs `owner` and `full_name` over
|
39
43
|
# `title`, so in the short run it's easier to support both and deprecate one
|
40
44
|
# later.
|
@@ -52,6 +56,9 @@ class R10K::Module::Base
|
|
52
56
|
@path = Pathname.new(File.join(@dirname, @name))
|
53
57
|
@environment = environment
|
54
58
|
@overrides = args.delete(:overrides) || {}
|
59
|
+
@spec_deletable = true
|
60
|
+
@exclude_spec = args.delete(:exclude_spec)
|
61
|
+
@exclude_spec = @overrides[:modules].delete(:exclude_spec) if @overrides.dig(:modules, :exclude_spec)
|
55
62
|
@origin = 'external' # Expect Puppetfile or R10k::Environment to set this to a specific value
|
56
63
|
|
57
64
|
@requested_modules = @overrides.dig(:modules, :requested_modules) || []
|
@@ -64,6 +71,36 @@ class R10K::Module::Base
|
|
64
71
|
path.to_s
|
65
72
|
end
|
66
73
|
|
74
|
+
# Delete the spec dir if @exclude_spec has been set to true and @spec_deletable is also true
|
75
|
+
def maybe_delete_spec_dir
|
76
|
+
if @exclude_spec
|
77
|
+
if @spec_deletable
|
78
|
+
delete_spec_dir
|
79
|
+
else
|
80
|
+
logger.info _("Spec dir for #{@title} will not be deleted because it is not in the moduledir")
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
# Actually remove the spec dir
|
86
|
+
def delete_spec_dir
|
87
|
+
spec_path = @path + 'spec'
|
88
|
+
if spec_path.symlink?
|
89
|
+
spec_path = spec_path.realpath
|
90
|
+
end
|
91
|
+
if spec_path.directory?
|
92
|
+
logger.debug2 _("Deleting spec data at #{spec_path}")
|
93
|
+
# Use the secure flag for the #rm_rf method to avoid security issues
|
94
|
+
# involving TOCTTOU(time of check to time of use); more details here:
|
95
|
+
# https://ruby-doc.org/stdlib-2.7.0/libdoc/fileutils/rdoc/FileUtils.html#method-c-rm_rf
|
96
|
+
# Additionally, #rm_rf also has problems in windows with with symlink targets
|
97
|
+
# also being deleted; this should be revisted if Windows becomes higher priority.
|
98
|
+
FileUtils.rm_rf(spec_path, secure: true)
|
99
|
+
else
|
100
|
+
logger.debug2 _("No spec dir detected at #{spec_path}, skipping deletion")
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
67
104
|
# Synchronize this module with the indicated state.
|
68
105
|
# @param [Hash] opts Deprecated
|
69
106
|
def sync(opts={})
|
data/lib/r10k/module/forge.rb
CHANGED
@@ -50,7 +50,7 @@ class R10K::Module::Forge < R10K::Module::Base
|
|
50
50
|
:version => :expected_version,
|
51
51
|
:source => ::R10K::Util::Setopts::Ignore,
|
52
52
|
:type => ::R10K::Util::Setopts::Ignore,
|
53
|
-
})
|
53
|
+
}, :raise_on_unhandled => false)
|
54
54
|
|
55
55
|
@expected_version ||= current_version || :latest
|
56
56
|
|
@@ -68,6 +68,7 @@ class R10K::Module::Forge < R10K::Module::Base
|
|
68
68
|
when :mismatched
|
69
69
|
reinstall
|
70
70
|
end
|
71
|
+
maybe_delete_spec_dir
|
71
72
|
end
|
72
73
|
end
|
73
74
|
|
@@ -83,7 +84,11 @@ class R10K::Module::Forge < R10K::Module::Base
|
|
83
84
|
def expected_version
|
84
85
|
if @expected_version == :latest
|
85
86
|
begin
|
86
|
-
|
87
|
+
if @v3_module.current_release
|
88
|
+
@expected_version = @v3_module.current_release.version
|
89
|
+
else
|
90
|
+
raise PuppetForge::ReleaseNotFound, _("The module %{title} does not appear to have any published releases, cannot determine latest version.") % { title: @title }
|
91
|
+
end
|
87
92
|
rescue Faraday::ResourceNotFound => e
|
88
93
|
raise PuppetForge::ReleaseNotFound, _("The module %{title} does not exist on %{url}.") % {title: @title, url: PuppetForge::V3::Release.conn.url_prefix}, e.backtrace
|
89
94
|
end
|
data/lib/r10k/module/git.rb
CHANGED
@@ -52,7 +52,7 @@ class R10K::Module::Git < R10K::Module::Base
|
|
52
52
|
:git => :remote,
|
53
53
|
:default_branch => :default_ref,
|
54
54
|
:default_branch_override => :default_override_ref,
|
55
|
-
})
|
55
|
+
}, :raise_on_unhandled => false)
|
56
56
|
|
57
57
|
force = @overrides.dig(:modules, :force)
|
58
58
|
@force = force == false ? false : true
|
@@ -86,6 +86,7 @@ class R10K::Module::Git < R10K::Module::Base
|
|
86
86
|
def sync(opts={})
|
87
87
|
force = opts[:force] || @force
|
88
88
|
@repo.sync(version, force) if should_sync?
|
89
|
+
maybe_delete_spec_dir
|
89
90
|
end
|
90
91
|
|
91
92
|
def status
|