Refactoring complete: v0.4.0 architectural milestone #1
+1
-1
@@ -278,7 +278,7 @@ Track progress here. Mark tasks complete as they land and pass review.
|
|||||||
|
|
||||||
### Phase 5 — Hardening & docs
|
### Phase 5 — Hardening & docs
|
||||||
- [x] T5.1 — Surface errors from service + storage
|
- [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.3 — Fill test gaps (folder filtering, cleanup, migration, concurrency)
|
||||||
- [ ] T5.4 — Run `go test -race ./...` clean on both platforms
|
- [ ] T5.4 — Run `go test -race ./...` clean on both platforms
|
||||||
- [ ] T5.5 — Update docs (ARCHITECTURE.md, TESTS.md, README)
|
- [ ] 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
|
// UpdateSettings validates and persists a new application configuration. The
|
||||||
// loaded jobs are re-saved because the jobs directory may have changed, and log
|
// loaded jobs are re-saved because the jobs directory may have changed, and log
|
||||||
// cleanup runs so a tightened retention policy takes effect immediately.
|
// 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 {
|
func (s *Service) UpdateSettings(config domain.Config) error {
|
||||||
if err := validateConfig(config); err != nil {
|
if err := validateConfig(config); err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
+10
-3
@@ -1,7 +1,6 @@
|
|||||||
package app
|
package app
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"gitea.mixdep.ru/mix/gosentry/src/platform/autostart"
|
|
||||||
"gitea.mixdep.ru/mix/gosentry/src/platform/desktop"
|
"gitea.mixdep.ru/mix/gosentry/src/platform/desktop"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -20,8 +19,12 @@ func (s *Service) AutostartStatus() (ok bool, message string) {
|
|||||||
s.mu.Lock()
|
s.mu.Lock()
|
||||||
enabled := s.store.Config.StartOnLogin
|
enabled := s.store.Config.StartOnLogin
|
||||||
execPath := s.store.Paths.ExecutablePath
|
execPath := s.store.Paths.ExecutablePath
|
||||||
|
manager := s.manager
|
||||||
s.mu.Unlock()
|
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
|
// 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
|
enabled := s.store.Config.StartOnLogin
|
||||||
execPath := s.store.Paths.ExecutablePath
|
execPath := s.store.Paths.ExecutablePath
|
||||||
iconPath := s.store.Paths.DesktopIcon
|
iconPath := s.store.Paths.DesktopIcon
|
||||||
|
manager := s.manager
|
||||||
s.mu.Unlock()
|
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"
|
"time"
|
||||||
|
|
||||||
"gitea.mixdep.ru/mix/gosentry/src/domain"
|
"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/runner"
|
||||||
"gitea.mixdep.ru/mix/gosentry/src/scheduler"
|
"gitea.mixdep.ru/mix/gosentry/src/scheduler"
|
||||||
"gitea.mixdep.ru/mix/gosentry/src/storage"
|
"gitea.mixdep.ru/mix/gosentry/src/storage"
|
||||||
@@ -58,6 +59,10 @@ type Service struct {
|
|||||||
sched *scheduler.Scheduler
|
sched *scheduler.Scheduler
|
||||||
cancel context.CancelFunc
|
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
|
// 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:
|
// 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.
|
// the Service must release mu before dispatching, per the locking contract.
|
||||||
@@ -136,7 +141,9 @@ func Open() (*Service, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
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
|
// 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"
|
"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 autostartDesktopFileName = "gosentry.desktop"
|
||||||
const legacyAutostartDesktopFileName = "pysentry.desktop"
|
const legacyAutostartDesktopFileName = "pysentry.desktop"
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,19 @@ package autostart
|
|||||||
|
|
||||||
import "fmt"
|
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 {
|
func SetAutostart(enabled bool, executablePath string, iconPath string) error {
|
||||||
if !enabled {
|
if !enabled {
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -11,6 +11,19 @@ import (
|
|||||||
"gitea.mixdep.ru/mix/gosentry/src/platform/winproc"
|
"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 autostartName = "GoSentry"
|
||||||
const legacyAutostartName = "PySentry"
|
const legacyAutostartName = "PySentry"
|
||||||
const startupShortcutFile = autostartName + ".lnk"
|
const startupShortcutFile = autostartName + ".lnk"
|
||||||
|
|||||||
Reference in New Issue
Block a user