浏览代码

Fix deploy: wait for app pools to actually stop before wiping RemoteDir

Stop-WebAppPool only requests a stop - the w3wp.exe worker process can
keep running for a few seconds afterwards, still holding the site's
files open, so the previous fix (stopping both sites before the wipe)
wasn't sufficient on its own.

- Wait-AppPoolFullyStopped: polls app pool state until Stopped (30s
  timeout), then force-kills any lingering w3wp.exe for that pool
- Remove-DirectoryContentsWithRetry: retries the wipe a few times with
  a short delay, since file handles can take a moment to release even
  after the worker process is confirmed gone
master
Daniel Covington 8 小时前
父节点
当前提交
23984040be
共有 1 个文件被更改,包括 50 次插入1 次删除
  1. +50
    -1
      scripts/deploy-iis-remote-apply.ps1

+ 50
- 1
scripts/deploy-iis-remote-apply.ps1 查看文件

@@ -25,6 +25,53 @@ param(
$ErrorActionPreference = 'Stop'
Import-Module WebAdministration

# Stop-WebAppPool only requests a stop - the w3wp.exe worker process can keep running for a
# few seconds afterwards (finishing in-flight requests), still holding the site's files open.
# Wait for the pool to actually report Stopped, then force-kill any worker process that's
# still lingering past the timeout so the wipe below doesn't hit "Access is denied".
function Wait-AppPoolFullyStopped {
param([string]$PoolName, [int]$TimeoutSec = 30)

if([string]::IsNullOrWhiteSpace($PoolName)){ return }

$deadline = (Get-Date).AddSeconds($TimeoutSec)
while((Get-Date) -lt $deadline){
if((Get-WebAppPoolState -Name $PoolName -ErrorAction SilentlyContinue).Value -eq 'Stopped'){
return
}
Start-Sleep -Milliseconds 500
}

Write-Host "App pool $PoolName did not report Stopped within ${TimeoutSec}s - killing any lingering worker process"
Get-CimInstance Win32_Process -Filter "Name = 'w3wp.exe'" -ErrorAction SilentlyContinue |
Where-Object { $_.CommandLine -like ('*' + $PoolName + '*') } |
ForEach-Object {
try { Stop-Process -Id $_.ProcessId -Force -ErrorAction SilentlyContinue } catch { }
}
}

# Wipes are attempted a few times with a short pause - file handles (IIS or AV scanning
# the just-stopped worker process's files) can take a moment to release even after the
# worker process itself is confirmed gone.
function Remove-DirectoryContentsWithRetry {
param([string]$Path, [int]$MaxAttempts = 5, [int]$DelayMs = 2000)

for($attempt = 1; $attempt -le $MaxAttempts; $attempt++){
$failure = $null
try {
Remove-Item -Recurse -Force $Path -ErrorAction Stop
return
} catch {
$failure = $_
if($attempt -lt $MaxAttempts){
Write-Host "Wipe attempt $attempt failed (files still locked?) - retrying in $($DelayMs)ms"
Start-Sleep -Milliseconds $DelayMs
}
}
}
throw $failure
}

if(!(Get-Website -Name $SiteName -ErrorAction SilentlyContinue)){
throw "IIS site '$SiteName' does not exist yet. Create the site and app pool once manually (or via a one-time setup script) before running this deploy."
}
@@ -35,6 +82,7 @@ if(!(Test-Path ('IIS:\AppPools\' + $AppPool))){
Write-Host "Stopping IIS site $SiteName and app pool $AppPool"
try { Stop-Website -Name $SiteName } catch { }
try { Stop-WebAppPool -Name $AppPool } catch { }
Wait-AppPoolFullyStopped -PoolName $AppPool

# The admin site's physical path may already point inside RemoteDir (e.g. RemoteDir\public-admin)
# from a previous deploy - if it's left running, IIS holds those files open and the wipe below
@@ -44,12 +92,13 @@ if(![string]::IsNullOrWhiteSpace($AdminSiteName) -and (Get-Website -Name $AdminS
try { Stop-Website -Name $AdminSiteName } catch { }
if(![string]::IsNullOrWhiteSpace($AdminAppPool)){
try { Stop-WebAppPool -Name $AdminAppPool } catch { }
Wait-AppPoolFullyStopped -PoolName $AdminAppPool
}
}

if(Test-Path $RemoteDir){
Write-Host "Wiping $RemoteDir"
Remove-Item -Recurse -Force (Join-Path $RemoteDir '*') -ErrorAction SilentlyContinue
Remove-DirectoryContentsWithRetry -Path (Join-Path $RemoteDir '*')
} else {
New-Item -ItemType Directory -Force -Path $RemoteDir | Out-Null
}


正在加载...
取消
保存

Powered by TurnKey Linux.