pdk 1.9.0 → 3.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/CHANGELOG.md +744 -711
- data/README.md +23 -21
- data/lib/pdk/answer_file.rb +3 -112
- data/lib/pdk/bolt.rb +20 -0
- data/lib/pdk/cli/build.rb +51 -54
- data/lib/pdk/cli/bundle.rb +33 -29
- data/lib/pdk/cli/console.rb +148 -0
- data/lib/pdk/cli/convert.rb +46 -37
- data/lib/pdk/cli/env.rb +51 -0
- data/lib/pdk/cli/errors.rb +4 -3
- data/lib/pdk/cli/exec/command.rb +285 -0
- data/lib/pdk/cli/exec/interactive_command.rb +109 -0
- data/lib/pdk/cli/exec.rb +32 -201
- data/lib/pdk/cli/exec_group.rb +79 -43
- data/lib/pdk/cli/get/config.rb +26 -0
- data/lib/pdk/cli/get.rb +22 -0
- data/lib/pdk/cli/new/class.rb +20 -22
- data/lib/pdk/cli/new/defined_type.rb +21 -21
- data/lib/pdk/cli/new/fact.rb +27 -0
- data/lib/pdk/cli/new/function.rb +27 -0
- data/lib/pdk/cli/new/module.rb +40 -29
- data/lib/pdk/cli/new/provider.rb +18 -18
- data/lib/pdk/cli/new/task.rb +23 -22
- data/lib/pdk/cli/new/test.rb +52 -0
- data/lib/pdk/cli/new/transport.rb +27 -0
- data/lib/pdk/cli/new.rb +15 -9
- data/lib/pdk/cli/release/prep.rb +39 -0
- data/lib/pdk/cli/release/publish.rb +46 -0
- data/lib/pdk/cli/release.rb +185 -0
- data/lib/pdk/cli/remove/config.rb +83 -0
- data/lib/pdk/cli/remove.rb +22 -0
- data/lib/pdk/cli/set/config.rb +121 -0
- data/lib/pdk/cli/set.rb +22 -0
- data/lib/pdk/cli/test/unit.rb +71 -69
- data/lib/pdk/cli/test.rb +9 -8
- data/lib/pdk/cli/update.rb +38 -21
- data/lib/pdk/cli/util/command_redirector.rb +13 -3
- data/lib/pdk/cli/util/interview.rb +25 -9
- data/lib/pdk/cli/util/option_normalizer.rb +6 -6
- data/lib/pdk/cli/util/option_validator.rb +19 -9
- data/lib/pdk/cli/util/spinner.rb +13 -0
- data/lib/pdk/cli/util/update_manager_printer.rb +82 -0
- data/lib/pdk/cli/util.rb +105 -48
- data/lib/pdk/cli/validate.rb +96 -111
- data/lib/pdk/cli.rb +134 -87
- data/lib/pdk/config/errors.rb +5 -0
- data/lib/pdk/config/ini_file.rb +184 -0
- data/lib/pdk/config/ini_file_setting.rb +35 -0
- data/lib/pdk/config/json.rb +35 -0
- data/lib/pdk/config/json_schema_namespace.rb +137 -0
- data/lib/pdk/config/json_schema_setting.rb +51 -0
- data/lib/pdk/config/json_with_schema.rb +47 -0
- data/lib/pdk/config/namespace.rb +362 -0
- data/lib/pdk/config/setting.rb +134 -0
- data/lib/pdk/config/task_schema.json +116 -0
- data/lib/pdk/config/validator.rb +31 -0
- data/lib/pdk/config/yaml.rb +41 -0
- data/lib/pdk/config/yaml_with_schema.rb +51 -0
- data/lib/pdk/config.rb +304 -0
- data/lib/pdk/context/control_repo.rb +61 -0
- data/lib/pdk/context/module.rb +28 -0
- data/lib/pdk/context/none.rb +22 -0
- data/lib/pdk/context.rb +98 -0
- data/lib/pdk/control_repo.rb +89 -0
- data/lib/pdk/generate/defined_type.rb +27 -33
- data/lib/pdk/generate/fact.rb +26 -0
- data/lib/pdk/generate/function.rb +49 -0
- data/lib/pdk/generate/module.rb +160 -153
- data/lib/pdk/generate/provider.rb +16 -69
- data/lib/pdk/generate/puppet_class.rb +27 -32
- data/lib/pdk/generate/puppet_object.rb +100 -159
- data/lib/pdk/generate/task.rb +34 -51
- data/lib/pdk/generate/transport.rb +34 -0
- data/lib/pdk/generate.rb +21 -8
- data/lib/pdk/logger.rb +24 -6
- data/lib/pdk/module/build.rb +125 -37
- data/lib/pdk/module/convert.rb +146 -65
- data/lib/pdk/module/metadata.rb +72 -71
- data/lib/pdk/module/release.rb +255 -0
- data/lib/pdk/module/update.rb +48 -37
- data/lib/pdk/module/update_manager.rb +75 -39
- data/lib/pdk/module.rb +10 -2
- data/lib/pdk/monkey_patches.rb +268 -0
- data/lib/pdk/report/event.rb +36 -48
- data/lib/pdk/report.rb +35 -22
- data/lib/pdk/template/fetcher/git.rb +84 -0
- data/lib/pdk/template/fetcher/local.rb +29 -0
- data/lib/pdk/template/fetcher.rb +100 -0
- data/lib/pdk/template/renderer/v1/legacy_template_dir.rb +108 -0
- data/lib/pdk/template/renderer/v1/renderer.rb +131 -0
- data/lib/pdk/template/renderer/v1/template_file.rb +100 -0
- data/lib/pdk/template/renderer/v1.rb +25 -0
- data/lib/pdk/template/renderer.rb +97 -0
- data/lib/pdk/template/template_dir.rb +67 -0
- data/lib/pdk/template.rb +52 -0
- data/lib/pdk/tests/unit.rb +101 -51
- data/lib/pdk/util/bundler.rb +44 -42
- data/lib/pdk/util/changelog_generator.rb +138 -0
- data/lib/pdk/util/env.rb +48 -0
- data/lib/pdk/util/filesystem.rb +139 -2
- data/lib/pdk/util/git.rb +108 -8
- data/lib/pdk/util/json_finder.rb +86 -0
- data/lib/pdk/util/puppet_strings.rb +125 -0
- data/lib/pdk/util/puppet_version.rb +71 -87
- data/lib/pdk/util/ruby_version.rb +49 -25
- data/lib/pdk/util/template_uri.rb +283 -0
- data/lib/pdk/util/vendored_file.rb +34 -42
- data/lib/pdk/util/version.rb +11 -10
- data/lib/pdk/util/windows/api_types.rb +74 -44
- data/lib/pdk/util/windows/file.rb +31 -27
- data/lib/pdk/util/windows/process.rb +74 -0
- data/lib/pdk/util/windows/string.rb +19 -12
- data/lib/pdk/util/windows.rb +2 -0
- data/lib/pdk/util.rb +111 -124
- data/lib/pdk/validate/control_repo/control_repo_validator_group.rb +23 -0
- data/lib/pdk/validate/control_repo/environment_conf_validator.rb +98 -0
- data/lib/pdk/validate/external_command_validator.rb +213 -0
- data/lib/pdk/validate/internal_ruby_validator.rb +101 -0
- data/lib/pdk/validate/invokable_validator.rb +238 -0
- data/lib/pdk/validate/metadata/metadata_json_lint_validator.rb +84 -0
- data/lib/pdk/validate/metadata/metadata_syntax_validator.rb +76 -0
- data/lib/pdk/validate/metadata/metadata_validator_group.rb +20 -0
- data/lib/pdk/validate/puppet/puppet_epp_validator.rb +131 -0
- data/lib/pdk/validate/puppet/puppet_lint_validator.rb +66 -0
- data/lib/pdk/validate/puppet/puppet_plan_syntax_validator.rb +38 -0
- data/lib/pdk/validate/puppet/puppet_syntax_validator.rb +135 -0
- data/lib/pdk/validate/puppet/puppet_validator_group.rb +22 -0
- data/lib/pdk/validate/ruby/ruby_rubocop_validator.rb +79 -0
- data/lib/pdk/validate/ruby/ruby_validator_group.rb +19 -0
- data/lib/pdk/validate/tasks/tasks_metadata_lint_validator.rb +83 -0
- data/lib/pdk/validate/tasks/tasks_name_validator.rb +45 -0
- data/lib/pdk/validate/tasks/tasks_validator_group.rb +20 -0
- data/lib/pdk/validate/validator.rb +120 -0
- data/lib/pdk/validate/validator_group.rb +107 -0
- data/lib/pdk/validate/yaml/yaml_syntax_validator.rb +86 -0
- data/lib/pdk/validate/yaml/yaml_validator_group.rb +19 -0
- data/lib/pdk/validate.rb +86 -12
- data/lib/pdk/version.rb +2 -2
- data/lib/pdk.rb +60 -10
- metadata +138 -100
- data/lib/pdk/cli/module/build.rb +0 -14
- data/lib/pdk/cli/module/generate.rb +0 -45
- data/lib/pdk/cli/module.rb +0 -14
- data/lib/pdk/i18n.rb +0 -4
- data/lib/pdk/module/templatedir.rb +0 -321
- data/lib/pdk/template_file.rb +0 -95
- data/lib/pdk/validate/base_validator.rb +0 -215
- data/lib/pdk/validate/metadata/metadata_json_lint.rb +0 -86
- data/lib/pdk/validate/metadata/metadata_syntax.rb +0 -109
- data/lib/pdk/validate/metadata_validator.rb +0 -30
- data/lib/pdk/validate/puppet/puppet_lint.rb +0 -67
- data/lib/pdk/validate/puppet/puppet_syntax.rb +0 -112
- data/lib/pdk/validate/puppet_validator.rb +0 -30
- data/lib/pdk/validate/ruby/rubocop.rb +0 -77
- data/lib/pdk/validate/ruby_validator.rb +0 -29
- data/lib/pdk/validate/tasks/metadata_lint.rb +0 -126
- data/lib/pdk/validate/tasks/name.rb +0 -88
- data/lib/pdk/validate/tasks_validator.rb +0 -33
- data/lib/pdk/validate/yaml/syntax.rb +0 -109
- data/lib/pdk/validate/yaml_validator.rb +0 -31
- data/locales/config.yaml +0 -21
- data/locales/pdk.pot +0 -1291
data/lib/pdk/cli.rb
CHANGED
@@ -1,113 +1,160 @@
|
|
1
1
|
require 'cri'
|
2
2
|
|
3
|
+
require 'pdk'
|
3
4
|
require 'pdk/cli/errors'
|
4
|
-
require 'pdk/cli/util'
|
5
|
-
require 'pdk/cli/util/command_redirector'
|
6
|
-
require 'pdk/cli/util/option_normalizer'
|
7
|
-
require 'pdk/cli/util/option_validator'
|
8
|
-
require 'pdk/cli/exec_group'
|
9
|
-
require 'pdk/generate/module'
|
10
|
-
require 'pdk/i18n'
|
11
|
-
require 'pdk/logger'
|
12
|
-
require 'pdk/report'
|
13
|
-
require 'pdk/util/version'
|
14
|
-
require 'pdk/util/puppet_version'
|
15
|
-
|
16
|
-
module PDK::CLI
|
17
|
-
def self.run(args)
|
18
|
-
@base_cmd.run(args)
|
19
|
-
rescue PDK::CLI::ExitWithError => e
|
20
|
-
PDK.logger.send(e.log_level, e.message)
|
21
|
-
|
22
|
-
exit e.exit_code
|
23
|
-
rescue PDK::CLI::FatalError => e
|
24
|
-
PDK.logger.fatal(e.message) if e.message
|
25
|
-
|
26
|
-
# If FatalError was raised as the result of another exception, send the
|
27
|
-
# details of that exception to the debug log. If there was no cause
|
28
|
-
# (FatalError raised on its own outside a rescue block), send the details
|
29
|
-
# of the FatalError exception to the debug log.
|
30
|
-
cause = e.cause
|
31
|
-
if cause.nil?
|
32
|
-
e.backtrace.each { |line| PDK.logger.debug(line) }
|
33
|
-
else
|
34
|
-
PDK.logger.debug("#{cause.class}: #{cause.message}")
|
35
|
-
cause.backtrace.each { |line| PDK.logger.debug(line) }
|
36
|
-
end
|
37
|
-
|
38
|
-
exit e.exit_code
|
39
|
-
end
|
40
5
|
|
41
|
-
|
42
|
-
|
6
|
+
module TTY
|
7
|
+
autoload :Prompt, 'tty/prompt'
|
43
8
|
|
44
|
-
|
9
|
+
class Prompt
|
10
|
+
autoload :Test, 'tty/prompt/test'
|
45
11
|
end
|
12
|
+
end
|
46
13
|
|
47
|
-
|
48
|
-
|
14
|
+
module Cri
|
15
|
+
class Command
|
16
|
+
class CriExitException
|
17
|
+
def initialize(is_error:)
|
18
|
+
@is_error = is_error
|
19
|
+
end
|
20
|
+
end
|
49
21
|
end
|
22
|
+
end
|
50
23
|
|
51
|
-
|
52
|
-
|
53
|
-
|
24
|
+
module PDK
|
25
|
+
module CLI
|
26
|
+
autoload :Util, 'pdk/cli/util'
|
27
|
+
|
28
|
+
# Attempt to anonymise the raw ARGV array if the command parsing failed.
|
29
|
+
#
|
30
|
+
# If an item does not start with '-' but is preceeded by an item that does
|
31
|
+
# start with '-', assume that these items are an option/value pair and redact
|
32
|
+
# the value. Any additional values that do not start with '-' that follow an
|
33
|
+
# option/value pair are assumed to be arguments (rather than subcommand
|
34
|
+
# names) and are also redacted.
|
35
|
+
#
|
36
|
+
# @example
|
37
|
+
# # Where PDK::CLI.args => ['new', 'plan', '--some', 'value', 'plan_name']
|
38
|
+
#
|
39
|
+
# PDK::CLI.anonymised_args
|
40
|
+
# => ['new', 'plan', '--some', 'redacted', 'redacted']
|
41
|
+
#
|
42
|
+
# @return Array[String] the command arguments with any identifying values
|
43
|
+
# redacted.
|
44
|
+
def self.anonymised_args
|
45
|
+
in_args = false
|
46
|
+
@args.map do |arg|
|
47
|
+
if arg.start_with?('-')
|
48
|
+
in_args = true
|
49
|
+
arg
|
50
|
+
else
|
51
|
+
in_args ? 'redacted' : arg
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
54
55
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
56
|
+
def self.run(args)
|
57
|
+
@args = args
|
58
|
+
@base_cmd.run(args)
|
59
|
+
rescue PDK::CLI::ExitWithError => e
|
60
|
+
PDK.logger.send(e.log_level, e.message)
|
61
|
+
|
62
|
+
exit e.exit_code
|
63
|
+
rescue PDK::CLI::FatalError => e
|
64
|
+
PDK.logger.fatal(e.message) if e.message
|
65
|
+
|
66
|
+
# If FatalError was raised as the result of another exception, send the
|
67
|
+
# details of that exception to the debug log. If there was no cause
|
68
|
+
# (FatalError raised on its own outside a rescue block), send the details
|
69
|
+
# of the FatalError exception to the debug log.
|
70
|
+
cause = e.cause
|
71
|
+
if cause.nil?
|
72
|
+
e.backtrace.each { |line| PDK.logger.debug(line) }
|
73
|
+
else
|
74
|
+
PDK.logger.debug("#{cause.class}: #{cause.message}")
|
75
|
+
cause.backtrace.each { |line| PDK.logger.debug(line) }
|
76
|
+
end
|
77
|
+
|
78
|
+
exit e.exit_code
|
79
|
+
end
|
59
80
|
|
60
|
-
|
61
|
-
|
62
|
-
'puppet-dev',
|
63
|
-
_('When specified, PDK will validate or test against the current Puppet source from github.com. To use this option, you must have network access to https://github.com.')
|
64
|
-
end
|
81
|
+
def self.template_url_option(dsl)
|
82
|
+
require 'pdk/util/template_uri'
|
65
83
|
|
66
|
-
|
67
|
-
name 'pdk'
|
68
|
-
usage _('pdk command [options]')
|
69
|
-
summary _('Puppet Development Kit')
|
70
|
-
description _('The shortest path to better modules.')
|
71
|
-
default_subcommand 'help'
|
84
|
+
desc = format('Specifies the URL to the template to use when creating new modules or classes. (default: %{default_url})', default_url: PDK::Util::TemplateURI.default_template_uri)
|
72
85
|
|
73
|
-
|
74
|
-
puts PDK::Util::Version.version_string
|
75
|
-
exit 0
|
86
|
+
dsl.option nil, 'template-url', desc, argument: :required
|
76
87
|
end
|
77
88
|
|
78
|
-
|
79
|
-
|
80
|
-
exit 0
|
89
|
+
def self.template_ref_option(dsl)
|
90
|
+
dsl.option nil, 'template-ref', 'Specifies the template git branch or tag to use when creating new modules or classes.', argument: :required
|
81
91
|
end
|
82
92
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
"for example: '--format=junit:report.xml'. This option may be specified " \
|
87
|
-
'multiple times if each option specifies a distinct target file.',
|
88
|
-
) % { available_formats: PDK::Report.formats.join("', '") }
|
93
|
+
def self.skip_interview_option(dsl)
|
94
|
+
dsl.option nil, 'skip-interview', 'When specified, skips interactive querying of metadata.'
|
95
|
+
end
|
89
96
|
|
90
|
-
|
91
|
-
|
97
|
+
def self.full_interview_option(dsl)
|
98
|
+
dsl.option nil, 'full-interview', 'When specified, interactive querying of metadata will include all optional questions.'
|
92
99
|
end
|
93
100
|
|
94
|
-
|
95
|
-
|
101
|
+
def self.puppet_version_options(dsl)
|
102
|
+
dsl.option nil, 'puppet-version', 'Puppet version to run tests or validations against.', argument: :required
|
103
|
+
dsl.option nil, 'pe-version', '(Deprecated) Puppet Enterprise version to run tests or validations against.', argument: :required
|
96
104
|
end
|
97
105
|
|
98
|
-
|
99
|
-
|
106
|
+
def self.puppet_dev_option(dsl)
|
107
|
+
dsl.option nil,
|
108
|
+
'puppet-dev',
|
109
|
+
'When specified, PDK will validate or test against the current Puppet source from github.com. To use this option, you must have network access to https://github.com.'
|
100
110
|
end
|
101
|
-
end
|
102
111
|
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
112
|
+
@base_cmd = Cri::Command.define do
|
113
|
+
name 'pdk'
|
114
|
+
usage 'pdk command [options]'
|
115
|
+
summary 'Puppet Development Kit'
|
116
|
+
description 'The shortest path to better modules.'
|
117
|
+
default_subcommand 'help'
|
118
|
+
|
119
|
+
flag nil, :version, 'Show version of pdk.' do |_, _|
|
120
|
+
puts PDK::Util::Version.version_string
|
121
|
+
exit 0
|
122
|
+
end
|
123
|
+
|
124
|
+
flag :h, :help, 'Show help for this command.' do |_, c|
|
125
|
+
puts c.help
|
126
|
+
exit 0
|
127
|
+
end
|
128
|
+
|
129
|
+
format_desc =
|
130
|
+
"Specify desired output format. Valid formats are '#{PDK::Report.formats.join("', '")}'. " \
|
131
|
+
'You may also specify a file to which the formatted output is sent, ' \
|
132
|
+
"for example: '--format=junit:report.xml'. This option may be specified " \
|
133
|
+
'multiple times if each option specifies a distinct target file.'
|
134
|
+
|
135
|
+
option :f, :format, format_desc, argument: :required, multiple: true do |values|
|
136
|
+
PDK::CLI::Util::OptionNormalizer.report_formats(values.compact)
|
137
|
+
end
|
138
|
+
|
139
|
+
flag :d, :debug, 'Enable debug output.' do |_, _|
|
140
|
+
PDK.logger.enable_debug_output
|
141
|
+
end
|
142
|
+
end
|
111
143
|
|
112
|
-
|
144
|
+
require 'pdk/cli/bundle'
|
145
|
+
require 'pdk/cli/build'
|
146
|
+
require 'pdk/cli/convert'
|
147
|
+
require 'pdk/cli/env'
|
148
|
+
require 'pdk/cli/get'
|
149
|
+
require 'pdk/cli/new'
|
150
|
+
require 'pdk/cli/set'
|
151
|
+
require 'pdk/cli/test'
|
152
|
+
require 'pdk/cli/update'
|
153
|
+
require 'pdk/cli/validate'
|
154
|
+
# require 'pdk/cli/console' Temporarily disabled while we work on the puppet-debugger gem. It will be back soon! (CONT-1154)
|
155
|
+
require 'pdk/cli/release'
|
156
|
+
require 'pdk/cli/remove'
|
157
|
+
|
158
|
+
@base_cmd.add_command Cri::Command.new_basic_help
|
159
|
+
end
|
113
160
|
end
|
@@ -0,0 +1,184 @@
|
|
1
|
+
require 'pdk'
|
2
|
+
|
3
|
+
module PDK
|
4
|
+
class Config
|
5
|
+
# Represents a configuration file using the INI file format
|
6
|
+
class IniFile < Namespace
|
7
|
+
# Ini Files have very strict valdiation rules which are set in the IniFileSetting class
|
8
|
+
# @see PDK::Config::Namespace.default_setting_class
|
9
|
+
def default_setting_class
|
10
|
+
PDK::Config::IniFileSetting
|
11
|
+
end
|
12
|
+
|
13
|
+
# Parses an IniFile document.
|
14
|
+
#
|
15
|
+
# @see PDK::Config::Namespace.parse_file
|
16
|
+
def parse_file(filename)
|
17
|
+
raise unless block_given?
|
18
|
+
|
19
|
+
data = load_data(filename)
|
20
|
+
return if data.nil? || data.empty?
|
21
|
+
|
22
|
+
ini_file = IniFileImpl.parse(data)
|
23
|
+
ini_file.to_hash.each do |name, value|
|
24
|
+
begin
|
25
|
+
new_setting = PDK::Config::IniFileSetting.new(name, self, value)
|
26
|
+
rescue StandardError
|
27
|
+
# We just ignore invalid initial settings
|
28
|
+
new_setting = PDK::Config::IniFileSetting.new(name, self, nil)
|
29
|
+
end
|
30
|
+
|
31
|
+
yield name, new_setting
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# Serializes object data into an INI file string.
|
36
|
+
#
|
37
|
+
# @see PDK::Config::Namespace.serialize_data
|
38
|
+
def serialize_data(data)
|
39
|
+
default_lines = ''
|
40
|
+
lines = ''
|
41
|
+
data.each do |name, value|
|
42
|
+
next if value.nil?
|
43
|
+
|
44
|
+
if value.is_a?(Hash)
|
45
|
+
# Hashes are an INI section
|
46
|
+
lines += "\n[#{name}]\n"
|
47
|
+
value.each do |child_name, child_value|
|
48
|
+
next if child_value.nil?
|
49
|
+
|
50
|
+
lines += "#{child_name} = #{munge_serialized_value(child_value)}\n"
|
51
|
+
end
|
52
|
+
else
|
53
|
+
default_lines += "#{name} = #{munge_serialized_value(value)}\n"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
default_lines + lines
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
def munge_serialized_value(value)
|
63
|
+
value = value.to_s unless value.is_a?(String)
|
64
|
+
# Add enclosing quotes if there's a space in the value
|
65
|
+
value = "\"#{value}\"" if value.include?(' ')
|
66
|
+
value
|
67
|
+
end
|
68
|
+
|
69
|
+
# Adapted from https://raw.githubusercontent.com/puppetlabs/puppet/6c257fc7827989c2af2901f974666f0f23611153/lib/puppet/settings/ini_file.rb
|
70
|
+
# rubocop:disable Style/PerlBackrefs
|
71
|
+
# rubocop:disable Style/RedundantSelf
|
72
|
+
# rubocop:disable Style/StringLiterals
|
73
|
+
class IniFileImpl
|
74
|
+
DEFAULT_SECTION_NAME = 'default_section_name'.freeze
|
75
|
+
|
76
|
+
def self.parse(config_fh)
|
77
|
+
config = new([DefaultSection.new])
|
78
|
+
config_fh.each_line do |line|
|
79
|
+
case line.chomp
|
80
|
+
when /^(\s*)\[([[:word:]]+)\](\s*)$/
|
81
|
+
config.append(SectionLine.new($1, $2, $3))
|
82
|
+
when /^(\s*)([[:word:]]+)(\s*=\s*)(.*?)(\s*)$/
|
83
|
+
config.append(SettingLine.new($1, $2, $3, $4, $5))
|
84
|
+
else
|
85
|
+
config.append(Line.new(line))
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
config
|
90
|
+
end
|
91
|
+
|
92
|
+
def initialize(lines = [])
|
93
|
+
@lines = lines
|
94
|
+
end
|
95
|
+
|
96
|
+
def to_hash
|
97
|
+
result = {}
|
98
|
+
|
99
|
+
current_section_name = nil
|
100
|
+
@lines.each do |line|
|
101
|
+
if line.instance_of?(SectionLine)
|
102
|
+
current_section_name = line.name
|
103
|
+
result[current_section_name] = {}
|
104
|
+
elsif line.instance_of?(SettingLine)
|
105
|
+
if current_section_name.nil?
|
106
|
+
result[line.name] = munge_value(line.value)
|
107
|
+
else
|
108
|
+
result[current_section_name][line.name] = munge_value(line.value)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
result
|
114
|
+
end
|
115
|
+
|
116
|
+
def munge_value(value)
|
117
|
+
value = value.to_s unless value.is_a?(String)
|
118
|
+
# Strip enclosing quotes
|
119
|
+
value = value.slice(1...-1) if value.start_with?('"') && value.end_with?('"')
|
120
|
+
value
|
121
|
+
end
|
122
|
+
|
123
|
+
def append(line)
|
124
|
+
line.previous = @lines.last
|
125
|
+
@lines << line
|
126
|
+
end
|
127
|
+
|
128
|
+
module LineNumber
|
129
|
+
attr_accessor :previous
|
130
|
+
|
131
|
+
def line_number
|
132
|
+
line = 0
|
133
|
+
previous_line = previous
|
134
|
+
while previous_line
|
135
|
+
line += 1
|
136
|
+
previous_line = previous_line.previous
|
137
|
+
end
|
138
|
+
line
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
Line = Struct.new(:text) do
|
143
|
+
include LineNumber
|
144
|
+
|
145
|
+
def to_s
|
146
|
+
text
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
SettingLine = Struct.new(:prefix, :name, :infix, :value, :suffix) do
|
151
|
+
include LineNumber
|
152
|
+
|
153
|
+
def to_s
|
154
|
+
"#{prefix}#{name}#{infix}#{value}#{suffix}"
|
155
|
+
end
|
156
|
+
|
157
|
+
def ==(other)
|
158
|
+
super(other) && self.line_number == other.line_number
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
SectionLine = Struct.new(:prefix, :name, :suffix) do
|
163
|
+
include LineNumber
|
164
|
+
|
165
|
+
def to_s
|
166
|
+
"#{prefix}[#{name}]#{suffix}"
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
class DefaultSection < SectionLine
|
171
|
+
attr_accessor :write_sectionline
|
172
|
+
|
173
|
+
def initialize
|
174
|
+
@write_sectionline = false
|
175
|
+
super("", DEFAULT_SECTION_NAME, "")
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
# rubocop:enable Style/StringLiterals
|
180
|
+
# rubocop:enable Style/RedundantSelf
|
181
|
+
# rubocop:enable Style/PerlBackrefs
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'pdk'
|
2
|
+
|
3
|
+
module PDK
|
4
|
+
class Config
|
5
|
+
class IniFileSetting < PDK::Config::Setting
|
6
|
+
# Initialises the PDK::Config::JSONSchemaSetting object.
|
7
|
+
#
|
8
|
+
# @see PDK::Config::Setting.initialize
|
9
|
+
def initialize(_name, namespace, initial_value = nil)
|
10
|
+
raise 'The IniFileSetting object can only be created within the IniFile Namespace' unless namespace.is_a?(PDK::Config::IniFile)
|
11
|
+
|
12
|
+
super
|
13
|
+
validate!(initial_value) unless initial_value.nil?
|
14
|
+
end
|
15
|
+
|
16
|
+
# Verifies that the new setting value is valid in an Ini File
|
17
|
+
#
|
18
|
+
# @see PDK::Config::Setting.validate!
|
19
|
+
def validate!(value)
|
20
|
+
# We're very restrictive here. Realistically Ini files only have string types
|
21
|
+
return if value.nil? || value.is_a?(String) || value.is_a?(Integer)
|
22
|
+
|
23
|
+
# The only other valid-ish type is a Hash
|
24
|
+
raise ArgumentError, format('The setting %{key} may only be a String or Integer, not %{class}', key: qualified_name, class: value.class) unless value.is_a?(Hash)
|
25
|
+
|
26
|
+
# Any hashes can only have a single String/Integer value
|
27
|
+
value.each do |child_name, child_value|
|
28
|
+
next if child_value.nil? || child_value.is_a?(String) || child_value.is_a?(Integer)
|
29
|
+
|
30
|
+
raise ArgumentError, format('The setting %{key} may only be a String or Integer, not %{class}', key: "#{qualified_name}.#{child_name}", class: child_value.class)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'pdk'
|
2
|
+
|
3
|
+
module PDK
|
4
|
+
class Config
|
5
|
+
class JSON < Namespace
|
6
|
+
# Parses a JSON document.
|
7
|
+
#
|
8
|
+
# @see PDK::Config::Namespace.parse_file
|
9
|
+
def parse_file(filename)
|
10
|
+
raise unless block_given?
|
11
|
+
|
12
|
+
data = load_data(filename)
|
13
|
+
return if data.nil? || data.empty?
|
14
|
+
|
15
|
+
require 'json'
|
16
|
+
|
17
|
+
data = ::JSON.parse(data)
|
18
|
+
return if data.nil? || data.empty?
|
19
|
+
|
20
|
+
data.each { |k, v| yield k, PDK::Config::Setting.new(k, self, v) }
|
21
|
+
rescue ::JSON::ParserError => e
|
22
|
+
raise PDK::Config::LoadError, e.message
|
23
|
+
end
|
24
|
+
|
25
|
+
# Serializes object data into a JSON string.
|
26
|
+
#
|
27
|
+
# @see PDK::Config::Namespace.serialize_data
|
28
|
+
def serialize_data(data)
|
29
|
+
require 'json'
|
30
|
+
|
31
|
+
::JSON.pretty_generate(data)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,137 @@
|
|
1
|
+
require 'pdk'
|
2
|
+
|
3
|
+
# Due to https://github.com/ruby-json-schema/json-schema/issues/439
|
4
|
+
# Windows file paths "appear" as uri's with no host and a schema of drive letter
|
5
|
+
# Also it is not possible to craft a URI with a Windows path due to the URI object
|
6
|
+
# always prepending the path with forward slash (`/`) so Windows paths end up looking
|
7
|
+
# like '/C:\schema.json', which can not be read.
|
8
|
+
# Instead we need to monkey patch the Schema Reader reader to remove the errant forward slash
|
9
|
+
require 'json-schema/schema/reader'
|
10
|
+
module JSON
|
11
|
+
class Schema
|
12
|
+
class Reader
|
13
|
+
alias original_read_file read_file
|
14
|
+
|
15
|
+
def read_file(pathname)
|
16
|
+
new_pathname = JSON::Util::URI.unescaped_path(pathname.to_s)
|
17
|
+
# Munge the path if it looks like a Windows path e.g. /C:/Windows ...
|
18
|
+
# Note that UNC style paths do not have the same issue (\\host\path)
|
19
|
+
new_pathname.slice!(0) if new_pathname.start_with?('/') && new_pathname[2] == ':'
|
20
|
+
original_read_file(Pathname.new(new_pathname))
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
require 'json-schema'
|
27
|
+
|
28
|
+
module PDK
|
29
|
+
class Config
|
30
|
+
class JSONSchemaNamespace < Namespace
|
31
|
+
# Initialises the PDK::Config::JSONSchemaNamespace object.
|
32
|
+
#
|
33
|
+
# @see PDK::Config::Namespace.initialize
|
34
|
+
#
|
35
|
+
# @option params [String] :schema_file Path to the JSON Schema document
|
36
|
+
def initialize(name = nil, file: nil, parent: nil, persistent_defaults: false, schema_file: nil, &block)
|
37
|
+
super(name, file: file, parent: parent, persistent_defaults: persistent_defaults, &block)
|
38
|
+
@schema_file = schema_file
|
39
|
+
@unmanaged_settings = {}
|
40
|
+
end
|
41
|
+
|
42
|
+
# The JSON Schema for the namespace
|
43
|
+
#
|
44
|
+
# @return [Hash]
|
45
|
+
def schema
|
46
|
+
document_schema.schema
|
47
|
+
end
|
48
|
+
|
49
|
+
# Whether the schema is valid but empty.
|
50
|
+
#
|
51
|
+
# @return [Boolean]
|
52
|
+
def empty_schema?
|
53
|
+
document_schema.schema.empty?
|
54
|
+
end
|
55
|
+
|
56
|
+
# Name of all the top level properties for the schema
|
57
|
+
#
|
58
|
+
# @return [String[]]
|
59
|
+
def schema_property_names
|
60
|
+
return [] if schema['properties'].nil?
|
61
|
+
|
62
|
+
schema['properties'].keys
|
63
|
+
end
|
64
|
+
|
65
|
+
# Extends the to_h namespace method to include unmanaged settings
|
66
|
+
#
|
67
|
+
# @see PDK::Config::Namespace.to_h
|
68
|
+
def to_h
|
69
|
+
# This may seem counter-intuitive but we need to call super first as the settings
|
70
|
+
# may not have been loaded yet, which means @unmanaged_settings will be empty.
|
71
|
+
# We call super first to force any file loading and then merge the unmanaged settings
|
72
|
+
settings_hash = super
|
73
|
+
@unmanaged_settings = {} if @unmanaged_settings.nil?
|
74
|
+
@unmanaged_settings.merge(settings_hash)
|
75
|
+
end
|
76
|
+
|
77
|
+
# Validates a document (Hash table) against the schema
|
78
|
+
#
|
79
|
+
# @return [Boolean]
|
80
|
+
def validate_document!(document)
|
81
|
+
::JSON::Validator.validate!(schema, document)
|
82
|
+
end
|
83
|
+
|
84
|
+
protected
|
85
|
+
|
86
|
+
# @!attribute [w] unmanaged_settings
|
87
|
+
# Sets the list of unmanaged settings. For subclass use only
|
88
|
+
#
|
89
|
+
# @param unmanaged_settings [Hash<String, Object]] A hashtable of all unmanaged settings which will be persisted, but not visible
|
90
|
+
# @protected
|
91
|
+
attr_writer :unmanaged_settings
|
92
|
+
|
93
|
+
private
|
94
|
+
|
95
|
+
# Override the create_setting method to always fail. This is called
|
96
|
+
# to dyanmically add settings. However as we're using a schema, no
|
97
|
+
# new settings can be created
|
98
|
+
#
|
99
|
+
# @see PDK::Config::Namespace.create_missing_setting
|
100
|
+
#
|
101
|
+
# @private
|
102
|
+
def create_missing_setting(key, _initial_value = nil)
|
103
|
+
raise ArgumentError, "Setting '#{key}' does not exist'"
|
104
|
+
end
|
105
|
+
|
106
|
+
# Create a valid, but empty schema
|
107
|
+
#
|
108
|
+
# @return [JSON::Schema]
|
109
|
+
def create_empty_schema
|
110
|
+
::JSON::Schema.new({}, 'http://json-schema.org/draft-06/schema#')
|
111
|
+
end
|
112
|
+
|
113
|
+
# Lazily retrieve the JSON schema from disk for this namespace
|
114
|
+
#
|
115
|
+
# @return [JSON::Schema]
|
116
|
+
def document_schema
|
117
|
+
return @document_schema unless @document_schema.nil?
|
118
|
+
|
119
|
+
# Create an empty schema by default.
|
120
|
+
@document_schema = create_empty_schema
|
121
|
+
|
122
|
+
return @document_schema if @schema_file.nil?
|
123
|
+
|
124
|
+
raise PDK::Config::LoadError, format('Unable to open %{file} for reading. File does not exist', file: @schema_file) unless PDK::Util::Filesystem.file?(@schema_file)
|
125
|
+
|
126
|
+
# The schema should not query external URI references, except for the meta-schema. Local files are allowed
|
127
|
+
schema_reader = ::JSON::Schema::Reader.new(
|
128
|
+
accept_file: true,
|
129
|
+
accept_uri: proc { |uri| uri.host.nil? || ['json-schema.org'].include?(uri.host) }
|
130
|
+
)
|
131
|
+
@document_schema = schema_reader.read(Addressable::URI.convert_path(@schema_file))
|
132
|
+
rescue ::JSON::Schema::JsonParseError => e
|
133
|
+
raise PDK::Config::LoadError, format('Unable to open %{file} for reading. JSON Error: %{msg}', file: @schema_file, msg: e.message)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|