@@ -43,85 +43,22 @@ const cdsCommandCache: CdsCommandCache = {
4343} ;
4444
4545/**
46- * Reset the command cache - primarily for testing
47- */
48- export function resetCdsCommandCache ( ) : void {
49- cdsCommandCache . commandResults . clear ( ) ;
50- cdsCommandCache . availableCacheDirs = [ ] ;
51- cdsCommandCache . globalCommand = undefined ;
52- cdsCommandCache . initialized = false ;
53- }
54-
55- /**
56- * Check if a CDS command is available and working
57- * @param command The command to test
58- * @param sourceRoot The source root directory to use as cwd when testing the command
59- * @param silent Whether to suppress logging of test failures
60- * @returns Object with test result and version information
46+ * Determine the `cds` command to use based on the environment and cache directory.
47+ *
48+ * This function uses a caching strategy to minimize repeated CLI command testing:
49+ * - Initializes a global cache on first call
50+ * - Tests global commands once and caches results
51+ * - Discovers all available cache directories upfront
52+ * - Reuses test results across multiple calls
6153 */
62- function testCdsCommand (
63- command : string ,
64- sourceRoot : string ,
65- silent : boolean = false ,
66- ) : { works : boolean ; version ?: string ; error ?: string } {
67- // Check cache first
68- const cachedResult = cdsCommandCache . commandResults . get ( command ) ;
69- if ( cachedResult ) {
70- return cachedResult ;
71- }
72-
54+ export function determineCdsCommand ( cacheDir : string | undefined , sourceRoot : string ) : string {
7355 try {
74- // Try to run the command with --version to see if it works
75- // CRITICAL: Use sourceRoot as cwd and clean environment to avoid conflicts
76- let result : string ;
77-
78- const cleanEnv = {
79- ...process . env ,
80- // Remove any CodeQL-specific environment variables that might interfere
81- CODEQL_EXTRACTOR_CDS_WIP_DATABASE : undefined ,
82- CODEQL_RUNNER : undefined ,
83- } ;
84-
85- if ( command . includes ( 'node ' ) ) {
86- // For node commands, we need to split and execute properly
87- const parts = command . split ( ' ' ) ;
88- const nodeExecutable = parts [ 0 ] ; // 'node'
89- const scriptPath = parts [ 1 ] . replace ( / " / g, '' ) ; // Remove quotes from path
90- result = execFileSync ( nodeExecutable , [ scriptPath , '--version' ] , {
91- encoding : 'utf8' ,
92- stdio : 'pipe' ,
93- timeout : 5000 , // Reduced timeout for faster failure
94- cwd : sourceRoot ,
95- env : cleanEnv ,
96- } ) . toString ( ) ;
97- } else {
98- // Use shell-quote to properly escape the command and prevent injection
99- const escapedCommand = quote ( [ command , '--version' ] ) ;
100- result = execFileSync ( 'sh' , [ '-c' , escapedCommand ] , {
101- encoding : 'utf8' ,
102- stdio : 'pipe' ,
103- timeout : 5000 , // Reduced timeout for faster failure
104- cwd : sourceRoot ,
105- env : cleanEnv ,
106- } ) . toString ( ) ;
107- }
108-
109- // Extract version from output (typically in format "@sap/cds-dk: 6.1.3" or just "6.1.3")
110- const versionMatch = result . match ( / ( \d + \. \d + \. \d + ) / ) ;
111- const version = versionMatch ? versionMatch [ 1 ] : undefined ;
112-
113- const testResult = { works : true , version } ;
114- cdsCommandCache . commandResults . set ( command , testResult ) ;
115- return testResult ;
56+ // Always use the efficient path - debug information is collected separately
57+ return getBestCdsCommand ( cacheDir , sourceRoot ) ;
11658 } catch ( error ) {
117- const errorMessage = String ( error ) ;
118- if ( ! silent ) {
119- cdsExtractorLog ( 'debug' , `CDS command test failed for '${ command } ': ${ errorMessage } ` ) ;
120- }
121-
122- const testResult = { works : false , error : errorMessage } ;
123- cdsCommandCache . commandResults . set ( command , testResult ) ;
124- return testResult ;
59+ const errorMessage = `Failed to determine CDS command: ${ String ( error ) } ` ;
60+ cdsExtractorLog ( 'error' , errorMessage ) ;
61+ throw new Error ( errorMessage ) ;
12562 }
12663}
12764
@@ -159,44 +96,6 @@ function discoverAvailableCacheDirs(sourceRoot: string): string[] {
15996 return availableDirs ;
16097}
16198
162- /**
163- * Initialize the CDS command cache by testing global commands
164- * @param sourceRoot The source root directory
165- */
166- function initializeCdsCommandCache ( sourceRoot : string ) : void {
167- if ( cdsCommandCache . initialized ) {
168- return ;
169- }
170-
171- cdsExtractorLog ( 'info' , 'Initializing CDS command cache...' ) ;
172-
173- // Test global commands first (most commonly used)
174- const globalCommands = [ 'cds' , 'npx -y --package @sap/cds-dk cds' ] ;
175-
176- for ( const command of globalCommands ) {
177- const result = testCdsCommand ( command , sourceRoot , true ) ; // Silent testing
178- if ( result . works ) {
179- cdsCommandCache . globalCommand = command ;
180- cdsExtractorLog (
181- 'info' ,
182- `Found working global CDS command: ${ command } (v${ result . version ?? 'unknown' } )` ,
183- ) ;
184- break ;
185- }
186- }
187-
188- // Discover available cache directories
189- const cacheDirs = discoverAvailableCacheDirs ( sourceRoot ) ;
190- if ( cacheDirs . length > 0 ) {
191- cdsExtractorLog (
192- 'info' ,
193- `Discovered ${ cacheDirs . length } CDS cache director${ cacheDirs . length === 1 ? 'y' : 'ies' } ` ,
194- ) ;
195- }
196-
197- cdsCommandCache . initialized = true ;
198- }
199-
20099/**
201100 * Get the best CDS command for a specific cache directory
202101 * @param cacheDir Optional specific cache directory
@@ -246,26 +145,6 @@ function getBestCdsCommand(cacheDir: string | undefined, sourceRoot: string): st
246145 return 'npx -y --package @sap/cds-dk cds' ;
247146}
248147
249- /**
250- * Determine the `cds` command to use based on the environment and cache directory.
251- *
252- * This function uses a caching strategy to minimize repeated CLI command testing:
253- * - Initializes a global cache on first call
254- * - Tests global commands once and caches results
255- * - Discovers all available cache directories upfront
256- * - Reuses test results across multiple calls
257- */
258- export function determineCdsCommand ( cacheDir : string | undefined , sourceRoot : string ) : string {
259- try {
260- // Always use the efficient path - debug information is collected separately
261- return getBestCdsCommand ( cacheDir , sourceRoot ) ;
262- } catch ( error ) {
263- const errorMessage = `Failed to determine CDS command: ${ String ( error ) } ` ;
264- cdsExtractorLog ( 'error' , errorMessage ) ;
265- throw new Error ( errorMessage ) ;
266- }
267- }
268-
269148/**
270149 * Get detailed command analysis for debug purposes
271150 * This replaces the old performComprehensiveAnalysis function
@@ -353,3 +232,124 @@ export function getCommandAnalysisForDebug(
353232 throw new Error ( `Failed to analyze CDS commands: ${ String ( error ) } ` ) ;
354233 }
355234}
235+
236+ /**
237+ * Initialize the CDS command cache by testing global commands
238+ * @param sourceRoot The source root directory
239+ */
240+ function initializeCdsCommandCache ( sourceRoot : string ) : void {
241+ if ( cdsCommandCache . initialized ) {
242+ return ;
243+ }
244+
245+ cdsExtractorLog ( 'info' , 'Initializing CDS command cache...' ) ;
246+
247+ // Test global commands first (most commonly used)
248+ const globalCommands = [ 'cds' , 'npx -y --package @sap/cds-dk cds' ] ;
249+
250+ for ( const command of globalCommands ) {
251+ const result = testCdsCommand ( command , sourceRoot , true ) ; // Silent testing
252+ if ( result . works ) {
253+ cdsCommandCache . globalCommand = command ;
254+ cdsExtractorLog (
255+ 'info' ,
256+ `Found working global CDS command: ${ command } (v${ result . version ?? 'unknown' } )` ,
257+ ) ;
258+ break ;
259+ }
260+ }
261+
262+ // Discover available cache directories
263+ const cacheDirs = discoverAvailableCacheDirs ( sourceRoot ) ;
264+ if ( cacheDirs . length > 0 ) {
265+ cdsExtractorLog (
266+ 'info' ,
267+ `Discovered ${ cacheDirs . length } CDS cache director${ cacheDirs . length === 1 ? 'y' : 'ies' } ` ,
268+ ) ;
269+ }
270+
271+ cdsCommandCache . initialized = true ;
272+ }
273+
274+ /**
275+ * Reset the command cache - primarily for testing
276+ */
277+ export function resetCdsCommandCache ( ) : void {
278+ cdsCommandCache . commandResults . clear ( ) ;
279+ cdsCommandCache . availableCacheDirs = [ ] ;
280+ cdsCommandCache . globalCommand = undefined ;
281+ cdsCommandCache . initialized = false ;
282+ }
283+
284+ /**
285+ * Check if a CDS command is available and working
286+ * @param command The command to test
287+ * @param sourceRoot The source root directory to use as cwd when testing the command
288+ * @param silent Whether to suppress logging of test failures
289+ * @returns Object with test result and version information
290+ */
291+ function testCdsCommand (
292+ command : string ,
293+ sourceRoot : string ,
294+ silent : boolean = false ,
295+ ) : { works : boolean ; version ?: string ; error ?: string } {
296+ // Check cache first
297+ const cachedResult = cdsCommandCache . commandResults . get ( command ) ;
298+ if ( cachedResult ) {
299+ return cachedResult ;
300+ }
301+
302+ try {
303+ // Try to run the command with --version to see if it works
304+ // CRITICAL: Use sourceRoot as cwd and clean environment to avoid conflicts
305+ let result : string ;
306+
307+ const cleanEnv = {
308+ ...process . env ,
309+ // Remove any CodeQL-specific environment variables that might interfere
310+ CODEQL_EXTRACTOR_CDS_WIP_DATABASE : undefined ,
311+ CODEQL_RUNNER : undefined ,
312+ } ;
313+
314+ if ( command . includes ( 'node ' ) ) {
315+ // For node commands, we need to split and execute properly
316+ const parts = command . split ( ' ' ) ;
317+ const nodeExecutable = parts [ 0 ] ; // 'node'
318+ const scriptPath = parts [ 1 ] . replace ( / " / g, '' ) ; // Remove quotes from path
319+ result = execFileSync ( nodeExecutable , [ scriptPath , '--version' ] , {
320+ encoding : 'utf8' ,
321+ stdio : 'pipe' ,
322+ timeout : 5000 , // Reduced timeout for faster failure
323+ cwd : sourceRoot ,
324+ env : cleanEnv ,
325+ } ) . toString ( ) ;
326+ } else {
327+ // Use shell-quote to properly escape the command and prevent injection
328+ const escapedCommand = quote ( [ command , '--version' ] ) ;
329+ result = execFileSync ( 'sh' , [ '-c' , escapedCommand ] , {
330+ encoding : 'utf8' ,
331+ stdio : 'pipe' ,
332+ timeout : 5000 , // Reduced timeout for faster failure
333+ cwd : sourceRoot ,
334+ env : cleanEnv ,
335+ } ) . toString ( ) ;
336+ }
337+
338+ // Extract version from output (typically in format "@sap/cds-dk: 6.1.3" or just "6.1.3")
339+ const versionMatch = result . match ( / ( \d + \. \d + \. \d + ) / ) ;
340+ const version = versionMatch ? versionMatch [ 1 ] : undefined ;
341+
342+ const testResult = { works : true , version } ;
343+ cdsCommandCache . commandResults . set ( command , testResult ) ;
344+ return testResult ;
345+ } catch ( error ) {
346+ const errorMessage = String ( error ) ;
347+ if ( ! silent ) {
348+ cdsExtractorLog ( 'debug' , `CDS command test failed for '${ command } ': ${ errorMessage } ` ) ;
349+ }
350+
351+ const testResult = { works : false , error : errorMessage } ;
352+ cdsCommandCache . commandResults . set ( command , testResult ) ;
353+ return testResult ;
354+ }
355+ }
0 commit comments