@@ -222,8 +222,12 @@ function windowsChannelInstallerEnv(base, installDir, keyXml) {
222222}
223223
224224function runWindowsInstaller ( env , timeout = 10000 ) {
225+ const action = env . CONTEXA_INSTALL_ACTION || 'install' ;
226+ const runtimeEnv = { ...env } ;
227+ delete runtimeEnv . CONTEXA_INSTALL_ACTION ;
225228 return run ( powershell ,
226- [ '-NoProfile' , '-NonInteractive' , '-ExecutionPolicy' , 'Bypass' , '-File' , ps1 ] , env , timeout ) ;
229+ [ '-NoProfile' , '-NonInteractive' , '-ExecutionPolicy' , 'Bypass' , '-File' , ps1 , '-Action' , action ] ,
230+ runtimeEnv , timeout ) ;
227231}
228232
229233function runWindowsInstallerViaIex ( env , timeout = 10000 , catchFailure = false ) {
@@ -236,7 +240,12 @@ function runWindowsInstallerViaIex(env, timeout = 10000, catchFailure = false) {
236240 [ '-NoProfile' , '-NonInteractive' , '-ExecutionPolicy' , 'Bypass' , '-Command' , command ] , env , timeout ) ;
237241}
238242function runPwshInstaller ( env ) {
239- return run ( pwsh , [ '-NoProfile' , '-NonInteractive' , '-ExecutionPolicy' , 'Bypass' , '-File' , ps1 ] , env ) ;
243+ const action = env . CONTEXA_INSTALL_ACTION || 'install' ;
244+ const runtimeEnv = { ...env } ;
245+ delete runtimeEnv . CONTEXA_INSTALL_ACTION ;
246+ return run ( pwsh ,
247+ [ '-NoProfile' , '-NonInteractive' , '-ExecutionPolicy' , 'Bypass' , '-File' , ps1 , '-Action' , action ] ,
248+ runtimeEnv ) ;
240249}
241250
242251function toPosixPath ( value ) {
@@ -281,10 +290,19 @@ function posixInstallerEnv(base, harness, publicKeyPath, version = '') {
281290 } ;
282291}
283292
284- function runPosixInstaller ( env , installer = sh ) {
293+ function runPosixInstaller ( env , installer = sh , explicitAction = undefined ) {
285294 const quotedPath = env . PATH . replace ( / ' / g, `'"'"'` ) ;
286295 const scriptPath = toPosixPath ( installer ) . replace ( / ' / g, `'"'"'` ) ;
287- return run ( gitSh , [ '-c' , `PATH='${ quotedPath } '; export PATH; exec '${ scriptPath } '` ] , env ) ;
296+ const runtimeEnv = { ...env } ;
297+ let action = '' ;
298+ if ( explicitAction === null ) {
299+ // Keep a leaked legacy environment value to prove the public install entrypoint ignores it.
300+ } else {
301+ action = explicitAction === undefined ? ( env . CONTEXA_INSTALL_ACTION || '' ) : explicitAction ;
302+ delete runtimeEnv . CONTEXA_INSTALL_ACTION ;
303+ }
304+ const actionArgument = action ? ` '${ String ( action ) . replace ( / ' / g, `'"'"'` ) } '` : '' ;
305+ return run ( gitSh , [ '-c' , `PATH='${ quotedPath } '; export PATH; exec '${ scriptPath } '${ actionArgument } ` ] , runtimeEnv ) ;
288306}
289307
290308function buildPosixCli ( version , options = { } ) {
@@ -402,7 +420,8 @@ test('PowerShell installer performs install, no-op, update, rollback and uninsta
402420 await withServer ( releaseHandler ( files ) , async ( base ) => {
403421 for ( let iteration = 1 ; iteration <= lifecycleRepeats ; iteration += 1 ) {
404422 const installDir = path . join ( temp , `설치 경로 with space ${ iteration } ` ) ;
405- const first = await runWindowsInstallerViaIex ( windowsInstallerEnv ( base , installDir , '9.9.1-test' , xml ) ) ;
423+ const leakedActionEnv = windowsInstallerEnv ( base , installDir , '9.9.1-test' , xml , 'uninstall' ) ;
424+ const first = await runWindowsInstallerViaIex ( leakedActionEnv ) ;
406425 assert . match ( first . stdout , / _ _ S H E L L _ A L I V E _ _ / ) ;
407426 assert . equal ( first . code , 0 , first . stderr || first . stdout ) ;
408427 assert . match ( first . stdout , / S t a r t i n g C o n t e x a C L I i n s t a l l a t i o n / ) ;
@@ -990,7 +1009,11 @@ test('POSIX installer performs lifecycle and preserves the existing binary for t
9901009 await withServer ( releaseHandler ( lifecycleFiles ) , async ( base ) => {
9911010 for ( let iteration = 1 ; iteration <= lifecycleRepeats ; iteration += 1 ) {
9921011 const lifecycleHarness = createPosixHarness ( path . join ( temp , `설치 경로 with space ${ iteration } ` ) ) ;
993- const first = await runPosixInstaller ( posixInstallerEnv ( base , lifecycleHarness , publicKeyPath , currentVersion ) ) ;
1012+ const leakedActionEnv = {
1013+ ...posixInstallerEnv ( base , lifecycleHarness , publicKeyPath , currentVersion ) ,
1014+ CONTEXA_INSTALL_ACTION : 'uninstall' ,
1015+ } ;
1016+ const first = await runPosixInstaller ( leakedActionEnv , sh , null ) ;
9941017 assert . equal ( first . code , 0 , first . stderr || first . stdout ) ;
9951018 const installed = path . join ( lifecycleHarness . installDir , 'contexa' ) ;
9961019 assert . equal ( spawnSync ( gitSh , [ toPosixPath ( installed ) , '--version' ] , { encoding : 'utf8' } ) . stdout . trim ( ) , currentVersion ) ;
@@ -1215,18 +1238,12 @@ test('POSIX installer emits intact Korean success and error messages', { timeout
12151238 CONTEXA_INSTALL_DIR : path . join ( temp , 'bin' ) ,
12161239 CONTEXA_SKIP_PATH_UPDATE : '1' ,
12171240 } ;
1218- const uninstall = await run ( gitSh , [ scriptPath ] , {
1219- ...baseEnv ,
1220- CONTEXA_INSTALL_ACTION : 'uninstall' ,
1221- } ) ;
1241+ const uninstall = await run ( gitSh , [ scriptPath , 'uninstall' ] , baseEnv ) ;
12221242 assert . equal ( uninstall . code , 0 , uninstall . stderr || uninstall . stdout ) ;
12231243 assert . match ( uninstall . stdout , / 바 이 너 리 .* 제 거 / ) ;
12241244 assert . equal ( ( uninstall . stdout + uninstall . stderr ) . includes ( '\uFFFD' ) , false ) ;
12251245
1226- const invalid = await run ( gitSh , [ scriptPath ] , {
1227- ...baseEnv ,
1228- CONTEXA_INSTALL_ACTION : 'invalid' ,
1229- } ) ;
1246+ const invalid = await run ( gitSh , [ scriptPath , 'invalid' ] , baseEnv ) ;
12301247 assert . equal ( invalid . code , 1 ) ;
12311248 assert . match ( invalid . stderr , / 설 치 프 로 그 램 실 패 \[ I N S T A L L E R _ O P E R A T I O N _ F A I L E D \] .* 오 류 코 드 / s) ;
12321249 assert . equal ( ( invalid . stdout + invalid . stderr ) . includes ( '\uFFFD' ) , false ) ;
0 commit comments