runner: hide PowerShell window + filter PS error noise from log
All checks were successful
release / build-and-release (push) Successful in 25s

- HideWindow: true in SysProcAttr (Windows) prevents powershell.exe
  from opening a console window over the GUI
- skipPSNoiseLine filters multi-line PS error blocks (At line:, CategoryInfo,
  FullyQualifiedErrorId, VERBOSE: etc.) - errors still appear via Write-Log

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
X9 Dev 2026-04-16 14:39:36 +02:00
parent d06af1a87f
commit 66dfdbf44c
3 changed files with 52 additions and 0 deletions

View file

@ -0,0 +1,7 @@
//go:build !windows
package runner
import "os/exec"
func hideWindow(cmd *exec.Cmd) {}

View file

@ -0,0 +1,14 @@
//go:build windows
package runner
import (
"os/exec"
"syscall"
)
// hideWindow prevents the child process from creating a visible console window.
// Without this, powershell.exe opens a full-size window on top of the GUI.
func hideWindow(cmd *exec.Cmd) {
cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true}
}

View file

@ -223,6 +223,7 @@ func (r *Runner) runScript(ctx context.Context, step Step, cfgArg string) error
}
cmd := exec.CommandContext(ctx, "powershell.exe", args...)
hideWindow(cmd) // prevent PS console window from appearing over the GUI
stdout, err := cmd.StdoutPipe()
if err != nil {
@ -237,6 +238,9 @@ func (r *Runner) runScript(ctx context.Context, step Step, cfgArg string) error
scanner := bufio.NewScanner(stdout)
for scanner.Scan() {
line := scanner.Text()
if skipPSNoiseLine(line) {
continue
}
r.onLog(LogLine{
StepID: step.ID,
Text: line,
@ -247,6 +251,33 @@ func (r *Runner) runScript(ctx context.Context, step Step, cfgArg string) error
return cmd.Wait()
}
// skipPSNoiseLine returns true for PowerShell stderr noise that clutters the log:
// multi-line error blocks (At line:N, CategoryInfo, FullyQualifiedErrorId, etc.),
// blank lines, and VERBOSE: prefix lines already handled by Write-Log.
func skipPSNoiseLine(line string) bool {
trimmed := strings.TrimSpace(line)
if trimmed == "" {
return true
}
for _, prefix := range []string{
"At line:",
"+ CategoryInfo",
"+ FullyQualifiedErrorId",
"+ PositionMessage",
"VERBOSE:",
"DEBUG:",
} {
if strings.HasPrefix(trimmed, prefix) {
return true
}
}
// PS error continuation lines start with spaces + "+" or "~"
if len(trimmed) > 0 && (trimmed[0] == '+' || strings.HasPrefix(trimmed, "~")) {
return true
}
return false
}
// parseLevel extracts the log level from lines formatted as "[HH:mm:ss] [LEVEL] message".
func parseLevel(line string) string {
if strings.Contains(line, "] [OK]") {