T1.2: Create src/platform/winproc; move configureHiddenWindow

Extract the hidden-window logic out of src/core runner and autostart files
into a new platform/winproc package with per-OS build-tag files. All call
sites updated to use winproc.ConfigureHiddenWindow. Builds clean on both
Windows and Linux; all tests pass.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
mixeme
2026-06-18 21:28:25 +03:00
parent 80c76a0cba
commit f4fb16c0ed
8 changed files with 40 additions and 24 deletions
+1 -1
View File
@@ -245,7 +245,7 @@ Track progress here. Mark tasks complete as they land and pass review.
### Phase 1 — Split flat `core` package
- [x] T1.1 — Create `src/domain`; move Job/RunRecord/Config/etc
- [ ] T1.2 — Create `src/platform/winproc`; move `configureHiddenWindow`
- [x] T1.2 — Create `src/platform/winproc`; move `configureHiddenWindow`
- [ ] T1.3 — Create `src/runner`; move runner logic
- [ ] T1.4 — Create `src/scheduler`; move scheduler
- [ ] T1.5 — Create `src/storage`; move store/paths
+5 -4
View File
@@ -8,6 +8,7 @@ import (
"strings"
"gitea.mixdep.ru/mix/gosentry/src/domain"
"gitea.mixdep.ru/mix/gosentry/src/platform/winproc"
)
const autostartName = "GoSentry"
@@ -107,7 +108,7 @@ func createStartupShortcut(shortcutPath string, executablePath string, iconPath
"GOSENTRY_WORKING_DIRECTORY="+workingDirectory,
"GOSENTRY_ICON_PATH="+iconPath,
)
configureHiddenWindow(command)
winproc.ConfigureHiddenWindow(command)
if output, err := command.CombinedOutput(); err != nil {
return fmt.Errorf("create startup shortcut: %w: %s", err, strings.TrimSpace(string(output)))
}
@@ -125,7 +126,7 @@ func readShortcut(shortcutPath string) (string, string, error) {
script := `[Console]::OutputEncoding = New-Object System.Text.UTF8Encoding($false); $shell = New-Object -ComObject WScript.Shell; $shortcut = $shell.CreateShortcut($env:GOSENTRY_SHORTCUT_PATH); [Console]::Out.Write($shortcut.TargetPath + [Environment]::NewLine + $shortcut.Arguments)`
command := exec.Command("powershell.exe", "-NoProfile", "-NonInteractive", "-ExecutionPolicy", "Bypass", "-Command", script)
command.Env = append(os.Environ(), "GOSENTRY_SHORTCUT_PATH="+shortcutPath)
configureHiddenWindow(command)
winproc.ConfigureHiddenWindow(command)
output, err := command.CombinedOutput()
if err != nil {
return "", "", fmt.Errorf("read startup shortcut: %w: %s", err, strings.TrimSpace(string(output)))
@@ -155,7 +156,7 @@ func removeIfExists(path string) error {
func cleanupLegacyRegistryAutostart() error {
for _, name := range []string{legacyAutostartName, autostartName} {
command := exec.Command("reg.exe", "delete", `HKCU\Software\Microsoft\Windows\CurrentVersion\Run`, "/v", name, "/f")
configureHiddenWindow(command)
winproc.ConfigureHiddenWindow(command)
_ = command.Run()
}
return nil
@@ -164,7 +165,7 @@ func cleanupLegacyRegistryAutostart() error {
func legacyRegistryAutostartExists() bool {
for _, name := range []string{legacyAutostartName, autostartName} {
command := exec.Command("reg.exe", "query", `HKCU\Software\Microsoft\Windows\CurrentVersion\Run`, "/v", name)
configureHiddenWindow(command)
winproc.ConfigureHiddenWindow(command)
if command.Run() == nil {
return true
}
+3 -2
View File
@@ -15,6 +15,7 @@ import (
"unicode"
"gitea.mixdep.ru/mix/gosentry/src/domain"
"gitea.mixdep.ru/mix/gosentry/src/platform/winproc"
)
const commandTimeout = 30 * time.Second
@@ -42,7 +43,7 @@ func RunJob(ctx context.Context, job *domain.Job, trigger string, logsDir string
command := invocation.command
command.WaitDelay = commandWaitDelay
if invocation.hideWindow {
configureHiddenWindow(command)
winproc.ConfigureHiddenWindow(command)
}
command.Stdout = &stdout
command.Stderr = &stderr
@@ -176,7 +177,7 @@ func sanitizeFileName(name string) string {
func startJobOnly(invocation commandInvocation, job domain.Job, started time.Time) (string, string, string) {
command := invocation.command
if invocation.hideWindow {
configureHiddenWindow(command)
winproc.ConfigureHiddenWindow(command)
}
err := command.Start()
duration := time.Since(started).Round(time.Millisecond)
-5
View File
@@ -13,8 +13,3 @@ func shellCommand(ctx context.Context, command string) *exec.Cmd {
return exec.CommandContext(ctx, "sh", "-c", command)
}
func configureHiddenWindow(command *exec.Cmd) {
// Non-Windows platforms do not create a new console window for sh -c from a
// desktop process in the same way Windows does, so no extra process attribute
// is required here.
}
+3 -2
View File
@@ -10,6 +10,7 @@ import (
"time"
"gitea.mixdep.ru/mix/gosentry/src/domain"
"gitea.mixdep.ru/mix/gosentry/src/platform/winproc"
)
func TestRunJobLogFileAllHeaders(t *testing.T) {
@@ -377,7 +378,7 @@ func TestShellCommandHidesWindow(t *testing.T) {
if !invocation.hideWindow {
t.Fatal("shell command should request hidden startup window")
}
configureHiddenWindow(invocation.command)
winproc.ConfigureHiddenWindow(invocation.command)
if invocation.command.SysProcAttr == nil || !invocation.command.SysProcAttr.HideWindow {
t.Fatal("expected shell command to be hidden")
}
@@ -389,7 +390,7 @@ func TestShellCommandUsesWindowsSafeQuoting(t *testing.T) {
}
command := shellCommand(context.Background(), `"C:\Program Files\FreeFileSync\FreeFileSync.exe" "D:\Local\Programs\FreeFileSync\Jobs\Auto.ffs_batch"`)
configureHiddenWindow(command)
winproc.ConfigureHiddenWindow(command)
want := `cmd.exe /S /C ""C:\Program Files\FreeFileSync\FreeFileSync.exe" "D:\Local\Programs\FreeFileSync\Jobs\Auto.ffs_batch""`
if command.SysProcAttr == nil {
-10
View File
@@ -57,13 +57,3 @@ func startsWithWindowsRootedPath(command string) bool {
(command[2] == '\\' || command[2] == '/')
}
func configureHiddenWindow(command *exec.Cmd) {
// GoSentry is a GUI scheduler, so child commands should not flash a console
// window on Windows. CREATE_NO_WINDOW keeps cmd.exe and simple console tools
// quiet while stdout/stderr are still captured through pipes.
if command.SysProcAttr == nil {
command.SysProcAttr = &syscall.SysProcAttr{}
}
command.SysProcAttr.CreationFlags |= 0x08000000
command.SysProcAttr.HideWindow = true
}
+10
View File
@@ -0,0 +1,10 @@
//go:build !windows
package winproc
import "os/exec"
// ConfigureHiddenWindow is a no-op on non-Windows platforms: launching sh -c
// from a desktop process does not create a new console window in the same way
// Windows does.
func ConfigureHiddenWindow(command *exec.Cmd) {}
+18
View File
@@ -0,0 +1,18 @@
package winproc
import (
"os/exec"
"syscall"
)
// ConfigureHiddenWindow suppresses the console window that Windows would
// otherwise flash when running a child process from a GUI application.
// CREATE_NO_WINDOW keeps cmd.exe and simple console tools quiet while
// stdout/stderr are still captured through pipes.
func ConfigureHiddenWindow(command *exec.Cmd) {
if command.SysProcAttr == nil {
command.SysProcAttr = &syscall.SysProcAttr{}
}
command.SysProcAttr.CreationFlags |= 0x08000000
command.SysProcAttr.HideWindow = true
}