diff --git a/scripts/download.ps1 b/scripts/download.ps1 new file mode 100644 index 0000000..d3ed06a --- /dev/null +++ b/scripts/download.ps1 @@ -0,0 +1,19 @@ +param( + [Parameter(Mandatory = $true)][string]$Url, + [Parameter(Mandatory = $true)][string]$Out, + [Parameter(Mandatory = $true)][string]$Lock +) + +# Download notify.exe with curl to a temp file, then atomic-rename on success. +# Always remove the lock dir at the end (success or failure). +$tmp = $Out + ".downloading" +try { + & curl.exe -fsSL $Url -o $tmp + if ((Test-Path -LiteralPath $tmp) -and ((Get-Item -LiteralPath $tmp).Length -gt 0)) { + Move-Item -LiteralPath $tmp -Destination $Out -Force + } +} +finally { + Remove-Item -LiteralPath $tmp -Force -ErrorAction SilentlyContinue + Remove-Item -LiteralPath $Lock -Recurse -Force -ErrorAction SilentlyContinue +} diff --git a/scripts/notify.cmd b/scripts/notify.cmd index ad1d06a..79ba2b9 100644 --- a/scripts/notify.cmd +++ b/scripts/notify.cmd @@ -8,7 +8,7 @@ set "BIN=%~dp0..\bin" set "EXE=%BIN%\notify.exe" set "LOCK=%BIN%\notify.download.lock" -rem only bootstrap on first run; the common path runs the exe directly (keeps piped stdin intact) +rem first run: kick off a background download, do NOT block the hook if not exist "%EXE%" call :bootstrap if exist "%EXE%" "%EXE%" %* @@ -17,29 +17,10 @@ exit /b :bootstrap if not exist "%BIN%" mkdir "%BIN%" 2>nul -set "TMP=%BIN%\notify.exe.%RANDOM%.tmp" -rem mkdir is atomic; success = this process downloads, failure = someone else is downloading +rem mkdir is atomic; success = we start the download, failure = already downloading mkdir "%LOCK%" 2>nul -if errorlevel 1 goto :waitdl -rem double-check in case it just finished +if errorlevel 1 exit /b if exist "%EXE%" ( rmdir "%LOCK%" 2>nul & exit /b ) -rem download to temp then atomic rename, so no half-written exe is ever seen -curl -fsSL "%DOWNLOAD_URL%" -o "%TMP%" -if errorlevel 1 ( del "%TMP%" 2>nul & rmdir "%LOCK%" 2>nul & exit /b ) -move /y "%TMP%" "%EXE%" >nul -rmdir "%LOCK%" 2>nul +rem launch a detached hidden PowerShell downloader (ShellExecute breaks away from the hook) +powershell -NoProfile -WindowStyle Hidden -Command "Start-Process -WindowStyle Hidden -FilePath powershell -ArgumentList @('-NoProfile','-ExecutionPolicy','Bypass','-File','%~dp0download.ps1','-Url','%DOWNLOAD_URL%','-Out','%EXE%','-Lock','%LOCK%')" exit /b - -:waitdl -rem did not get the lock; wait for the exe to appear (up to ~60s) -set /a _w=0 -:waitloop -if exist "%EXE%" exit /b -if %_w% geq 120 ( - rem timed out; a killed download may have left a stale lock, clear it for next time - rmdir "%LOCK%" 2>nul - exit /b -) -ping -n 2 127.0.0.1 >nul -set /a _w+=1 -goto :waitloop diff --git a/scripts/notify.sh b/scripts/notify.sh index 123ce6a..cc41ae1 100644 --- a/scripts/notify.sh +++ b/scripts/notify.sh @@ -9,32 +9,16 @@ BIN="$DIR/../bin" EXE="$BIN/notify.exe" LOCK="$BIN/notify.download.lock" +# 首次运行:后台下载,不阻塞钩子 if [ ! -f "$EXE" ]; then mkdir -p "$BIN" 2>/dev/null - # mkdir 是原子操作,用作锁:成功=本进程负责下载,失败=已有进程在下 + # mkdir 原子:成功=本进程负责下载,失败=已有进程在下 if mkdir "$LOCK" 2>/dev/null; then - # 双重检查,避免刚好别人下完 - if [ ! -f "$EXE" ]; then - TMP="$BIN/notify.exe.$$.tmp" - if curl -fsSL "$DOWNLOAD_URL" -o "$TMP"; then - mv -f "$TMP" "$EXE" # 原子改名,避免半截 exe - chmod +x "$EXE" 2>/dev/null - else - rm -f "$TMP" - fi - fi - rmdir "$LOCK" 2>/dev/null - else - # 没抢到锁:等 exe 出现(最多约 60 秒) - i=0 - while [ ! -f "$EXE" ] && [ "$i" -lt 120 ]; do - sleep 0.5 - i=$((i + 1)) - done - # 超时仍没下好:可能上次下载被杀留下陈旧锁,清掉让下次重下 - [ ! -f "$EXE" ] && rmdir "$LOCK" 2>/dev/null + TMP="$EXE.downloading" + # nohup + & 让下载脱离钩子在后台跑;完成后原子改名并清锁 + nohup sh -c "curl -fsSL '$DOWNLOAD_URL' -o '$TMP' && mv -f '$TMP' '$EXE'; rmdir '$LOCK' 2>/dev/null" >/dev/null 2>&1 & fi fi -# 转发全部参数与 stdin 给真正的 exe +# 本次不等下载;exe 就绪后才转发参数与 stdin [ -f "$EXE" ] && exec "$EXE" "$@"