-
Notifications
You must be signed in to change notification settings - Fork 0
fix(update): auto-restart after Windows binary swap #6
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -68,16 +68,20 @@ func platformRestart() error { | |
| } | ||
|
|
||
| // buildWindowsRestartScript waits for the current PID to exit, then starts the | ||
| // service again. Prefer native-control.ps1 (preserves env + log redirection + | ||
| // pid file); otherwise re-launch the binary in the same working directory. | ||
| // service again. Prefer native-control.ps1 restart-app (app-only, keeps DB/Redis); | ||
| // fall back to start; otherwise re-launch the binary in the same working directory. | ||
| func buildWindowsRestartScript(pid int, exe, workDir string) string { | ||
| exeDir := filepath.Dir(exe) | ||
| nativeControl := filepath.Join(exeDir, "native-control.ps1") | ||
|
|
||
| var relaunch string | ||
| if _, err := os.Stat(nativeControl); err == nil { | ||
| // Prefer restart-app (only cycles sub2api). Older scripts without that | ||
| // action fall back to start (safe when the old process has already exited). | ||
| relaunch = fmt.Sprintf( | ||
| `& %s start; if ($LASTEXITCODE -ne 0) { throw "native-control start failed: $LASTEXITCODE" }`, | ||
| `& %s restart-app; if ($LASTEXITCODE -ne 0) { & %s start }; `+ | ||
| `if ($LASTEXITCODE -ne 0) { throw "native-control restart/start failed: $LASTEXITCODE" }`, | ||
| psQuote(nativeControl), | ||
| psQuote(nativeControl), | ||
|
Comment on lines
81
to
85
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
# Inspect native-control action dispatch and its failure signaling; do not execute it.
fd -a '^native-control\.ps1$' . -0 |
xargs -0 -r rg -n -C3 'restart-app|Unknown|throw|exit|ErrorActionPreference'Repository: 706412584/sub2api Length of output: 155 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "== files =="
git ls-files | rg '(^|/)native-control\.ps1$|restart_windows\.go$|restart.*windows' || true
echo "== locate native-control =="
fd -a 'native-control|restart_windows' . -0 | sed 's#^\./##' | tr '\0' '\n'
echo "== outline restart_windows if present =="
f="$(fd 'restart_windows\.go' . | head -n 1)"
if [ -n "$f" ]; then
echo "FILE $f"
wc -l "$f"
ast-grep outline "$f" || true
sed -n '60,130p' "$f"
fi
echo "== search restart-app/native-control references =="
rg -n -C3 'restart-app|native-control|Restart-App|restartApp|Start-App|start-app|Start-Service|ErrorActionPreference' . --glob '!**/.git/**' || trueRepository: 706412584/sub2api Length of output: 8223 🌐 Web query:
💡 Result: In PowerShell, $ErrorActionPreference = 'Stop' controls how non-terminating errors are handled, but it does not directly monitor or react to the $LASTEXITCODE of external commands [1][2]. Mechanism and Behavior: 1. $ErrorActionPreference = 'Stop' escalates non-terminating errors (cmdlet-based) into terminating errors [1][3][4]. 2. This preference does not affect external program exit codes by default [2][5]. Even if set to 'Stop', a failing external command will simply set $LASTEXITCODE to a non-zero value without throwing an exception or aborting the script [2]. 3. To trigger an error in response to a non-zero $LASTEXITCODE, you must manually check the variable and handle it, typically by using 'throw' [2][5]. 4. An exception to this is in PowerShell 7.4+, where you can set $PSNativeCommandUseErrorActionPreference = $true [2]. When enabled, non-zero exit codes from external programs generate a PowerShell error, which is then governed by your $ErrorActionPreference [2]. If $ErrorActionPreference is set to 'Stop' in this scenario, the script will abort upon an external command failure [2]. Throw Behavior: - The 'throw' keyword generates a script-terminating error by default, unwinding the call stack unless caught by a try/catch or trap statement [6]. - $ErrorActionPreference can suppress 'throw' if set to 'SilentlyContinue' or 'Ignore', though the error record is still captured in the $Error variable [6][7]. - If you encounter an external command failure, manually calling 'throw' is the standard way to bridge the gap between $LASTEXITCODE and the PowerShell try/catch error handling mechanism [2][5]. Summary of Interaction: - $ErrorActionPreference = 'Stop' applies to native PowerShell cmdlet errors [4][5]. - To handle external exit codes, check $LASTEXITCODE manually or use $PSNativeCommandUseErrorActionPreference = $true in PowerShell 7.4+ to integrate them into the standard error stream [2]. Citations:
🌐 Web query:
💡 Result: In PowerShell 7.4, the preference variable $PSNativeCommandUseErrorActionPreference allows you to manage how native commands with non-zero exit codes are handled [1][2]. When set to $true, a non-zero exit code triggers the creation of a NativeCommandExitException, which is treated as a PowerShell error [3][4]. Key behaviors include: 1. Error Type: When $PSNativeCommandUseErrorActionPreference is $true, a non-zero exit code results in a non-terminating error by default [3][5]. 2. ErrorActionPreference Integration: This error respects the current $ErrorActionPreference [3][2]. If you set $ErrorActionPreference to Stop, the non-terminating error is promoted to a script-terminating error [3][5]. This allows you to catch the exception using standard try/catch blocks [3][6]. 3. Exception Details: The error is specifically represented by a NativeCommandExitException [3][4]. This exception contains details such as the path of the native command, the non-zero exit code returned, and the process ID [4]. 4. Scope Control: Because it is a preference variable, you can localize its effect by setting it within a script block [1][2]. This is particularly useful for native commands like robocopy that may return non-zero exit codes for informational purposes rather than errors [1]. Prior to PowerShell 7.4, this functionality was available as an experimental feature (PSNativeCommandErrorActionPreference) [7][6]. In 7.4, it became a mainstream, stable feature [7][8]. Example of handling native command errors with try/catch: $ErrorActionPreference = 'Stop' $PSNativeCommandUseErrorActionPreference = Citations:
🌐 Web query:
💡 Result: In PowerShell, the behavior of an undefined command (a command not found by the shell) is determined by the shell's parsing process before it ever interacts with the $ErrorActionPreference variable or generates an exit code [1][2]. When you attempt to run an undefined command: 1. Command Parsing: PowerShell's engine attempts to resolve the command name. If it cannot find the command as a cmdlet, function, script, or executable, it throws a non-terminating error (or a statement-terminating error depending on context/host) indicating the command is not recognized. Because this occurs at the parsing or binding stage, $ErrorActionPreference = 'Stop' generally does not intercept or handle "command not found" errors in the way it handles errors emitted by executing cmdlets [1][2]. 2. $LASTEXITCODE Behavior: $LASTEXITCODE is specifically designed to store the exit code of the last Win32 executable that was executed [3][4]. If an attempt to run a command fails because the command itself is undefined (and thus no external process was ever successfully launched), Citations:
🌐 Web query:
💡 Result: In PowerShell, a CommandNotFoundException is classified as a statement-terminating error [1]. By default, statement-terminating errors stop the current statement from executing but do not necessarily terminate the entire script [2]. To ensure a CommandNotFoundException is caught by a try/catch block, you must escalate it to a script-terminating error [3][4]. 1. Using $ErrorActionPreference = 'Stop': When you set Citations:
🌐 Web query:
💡 Result: When a command (external program) is not found in PowerShell, it typically triggers a CommandNotFoundException [1]. Whether this behavior interacts with $ErrorActionPreference and affects the final exit code depends on how you run the command and the PowerShell version [2][3][4]. To ensure robust error handling and proper exit code management, follow these guidelines: 1. Understanding CommandNotFound: When PowerShell fails to find an executable, it throws a non-terminating error by default [1]. If $ErrorActionPreference is set to 'Stop', this error is promoted to a script-terminating error [5][6]. 2. Using $PSNativeCommandUseErrorActionPreference (PowerShell 7.4+): For external programs, $ErrorActionPreference alone does not natively control exit code behavior in older versions [3][4]. In PowerShell 7.4 and later, you can enable the $PSNativeCommandUseErrorActionPreference feature [2][4]: - Set $PSNativeCommandUseErrorActionPreference = $true. - Set $ErrorActionPreference = 'Stop'. When enabled, any non-zero exit code from an external program generates a NativeCommandExitException [2][7]. Because this is a PowerShell error, it respects $ErrorActionPreference, effectively allowing you to catch the failure in a try/catch block [2][8][4]. 3. Manual Exit Code Handling (Recommended for Compatibility): If you cannot rely on the $PSNativeCommandUseErrorActionPreference feature (e.g., in Windows PowerShell or for broader script compatibility), you must manually check the exit code [3][4]: try { # Attempt to run the command $commandOutput = my-command if ($LASTEXITCODE -ne 0) { throw "Command failed with exit code Citations:
Wrap With 🤖 Prompt for AI Agents |
||
| ) | ||
| } else { | ||
|
|
@@ -94,16 +98,20 @@ func buildWindowsRestartScript(pid int, exe, workDir string) string { | |
| ) | ||
| } | ||
|
|
||
| // Wait until the old process is gone, then give handles a moment to release | ||
| // (important when stdout/stderr log files are still locked on Windows). | ||
| // Wait until the old process is gone; force-kill if it ignores graceful exit | ||
| // (stuck handlers). Then give handles a moment to release log file locks. | ||
| return fmt.Sprintf( | ||
| `$ErrorActionPreference = 'Stop'; `+ | ||
| `$targetPid = %d; `+ | ||
| `for ($i = 0; $i -lt 150; $i++) { `+ | ||
| `for ($i = 0; $i -lt 100; $i++) { `+ | ||
| `if (-not (Get-Process -Id $targetPid -ErrorAction SilentlyContinue)) { break }; `+ | ||
| `Start-Sleep -Milliseconds 200 `+ | ||
| `}; `+ | ||
| `if (Get-Process -Id $targetPid -ErrorAction SilentlyContinue) { `+ | ||
| `Stop-Process -Id $targetPid -Force -ErrorAction SilentlyContinue; `+ | ||
| `Start-Sleep -Milliseconds 500 `+ | ||
| `}; `+ | ||
| `if (Get-Process -Id $targetPid -ErrorAction SilentlyContinue) { `+ | ||
| `throw "timed out waiting for process $targetPid to exit" `+ | ||
| `}; `+ | ||
| `Start-Sleep -Milliseconds 500; `+ | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift
Keep system-operation exclusion active through the restart handoff.
The deferred
release(..., succeeded=true)runs as each callback returns, while the restart waits another 500ms. A second update or rollback can therefore acquire the lock and begin before the first operation’s scheduled restart terminates it, risking an interrupted binary operation. Represent “restart pending” in the lock/status flow, or otherwise keep competing system operations excluded until handoff.backend/internal/handler/admin/system_handler.go#L123-L131: prevent a new system operation from starting before the update-triggered restart begins.backend/internal/handler/admin/system_handler.go#L202-L206: apply the same restart-pending exclusion to rollback.📍 Affects 1 file
backend/internal/handler/admin/system_handler.go#L123-L131(this comment)backend/internal/handler/admin/system_handler.go#L202-L206🤖 Prompt for AI Agents