@@ -209,12 +209,31 @@ export class ConnectionManager implements IConnectionManager {
209209 callback ( proc ) ;
210210 }
211211
212- private createLanguageServerProcess ( serverExe : string , callback : Function ) {
213- this . logger . debug ( 'Language server found at: ' + serverExe )
212+ private getDirectories ( parent ) {
213+ return fs . readdirSync ( parent ) . filter ( function ( file ) {
214+ return fs . statSync ( path . join ( parent , file ) ) . isDirectory ( ) ;
215+ } ) ;
216+ }
214217
215- let cmd : string = undefined ;
216- let args = [ serverExe ] ;
217- let options : cp . SpawnOptions = { } ;
218+ private pathEnvSeparator ( ) {
219+ if ( process . platform == 'win32' ) {
220+ return ";" ;
221+ } else {
222+ return ":" ;
223+ }
224+ }
225+
226+ private getLanguageServerFromPuppetAgent ( serverExe ) {
227+ let logPrefix : string = '[getLanguageServerFromPuppetAgent] ' ;
228+ // setup defaults
229+ let spawn_options : cp . SpawnOptions = { }
230+ spawn_options . env = process . env ;
231+ let result = {
232+ command : 'ruby' ,
233+ args : [ serverExe ] ,
234+ options : spawn_options ,
235+ }
236+ let puppetAgentDir : string = null ;
218237
219238 // type Platform = 'aix'
220239 // | 'android'
@@ -234,69 +253,190 @@ export class ConnectionManager implements IConnectionManager {
234253 comspec = path . join ( process . env [ "WINDIR" ] , "sysnative" , "cmd.exe" ) ;
235254 programFiles = process . env [ "ProgramW6432" ] ;
236255 }
237- let puppetDir : string = undefined ;
256+
238257 if ( this . connectionConfiguration . puppetAgentDir == undefined ) {
239- puppetDir = path . join ( programFiles , "Puppet Labs" , "Puppet" ) ;
258+ puppetAgentDir = path . join ( programFiles , "Puppet Labs" , "Puppet" ) ;
240259 } else {
241- puppetDir = this . connectionConfiguration . puppetAgentDir ;
242- }
243- let environmentBat : string = path . join ( puppetDir , "bin" , "environment.bat" )
244-
245- if ( ! fs . existsSync ( puppetDir ) ) {
246- this . setSessionFailure ( "Could not find Puppet Agent at " + puppetDir ) ;
247- vscode . window . showWarningMessage ( 'Could not find Puppet Agent installed at "' + puppetDir + '". Functionality will be limited to syntax highlighting' ) ;
248- return ;
260+ puppetAgentDir = this . connectionConfiguration . puppetAgentDir ;
249261 }
250262
251- cmd = comspec ;
252- args = [ '/K' , 'CALL' , environmentBat , '&&' , 'ruby.exe' , serverExe ]
253- options = {
254- env : process . env ,
255- stdio : 'pipe' ,
256- } ;
263+ result . options . stdio = 'pipe' ;
257264 break ;
258265 default :
259- this . logger . debug ( 'Starting language server' )
260-
261- let rubyPath : string = undefined ;
262266 if ( this . connectionConfiguration . puppetAgentDir == undefined ) {
263- rubyPath = '/opt/puppetlabs/puppet/bin/ruby ' ;
267+ puppetAgentDir = '/opt/puppetlabs/puppet' ;
264268 } else {
265- rubyPath = path . join ( this . connectionConfiguration . puppetAgentDir , "bin" , "ruby" ) ;
269+ puppetAgentDir = this . connectionConfiguration . puppetAgentDir ;
266270 }
267- if ( fs . existsSync ( rubyPath ) ) { cmd = rubyPath }
268-
269- // Default to ruby on the path
270- if ( cmd == undefined ) { cmd = 'ruby' }
271- options = {
272- shell : true ,
273- env : process . env ,
274- stdio : 'pipe' ,
275- } ;
271+
272+ result . options . stdio = 'pipe' ;
273+ result . options . shell = true ;
274+ break ;
275+ }
276+ // Check if this really is a Puppet Agent installation
277+ if ( ! fs . existsSync ( path . join ( puppetAgentDir , "VERSION" ) ) ) {
278+ this . logger . debug ( logPrefix + "Could not find a valid Puppet Agent installation at " + puppetAgentDir ) ;
279+ return null ;
280+ } else {
281+ this . logger . debug ( logPrefix + "Found a valid Puppet Agent installation at " + puppetAgentDir ) ;
276282 }
277283
278- if ( cmd == undefined ) {
279- this . setSessionFailure ( "Unable to start the Language Server on this platform" ) ;
280- vscode . window . showWarningMessage ( 'The Puppet Language Server is not supported on this platform (' + process . platform + '). Functionality will be limited to syntax highlighting' ) ;
284+ let puppetDir = path . join ( puppetAgentDir , "puppet" ) ;
285+ let facterDir = path . join ( puppetAgentDir , "facter" ) ;
286+ let hieraDir = path . join ( puppetAgentDir , "hiera" ) ;
287+ let mcoDir = path . join ( puppetAgentDir , "mcollective" ) ;
288+ let rubydir = path . join ( puppetAgentDir , "sys" , "ruby" ) ;
289+ let rubylib = path . join ( puppetDir , "lib" ) + this . pathEnvSeparator ( ) + path . join ( facterDir , "lib" ) + this . pathEnvSeparator ( ) + path . join ( hieraDir , "lib" ) + this . pathEnvSeparator ( ) + path . join ( mcoDir , "lib" )
290+
291+ if ( process . platform == 'win32' ) {
292+ // Translate all slashes to / style to avoid puppet/ruby issue #11930
293+ rubylib = rubylib . replace ( / \\ / g, "/" ) ;
294+ }
295+
296+ // Setup the process environment variables
297+ if ( result . options . env . PATH == undefined ) { result . options . env . PATH = '' ; }
298+ if ( result . options . env . RUBYLIB == undefined ) { result . options . env . RUBYLIB = '' ; }
299+ result . options . env . RUBY_DIR = rubydir ;
300+ result . options . env . PATH = path . join ( puppetDir , "bin" ) + this . pathEnvSeparator ( ) + path . join ( facterDir , "bin" ) + this . pathEnvSeparator ( ) + path . join ( hieraDir , "bin" ) + this . pathEnvSeparator ( ) + path . join ( mcoDir , "bin" ) +
301+ this . pathEnvSeparator ( ) + path . join ( puppetAgentDir , "bin" ) + this . pathEnvSeparator ( ) + path . join ( rubydir , "bin" ) + this . pathEnvSeparator ( ) + path . join ( puppetAgentDir , "sys" , "tools" , "bin" ) +
302+ this . pathEnvSeparator ( ) + result . options . env . PATH ;
303+ result . options . env . RUBYLIB = rubylib + this . pathEnvSeparator ( ) + result . options . env . RUBYLIB ;
304+ result . options . env . RUBYOPT = 'rubygems' ;
305+ result . options . env . SSL_CERT_FILE = path . join ( puppetDir , "ssl" , "cert.pem" ) ;
306+ result . options . env . SSL_CERT_DIR = path . join ( puppetDir , "ssl" , "certs" ) ;
307+
308+ this . logger . debug ( logPrefix + "Using environment variable RUBY_DIR=" + result . options . env . RUBY_DIR ) ;
309+ this . logger . debug ( logPrefix + "Using environment variable PATH=" + result . options . env . PATH ) ;
310+ this . logger . debug ( logPrefix + "Using environment variable RUBYLIB=" + result . options . env . RUBYLIB ) ;
311+ this . logger . debug ( logPrefix + "Using environment variable RUBYOPT=" + result . options . env . RUBYOPT ) ;
312+ this . logger . debug ( logPrefix + "Using environment variable SSL_CERT_FILE=" + result . options . env . SSL_CERT_FILE ) ;
313+ this . logger . debug ( logPrefix + "Using environment variable SSL_CERT_DIR=" + result . options . env . SSL_CERT_DIR ) ;
314+
315+ return result ;
316+ }
317+
318+ // Commented out for the moment. This will be enabled once the configuration and
319+ // exact user story is figured out.
320+ //
321+ // private getLanguageServerFromPDK(serverExe) {
322+ // let logPrefix: string = '[getLanguageServerFromPDK] ';
323+ // // setup defaults
324+ // let spawn_options: cp.SpawnOptions = {}
325+ // spawn_options.env = process.env;
326+ // let result = {
327+ // command: 'ruby',
328+ // args: [serverExe],
329+ // options: spawn_options,
330+ // }
331+ // let pdkDir: string = null;
332+
333+ // // type Platform = 'aix'
334+ // // | 'android'
335+ // // | 'darwin'
336+ // // | 'freebsd'
337+ // // | 'linux'
338+ // // | 'openbsd'
339+ // // | 'sunos'
340+ // // | 'win32';
341+ // switch (process.platform) {
342+ // case 'win32':
343+ // let comspec: string = process.env["COMSPEC"];
344+ // let programFiles = process.env["ProgramFiles"];
345+ // if (process.env["PROCESSOR_ARCHITEW6432"] == "AMD64") {
346+ // // VSCode is running as 32bit process on a 64bit Operating System. Need to break out
347+ // // of the 32bit using the sysnative redirection and environment variables
348+ // comspec = path.join(process.env["WINDIR"],"sysnative","cmd.exe");
349+ // programFiles = process.env["ProgramW6432"];
350+ // }
351+
352+ // pdkDir = path.join(programFiles, "Puppet Labs", "DevelopmentKit");
353+
354+ // result.options.stdio = 'pipe';
355+ // break;
356+ // default:
357+ // pdkDir = '/opt/puppetlabs/pdk';
358+
359+ // result.options.stdio = 'pipe';
360+ // result.options.shell = true;
361+ // break;
362+ // }
363+ // // Check if this really is a PDK installation
364+ // if (!fs.existsSync(path.join(pdkDir, "PDK_VERSION"))) {
365+ // this.logger.debug(logPrefix + "Could not find a valid PDK installation at " + pdkDir);
366+ // return null;
367+ // } else {
368+ // this.logger.debug(logPrefix + "Found a valid PDK installation at " + pdkDir);
369+ // }
370+
371+ // // Now to detect ruby versions
372+ // let subdirs = this.getDirectories(path.join(pdkDir,"private", "ruby"));
373+ // if (subdirs.length == 0) { return null; }
374+ // let rubyDir = path.join(pdkDir,"private", "ruby",subdirs[0]);
375+
376+ // subdirs = this.getDirectories(path.join(pdkDir,"share","cache","ruby"));
377+ // if (subdirs.length == 0) { return null; }
378+ // let gemDir = path.join(pdkDir,"share","cache","ruby",subdirs[0]);
379+
380+ // let rubylib = path.join(pdkDir,'lib')
381+ // if (process.platform == 'win32') {
382+ // // Translate all slashes to / style to avoid puppet/ruby issue #11930
383+ // rubylib = rubylib.replace(/\\/g,"/");
384+ // gemDir = gemDir.replace(/\\/g,"/");
385+ // }
386+
387+ // // Setup the process environment variables
388+ // if (result.options.env.PATH == undefined) { result.options.env.PATH = '' }
389+ // if (result.options.env.RUBYLIB == undefined) { result.options.env.RUBYLIB = '' }
390+
391+ // result.options.env.RUBY_DIR = rubyDir;
392+ // result.options.env.PATH = path.join(pdkDir,'bin') + this.pathEnvSeparator() + path.join(rubyDir,'bin') + this.pathEnvSeparator() + result.options.env.PATH;
393+ // result.options.env.RUBYLIB = path.join(pdkDir,'lib') + this.pathEnvSeparator() + result.options.env.RUBYLIB;
394+ // result.options.env.GEM_PATH = gemDir;
395+ // result.options.env.GEM_HOME = gemDir;
396+ // result.options.env.RUBYOPT = 'rubygems';
397+
398+ // this.logger.debug(logPrefix + "Using environment variable RUBY_DIR=" + result.options.env.RUBY_DIR);
399+ // this.logger.debug(logPrefix + "Using environment variable PATH=" + result.options.env.PATH);
400+ // this.logger.debug(logPrefix + "Using environment variable RUBYLIB=" + result.options.env.RUBYLIB);
401+ // this.logger.debug(logPrefix + "Using environment variable GEM_PATH=" + result.options.env.GEM_PATH);
402+ // this.logger.debug(logPrefix + "Using environment variable GEM_HOME=" + result.options.env.GEM_HOME);
403+ // this.logger.debug(logPrefix + "Using environment variable RUBYOPT=" + result.options.env.RUBYOPT);
404+
405+ // return result;
406+ // }
407+
408+ private createLanguageServerProcess ( serverExe : string , callback : Function ) {
409+ let logPrefix : string = '[createLanguageServerProcess] ' ;
410+ this . logger . debug ( logPrefix + 'Language server found at: ' + serverExe )
411+
412+ let localServer = null
413+
414+ if ( localServer == null ) { localServer = this . getLanguageServerFromPuppetAgent ( serverExe ) ; }
415+ // if (localServer == null) { localServer = this.getLanguageServerFromPDK(serverExe); }
416+
417+ if ( localServer == null ) {
418+ this . logger . warning ( logPrefix + "Could not find a valid Puppet Agent installation" ) ;
419+ this . setSessionFailure ( "Could not find a valid Puppet Agent installation" ) ;
420+ vscode . window . showWarningMessage ( 'Could not find a valid Puppet Agent installation. Functionality will be limited to syntax highlighting' ) ;
281421 return ;
282422 }
283423
284424 let connMgr : ConnectionManager = this ;
285425 let logger = this . logger ;
286426 // Start a server to get a random port
287- this . logger . debug ( 'Creating server process to identify random port' )
427+ this . logger . debug ( logPrefix + 'Creating server process to identify random port' )
288428 const server = net . createServer ( )
289429 . on ( 'close' , ( ) => {
290- logger . debug ( 'Server process to identify random port disconnected' ) ;
291- connMgr . startLanguageServerProcess ( cmd , args , options , callback ) ;
430+ logger . debug ( logPrefix + 'Server process to identify random port disconnected' ) ;
431+ connMgr . startLanguageServerProcess ( localServer . command , localServer . args , localServer . options , callback ) ;
292432 } )
293433 . on ( 'error' , ( err ) => {
294434 throw err ;
295435 } ) ;
296436
297437 // Listen on random port
298438 server . listen ( 0 ) ;
299- this . logger . debug ( 'Selected port for local language server: ' + server . address ( ) . port ) ;
439+ this . logger . debug ( logPrefix + 'Selected port for local language server: ' + server . address ( ) . port ) ;
300440 connMgr . connectionConfiguration . port = server . address ( ) . port ;
301441 server . close ( ) ;
302442 }
0 commit comments