@@ -13,6 +13,7 @@ import { getVsixFilesFromDir } from './system-operations';
1313import { TestConfig } from './core/types' ;
1414import { createDefaultTestConfig , validateTestConfig , normalizePath } from './core/helpers' ;
1515import { verifyAliasAndUserName } from './salesforce-components/authorization' ;
16+ import { retryOperation } from './retryUtils' ;
1617
1718// Set Ubuntu Chrome arguments immediately when module loads
1819if ( process . platform === 'linux' ) {
@@ -213,6 +214,89 @@ class TestSetupAndRunner extends ExTester {
213214 }
214215 }
215216
217+ /**
218+ * Kills existing VS Code processes on Windows to prevent file locking issues during extension installation
219+ */
220+ private async killExistingVSCodeProcesses ( ) : Promise < void > {
221+ if ( process . platform !== 'win32' ) {
222+ return ;
223+ }
224+
225+ try {
226+ const { exec } = await import ( 'child_process' ) ;
227+ const { promisify } = await import ( 'util' ) ;
228+ const execAsync = promisify ( exec ) ;
229+
230+ log ( 'Attempting to kill existing VS Code processes on Windows...' ) ;
231+
232+ // Kill VS Code processes on Windows
233+ const commands = [
234+ 'taskkill /f /im Code.exe' ,
235+ 'taskkill /f /im code.exe' ,
236+ 'taskkill /f /im VSCode.exe' ,
237+ 'taskkill /f /im electron.exe /fi "WINDOWTITLE eq Visual Studio Code*"' ,
238+ 'taskkill /f /im node.exe /fi "COMMANDLINE eq *vscode*"'
239+ ] ;
240+
241+ for ( const command of commands ) {
242+ try {
243+ await execAsync ( command ) ;
244+ log ( `Executed: ${ command } ` ) ;
245+ } catch ( error ) {
246+ // It's normal for taskkill to exit with code 1 if no processes are found
247+ log ( `Command ${ command } completed (processes may not have been running)` ) ;
248+ }
249+ }
250+
251+ // Wait a moment for processes to fully terminate and release file locks
252+ await new Promise ( resolve => setTimeout ( resolve , 3000 ) ) ;
253+ log ( 'VS Code process cleanup completed' ) ;
254+
255+ } catch ( error ) {
256+ log ( `Warning: Could not kill VS Code processes: ${ error } ` ) ;
257+ }
258+ }
259+
260+ /**
261+ * Cleans up Windows VS Code extension temporary files that may cause locking issues
262+ */
263+ private async cleanupWindowsExtensionTempFiles ( ) : Promise < void > {
264+ if ( process . platform !== 'win32' ) {
265+ return ;
266+ }
267+
268+ try {
269+ const { exec } = await import ( 'child_process' ) ;
270+ const { promisify } = await import ( 'util' ) ;
271+ const execAsync = promisify ( exec ) ;
272+
273+ log ( 'Cleaning up Windows VS Code extension temporary files...' ) ;
274+
275+ // Clean up VS Code extension temporary files
276+ const cleanupCommands = [
277+ 'del /f /q "%USERPROFILE%\\.vscode\\extensions\\*.vsctmp" 2>nul' ,
278+ 'del /f /q "%USERPROFILE%\\.vscode\\extensions\\*.tmp" 2>nul' ,
279+ 'rmdir /s /q "%TEMP%\\vscode-*" 2>nul' ,
280+ 'rmdir /s /q "%TEMP%\\Code-*" 2>nul'
281+ ] ;
282+
283+ for ( const command of cleanupCommands ) {
284+ try {
285+ await execAsync ( command ) ;
286+ log ( `Executed cleanup: ${ command } ` ) ;
287+ } catch ( error ) {
288+ // It's normal for cleanup commands to fail if files don't exist
289+ log ( `Cleanup command completed: ${ command } ` ) ;
290+ }
291+ }
292+
293+ log ( 'Windows extension temp file cleanup completed' ) ;
294+
295+ } catch ( error ) {
296+ log ( `Warning: Could not cleanup Windows extension temp files: ${ error } ` ) ;
297+ }
298+ }
299+
216300 /**
217301 * Creates a Chrome wrapper script that forces Ubuntu-specific arguments
218302 */
@@ -328,6 +412,12 @@ exec "${chromeExePath}" \\
328412 await this . createChromeWrapper ( ) ;
329413 }
330414
415+ // Kill any existing VS Code processes on Windows before installing extensions
416+ if ( process . platform === 'win32' ) {
417+ await this . killExistingVSCodeProcesses ( ) ;
418+ await this . cleanupWindowsExtensionTempFiles ( ) ;
419+ }
420+
331421 try {
332422 await this . installExtensions ( ) ;
333423 } catch ( error : unknown ) {
@@ -491,7 +581,20 @@ exec "${chromeExePath}" \\
491581 }
492582 }
493583
494- await this . installExtension ( vsixPath ) ;
584+ // Use retry logic for Windows to handle file locking issues
585+ if ( process . platform === 'win32' ) {
586+ await retryOperation (
587+ async ( ) => {
588+ // Add a small delay before each retry attempt to allow file handles to be released
589+ await new Promise ( resolve => setTimeout ( resolve , TestSetupAndRunner . RETRY_DELAY ) ) ;
590+ await this . installExtension ( vsixPath ) ;
591+ } ,
592+ TestSetupAndRunner . MAX_RETRIES ,
593+ `Failed to install extension ${ path . basename ( vsixPath ) } after retries due to Windows file locking issues`
594+ ) ;
595+ } else {
596+ await this . installExtension ( vsixPath ) ;
597+ }
495598 }
496599 }
497600
0 commit comments