puppet-editor-services 2.0.4
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 +7 -0
- data/CHANGELOG.md +510 -0
- data/CODEOWNERS +2 -0
- data/CODE_OF_CONDUCT.md +46 -0
- data/CONTRIBUTING.md +54 -0
- data/Gemfile +53 -0
- data/LICENSE +201 -0
- data/README.md +308 -0
- data/Rakefile +185 -0
- data/bin/puppet-debugserver +8 -0
- data/bin/puppet-languageserver +7 -0
- data/bin/puppet-languageserver-sidecar +7 -0
- data/lib/dsp/dsp.rb +7 -0
- data/lib/dsp/dsp_base.rb +62 -0
- data/lib/dsp/dsp_protocol.rb +4619 -0
- data/lib/lsp/lsp.rb +10 -0
- data/lib/lsp/lsp_base.rb +63 -0
- data/lib/lsp/lsp_custom.rb +170 -0
- data/lib/lsp/lsp_enums.rb +143 -0
- data/lib/lsp/lsp_protocol.rb +2785 -0
- data/lib/lsp/lsp_protocol_callhierarchy.proposed.rb +239 -0
- data/lib/lsp/lsp_protocol_colorprovider.rb +100 -0
- data/lib/lsp/lsp_protocol_configuration.rb +82 -0
- data/lib/lsp/lsp_protocol_declaration.rb +73 -0
- data/lib/lsp/lsp_protocol_foldingrange.rb +129 -0
- data/lib/lsp/lsp_protocol_implementation.rb +75 -0
- data/lib/lsp/lsp_protocol_progress.rb +200 -0
- data/lib/lsp/lsp_protocol_selectionrange.rb +79 -0
- data/lib/lsp/lsp_protocol_sematictokens.proposed.rb +340 -0
- data/lib/lsp/lsp_protocol_typedefinition.rb +75 -0
- data/lib/lsp/lsp_protocol_workspacefolders.rb +174 -0
- data/lib/lsp/lsp_types.rb +1534 -0
- data/lib/puppet-debugserver/debug_session/break_points.rb +137 -0
- data/lib/puppet-debugserver/debug_session/flow_control.rb +161 -0
- data/lib/puppet-debugserver/debug_session/hook_handlers.rb +295 -0
- data/lib/puppet-debugserver/debug_session/puppet_session_run_mode.rb +66 -0
- data/lib/puppet-debugserver/debug_session/puppet_session_state.rb +122 -0
- data/lib/puppet-debugserver/hooks.rb +132 -0
- data/lib/puppet-debugserver/message_handler.rb +277 -0
- data/lib/puppet-debugserver/puppet_debug_session.rb +541 -0
- data/lib/puppet-debugserver/puppet_monkey_patches.rb +118 -0
- data/lib/puppet-languageserver/client_session_state.rb +119 -0
- data/lib/puppet-languageserver/crash_dump.rb +50 -0
- data/lib/puppet-languageserver/epp/validation_provider.rb +34 -0
- data/lib/puppet-languageserver/facter_helper.rb +25 -0
- data/lib/puppet-languageserver/global_queues/sidecar_queue.rb +205 -0
- data/lib/puppet-languageserver/global_queues/single_instance_queue.rb +126 -0
- data/lib/puppet-languageserver/global_queues/validation_queue.rb +102 -0
- data/lib/puppet-languageserver/global_queues.rb +16 -0
- data/lib/puppet-languageserver/manifest/completion_provider.rb +331 -0
- data/lib/puppet-languageserver/manifest/definition_provider.rb +99 -0
- data/lib/puppet-languageserver/manifest/document_symbol_provider.rb +228 -0
- data/lib/puppet-languageserver/manifest/folding_provider.rb +226 -0
- data/lib/puppet-languageserver/manifest/format_on_type_provider.rb +143 -0
- data/lib/puppet-languageserver/manifest/hover_provider.rb +221 -0
- data/lib/puppet-languageserver/manifest/signature_provider.rb +169 -0
- data/lib/puppet-languageserver/manifest/validation_provider.rb +127 -0
- data/lib/puppet-languageserver/message_handler.rb +462 -0
- data/lib/puppet-languageserver/providers.rb +18 -0
- data/lib/puppet-languageserver/puppet_helper.rb +108 -0
- data/lib/puppet-languageserver/puppet_lexer_helper.rb +55 -0
- data/lib/puppet-languageserver/puppet_monkey_patches.rb +39 -0
- data/lib/puppet-languageserver/puppet_parser_helper.rb +212 -0
- data/lib/puppet-languageserver/puppetfile/validation_provider.rb +185 -0
- data/lib/puppet-languageserver/server_capabilities.rb +48 -0
- data/lib/puppet-languageserver/session_state/document_store.rb +272 -0
- data/lib/puppet-languageserver/session_state/language_client.rb +239 -0
- data/lib/puppet-languageserver/session_state/object_cache.rb +162 -0
- data/lib/puppet-languageserver/sidecar_protocol.rb +532 -0
- data/lib/puppet-languageserver/uri_helper.rb +46 -0
- data/lib/puppet-languageserver-sidecar/cache/base.rb +36 -0
- data/lib/puppet-languageserver-sidecar/cache/filesystem.rb +111 -0
- data/lib/puppet-languageserver-sidecar/cache/null.rb +27 -0
- data/lib/puppet-languageserver-sidecar/facter_helper.rb +41 -0
- data/lib/puppet-languageserver-sidecar/puppet_environment_monkey_patches.rb +52 -0
- data/lib/puppet-languageserver-sidecar/puppet_helper.rb +281 -0
- data/lib/puppet-languageserver-sidecar/puppet_modulepath_monkey_patches.rb +146 -0
- data/lib/puppet-languageserver-sidecar/puppet_monkey_patches.rb +9 -0
- data/lib/puppet-languageserver-sidecar/puppet_parser_helper.rb +77 -0
- data/lib/puppet-languageserver-sidecar/puppet_strings_helper.rb +399 -0
- data/lib/puppet-languageserver-sidecar/puppet_strings_monkey_patches.rb +16 -0
- data/lib/puppet-languageserver-sidecar/sidecar_protocol_extensions.rb +16 -0
- data/lib/puppet-languageserver-sidecar/workspace.rb +89 -0
- data/lib/puppet_debugserver.rb +164 -0
- data/lib/puppet_editor_services/connection/base.rb +62 -0
- data/lib/puppet_editor_services/connection/stdio.rb +25 -0
- data/lib/puppet_editor_services/connection/tcp.rb +34 -0
- data/lib/puppet_editor_services/handler/base.rb +16 -0
- data/lib/puppet_editor_services/handler/debug_adapter.rb +63 -0
- data/lib/puppet_editor_services/handler/json_rpc.rb +133 -0
- data/lib/puppet_editor_services/logging.rb +45 -0
- data/lib/puppet_editor_services/protocol/base.rb +27 -0
- data/lib/puppet_editor_services/protocol/debug_adapter.rb +135 -0
- data/lib/puppet_editor_services/protocol/debug_adapter_messages.rb +171 -0
- data/lib/puppet_editor_services/protocol/json_rpc.rb +241 -0
- data/lib/puppet_editor_services/protocol/json_rpc_messages.rb +200 -0
- data/lib/puppet_editor_services/server/base.rb +42 -0
- data/lib/puppet_editor_services/server/stdio.rb +85 -0
- data/lib/puppet_editor_services/server/tcp.rb +349 -0
- data/lib/puppet_editor_services/server.rb +15 -0
- data/lib/puppet_editor_services/version.rb +36 -0
- data/lib/puppet_editor_services.rb +8 -0
- data/lib/puppet_languageserver.rb +263 -0
- data/lib/puppet_languageserver_sidecar.rb +361 -0
- data/puppet-debugserver +11 -0
- data/puppet-editor-services.gemspec +29 -0
- data/puppet-languageserver +15 -0
- data/puppet-languageserver-sidecar +14 -0
- metadata +240 -0
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module PuppetEditorServices
|
|
4
|
+
PUPPETEDITORSERVICESVERSION = '2.0.4' unless defined? PUPPETEDITORSERVICESVERSION
|
|
5
|
+
|
|
6
|
+
# @api public
|
|
7
|
+
#
|
|
8
|
+
# @return [String] containing the editor services version, e.g. "0.4.0"
|
|
9
|
+
def self.version
|
|
10
|
+
return @editor_services_version if @editor_services_version
|
|
11
|
+
|
|
12
|
+
version_file = File.join(File.dirname(__FILE__), 'VERSION')
|
|
13
|
+
version = read_version_file(version_file)
|
|
14
|
+
|
|
15
|
+
@editor_services_version = version.nil? ? PUPPETEDITORSERVICESVERSION : version
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Sets the editor services version
|
|
19
|
+
# Typically only used in testing
|
|
20
|
+
#
|
|
21
|
+
# @return [void]
|
|
22
|
+
#
|
|
23
|
+
# @api private
|
|
24
|
+
def self.version=(version)
|
|
25
|
+
@editor_services_version = version
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# @api private
|
|
29
|
+
#
|
|
30
|
+
# @return [String] the version -- for example: "0.4.0" or nil if the VERSION
|
|
31
|
+
# file does not exist.
|
|
32
|
+
def self.read_version_file(path)
|
|
33
|
+
File.read(path).chomp if File.exist?(path)
|
|
34
|
+
end
|
|
35
|
+
private_class_method :read_version_file
|
|
36
|
+
end
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
begin
|
|
4
|
+
original_verbose = $VERBOSE
|
|
5
|
+
$VERBOSE = nil
|
|
6
|
+
|
|
7
|
+
require 'puppet_editor_services'
|
|
8
|
+
require 'optparse'
|
|
9
|
+
require 'logger'
|
|
10
|
+
ensure
|
|
11
|
+
$VERBOSE = original_verbose
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
module PuppetLanguageServer
|
|
15
|
+
def self.version
|
|
16
|
+
PuppetEditorServices.version
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Whether the language server is actually in a state that can be used.
|
|
20
|
+
# Typically this is false when a catastrophic error occurs during startup e.g. Puppet is missing.
|
|
21
|
+
#
|
|
22
|
+
# @return [Bool] Whether the language server is actually in a state that can be used
|
|
23
|
+
def self.active?
|
|
24
|
+
@server_is_active
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def self.static_data_dir
|
|
28
|
+
@static_data_dir ||= File.expand_path(File.join(__dir__, 'puppet-languageserver', 'static_data'))
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def self.configure_featureflags(flags)
|
|
32
|
+
@flags = flags
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def self.featureflag?(flagname)
|
|
36
|
+
return false if @flags.nil? || @flags.empty?
|
|
37
|
+
|
|
38
|
+
@flags.include?(flagname)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def self.require_gems(options)
|
|
42
|
+
original_verbose = $VERBOSE
|
|
43
|
+
$VERBOSE = nil
|
|
44
|
+
@server_is_active = false
|
|
45
|
+
|
|
46
|
+
# Use specific Puppet Gem version if possible
|
|
47
|
+
unless options[:puppet_version].nil?
|
|
48
|
+
available_puppet_gems = Gem::Specification
|
|
49
|
+
.select { |item| item.name.casecmp('puppet').zero? }
|
|
50
|
+
.map { |item| item.version.to_s }
|
|
51
|
+
if available_puppet_gems.include?(options[:puppet_version])
|
|
52
|
+
gem 'puppet', options[:puppet_version]
|
|
53
|
+
else
|
|
54
|
+
log_message(:warn, "Unable to use puppet version #{options[:puppet_version]}, as only the following versions are available [#{available_puppet_gems.join(', ')}]")
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# These libraries do not require the puppet gem and required for the
|
|
59
|
+
# server to respond to clients.
|
|
60
|
+
%w[
|
|
61
|
+
client_session_state
|
|
62
|
+
crash_dump
|
|
63
|
+
message_handler
|
|
64
|
+
server_capabilities
|
|
65
|
+
].each do |lib|
|
|
66
|
+
require "puppet-languageserver/#{lib}"
|
|
67
|
+
rescue LoadError
|
|
68
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'puppet-languageserver', lib))
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
begin
|
|
72
|
+
require 'lsp/lsp'
|
|
73
|
+
require 'puppet'
|
|
74
|
+
require 'facter'
|
|
75
|
+
rescue LoadError => e
|
|
76
|
+
log_message(:error, "Error while loading a critical gem: #{e} #{e.backtrace}")
|
|
77
|
+
return
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# These libraries require the puppet and LSP gems.
|
|
81
|
+
%w[
|
|
82
|
+
sidecar_protocol
|
|
83
|
+
global_queues
|
|
84
|
+
puppet_lexer_helper
|
|
85
|
+
puppet_parser_helper
|
|
86
|
+
puppet_helper
|
|
87
|
+
facter_helper
|
|
88
|
+
uri_helper
|
|
89
|
+
puppet_monkey_patches
|
|
90
|
+
providers
|
|
91
|
+
].each do |lib|
|
|
92
|
+
require "puppet-languageserver/#{lib}"
|
|
93
|
+
rescue LoadError
|
|
94
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'puppet-languageserver', lib))
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# Validate the feature flags
|
|
98
|
+
unless options[:flags].nil? || options[:flags].empty?
|
|
99
|
+
flags = options[:flags]
|
|
100
|
+
log_message(:debug, "Detected feature flags [#{flags.join(', ')}]")
|
|
101
|
+
|
|
102
|
+
configure_featureflags(flags)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
@server_is_active = true
|
|
106
|
+
ensure
|
|
107
|
+
$VERBOSE = original_verbose
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
class CommandLineParser
|
|
111
|
+
def self.parse(options)
|
|
112
|
+
# Set defaults here
|
|
113
|
+
args = {
|
|
114
|
+
connection_timeout: 10,
|
|
115
|
+
debug: nil,
|
|
116
|
+
disable_sidecar_cache: false,
|
|
117
|
+
fast_start_langserver: true,
|
|
118
|
+
flags: [],
|
|
119
|
+
ipaddress: 'localhost',
|
|
120
|
+
port: nil,
|
|
121
|
+
puppet_version: nil,
|
|
122
|
+
puppet_settings: [],
|
|
123
|
+
preload_puppet: true,
|
|
124
|
+
stdio: false,
|
|
125
|
+
stop_on_client_exit: true,
|
|
126
|
+
workspace: nil
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
opt_parser = OptionParser.new do |opts|
|
|
130
|
+
opts.banner = 'Usage: puppet-languageserver.rb [options]'
|
|
131
|
+
|
|
132
|
+
opts.on('-pPORT', '--port=PORT', 'TCP Port to listen on. Default is random port') do |port|
|
|
133
|
+
args[:port] = port.to_i
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
opts.on('-ipADDRESS', '--ip=ADDRESS', "IP Address to listen on (0.0.0.0 for all interfaces). Default is #{args[:ipaddress]}") do |ipaddress|
|
|
137
|
+
args[:ipaddress] = ipaddress
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
opts.on('-c', '--no-stop', 'Do not stop the language server once a client disconnects. Default is to stop') do |_misc|
|
|
141
|
+
args[:stop_on_client_exit] = false
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
opts.on('-tTIMEOUT', '--timeout=TIMEOUT', "Stop the language server if a client does not connection within TIMEOUT seconds. A value of zero will not timeout. Default is #{args[:connection_timeout]} seconds") do |timeout|
|
|
145
|
+
args[:connection_timeout] = timeout.to_i
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
opts.on('-d', '--no-preload', '** DEPRECATED ** Do not preload Puppet information when the language server starts. Default is to preload') do |_misc|
|
|
149
|
+
puts '** WARNING ** Using "--no-preload" may cause Puppet Type loading to be incomplete.'
|
|
150
|
+
args[:preload_puppet] = false
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
opts.on('--debug=DEBUG', "Output debug information. Either specify a filename or 'STDOUT'. Default is no debug output") do |debug|
|
|
154
|
+
args[:debug] = debug
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
opts.on('-s', '--slow-start', '** DEPRECATED ** Delay starting the Language Server until Puppet initialisation has completed. Default is to start fast') do |_misc|
|
|
158
|
+
args[:fast_start_langserver] = false
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
opts.on('--stdio', 'Runs the server in stdio mode, without a TCP listener') do |_misc|
|
|
162
|
+
args[:stdio] = true
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
opts.on('--enable-file-cache', '** DEPRECATED ** Enables the file system cache for Puppet Objects (types, class etc.)') do |_misc|
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
# These options are normally passed through to the Sidecar
|
|
169
|
+
opts.on('--[no-]cache', 'Enable or disable all caching inside the sidecar. By default caching is enabled.') do |cache|
|
|
170
|
+
args[:disable_sidecar_cache] = !cache
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
opts.on('--feature-flags=FLAGS', Array, 'A list of comma delimited feature flags') do |flags|
|
|
174
|
+
args[:flags] = flags
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
opts.on('--puppet-settings=TEXT', Array, 'Comma delimited list of settings to pass into Puppet e.g. --vardir,/opt/test-fixture') do |text|
|
|
178
|
+
args[:puppet_settings] = text
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
opts.on('--puppet-version=TEXT', String, 'The version of the Puppet Gem to use (defaults to latest version if not specified or the version does not exist) e.g. --puppet-version=5.4.0') do |text|
|
|
182
|
+
args[:puppet_version] = text
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
opts.on('--local-workspace=PATH', '** DEPRECATED ** The workspace or file path that will be used to provide module-specific functionality. Default is no workspace path.') do |_path|
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
opts.on('-h', '--help', 'Prints this help') do
|
|
189
|
+
puts opts
|
|
190
|
+
exit
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
opts.on('-v', '--version', 'Prints the Langauge Server version') do
|
|
194
|
+
puts PuppetLanguageServer.version
|
|
195
|
+
exit
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
opt_parser.parse!(options.dup)
|
|
200
|
+
args
|
|
201
|
+
end
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
def self.log_message(severity, message)
|
|
205
|
+
PuppetEditorServices.log_message(severity, message)
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
def self.init_puppet(options)
|
|
209
|
+
PuppetEditorServices.init_logging(options)
|
|
210
|
+
log_message(:info, "Language Server is v#{PuppetEditorServices.version}")
|
|
211
|
+
log_message(:debug, 'Loading gems...')
|
|
212
|
+
require_gems(options)
|
|
213
|
+
return unless active?
|
|
214
|
+
|
|
215
|
+
log_message(:info, "Using Puppet v#{Puppet.version}")
|
|
216
|
+
log_message(:info, "Using Facter v#{Facter.version}")
|
|
217
|
+
|
|
218
|
+
raise("Detected Puppet #{Puppet.version} however the Language Server requires Puppet 5.0 and above") if Gem::Version.new(Puppet.version) < Gem::Version.new('5.0.0')
|
|
219
|
+
|
|
220
|
+
log_message(:debug, "Detected additional puppet settings #{options[:puppet_settings]}")
|
|
221
|
+
options[:puppet_settings].nil? ? Puppet.initialize_settings : Puppet.initialize_settings(options[:puppet_settings])
|
|
222
|
+
|
|
223
|
+
log_message(:info, 'Initializing settings...')
|
|
224
|
+
|
|
225
|
+
# Remove all other logging destinations except for ours
|
|
226
|
+
Puppet::Util::Log.destinations.clear
|
|
227
|
+
Puppet::Util::Log.newdestination('null_logger')
|
|
228
|
+
|
|
229
|
+
true
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
def self.rpc_server(options)
|
|
233
|
+
log_message(:info, 'Starting RPC Server...')
|
|
234
|
+
options[:servicename] = 'LANGUAGE SERVER'
|
|
235
|
+
|
|
236
|
+
require 'puppet_editor_services/protocol/json_rpc'
|
|
237
|
+
|
|
238
|
+
server_options = options
|
|
239
|
+
protocol_options = { class: PuppetEditorServices::Protocol::JsonRPC }.merge(options)
|
|
240
|
+
handler_options = { class: PuppetLanguageServer::MessageHandler }.merge(options)
|
|
241
|
+
|
|
242
|
+
unless active?
|
|
243
|
+
handler_options[:class] = PuppetLanguageServer::DisabledMessageHandler
|
|
244
|
+
log_message(:info, 'Configured the Language Server to use the Disabled Message Router')
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
if options[:stdio]
|
|
248
|
+
log_message(:debug, 'Using STDIO Server')
|
|
249
|
+
require 'puppet_editor_services/server/stdio'
|
|
250
|
+
server = ::PuppetEditorServices::Server::Stdio.new(server_options, protocol_options, handler_options)
|
|
251
|
+
trap('INT') { server.stop }
|
|
252
|
+
else
|
|
253
|
+
log_message(:debug, 'Using TCP Server')
|
|
254
|
+
require 'puppet_editor_services/server/tcp'
|
|
255
|
+
# TODO: Add max threads?
|
|
256
|
+
server = ::PuppetEditorServices::Server::Tcp.new(server_options, protocol_options, handler_options)
|
|
257
|
+
trap('INT') { server.stop_services(true) }
|
|
258
|
+
end
|
|
259
|
+
server.start
|
|
260
|
+
|
|
261
|
+
log_message(:info, 'Language Server exited.')
|
|
262
|
+
end
|
|
263
|
+
end
|
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
begin
|
|
4
|
+
original_verbose = $VERBOSE
|
|
5
|
+
$VERBOSE = nil
|
|
6
|
+
|
|
7
|
+
require 'puppet_editor_services'
|
|
8
|
+
require 'optparse'
|
|
9
|
+
require 'logger'
|
|
10
|
+
require 'json'
|
|
11
|
+
|
|
12
|
+
%w[
|
|
13
|
+
sidecar_protocol
|
|
14
|
+
].each do |lib|
|
|
15
|
+
require "puppet-languageserver/#{lib}"
|
|
16
|
+
rescue LoadError
|
|
17
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'puppet-languageserver', lib))
|
|
18
|
+
end
|
|
19
|
+
ensure
|
|
20
|
+
$VERBOSE = original_verbose
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
module PuppetLanguageServerSidecar
|
|
24
|
+
def self.version
|
|
25
|
+
PuppetEditorServices.version
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def self.configure_featureflags(flags)
|
|
29
|
+
@flags = flags
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def self.featureflag?(flagname)
|
|
33
|
+
return false if @flags.nil? || @flags.empty?
|
|
34
|
+
|
|
35
|
+
@flags.include?(flagname)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def self.require_gems(options)
|
|
39
|
+
original_verbose = $VERBOSE
|
|
40
|
+
$VERBOSE = nil
|
|
41
|
+
|
|
42
|
+
# Use specific Puppet Gem version if possible
|
|
43
|
+
unless options[:puppet_version].nil?
|
|
44
|
+
available_puppet_gems = Gem::Specification
|
|
45
|
+
.select { |item| item.name.casecmp('puppet').zero? }
|
|
46
|
+
.map { |item| item.version.to_s }
|
|
47
|
+
if available_puppet_gems.include?(options[:puppet_version])
|
|
48
|
+
gem 'puppet', options[:puppet_version]
|
|
49
|
+
else
|
|
50
|
+
log_message(:warn, "Unable to use puppet version #{options[:puppet_version]}, as only the following versions are available [#{available_puppet_gems.join(', ')}]")
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
require 'puppet'
|
|
55
|
+
|
|
56
|
+
# Validate the feature flags
|
|
57
|
+
unless options[:flags].nil? || options[:flags].empty?
|
|
58
|
+
flags = options[:flags]
|
|
59
|
+
log_message(:debug, "Detected feature flags [#{options[:flags].join(', ')}]")
|
|
60
|
+
|
|
61
|
+
configure_featureflags(flags)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
require_list = %w[
|
|
65
|
+
cache/base
|
|
66
|
+
cache/null
|
|
67
|
+
cache/filesystem
|
|
68
|
+
puppet_parser_helper
|
|
69
|
+
sidecar_protocol_extensions
|
|
70
|
+
workspace
|
|
71
|
+
facter_helper
|
|
72
|
+
puppet_helper
|
|
73
|
+
puppet_monkey_patches
|
|
74
|
+
puppet_strings_helper
|
|
75
|
+
]
|
|
76
|
+
|
|
77
|
+
require_list.each do |lib|
|
|
78
|
+
require "puppet-languageserver-sidecar/#{lib}"
|
|
79
|
+
rescue LoadError
|
|
80
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'puppet-languageserver-sidecar', lib))
|
|
81
|
+
end
|
|
82
|
+
ensure
|
|
83
|
+
$VERBOSE = original_verbose
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
ACTION_LIST = %w[
|
|
87
|
+
noop
|
|
88
|
+
default_aggregate
|
|
89
|
+
default_classes
|
|
90
|
+
default_datatypes
|
|
91
|
+
default_functions
|
|
92
|
+
default_types
|
|
93
|
+
node_graph
|
|
94
|
+
resource_list
|
|
95
|
+
workspace_aggregate
|
|
96
|
+
workspace_classes
|
|
97
|
+
workspace_datatypes
|
|
98
|
+
workspace_functions
|
|
99
|
+
workspace_types
|
|
100
|
+
facts
|
|
101
|
+
].freeze
|
|
102
|
+
|
|
103
|
+
class CommandLineParser
|
|
104
|
+
def self.parse(options)
|
|
105
|
+
# Set defaults here
|
|
106
|
+
args = {
|
|
107
|
+
action: nil,
|
|
108
|
+
action_parameters: PuppetLanguageServer::Sidecar::Protocol::ActionParams.new,
|
|
109
|
+
debug: nil,
|
|
110
|
+
disable_cache: false,
|
|
111
|
+
flags: [],
|
|
112
|
+
output: nil,
|
|
113
|
+
puppet_settings: [],
|
|
114
|
+
puppet_version: nil,
|
|
115
|
+
workspace: nil
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
opt_parser = OptionParser.new do |opts|
|
|
119
|
+
opts.banner = 'Usage: puppet-languageserver-sidecar.rb [options]'
|
|
120
|
+
|
|
121
|
+
opts.on('-a', '--action=NAME', ACTION_LIST, "The action for the sidecar to take. Expected #{ACTION_LIST}") do |name|
|
|
122
|
+
args[:action] = name
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
opts.on('-c', '--action-parameters=JSON', 'JSON Encoded string containing the parameters for the sidecar action') do |json_string|
|
|
126
|
+
ap = PuppetLanguageServer::Sidecar::Protocol::ActionParams.new
|
|
127
|
+
begin
|
|
128
|
+
ap.from_json!(json_string)
|
|
129
|
+
args[:action_parameters] = ap
|
|
130
|
+
rescue StandardError => e
|
|
131
|
+
raise "Unable to parse the action parameters: #{e}"
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
opts.on('-w', '--local-workspace=PATH', 'The workspace or file path that will be used to provide module-specific functionality. Default is no workspace path') do |path|
|
|
136
|
+
args[:workspace] = path
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
opts.on('-o', '--output=PATH', 'The file to save the output from the sidecar. Default is output to STDOUT') do |path|
|
|
140
|
+
args[:output] = path
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
opts.on('-p', '--puppet-settings=TEXT', Array, 'Comma delimited list of settings to pass into Puppet e.g. --vardir,/opt/test-fixture') do |text|
|
|
144
|
+
args[:puppet_settings] = text
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
opts.on('--puppet-version=TEXT', String, 'The version of the Puppet Gem to use (defaults to latest version if not specified or the version does not exist) e.g. --puppet-version=5.4.0') do |text|
|
|
148
|
+
args[:puppet_version] = text
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
opts.on('-f', '--feature-flags=FLAGS', Array, 'A list of comma delimited feature flags to pass the the sidecar') do |flags|
|
|
152
|
+
args[:flags] = flags
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
opts.on('-n', '--[no-]cache', 'Enable or disable all caching inside the sidecar. By default caching is enabled.') do |cache|
|
|
156
|
+
args[:disable_cache] = !cache
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
opts.on('--debug=DEBUG', "Output debug information. Either specify a filename or 'STDOUT'. Default is no debug output") do |debug|
|
|
160
|
+
args[:debug] = debug
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
opts.on('-h', '--help', 'Prints this help') do
|
|
164
|
+
puts opts
|
|
165
|
+
exit
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
opts.on('-v', '--version', 'Prints the Langauge Server version') do
|
|
169
|
+
puts PuppetLanguageServerSidecar.version
|
|
170
|
+
exit
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
opt_parser.parse!(options.dup)
|
|
175
|
+
|
|
176
|
+
raise('The action parameter is mandatory') if args[:action].nil?
|
|
177
|
+
|
|
178
|
+
args
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
def self.log_message(severity, message)
|
|
183
|
+
PuppetEditorServices.log_message(severity, message)
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
def self.init_puppet_sidecar(options)
|
|
187
|
+
PuppetEditorServices.init_logging(options)
|
|
188
|
+
log_message(:info, "Language Server Sidecar is v#{PuppetLanguageServerSidecar.version}")
|
|
189
|
+
log_message(:debug, 'Loading gems...')
|
|
190
|
+
require_gems(options)
|
|
191
|
+
log_message(:info, "Using Puppet v#{Puppet.version}")
|
|
192
|
+
|
|
193
|
+
raise("Detected Puppet #{Puppet.version} however the Language Server Sidecar requires Puppet 5.0 and above") if Gem::Version.new(Puppet.version) < Gem::Version.new('5.0.0')
|
|
194
|
+
|
|
195
|
+
log_message(:debug, "Detected additional puppet settings #{options[:puppet_settings]}")
|
|
196
|
+
options[:puppet_settings].nil? ? Puppet.initialize_settings : Puppet.initialize_settings(options[:puppet_settings])
|
|
197
|
+
|
|
198
|
+
PuppetLanguageServerSidecar::Workspace.detect_workspace(options[:workspace])
|
|
199
|
+
log_message(:debug, 'Detected Module Metadata in the workspace') if PuppetLanguageServerSidecar::Workspace.has_module_metadata?
|
|
200
|
+
log_message(:debug, 'Detected Environment Config in the workspace') if PuppetLanguageServerSidecar::Workspace.has_environmentconf?
|
|
201
|
+
|
|
202
|
+
# Remove all other logging destinations except for ours
|
|
203
|
+
Puppet::Util::Log.destinations.clear
|
|
204
|
+
Puppet::Util::Log.newdestination('null_logger')
|
|
205
|
+
|
|
206
|
+
true
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
def self.inject_workspace_as_module
|
|
210
|
+
return false unless PuppetLanguageServerSidecar::Workspace.has_module_metadata?
|
|
211
|
+
|
|
212
|
+
%w[puppet_modulepath_monkey_patches].each do |lib|
|
|
213
|
+
require "puppet-languageserver-sidecar/#{lib}"
|
|
214
|
+
rescue LoadError
|
|
215
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'puppet-languageserver-sidecar', lib))
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
log_message(:debug, 'Injected the workspace into the module loader')
|
|
219
|
+
true
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
def self.inject_workspace_as_environment
|
|
223
|
+
return false unless PuppetLanguageServerSidecar::Workspace.has_environmentconf?
|
|
224
|
+
|
|
225
|
+
Puppet.settings[:environment] = PuppetLanguageServerSidecar::PuppetHelper::SIDECAR_PUPPET_ENVIRONMENT
|
|
226
|
+
|
|
227
|
+
%w[puppet_environment_monkey_patches].each do |lib|
|
|
228
|
+
require "puppet-languageserver-sidecar/#{lib}"
|
|
229
|
+
rescue LoadError
|
|
230
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'puppet-languageserver-sidecar', lib))
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
log_message(:debug, 'Injected the workspace into the environment loader')
|
|
234
|
+
true
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
def self.execute(options)
|
|
238
|
+
unless Gem::Version.new(Puppet.version) >= Gem::Version.new('5.0.0')
|
|
239
|
+
log_message(:error, 'Only Puppet Version 5.0 and above is supported.')
|
|
240
|
+
return []
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
log_message(:debug, "Executing #{options[:action]} action")
|
|
244
|
+
case options[:action].downcase
|
|
245
|
+
when 'noop'
|
|
246
|
+
[]
|
|
247
|
+
|
|
248
|
+
when 'default_aggregate'
|
|
249
|
+
cache = options[:disable_cache] ? PuppetLanguageServerSidecar::Cache::Null.new : PuppetLanguageServerSidecar::Cache::FileSystem.new
|
|
250
|
+
PuppetLanguageServerSidecar::PuppetHelper.retrieve_via_puppet_strings(cache, object_types: PuppetLanguageServerSidecar::PuppetHelper.available_documentation_types)
|
|
251
|
+
|
|
252
|
+
when 'default_classes'
|
|
253
|
+
cache = options[:disable_cache] ? PuppetLanguageServerSidecar::Cache::Null.new : PuppetLanguageServerSidecar::Cache::FileSystem.new
|
|
254
|
+
PuppetLanguageServerSidecar::PuppetHelper.retrieve_via_puppet_strings(cache, object_types: [:class]).classes
|
|
255
|
+
|
|
256
|
+
when 'default_datatypes'
|
|
257
|
+
cache = options[:disable_cache] ? PuppetLanguageServerSidecar::Cache::Null.new : PuppetLanguageServerSidecar::Cache::FileSystem.new
|
|
258
|
+
PuppetLanguageServerSidecar::PuppetHelper.retrieve_via_puppet_strings(cache, object_types: [:datatype]).datatypes
|
|
259
|
+
|
|
260
|
+
when 'default_functions'
|
|
261
|
+
cache = options[:disable_cache] ? PuppetLanguageServerSidecar::Cache::Null.new : PuppetLanguageServerSidecar::Cache::FileSystem.new
|
|
262
|
+
PuppetLanguageServerSidecar::PuppetHelper.retrieve_via_puppet_strings(cache, object_types: [:function]).functions
|
|
263
|
+
|
|
264
|
+
when 'default_types'
|
|
265
|
+
cache = options[:disable_cache] ? PuppetLanguageServerSidecar::Cache::Null.new : PuppetLanguageServerSidecar::Cache::FileSystem.new
|
|
266
|
+
PuppetLanguageServerSidecar::PuppetHelper.retrieve_via_puppet_strings(cache, object_types: [:type]).types
|
|
267
|
+
|
|
268
|
+
when 'node_graph'
|
|
269
|
+
inject_workspace_as_module || inject_workspace_as_environment
|
|
270
|
+
result = PuppetLanguageServerSidecar::Protocol::PuppetNodeGraph.new
|
|
271
|
+
if options[:action_parameters]['source'].nil?
|
|
272
|
+
log_message(:error, 'Missing source action parameter')
|
|
273
|
+
return result.set_error('Missing source action parameter')
|
|
274
|
+
end
|
|
275
|
+
begin
|
|
276
|
+
manifest = File.open(options[:action_parameters]['source'], 'r:UTF-8') { |f| f.read }
|
|
277
|
+
PuppetLanguageServerSidecar::PuppetParserHelper.compile_node_graph(manifest)
|
|
278
|
+
rescue StandardError => e
|
|
279
|
+
log_message(:error, "Unable to compile the manifest. #{e}")
|
|
280
|
+
result.set_error("Unable to compile the manifest. #{e}")
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
when 'resource_list'
|
|
284
|
+
inject_workspace_as_module || inject_workspace_as_environment
|
|
285
|
+
typename = options[:action_parameters]['typename']
|
|
286
|
+
title = options[:action_parameters]['title']
|
|
287
|
+
if typename.nil?
|
|
288
|
+
log_message(:error, 'Missing typename action paramater')
|
|
289
|
+
return []
|
|
290
|
+
end
|
|
291
|
+
PuppetLanguageServerSidecar::PuppetHelper.get_puppet_resource(typename, title)
|
|
292
|
+
|
|
293
|
+
when 'workspace_aggregate'
|
|
294
|
+
return nil unless inject_workspace_as_module || inject_workspace_as_environment
|
|
295
|
+
|
|
296
|
+
cache = options[:disable_cache] ? PuppetLanguageServerSidecar::Cache::Null.new : PuppetLanguageServerSidecar::Cache::FileSystem.new
|
|
297
|
+
PuppetLanguageServerSidecar::PuppetHelper.retrieve_via_puppet_strings(cache,
|
|
298
|
+
object_types: PuppetLanguageServerSidecar::PuppetHelper.available_documentation_types,
|
|
299
|
+
root_path: PuppetLanguageServerSidecar::Workspace.root_path)
|
|
300
|
+
|
|
301
|
+
when 'workspace_classes'
|
|
302
|
+
return nil unless inject_workspace_as_module || inject_workspace_as_environment
|
|
303
|
+
|
|
304
|
+
cache = options[:disable_cache] ? PuppetLanguageServerSidecar::Cache::Null.new : PuppetLanguageServerSidecar::Cache::FileSystem.new
|
|
305
|
+
PuppetLanguageServerSidecar::PuppetHelper.retrieve_via_puppet_strings(cache,
|
|
306
|
+
object_types: [:class],
|
|
307
|
+
root_path: PuppetLanguageServerSidecar::Workspace.root_path).classes
|
|
308
|
+
|
|
309
|
+
when 'workspace_datatypes'
|
|
310
|
+
return nil unless inject_workspace_as_module || inject_workspace_as_environment
|
|
311
|
+
|
|
312
|
+
cache = options[:disable_cache] ? PuppetLanguageServerSidecar::Cache::Null.new : PuppetLanguageServerSidecar::Cache::FileSystem.new
|
|
313
|
+
PuppetLanguageServerSidecar::PuppetHelper.retrieve_via_puppet_strings(cache,
|
|
314
|
+
object_types: [:datatype],
|
|
315
|
+
root_path: PuppetLanguageServerSidecar::Workspace.root_path).datatypes
|
|
316
|
+
|
|
317
|
+
when 'workspace_functions'
|
|
318
|
+
return nil unless inject_workspace_as_module || inject_workspace_as_environment
|
|
319
|
+
|
|
320
|
+
cache = options[:disable_cache] ? PuppetLanguageServerSidecar::Cache::Null.new : PuppetLanguageServerSidecar::Cache::FileSystem.new
|
|
321
|
+
PuppetLanguageServerSidecar::PuppetHelper.retrieve_via_puppet_strings(cache,
|
|
322
|
+
object_types: [:function],
|
|
323
|
+
root_path: PuppetLanguageServerSidecar::Workspace.root_path).functions
|
|
324
|
+
|
|
325
|
+
when 'workspace_types'
|
|
326
|
+
return nil unless inject_workspace_as_module || inject_workspace_as_environment
|
|
327
|
+
|
|
328
|
+
cache = options[:disable_cache] ? PuppetLanguageServerSidecar::Cache::Null.new : PuppetLanguageServerSidecar::Cache::FileSystem.new
|
|
329
|
+
PuppetLanguageServerSidecar::PuppetHelper.retrieve_via_puppet_strings(cache,
|
|
330
|
+
object_types: [:type],
|
|
331
|
+
root_path: PuppetLanguageServerSidecar::Workspace.root_path).types
|
|
332
|
+
|
|
333
|
+
when 'facts'
|
|
334
|
+
# Can't cache for facts
|
|
335
|
+
cache = PuppetLanguageServerSidecar::Cache::Null.new
|
|
336
|
+
# Inject the workspace etc. if present
|
|
337
|
+
injected = inject_workspace_as_module
|
|
338
|
+
inject_workspace_as_environment unless injected
|
|
339
|
+
PuppetLanguageServerSidecar::FacterHelper.retrieve_facts(cache)
|
|
340
|
+
|
|
341
|
+
else
|
|
342
|
+
log_message(:error, "Unknown action #{options[:action]}. Expected one of #{ACTION_LIST}")
|
|
343
|
+
end
|
|
344
|
+
end
|
|
345
|
+
|
|
346
|
+
def self.output(result, options)
|
|
347
|
+
if options[:output].nil? || options[:output].empty?
|
|
348
|
+
$stdout.binmode
|
|
349
|
+
$stdout.write(result.to_json)
|
|
350
|
+
else
|
|
351
|
+
File.open(options[:output], 'wb:UTF-8') do |f|
|
|
352
|
+
f.write result.to_json
|
|
353
|
+
end
|
|
354
|
+
end
|
|
355
|
+
end
|
|
356
|
+
|
|
357
|
+
def self.execute_and_output(options)
|
|
358
|
+
output(execute(options), options)
|
|
359
|
+
nil
|
|
360
|
+
end
|
|
361
|
+
end
|
data/puppet-debugserver
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# Add the debug server into the load path
|
|
5
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), 'lib'))
|
|
6
|
+
|
|
7
|
+
require 'puppet_debugserver'
|
|
8
|
+
|
|
9
|
+
PuppetDebugServer.init_puppet(PuppetDebugServer::CommandLineParser.parse(ARGV))
|
|
10
|
+
rpc_thread = PuppetDebugServer.rpc_server_async(PuppetDebugServer::CommandLineParser.parse(ARGV))
|
|
11
|
+
PuppetDebugServer.execute(rpc_thread)
|