공개 런타임 빌드 종료 판정을 보강

This commit is contained in:
Yun Chan 2026-08-29 14:41:49 +09:00
parent 471eb9621a
commit 44b7835ce3
2 changed files with 84 additions and 2 deletions

View file

@ -1574,6 +1574,10 @@ if (!$SkipWebRestart) {
-WorkingDirectory $WebDir ` -WorkingDirectory $WebDir `
-NoNewWindow ` -NoNewWindow `
-PassThru -PassThru
# Windows PowerShell 5.1의 Start-Process -PassThru는 Process.Handle을 먼저
# 열지 않으면 timed WaitForExit 뒤에도 ExitCode가 $null로 남을 수 있다.
# 성공한 빌드를 실패로 오판하지 않도록 핸들을 대기 전에 확보한다.
$null = $build.Handle
if (-not $build.WaitForExit($WebBuildTimeoutSeconds * 1000)) { if (-not $build.WaitForExit($WebBuildTimeoutSeconds * 1000)) {
$null = @( $null = @(
Stop-ProcessTreeBounded ` Stop-ProcessTreeBounded `
@ -1584,6 +1588,9 @@ if (!$SkipWebRestart) {
throw "Web build timed out after $WebBuildTimeoutSeconds seconds" throw "Web build timed out after $WebBuildTimeoutSeconds seconds"
} }
$build.Refresh() $build.Refresh()
if ($null -eq $build.ExitCode) {
throw "Web build exit code is unavailable"
}
if ($build.ExitCode -ne 0) { if ($build.ExitCode -ne 0) {
throw "Web build failed with exit code $($build.ExitCode)" throw "Web build failed with exit code $($build.ExitCode)"
} }

View file

@ -101,6 +101,70 @@ exit 0
with self.subTest(expected=expected): with self.subTest(expected=expected):
self.assertIn(expected, PUBLIC_RUNTIME) self.assertIn(expected, PUBLIC_RUNTIME)
def test_web_build_keeps_process_handle_before_timed_wait(self) -> None:
build_start = PUBLIC_RUNTIME.index(
'$build = Start-Process -FilePath "cmd.exe"'
)
handle = PUBLIC_RUNTIME.index("$null = $build.Handle", build_start)
timed_wait = PUBLIC_RUNTIME.index(
"$build.WaitForExit($WebBuildTimeoutSeconds * 1000)", handle
)
null_guard = PUBLIC_RUNTIME.index(
"if ($null -eq $build.ExitCode)", timed_wait
)
failure_check = PUBLIC_RUNTIME.index(
"if ($build.ExitCode -ne 0)", null_guard
)
self.assertLess(build_start, handle)
self.assertLess(handle, timed_wait)
self.assertLess(timed_wait, null_guard)
self.assertLess(null_guard, failure_check)
def test_web_build_exit_code_is_available_in_windows_powershell(self) -> None:
powershell = shutil.which("powershell.exe")
if powershell is None:
self.skipTest("Windows PowerShell 5.1 is not available")
with tempfile.TemporaryDirectory() as temporary_directory:
harness = Path(temporary_directory) / "process-exit-code.ps1"
harness.write_text(
"""
$process = Start-Process -FilePath 'cmd.exe' `
-ArgumentList @('/c', 'exit 0') `
-NoNewWindow `
-PassThru
$null = $process.Handle
if (-not $process.WaitForExit(10000)) { exit 2 }
$process.Refresh()
if ($null -eq $process.ExitCode) { exit 3 }
exit $process.ExitCode
""",
encoding="utf-8-sig",
)
completed = subprocess.run(
[
powershell,
"-NoLogo",
"-NoProfile",
"-NonInteractive",
"-ExecutionPolicy",
"Bypass",
"-File",
str(harness),
],
capture_output=True,
text=True,
encoding="utf-8",
errors="replace",
timeout=30,
check=False,
)
self.assertEqual(
completed.returncode,
0,
msg=f"stdout={completed.stdout}\nstderr={completed.stderr}",
)
def test_web_build_timeout_stops_the_owned_process_tree(self) -> None: def test_web_build_timeout_stops_the_owned_process_tree(self) -> None:
powershell = shutil.which("powershell.exe") powershell = shutil.which("powershell.exe")
if powershell is None: if powershell is None:
@ -129,6 +193,7 @@ $root = Start-Process -FilePath 'cmd.exe' `
-ArgumentList @('/c', 'powershell.exe -NoProfile -ExecutionPolicy Bypass -File "{quoted_child}"') ` -ArgumentList @('/c', 'powershell.exe -NoProfile -ExecutionPolicy Bypass -File "{quoted_child}"') `
-WindowStyle Hidden ` -WindowStyle Hidden `
-PassThru -PassThru
$childProcessId = $null
try {{ try {{
$deadline = (Get-Date).AddSeconds(10) $deadline = (Get-Date).AddSeconds(10)
while (-not (Test-Path -LiteralPath '{quoted_child_pid_for_harness}') -and (Get-Date) -lt $deadline) {{ while (-not (Test-Path -LiteralPath '{quoted_child_pid_for_harness}') -and (Get-Date) -lt $deadline) {{
@ -137,11 +202,21 @@ try {{
if (-not (Test-Path -LiteralPath '{quoted_child_pid_for_harness}')) {{ exit 2 }} if (-not (Test-Path -LiteralPath '{quoted_child_pid_for_harness}')) {{ exit 2 }}
$childProcessId = [int](Get-Content -LiteralPath '{quoted_child_pid_for_harness}' -Raw) $childProcessId = [int](Get-Content -LiteralPath '{quoted_child_pid_for_harness}' -Raw)
$stopped = @(Stop-ProcessTreeBounded -RootProcessId $root.Id -TimeoutSec 10 -Role 'test tree') $stopped = @(Stop-ProcessTreeBounded -RootProcessId $root.Id -TimeoutSec 10 -Role 'test tree')
if ($null -ne (Get-Process -Id $root.Id -ErrorAction SilentlyContinue)) {{ exit 3 }} $exitDeadline = (Get-Date).AddSeconds(10)
if ($null -ne (Get-Process -Id $childProcessId -ErrorAction SilentlyContinue)) {{ exit 4 }} do {{
$rootAlive = $null -ne (Get-Process -Id $root.Id -ErrorAction SilentlyContinue)
$childAlive = $null -ne (Get-Process -Id $childProcessId -ErrorAction SilentlyContinue)
if (-not $rootAlive -and -not $childAlive) {{ break }}
Start-Sleep -Milliseconds 200
}} while ((Get-Date) -lt $exitDeadline)
if ($rootAlive) {{ exit 3 }}
if ($childAlive) {{ exit 4 }}
exit 0 exit 0
}} finally {{ }} finally {{
Stop-Process -Id $root.Id -Force -ErrorAction SilentlyContinue Stop-Process -Id $root.Id -Force -ErrorAction SilentlyContinue
if ($null -ne $childProcessId) {{
Stop-Process -Id $childProcessId -Force -ErrorAction SilentlyContinue
}}
}} }}
''', ''',
encoding="utf-8-sig", encoding="utf-8-sig",