Skip to content

Commit a5ff75b

Browse files
Merge pull request #29 from glennsarti/gh-26-puppet-lint-in-control-repos
(GH-26) Refactor workspace detection for control repos and modules
2 parents 85f123c + d259969 commit a5ff75b

File tree

16 files changed

+348
-82
lines changed

16 files changed

+348
-82
lines changed

lib/puppet-languageserver.rb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,9 @@ def self.init_puppet(options)
126126
log_message(:info, 'Initializing Puppet Helper Cache...')
127127
PuppetLanguageServer::PuppetHelper.configure_cache(options[:cache])
128128

129+
log_message(:debug, 'Initializing Document Store...')
130+
PuppetLanguageServer::DocumentStore.initialize_store(options)
131+
129132
log_message(:info, 'Initializing settings...')
130133
if options[:fast_start_langserver]
131134
Thread.new do

lib/puppet-languageserver/document_store.rb

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,5 +51,105 @@ def self.document_type(uri)
5151
:unknown
5252
end
5353
end
54+
55+
# Workspace management
56+
WORKSPACE_CACHE_TTL_SECONDS = 60
57+
def self.initialize_store(options = {})
58+
@workspace_path = options[:workspace]
59+
@workspace_info_cache = {
60+
:expires => Time.new - 120
61+
}
62+
end
63+
64+
# This method is mainly used for testin. It expires the cache
65+
def self.expire_store_information
66+
@doc_mutex.synchronize do
67+
@workspace_info_cache[:expires] = Time.new - 120
68+
end
69+
end
70+
71+
def self.store_root_path
72+
store_details[:root_path]
73+
end
74+
75+
def self.store_has_module_metadata?
76+
store_details[:has_metadatajson]
77+
end
78+
79+
def self.store_has_puppetfile?
80+
store_details[:has_puppetfile]
81+
end
82+
83+
# Given a path, locate a metadata.json or Puppetfile file to determine where the
84+
# root of the module/control repo actually is
85+
def self.find_root_path(path)
86+
return nil if path.nil?
87+
filepath = File.expand_path(path)
88+
89+
if dir_exist?(filepath)
90+
directory = filepath
91+
elsif file_exist?(filepath)
92+
directory = File.dirname(filepath)
93+
else
94+
return nil
95+
end
96+
97+
until directory.nil?
98+
break if file_exist?(File.join(directory, 'metadata.json')) || file_exist?(File.join(directory, 'Puppetfile'))
99+
parent = File.dirname(directory)
100+
# If the parent is the same as the original, then we've reached the end of the path chain
101+
if parent == directory
102+
directory = nil
103+
else
104+
directory = parent
105+
end
106+
end
107+
108+
directory
109+
end
110+
private_class_method :find_root_path
111+
112+
def self.store_details
113+
return @workspace_info_cache unless @workspace_info_cache[:never_expires] || @workspace_info_cache[:expires] < Time.new
114+
# TTL has expired, time to calculate the document store details
115+
116+
new_cache = {
117+
:root_path => nil,
118+
:has_puppetfile => false,
119+
:has_metadatajson => false
120+
}
121+
if @workspace_path.nil?
122+
# If we have never been given a local workspace path on the command line then there is really no
123+
# way to know where the module file system path is. Therefore the root_path is nil and assume that
124+
# puppetfile and metadata.json does not exist. And don't bother trying to re-evaluate
125+
new_cache[:never_expires] = true
126+
else
127+
root_path = find_root_path(@workspace_path)
128+
if root_path.nil?
129+
new_cache[:root_path] = @workspace_path
130+
else
131+
new_cache[:root_path] = root_path
132+
new_cache[:has_metadatajson] = file_exist?(File.join(root_path, 'metadata.json'))
133+
new_cache[:has_puppetfile] = file_exist?(File.join(root_path, 'Puppetfile'))
134+
end
135+
end
136+
new_cache[:expires] = Time.new + WORKSPACE_CACHE_TTL_SECONDS
137+
138+
@doc_mutex.synchronize do
139+
@workspace_info_cache = new_cache
140+
end
141+
@workspace_info_cache
142+
end
143+
private_class_method :store_details
144+
145+
def self.file_exist?(path)
146+
File.exist?(path) && !File.directory?(path)
147+
end
148+
private_class_method :file_exist?
149+
150+
def self.dir_exist?(path)
151+
Dir.exist?(path)
152+
end
153+
private_class_method :dir_exist?
54154
end
55155
end

lib/puppet-languageserver/epp/validation_provider.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
module PuppetLanguageServer
22
module Epp
33
module ValidationProvider
4-
def self.validate(content, _workspace, _max_problems = 100)
4+
def self.validate(content, _max_problems = 100)
55
result = []
66
# TODO: Need to implement max_problems
77
_problems = 0

lib/puppet-languageserver/manifest/validation_provider.rb

Lines changed: 4 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -2,39 +2,15 @@
22
module PuppetLanguageServer
33
module Manifest
44
module ValidationProvider
5-
def self.find_module_root_from_path(path)
6-
return nil if path.nil?
7-
8-
filepath = Pathname.new(path).expand_path
9-
return nil unless filepath.exist?
10-
11-
if filepath.directory?
12-
directory = filepath
13-
else
14-
directory = filepath.dirname
15-
end
16-
17-
module_root = nil
18-
directory.ascend do |p|
19-
if p.join('metadata.json').exist?
20-
module_root = p
21-
break
22-
end
23-
end
24-
25-
module_root
26-
end
27-
285
# Similar to 'validate' this will run puppet-lint and returns
296
# the manifest with any fixes applied
307
#
318
# Returns:
329
# [ <Int> Number of problems fixed,
3310
# <String> New Content
3411
# ]
35-
def self.fix_validate_errors(content, workspace)
36-
# Find module root and attempt to build PuppetLint options
37-
module_root = find_module_root_from_path(workspace)
12+
def self.fix_validate_errors(content)
13+
module_root = PuppetLanguageServer::DocumentStore.store_root_path
3814
linter_options = nil
3915
if module_root.nil?
4016
linter_options = PuppetLint::OptParser.build
@@ -52,13 +28,12 @@ def self.fix_validate_errors(content, workspace)
5228
[problems_fixed, linter.manifest]
5329
end
5430

55-
def self.validate(content, workspace, _max_problems = 100)
31+
def self.validate(content, _max_problems = 100)
5632
result = []
5733
# TODO: Need to implement max_problems
5834
problems = 0
5935

60-
# Find module root and attempt to build PuppetLint options
61-
module_root = find_module_root_from_path(workspace)
36+
module_root = PuppetLanguageServer::DocumentStore.store_root_path
6237
linter_options = nil
6338
if module_root.nil?
6439
linter_options = PuppetLint::OptParser.build

lib/puppet-languageserver/message_router.rb

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,7 @@ module PuppetLanguageServer
22
class MessageRouter
33
attr_accessor :json_rpc_handler
44

5-
def initialize(options = {})
6-
options = {} if options.nil?
7-
@workspace = options[:workspace]
5+
def initialize(_options = {})
86
end
97

108
def documents
@@ -90,7 +88,7 @@ def receive_request(request)
9088

9189
case documents.document_type(file_uri)
9290
when :manifest
93-
changes, new_content = PuppetLanguageServer::Manifest::ValidationProvider.fix_validate_errors(content, @workspace)
91+
changes, new_content = PuppetLanguageServer::Manifest::ValidationProvider.fix_validate_errors(content)
9492
else
9593
raise "Unable to fixDiagnosticErrors on #{file_uri}"
9694
end
@@ -194,7 +192,7 @@ def receive_notification(method, params)
194192
content = params['textDocument']['text']
195193
doc_version = params['textDocument']['version']
196194
documents.set_document(file_uri, content, doc_version)
197-
PuppetLanguageServer::ValidationQueue.enqueue(file_uri, doc_version, @workspace, @json_rpc_handler)
195+
PuppetLanguageServer::ValidationQueue.enqueue(file_uri, doc_version, @json_rpc_handler)
198196

199197
when 'textDocument/didClose'
200198
PuppetLanguageServer.log_message(:info, 'Received textDocument/didClose notification.')
@@ -207,7 +205,7 @@ def receive_notification(method, params)
207205
content = params['contentChanges'][0]['text'] # TODO: Bad hardcoding zero
208206
doc_version = params['textDocument']['version']
209207
documents.set_document(file_uri, content, doc_version)
210-
PuppetLanguageServer::ValidationQueue.enqueue(file_uri, doc_version, @workspace, @json_rpc_handler)
208+
PuppetLanguageServer::ValidationQueue.enqueue(file_uri, doc_version, @json_rpc_handler)
211209

212210
when 'textDocument/didSave'
213211
PuppetLanguageServer.log_message(:info, 'Received textDocument/didSave notification.')

lib/puppet-languageserver/validation_queue.rb

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ module ValidationQueue
1111
@queue_thread = nil
1212

1313
# Enqueue a file to be validated
14-
def self.enqueue(file_uri, doc_version, workspace, connection_object)
14+
def self.enqueue(file_uri, doc_version, connection_object)
1515
document_type = PuppetLanguageServer::DocumentStore.document_type(file_uri)
1616

1717
unless %i[manifest epp puppetfile].include?(document_type)
@@ -27,7 +27,6 @@ def self.enqueue(file_uri, doc_version, workspace, connection_object)
2727
'file_uri' => file_uri,
2828
'doc_version' => doc_version,
2929
'document_type' => document_type,
30-
'workspace' => workspace,
3130
'connection_object' => connection_object
3231
}
3332
end
@@ -47,11 +46,11 @@ def self.enqueue(file_uri, doc_version, workspace, connection_object)
4746
end
4847

4948
# Synchronously validate a file
50-
def self.validate_sync(file_uri, doc_version, workspace, connection_object)
49+
def self.validate_sync(file_uri, doc_version, connection_object)
5150
document_type = PuppetLanguageServer::DocumentStore.document_type(file_uri)
5251
content = documents.document(file_uri, doc_version)
5352
return nil if content.nil?
54-
result = validate(document_type, content, workspace)
53+
result = validate(document_type, content)
5554

5655
# Send the response
5756
connection_object.reply_diagnostics(file_uri, result)
@@ -79,15 +78,15 @@ def self.reset_queue(initial_state = [])
7978
end
8079

8180
# Validate a document
82-
def self.validate(document_type, content, workspace)
81+
def self.validate(document_type, content)
8382
# Perform validation
8483
case document_type
8584
when :manifest
86-
PuppetLanguageServer::Manifest::ValidationProvider.validate(content, workspace)
85+
PuppetLanguageServer::Manifest::ValidationProvider.validate(content)
8786
when :epp
88-
PuppetLanguageServer::Epp::ValidationProvider.validate(content, workspace)
87+
PuppetLanguageServer::Epp::ValidationProvider.validate(content)
8988
when :puppetfile
90-
PuppetLanguageServer::Puppetfile::ValidationProvider.validate(content, workspace)
89+
PuppetLanguageServer::Puppetfile::ValidationProvider.validate(content)
9190
else
9291
[]
9392
end
@@ -109,7 +108,6 @@ def self.worker
109108
doc_version = work_item['doc_version']
110109
connection_object = work_item['connection_object']
111110
document_type = work_item['document_type']
112-
workspace = work_item['workspace']
113111

114112
# Check if the document is the latest version
115113
content = documents.document(file_uri, doc_version)
@@ -119,7 +117,7 @@ def self.worker
119117
end
120118

121119
# Perform validation
122-
result = validate(document_type, content, workspace)
120+
result = validate(document_type, content)
123121

124122
# Check if the document is still latest version
125123
current_version = documents.document_version(file_uri)
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
forge 'https://forge.puppetlabs.com/'
2+
3+
# Modules from the Puppet Forge
4+
mod 'puppetlabs-stdlib', '1.0.0'

spec/languageserver/fixtures/control_repos/valid/data/.gitkeep

Whitespace-only changes.

spec/languageserver/fixtures/control_repos/valid/site/profile/.gitkeep

Whitespace-only changes.

spec/languageserver/fixtures/control_repos/valid/site/role/.gitkeep

Whitespace-only changes.

0 commit comments

Comments
 (0)