pdk 1.10.0 → 1.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 +5 -5
- data/CHANGELOG.md +50 -1
- data/lib/pdk.rb +16 -1
- data/lib/pdk/analytics.rb +28 -0
- data/lib/pdk/analytics/client/google_analytics.rb +138 -0
- data/lib/pdk/analytics/client/noop.rb +23 -0
- data/lib/pdk/analytics/util.rb +17 -0
- data/lib/pdk/cli.rb +37 -0
- data/lib/pdk/cli/build.rb +2 -0
- data/lib/pdk/cli/bundle.rb +2 -1
- data/lib/pdk/cli/convert.rb +2 -0
- data/lib/pdk/cli/exec.rb +28 -1
- data/lib/pdk/cli/new/class.rb +2 -0
- data/lib/pdk/cli/new/defined_type.rb +2 -0
- data/lib/pdk/cli/new/module.rb +2 -0
- data/lib/pdk/cli/new/provider.rb +2 -0
- data/lib/pdk/cli/new/task.rb +2 -0
- data/lib/pdk/cli/test.rb +0 -1
- data/lib/pdk/cli/test/unit.rb +13 -10
- data/lib/pdk/cli/update.rb +21 -0
- data/lib/pdk/cli/util.rb +35 -0
- data/lib/pdk/cli/util/interview.rb +7 -1
- data/lib/pdk/cli/validate.rb +9 -2
- data/lib/pdk/config.rb +94 -0
- data/lib/pdk/config/errors.rb +5 -0
- data/lib/pdk/config/json.rb +23 -0
- data/lib/pdk/config/namespace.rb +273 -0
- data/lib/pdk/config/validator.rb +31 -0
- data/lib/pdk/config/value.rb +94 -0
- data/lib/pdk/config/yaml.rb +31 -0
- data/lib/pdk/generate/module.rb +3 -2
- data/lib/pdk/logger.rb +21 -1
- data/lib/pdk/module/build.rb +58 -0
- data/lib/pdk/module/convert.rb +1 -1
- data/lib/pdk/module/metadata.rb +1 -0
- data/lib/pdk/module/templatedir.rb +24 -5
- data/lib/pdk/module/update_manager.rb +2 -2
- data/lib/pdk/report/event.rb +3 -3
- data/lib/pdk/template_file.rb +1 -1
- data/lib/pdk/tests/unit.rb +10 -12
- data/lib/pdk/util.rb +9 -0
- data/lib/pdk/util/bundler.rb +5 -9
- data/lib/pdk/util/filesystem.rb +37 -0
- data/lib/pdk/util/puppet_version.rb +1 -1
- data/lib/pdk/util/ruby_version.rb +16 -6
- data/lib/pdk/util/template_uri.rb +72 -43
- data/lib/pdk/util/version.rb +1 -1
- data/lib/pdk/util/windows.rb +1 -0
- data/lib/pdk/util/windows/api_types.rb +0 -7
- data/lib/pdk/util/windows/file.rb +1 -1
- data/lib/pdk/util/windows/string.rb +1 -1
- data/lib/pdk/validate/base_validator.rb +8 -6
- data/lib/pdk/validate/puppet/puppet_syntax.rb +1 -1
- data/lib/pdk/validate/ruby/rubocop.rb +1 -1
- data/lib/pdk/version.rb +1 -1
- data/locales/pdk.pot +223 -114
- metadata +103 -50
@@ -0,0 +1,31 @@
|
|
1
|
+
module PDK
|
2
|
+
class Config
|
3
|
+
# A collection of predefined validators for use with {PDK::Config::Value}.
|
4
|
+
#
|
5
|
+
# @example
|
6
|
+
# value :enabled do
|
7
|
+
# validate PDK::Config::Validator.boolean
|
8
|
+
# end
|
9
|
+
module Validator
|
10
|
+
# @return [Hash{Symbol => [Proc,String]}] a {PDK::Config::Value}
|
11
|
+
# validator that ensures that the value is either a TrueClass or
|
12
|
+
# FalseClass.
|
13
|
+
def self.boolean
|
14
|
+
{
|
15
|
+
proc: ->(value) { [true, false].include?(value) },
|
16
|
+
message: _('must be a boolean true or false'),
|
17
|
+
}
|
18
|
+
end
|
19
|
+
|
20
|
+
# @return [Hash{Symbol => [Proc,String]}] a {PDK::Config::Value}
|
21
|
+
# validator that ensures that the value is a String that matches the
|
22
|
+
# regex for a version 4 UUID.
|
23
|
+
def self.uuid
|
24
|
+
{
|
25
|
+
proc: ->(value) { value.match(%r{\A\h{8}(?:-\h{4}){3}-\h{12}\z}) },
|
26
|
+
message: _('must be a version 4 UUID'),
|
27
|
+
}
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
module PDK
|
2
|
+
class Config
|
3
|
+
# A class for describing the value of a {PDK::Config} setting.
|
4
|
+
#
|
5
|
+
# Generally, this is never instantiated manually, but is instead
|
6
|
+
# instantiated by passing a block to {PDK::Config::Namespace#value}.
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
#
|
10
|
+
# PDK::Config::Namespace.new('analytics') do
|
11
|
+
# value :disabled do
|
12
|
+
# validate PDK::Config::Validator.boolean
|
13
|
+
# default_to { false }
|
14
|
+
# end
|
15
|
+
# end
|
16
|
+
class Value
|
17
|
+
# Initialises an empty value definition.
|
18
|
+
#
|
19
|
+
# @param name [String,Symbol] the name of the value.
|
20
|
+
def initialize(name)
|
21
|
+
@name = name
|
22
|
+
@validators = []
|
23
|
+
end
|
24
|
+
|
25
|
+
# Assign a validator to the value.
|
26
|
+
#
|
27
|
+
# @param validator [Hash{Symbol => [Proc,String]}]
|
28
|
+
# @option validator [Proc] :proc a lambda that takes the value to be
|
29
|
+
# validated as the argument and returns `true` if the value is valid.
|
30
|
+
# @option validator [String] :message a description of what the validator
|
31
|
+
# is testing for, that is displayed to the user as part of the error
|
32
|
+
# message for invalid values.
|
33
|
+
#
|
34
|
+
# @raise [ArgumentError] if not passed a Hash.
|
35
|
+
# @raise [ArgumentError] if the Hash doesn't have a `:proc` key that
|
36
|
+
# contains a Proc.
|
37
|
+
# @raise [ArgumentError] if the Hash doesn't have a `:message` key that
|
38
|
+
# contains a String.
|
39
|
+
#
|
40
|
+
# @return [nil]
|
41
|
+
def validate(validator)
|
42
|
+
raise ArgumentError, _('validator must be a Hash') unless validator.is_a?(Hash)
|
43
|
+
raise ArgumentError, _('the :proc key must contain a Proc') unless validator.key?(:proc) && validator[:proc].is_a?(Proc)
|
44
|
+
raise ArgumentError, _('the :message key must contain a String') unless validator.key?(:message) && validator[:message].is_a?(String)
|
45
|
+
|
46
|
+
@validators << validator
|
47
|
+
end
|
48
|
+
|
49
|
+
# Validate a value against the assigned validators.
|
50
|
+
#
|
51
|
+
# @param key [String] the name of the value being validated.
|
52
|
+
# @param value [Object] the value being validated.
|
53
|
+
#
|
54
|
+
# @raise [ArgumentError] if any of the assigned validators fail to
|
55
|
+
# validate the value.
|
56
|
+
#
|
57
|
+
# @return [nil]
|
58
|
+
def validate!(key, value)
|
59
|
+
@validators.each do |validator|
|
60
|
+
next if validator[:proc].call(value)
|
61
|
+
|
62
|
+
raise ArgumentError, _('%{key} %{message}') % {
|
63
|
+
key: key,
|
64
|
+
message: validator[:message],
|
65
|
+
}
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# Assign a default value.
|
70
|
+
#
|
71
|
+
# @param block [Proc] a block that is lazy evaluated when necessary in
|
72
|
+
# order to determine the default value.
|
73
|
+
#
|
74
|
+
# @return [nil]
|
75
|
+
def default_to(&block)
|
76
|
+
raise ArgumentError, _('must be passed a block') unless block_given?
|
77
|
+
@default_to = block
|
78
|
+
end
|
79
|
+
|
80
|
+
# Evaluate the default value block.
|
81
|
+
#
|
82
|
+
# @return [Object,nil] the result of evaluating the block given to
|
83
|
+
# {#default_to}, or `nil` if the value has no default.
|
84
|
+
def default
|
85
|
+
default? ? @default_to.call : nil
|
86
|
+
end
|
87
|
+
|
88
|
+
# @return [Boolean] true if the value has a default value block.
|
89
|
+
def default?
|
90
|
+
!@default_to.nil?
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'pdk/config/namespace'
|
2
|
+
|
3
|
+
module PDK
|
4
|
+
class Config
|
5
|
+
class YAML < Namespace
|
6
|
+
def parse_data(data, filename)
|
7
|
+
return {} if data.nil? || data.empty?
|
8
|
+
|
9
|
+
require 'yaml'
|
10
|
+
|
11
|
+
::YAML.safe_load(data, [Symbol], [], true)
|
12
|
+
rescue Psych::SyntaxError => e
|
13
|
+
raise PDK::Config::LoadError, _('Syntax error when loading %{file}: %{error}') % {
|
14
|
+
file: filename,
|
15
|
+
error: "#{e.problem} #{e.context}",
|
16
|
+
}
|
17
|
+
rescue Psych::DisallowedClass => e
|
18
|
+
raise PDK::Config::LoadError, _('Unsupported class in %{file}: %{error}') % {
|
19
|
+
file: filename,
|
20
|
+
error: e.message,
|
21
|
+
}
|
22
|
+
end
|
23
|
+
|
24
|
+
def serialize_data(data)
|
25
|
+
require 'yaml'
|
26
|
+
|
27
|
+
::YAML.dump(data)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
data/lib/pdk/generate/module.rb
CHANGED
@@ -124,7 +124,7 @@ module PDK
|
|
124
124
|
defaults['name'] = "#{opts[:username]}-#{opts[:module_name]}" unless opts[:module_name].nil?
|
125
125
|
defaults['author'] = PDK.answers['author'] unless PDK.answers['author'].nil?
|
126
126
|
defaults['license'] = PDK.answers['license'] unless PDK.answers['license'].nil?
|
127
|
-
defaults['license'] = opts[:license] if opts.key?
|
127
|
+
defaults['license'] = opts[:license] if opts.key?(:license)
|
128
128
|
|
129
129
|
metadata = PDK::Module::Metadata.new(defaults)
|
130
130
|
module_interview(metadata, opts) unless opts[:'skip-interview']
|
@@ -199,6 +199,7 @@ module PDK
|
|
199
199
|
question: _('What operating systems does this module support?'),
|
200
200
|
help: _('Use the up and down keys to move between the choices, space to select and enter to continue.'),
|
201
201
|
required: true,
|
202
|
+
type: :multi_select,
|
202
203
|
choices: PDK::Module::Metadata::OPERATING_SYSTEMS,
|
203
204
|
default: PDK::Module::Metadata::DEFAULT_OPERATING_SYSTEMS.map do |os_name|
|
204
205
|
# tty-prompt uses a 1-index
|
@@ -252,7 +253,7 @@ module PDK
|
|
252
253
|
else
|
253
254
|
questions.reject! { |q| q[:name] == 'module_name' } if opts.key?(:module_name)
|
254
255
|
questions.reject! { |q| q[:name] == 'license' } if opts.key?(:license)
|
255
|
-
questions.reject! { |q| q[:forge_only] } unless opts
|
256
|
+
questions.reject! { |q| q[:forge_only] } unless opts[:'full-interview']
|
256
257
|
end
|
257
258
|
|
258
259
|
interview.add_questions(questions)
|
data/lib/pdk/logger.rb
CHANGED
@@ -6,17 +6,37 @@ module PDK
|
|
6
6
|
end
|
7
7
|
|
8
8
|
class Logger < ::Logger
|
9
|
+
WRAP_COLUMN_LIMIT = 78
|
10
|
+
|
9
11
|
def initialize
|
10
12
|
super(STDERR)
|
13
|
+
@sent_messages = {}
|
11
14
|
|
12
15
|
# TODO: Decide on output format.
|
13
16
|
self.formatter = proc do |severity, _datetime, _progname, msg|
|
14
|
-
"pdk (#{severity}):
|
17
|
+
prefix = "pdk (#{severity}): "
|
18
|
+
if msg.is_a?(Hash)
|
19
|
+
if msg.fetch(:wrap, false)
|
20
|
+
wrap_pattern = %r{(.{1,#{WRAP_COLUMN_LIMIT - prefix.length}})(\s+|\Z)}
|
21
|
+
"#{prefix}#{msg[:text].gsub(wrap_pattern, "\\1\n#{' ' * prefix.length}")}\n"
|
22
|
+
else
|
23
|
+
"#{prefix}#{msg[:text]}\n"
|
24
|
+
end
|
25
|
+
else
|
26
|
+
"#{prefix}#{msg}\n"
|
27
|
+
end
|
15
28
|
end
|
16
29
|
|
17
30
|
self.level = ::Logger::INFO
|
18
31
|
end
|
19
32
|
|
33
|
+
def warn_once(*args)
|
34
|
+
hash = args.inspect.hash
|
35
|
+
return if (@sent_messages[::Logger::WARN] ||= {}).key?(hash)
|
36
|
+
@sent_messages[::Logger::WARN][hash] = true
|
37
|
+
warn(*args)
|
38
|
+
end
|
39
|
+
|
20
40
|
def enable_debug_output
|
21
41
|
self.level = ::Logger::DEBUG
|
22
42
|
end
|
data/lib/pdk/module/build.rb
CHANGED
@@ -120,8 +120,14 @@ module PDK
|
|
120
120
|
elsif File.symlink?(path)
|
121
121
|
warn_symlink(path)
|
122
122
|
else
|
123
|
+
validate_ustar_path!(relative_path.to_path)
|
123
124
|
FileUtils.cp(path, dest_path, preserve: true)
|
124
125
|
end
|
126
|
+
rescue ArgumentError => e
|
127
|
+
raise PDK::CLI::ExitWithError, _(
|
128
|
+
'%{message} Please rename the file or exclude it from the package ' \
|
129
|
+
'by adding it to the .pdkignore file in your module.',
|
130
|
+
) % { message: e.message }
|
125
131
|
end
|
126
132
|
|
127
133
|
# Check if the given path matches one of the patterns listed in the
|
@@ -152,6 +158,58 @@ module PDK
|
|
152
158
|
}
|
153
159
|
end
|
154
160
|
|
161
|
+
# Checks if the path length will fit into the POSIX.1-1998 (ustar) tar
|
162
|
+
# header format.
|
163
|
+
#
|
164
|
+
# POSIX.1-2001 (which allows paths of infinite length) was adopted by GNU
|
165
|
+
# tar in 2004 and is supported by minitar 0.7 and above. Unfortunately
|
166
|
+
# much of the Puppet ecosystem still uses minitar 0.6.1.
|
167
|
+
#
|
168
|
+
# POSIX.1-1998 tar format does not allow for paths greater than 256 bytes,
|
169
|
+
# or paths that can't be split into a prefix of 155 bytes (max) and
|
170
|
+
# a suffix of 100 bytes (max).
|
171
|
+
#
|
172
|
+
# This logic was pretty much copied from the private method
|
173
|
+
# {Archive::Tar::Minitar::Writer#split_name}.
|
174
|
+
#
|
175
|
+
# @param path [String] the relative path to be added to the tar file.
|
176
|
+
#
|
177
|
+
# @raise [ArgumentError] if the path is too long or could not be split.
|
178
|
+
#
|
179
|
+
# @return [nil]
|
180
|
+
def validate_ustar_path!(path)
|
181
|
+
if path.bytesize > 256
|
182
|
+
raise ArgumentError, _("The path '%{path}' is longer than 256 bytes.") % {
|
183
|
+
path: path,
|
184
|
+
}
|
185
|
+
end
|
186
|
+
|
187
|
+
if path.bytesize <= 100
|
188
|
+
prefix = ''
|
189
|
+
else
|
190
|
+
parts = path.split(File::SEPARATOR)
|
191
|
+
newpath = parts.pop
|
192
|
+
nxt = ''
|
193
|
+
|
194
|
+
loop do
|
195
|
+
nxt = parts.pop || ''
|
196
|
+
break if newpath.bytesize + 1 + nxt.bytesize >= 100
|
197
|
+
newpath = File.join(nxt, newpath)
|
198
|
+
end
|
199
|
+
|
200
|
+
prefix = File.join(*parts, nxt)
|
201
|
+
path = newpath
|
202
|
+
end
|
203
|
+
|
204
|
+
return unless path.bytesize > 100 || prefix.bytesize > 155
|
205
|
+
|
206
|
+
raise ArgumentError, _(
|
207
|
+
"'%{path}' could not be split at a directory separator into two " \
|
208
|
+
'parts, the first having a maximum length of 155 bytes and the ' \
|
209
|
+
'second having a maximum length of 100 bytes.',
|
210
|
+
) % { path: path }
|
211
|
+
end
|
212
|
+
|
155
213
|
# Creates a gzip compressed tarball of the build directory.
|
156
214
|
#
|
157
215
|
# If the destination package already exists, it will be removed before
|
data/lib/pdk/module/convert.rb
CHANGED
data/lib/pdk/module/metadata.rb
CHANGED
@@ -108,6 +108,7 @@ module PDK
|
|
108
108
|
raise ArgumentError, _('Invalid JSON in metadata.json: %{msg}') % { msg: e.message }
|
109
109
|
end
|
110
110
|
|
111
|
+
data['template-url'] = PDK::Util::TemplateURI.default_template_uri.metadata_format if PDK::Util.package_install? && data['template-url'] == PDK::Util::TemplateURI::PACKAGED_TEMPLATE_KEYWORD
|
111
112
|
new(data)
|
112
113
|
end
|
113
114
|
|
@@ -9,6 +9,7 @@ module PDK
|
|
9
9
|
module Module
|
10
10
|
class TemplateDir
|
11
11
|
attr_accessor :module_metadata
|
12
|
+
attr_reader :uri
|
12
13
|
|
13
14
|
# Initialises the TemplateDir object with the path or URL to the template
|
14
15
|
# and the block of code to run to be run while the template is available.
|
@@ -61,7 +62,7 @@ module PDK
|
|
61
62
|
}
|
62
63
|
end
|
63
64
|
end
|
64
|
-
@
|
65
|
+
@uri = uri
|
65
66
|
|
66
67
|
@init = init
|
67
68
|
@moduleroot_dir = File.join(@path, 'moduleroot')
|
@@ -74,6 +75,9 @@ module PDK
|
|
74
75
|
|
75
76
|
@module_metadata = module_metadata
|
76
77
|
|
78
|
+
template_type = uri.default? ? 'default' : 'custom'
|
79
|
+
PDK.analytics.event('TemplateDir', 'initialize', label: template_type)
|
80
|
+
|
77
81
|
yield self
|
78
82
|
ensure
|
79
83
|
# If we cloned a git repo to get the template, remove the clone once
|
@@ -94,7 +98,7 @@ module PDK
|
|
94
98
|
def metadata
|
95
99
|
{
|
96
100
|
'pdk-version' => PDK::Util::Version.version_string,
|
97
|
-
'template-url' =>
|
101
|
+
'template-url' => uri.metadata_format,
|
98
102
|
'template-ref' => cache_template_ref(@path),
|
99
103
|
}
|
100
104
|
end
|
@@ -212,6 +216,7 @@ module PDK
|
|
212
216
|
raise ArgumentError, _("The template at '%{path}' does not contain a 'moduleroot_init/' directory, which indicates you are using an older style of template. Before continuing please use the --template-url flag when running the pdk new commands to pass a new style template.") % { path: @path }
|
213
217
|
# rubocop:enable Metrics/LineLength Style/GuardClause
|
214
218
|
end
|
219
|
+
# rubocop:enable Style/GuardClause
|
215
220
|
end
|
216
221
|
|
217
222
|
# Get a list of template files in the template directory.
|
@@ -254,14 +259,28 @@ module PDK
|
|
254
259
|
|
255
260
|
if @config.nil?
|
256
261
|
conf_defaults = read_config(config_path)
|
257
|
-
sync_config = read_config(sync_config_path) unless sync_config_path.nil?
|
262
|
+
@sync_config = read_config(sync_config_path) unless sync_config_path.nil?
|
258
263
|
@config = conf_defaults
|
259
|
-
@config.deep_merge!(sync_config, knockout_prefix: '---') unless sync_config.nil?
|
264
|
+
@config.deep_merge!(@sync_config, knockout_prefix: '---') unless @sync_config.nil?
|
260
265
|
end
|
261
266
|
file_config = @config.fetch(:global, {})
|
262
267
|
file_config['module_metadata'] = @module_metadata
|
263
268
|
file_config.merge!(@config.fetch(dest_path, {})) unless dest_path.nil?
|
264
|
-
file_config.merge!(@config)
|
269
|
+
file_config.merge!(@config).tap do |c|
|
270
|
+
if uri.default?
|
271
|
+
file_value = if c['unmanaged']
|
272
|
+
'unmanaged'
|
273
|
+
elsif c['delete']
|
274
|
+
'deleted'
|
275
|
+
elsif @sync_config && @sync_config.key?(dest_path)
|
276
|
+
'customized'
|
277
|
+
else
|
278
|
+
'default'
|
279
|
+
end
|
280
|
+
|
281
|
+
PDK.analytics.event('TemplateDir', 'file', label: dest_path, value: file_value)
|
282
|
+
end
|
283
|
+
end
|
265
284
|
end
|
266
285
|
|
267
286
|
# Generates a hash of data from a given yaml file location.
|
@@ -73,7 +73,7 @@ module PDK
|
|
73
73
|
def changed?(path)
|
74
74
|
changes[:added].any? { |add| add[:path] == path } ||
|
75
75
|
changes[:removed].include?(path) ||
|
76
|
-
changes[:modified].
|
76
|
+
changes[:modified].key?(path)
|
77
77
|
end
|
78
78
|
|
79
79
|
# Apply any pending changes stored in the UpdateManager to the module.
|
@@ -174,7 +174,7 @@ module PDK
|
|
174
174
|
|
175
175
|
diffs = Diff::LCS.diff(old_lines, new_lines)
|
176
176
|
|
177
|
-
return
|
177
|
+
return if diffs.empty?
|
178
178
|
|
179
179
|
file_mtime = File.stat(path).mtime.localtime.strftime('%Y-%m-%d %H:%M:%S.%N %z')
|
180
180
|
now = Time.now.localtime.strftime('%Y-%m-%d %H:%M:%S.%N %z')
|
data/lib/pdk/report/event.rb
CHANGED
@@ -267,7 +267,7 @@ module PDK
|
|
267
267
|
# @return [Integer] the provided value, converted into an Integer if
|
268
268
|
# necessary.
|
269
269
|
def sanitise_line(value)
|
270
|
-
return
|
270
|
+
return if value.nil?
|
271
271
|
|
272
272
|
valid_types = [String, Integer]
|
273
273
|
if RUBY_VERSION.split('.')[0..1].join('.').to_f < 2.4
|
@@ -292,7 +292,7 @@ module PDK
|
|
292
292
|
# @return [Integer] the provided value, converted into an Integer if
|
293
293
|
# necessary.
|
294
294
|
def sanitise_column(value)
|
295
|
-
return
|
295
|
+
return if value.nil?
|
296
296
|
|
297
297
|
valid_types = [String, Integer]
|
298
298
|
if RUBY_VERSION.split('.')[0..1].join('.').to_f < 2.4
|
@@ -317,7 +317,7 @@ module PDK
|
|
317
317
|
#
|
318
318
|
# @return [Array] Array of stack trace lines with less relevant lines excluded
|
319
319
|
def sanitise_trace(value)
|
320
|
-
return
|
320
|
+
return if value.nil?
|
321
321
|
|
322
322
|
valid_types = [Array]
|
323
323
|
|