Refactoring complete: v0.4.0 architectural milestone #1

Merged
mix merged 48 commits from docs/refactoring-plan into main 2026-06-22 08:05:10 +03:00
8 changed files with 40 additions and 24 deletions
Showing only changes of commit f4fb16c0ed - Show all commits
+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 ### Phase 1 — Split flat `core` package
- [x] T1.1 — Create `src/domain`; move Job/RunRecord/Config/etc - [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.3 — Create `src/runner`; move runner logic
- [ ] T1.4 — Create `src/scheduler`; move scheduler - [ ] T1.4 — Create `src/scheduler`; move scheduler
- [ ] T1.5 — Create `src/storage`; move store/paths - [ ] T1.5 — Create `src/storage`; move store/paths
+5 -4
View File
@@ -8,6 +8,7 @@ import (
"strings" "strings"
"gitea.mixdep.ru/mix/gosentry/src/domain" "gitea.mixdep.ru/mix/gosentry/src/domain"
"gitea.mixdep.ru/mix/gosentry/src/platform/winproc"
) )
const autostartName = "GoSentry" const autostartName = "GoSentry"
@@ -107,7 +108,7 @@ func createStartupShortcut(shortcutPath string, executablePath string, iconPath
"GOSENTRY_WORKING_DIRECTORY="+workingDirectory, "GOSENTRY_WORKING_DIRECTORY="+workingDirectory,
"GOSENTRY_ICON_PATH="+iconPath, "GOSENTRY_ICON_PATH="+iconPath,
) )
configureHiddenWindow(command) winproc.ConfigureHiddenWindow(command)
if output, err := command.CombinedOutput(); err != nil { if output, err := command.CombinedOutput(); err != nil {
return fmt.Errorf("create startup shortcut: %w: %s", err, strings.TrimSpace(string(output))) 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)` 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 := exec.Command("powershell.exe", "-NoProfile", "-NonInteractive", "-ExecutionPolicy", "Bypass", "-Command", script)
command.Env = append(os.Environ(), "GOSENTRY_SHORTCUT_PATH="+shortcutPath) command.Env = append(os.Environ(), "GOSENTRY_SHORTCUT_PATH="+shortcutPath)
configureHiddenWindow(command) winproc.ConfigureHiddenWindow(command)
output, err := command.CombinedOutput() output, err := command.CombinedOutput()
if err != nil { if err != nil {
return "", "", fmt.Errorf("read startup shortcut: %w: %s", err, strings.TrimSpace(string(output))) 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 { func cleanupLegacyRegistryAutostart() error {
for _, name := range []string{legacyAutostartName, autostartName} { for _, name := range []string{legacyAutostartName, autostartName} {
command := exec.Command("reg.exe", "delete", `HKCU\Software\Microsoft\Windows\CurrentVersion\Run`, "/v", name, "/f") command := exec.Command("reg.exe", "delete", `HKCU\Software\Microsoft\Windows\CurrentVersion\Run`, "/v", name, "/f")
configureHiddenWindow(command) winproc.ConfigureHiddenWindow(command)
_ = command.Run() _ = command.Run()
} }
return nil return nil
@@ -164,7 +165,7 @@ func cleanupLegacyRegistryAutostart() error {
func legacyRegistryAutostartExists() bool { func legacyRegistryAutostartExists() bool {
for _, name := range []string{legacyAutostartName, autostartName} { for _, name := range []string{legacyAutostartName, autostartName} {
command := exec.Command("reg.exe", "query", `HKCU\Software\Microsoft\Windows\CurrentVersion\Run`, "/v", name) command := exec.Command("reg.exe", "query", `HKCU\Software\Microsoft\Windows\CurrentVersion\Run`, "/v", name)
configureHiddenWindow(command) winproc.ConfigureHiddenWindow(command)
if command.Run() == nil { if command.Run() == nil {
return true return true
} }
+3 -2
View File
@@ -15,6 +15,7 @@ import (
"unicode" "unicode"
"gitea.mixdep.ru/mix/gosentry/src/domain" "gitea.mixdep.ru/mix/gosentry/src/domain"
"gitea.mixdep.ru/mix/gosentry/src/platform/winproc"
) )
const commandTimeout = 30 * time.Second 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 := invocation.command
command.WaitDelay = commandWaitDelay command.WaitDelay = commandWaitDelay
if invocation.hideWindow { if invocation.hideWindow {
configureHiddenWindow(command) winproc.ConfigureHiddenWindow(command)
} }
command.Stdout = &stdout command.Stdout = &stdout
command.Stderr = &stderr 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) { func startJobOnly(invocation commandInvocation, job domain.Job, started time.Time) (string, string, string) {
command := invocation.command command := invocation.command
if invocation.hideWindow { if invocation.hideWindow {
configureHiddenWindow(command) winproc.ConfigureHiddenWindow(command)
} }
err := command.Start() err := command.Start()
duration := time.Since(started).Round(time.Millisecond) 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) 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" "time"
"gitea.mixdep.ru/mix/gosentry/src/domain" "gitea.mixdep.ru/mix/gosentry/src/domain"
"gitea.mixdep.ru/mix/gosentry/src/platform/winproc"
) )
func TestRunJobLogFileAllHeaders(t *testing.T) { func TestRunJobLogFileAllHeaders(t *testing.T) {
@@ -377,7 +378,7 @@ func TestShellCommandHidesWindow(t *testing.T) {
if !invocation.hideWindow { if !invocation.hideWindow {
t.Fatal("shell command should request hidden startup window") 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 { if invocation.command.SysProcAttr == nil || !invocation.command.SysProcAttr.HideWindow {
t.Fatal("expected shell command to be hidden") 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"`) 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""` want := `cmd.exe /S /C ""C:\Program Files\FreeFileSync\FreeFileSync.exe" "D:\Local\Programs\FreeFileSync\Jobs\Auto.ffs_batch""`
if command.SysProcAttr == nil { if command.SysProcAttr == nil {
-10
View File
@@ -57,13 +57,3 @@ func startsWithWindowsRootedPath(command string) bool {
(command[2] == '\\' || command[2] == '/') (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
}