T5.2: Introduce autostart.Manager interface + per-platform impls
Define Manager with Set/Status in autostart.go; add concrete types (windowsManager, linuxManager, otherManager) in the per-platform files. Service gains a manager field wired by Open() via autostart.New(); AutostartStatus and ApplyAutostart delegate to it instead of calling package-level functions directly. Tests that don't need autostart get a nil manager, which is safe (no-op returns). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+1
-1
@@ -278,7 +278,7 @@ Track progress here. Mark tasks complete as they land and pass review.
|
||||
|
||||
### Phase 5 — Hardening & docs
|
||||
- [x] T5.1 — Surface errors from service + storage
|
||||
- [ ] T5.2 — Introduce `autostart.Manager` interface
|
||||
- [x] T5.2 — Introduce `autostart.Manager` interface
|
||||
- [ ] T5.3 — Fill test gaps (folder filtering, cleanup, migration, concurrency)
|
||||
- [ ] T5.4 — Run `go test -race ./...` clean on both platforms
|
||||
- [ ] T5.5 — Update docs (ARCHITECTURE.md, TESTS.md, README)
|
||||
|
||||
@@ -240,8 +240,6 @@ func (s *Service) RunDue(now time.Time) {
|
||||
// UpdateSettings validates and persists a new application configuration. The
|
||||
// loaded jobs are re-saved because the jobs directory may have changed, and log
|
||||
// cleanup runs so a tightened retention policy takes effect immediately.
|
||||
// Autostart is intentionally left to the caller until T5.2 introduces an
|
||||
// injectable autostart.Manager.
|
||||
func (s *Service) UpdateSettings(config domain.Config) error {
|
||||
if err := validateConfig(config); err != nil {
|
||||
return err
|
||||
|
||||
+10
-3
@@ -1,7 +1,6 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"gitea.mixdep.ru/mix/gosentry/src/platform/autostart"
|
||||
"gitea.mixdep.ru/mix/gosentry/src/platform/desktop"
|
||||
)
|
||||
|
||||
@@ -20,8 +19,12 @@ func (s *Service) AutostartStatus() (ok bool, message string) {
|
||||
s.mu.Lock()
|
||||
enabled := s.store.Config.StartOnLogin
|
||||
execPath := s.store.Paths.ExecutablePath
|
||||
manager := s.manager
|
||||
s.mu.Unlock()
|
||||
return autostart.AutostartStatus(enabled, execPath)
|
||||
if manager == nil {
|
||||
return false, "autostart not available"
|
||||
}
|
||||
return manager.Status(enabled, execPath)
|
||||
}
|
||||
|
||||
// ApplyAutostart writes or removes the platform autostart entry to match the
|
||||
@@ -31,6 +34,10 @@ func (s *Service) ApplyAutostart() error {
|
||||
enabled := s.store.Config.StartOnLogin
|
||||
execPath := s.store.Paths.ExecutablePath
|
||||
iconPath := s.store.Paths.DesktopIcon
|
||||
manager := s.manager
|
||||
s.mu.Unlock()
|
||||
return autostart.SetAutostart(enabled, execPath, iconPath)
|
||||
if manager == nil {
|
||||
return nil
|
||||
}
|
||||
return manager.Set(enabled, execPath, iconPath)
|
||||
}
|
||||
|
||||
+8
-1
@@ -6,6 +6,7 @@ import (
|
||||
"time"
|
||||
|
||||
"gitea.mixdep.ru/mix/gosentry/src/domain"
|
||||
"gitea.mixdep.ru/mix/gosentry/src/platform/autostart"
|
||||
"gitea.mixdep.ru/mix/gosentry/src/runner"
|
||||
"gitea.mixdep.ru/mix/gosentry/src/scheduler"
|
||||
"gitea.mixdep.ru/mix/gosentry/src/storage"
|
||||
@@ -58,6 +59,10 @@ type Service struct {
|
||||
sched *scheduler.Scheduler
|
||||
cancel context.CancelFunc
|
||||
|
||||
// manager is the platform autostart implementation. It is nil in tests that
|
||||
// do not exercise autostart; Open() wires it via autostart.New().
|
||||
manager autostart.Manager
|
||||
|
||||
// observers and their guard live in events.go. dispatchMu is separate from mu
|
||||
// so that emitting an event never requires (or is held under) the state lock:
|
||||
// the Service must release mu before dispatching, per the locking contract.
|
||||
@@ -136,7 +141,9 @@ func Open() (*Service, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return NewService(store, jobs), nil
|
||||
svc := NewService(store, jobs)
|
||||
svc.manager = autostart.New()
|
||||
return svc, nil
|
||||
}
|
||||
|
||||
// Store returns the underlying store. It is exposed so callers that still need
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
package autostart
|
||||
|
||||
// Manager controls platform autostart for the application.
|
||||
type Manager interface {
|
||||
// Set writes or removes the platform autostart entry to match enabled.
|
||||
Set(enabled bool, executablePath, iconPath string) error
|
||||
// Status reports whether the platform autostart entry matches expectedEnabled.
|
||||
Status(expectedEnabled bool, executablePath string) (ok bool, message string)
|
||||
}
|
||||
@@ -13,6 +13,19 @@ import (
|
||||
"gitea.mixdep.ru/mix/gosentry/src/domain"
|
||||
)
|
||||
|
||||
type linuxManager struct{}
|
||||
|
||||
// New returns the Linux autostart Manager.
|
||||
func New() Manager { return linuxManager{} }
|
||||
|
||||
func (linuxManager) Set(enabled bool, executablePath, iconPath string) error {
|
||||
return SetAutostart(enabled, executablePath, iconPath)
|
||||
}
|
||||
|
||||
func (linuxManager) Status(expectedEnabled bool, executablePath string) (bool, string) {
|
||||
return AutostartStatus(expectedEnabled, executablePath)
|
||||
}
|
||||
|
||||
const autostartDesktopFileName = "gosentry.desktop"
|
||||
const legacyAutostartDesktopFileName = "pysentry.desktop"
|
||||
|
||||
|
||||
@@ -4,6 +4,19 @@ package autostart
|
||||
|
||||
import "fmt"
|
||||
|
||||
type otherManager struct{}
|
||||
|
||||
// New returns the stub autostart Manager for unsupported platforms.
|
||||
func New() Manager { return otherManager{} }
|
||||
|
||||
func (otherManager) Set(enabled bool, executablePath, iconPath string) error {
|
||||
return SetAutostart(enabled, executablePath, iconPath)
|
||||
}
|
||||
|
||||
func (otherManager) Status(expectedEnabled bool, executablePath string) (bool, string) {
|
||||
return AutostartStatus(expectedEnabled, executablePath)
|
||||
}
|
||||
|
||||
func SetAutostart(enabled bool, executablePath string, iconPath string) error {
|
||||
if !enabled {
|
||||
return nil
|
||||
|
||||
@@ -11,6 +11,19 @@ import (
|
||||
"gitea.mixdep.ru/mix/gosentry/src/platform/winproc"
|
||||
)
|
||||
|
||||
type windowsManager struct{}
|
||||
|
||||
// New returns the Windows autostart Manager.
|
||||
func New() Manager { return windowsManager{} }
|
||||
|
||||
func (windowsManager) Set(enabled bool, executablePath, iconPath string) error {
|
||||
return SetAutostart(enabled, executablePath, iconPath)
|
||||
}
|
||||
|
||||
func (windowsManager) Status(expectedEnabled bool, executablePath string) (bool, string) {
|
||||
return AutostartStatus(expectedEnabled, executablePath)
|
||||
}
|
||||
|
||||
const autostartName = "GoSentry"
|
||||
const legacyAutostartName = "PySentry"
|
||||
const startupShortcutFile = autostartName + ".lnk"
|
||||
|
||||
Reference in New Issue
Block a user