Commit Graph

46 Commits

Author SHA1 Message Date
mixeme a9eea8cbe7 T5.1: Surface background save/cleanup errors via ErrorOccurred event
Replace the two _ = discards in executeRun (SaveJobs + CleanupLogs after
an async run) and the _ = in RunDue (SaveJobs before a scheduled run)
with captured errors emitted as ErrorOccurred events after the state
lock is released. The UI subscriber in mainwindow.go handles the new
event by appending an "Error" record to History so failed saves are
visible to the user instead of silently dropped.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-22 07:17:22 +03:00
mixeme e929de54a7 Bump version to 0.3.6: Phase 4 refactoring complete
Extracted view components and platform wiring from src/ui. Upgraded
Fyne v2.5.3 -> v2.6.3 for fyne.Do cross-thread marshaling (issue #4).

Add performance documentation: ~290ms startup increase is entirely
Fyne 2.6.3's w.Show() cost, not the Service/domain/UI restructuring.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-06-22 00:36:21 +03:00
mixeme 711cb73ea5 docs: record startup-time research; plan re-check after Fyne 2.7.x
Add docs/PERFORMANCE.md documenting the post-Phase-4 startup slowdown:
the ~290ms increase is entirely Fyne 2.5.3->2.6.3's w.Show() cost, not
the Service/domain/UI refactor. Includes the GOSENTRY_TIMING method and
warm-run results table so it can be reproduced.

Wire the startup re-measurement into the existing Fyne 2.6.3->2.7.x
upgrade task in ROADMAP.md so 2.7's effect on w.Show() gets checked.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 00:31:31 +03:00
mixeme 21a93517eb T4.7: Remove forbidden platform imports from src/ui
src/ui now imports only src/app, src/domain, and Fyne.
Three violations were fixed:

- src/scheduler (NewRealClock): Service.Start() now creates the real
  clock internally; StartWith(clock) is the injectable seam for tests.
- src/platform/desktop (InstallDesktopIntegration): moved into
  Service.InstallDesktopIcon in new src/app/platform.go.
- src/platform/autostart (AutostartStatus, SetAutostart): moved into
  Service.AutostartStatus and Service.ApplyAutostart in platform.go.

go test -race ./... green.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-22 00:00:24 +03:00
mixeme 93b57979ec Mark T4.6 complete in REFACTORING.md
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-06-21 23:48:48 +03:00
mixeme 832b3dcc68 T4.5: Extract settings_view.go
Move settingsView and all its helpers (fyneVersion, mustParseURL,
chooseFolder, settingsRow, settingsRowWithStatus) plus their constants
out of mainwindow.go into the new settings_view.go. Save/autostart
errors were already surfaced to the status label; no logic change.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-21 23:41:12 +03:00
mixeme fb12fab71d T4.4: Extract history_view.go
Move newHistoryView, historyCellText, logFileName, newEvent, and
collectActivity out of mainwindow.go into src/ui/history_view.go.
Drop the now-unused "sort" import from mainwindow.go.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-21 23:30:21 +03:00
mixeme 91679efa3c T4.3: Extract job_dialog.go; validate schedule via domain.Validate
Moves showJobDialog out of jobs_view.go into its own file and adds a
domain.Validate call on the schedule field so malformed cron expressions
are rejected with an error dialog before onSave is invoked.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-21 23:19:00 +03:00
mixeme 760700f6ac T4.2: Extract jobs_view.go (list + details + toolbar)
Move the jobs list, details panel, folder filter, toolbar buttons, and
all related helpers out of mainwindow.go into the new jobs_view.go.
newMainView now calls newJobsView(w, svc) which returns a panel and a
refresh closure; the subscriber and history wiring remain in mainwindow.go.

showJobDialog lives in jobs_view.go as a temporary home until T4.3
extracts it to job_dialog.go.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-21 23:07:34 +03:00
mixeme f28df5e77b docs: plan tray click via Fyne 2.7 SetSystemTrayWindow
Fyne 2.7.0 adds desktop.App.SetSystemTrayWindow, which shows the window
on left-click and unblocks the deferred Tray Interaction item. Record the
2.6.3 -> 2.7.x upgrade dependency and the concrete wiring in run.go.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 21:09:55 +03:00
mixeme f82eca8777 T4.1: Rename gui->ui, split lifecycle into run.go + mainwindow.go
Carve src/gui/app.go into the new src/ui package:
- run.go: process lifecycle (single instance, app/window, tray, startup
  timing).
- mainwindow.go: view assembly + the app.Service event listener.

Route every widget update driven by Service events through fyne.Do so the
run goroutine (executeRun) no longer mutates Fyne widgets directly. Also
wrap serveSingleInstance's Show/RequestFocus, which runs on the Accept
goroutine. (Resolves refactoring problem #4.)

fyne.Do/DoAndWait only exist in Fyne v2.6+, so upgrade fyne.io/fyne/v2
v2.5.3 -> v2.6.3. Mark the tray Quit item IsQuit so Fyne's
addMissingQuitForMenu reuses it instead of appending a second, localized
Quit now that v2.6 ships Russian translations.

go build / go vet / go test -race all clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 20:55:23 +03:00
mixeme c5e0ef9617 Bump version to 0.3.5: Phase 3 service and state management refactoring complete
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-06-19 08:36:12 +03:00
mixeme 6073d5e112 T3.6: Add src/app unit tests (no Fyne)
Fill the test gaps in the app package, raising coverage from 82% to 97%:

- format_test.go: cover all display helpers moved in T3.5 (StatusText,
  EventText, Display*), previously untested.
- operations_test.go: add RunNow not-found and refused-while-running
  paths, DeleteJob/SetEnabled not-found errors, UpdateJob re-enable,
  runtimeForLocked lazy-recreate, UpdateSettings invalid-config branches,
  and prependLog activity-list capping.

All tests are Fyne-free, using a temp-dir store, a fake runner seam, and
a fake Clock. go vet and go test -race pass across the module.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 08:31:04 +03:00
mixeme 6e5c42fbed T3.5: Move display helpers to src/app/format.go
Extract pure display/formatting functions from the GUI:
- StatusText, EventText, DisplayFolder, DisplayArguments,
  DisplaySuccessExitCodes, DisplayRunMode, DisplayInvocation, DisplayIndex.

These have no Fyne dependencies and serve as a shared formatting layer for
the service to use (e.g. StatusText in the tests when mocking the GUI).
The GUI imports them as app.DisplayFolder(...) etc.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-06-19 08:25:37 +03:00
mixeme a4c93a5122 T3.4: Convert scheduler to drive app.Service; inject Clock
The scheduler no longer shares a *[]domain.Job with the GUI. It is now a
thin timing loop with an injected Clock that calls a tick callback; the
application service is the sole writer of job and runtime state.

- scheduler: add Clock interface + RealClock (clock.go); strip all job
  logic from scheduler.go (NewScheduler(clock, tick)); rewrite tests to
  cover the loop with a fake clock.
- app.Service: add RunDue(now) (pause + one-run-per-tick policy, records
  back through the service) and Start(Clock)/Stop() owning a cancelable
  run context; prime each job's first next-run at construction. Capture
  the run context under the lock for executeRun.
- gui: talk only to app.Service (no shared state) — Open() the service,
  keep a refreshed snapshot, route every mutation through the service,
  and react to changes via a single Subscribe listener.
- Tests: add RunDue (due/not-due/paused) and Start-drives-RunDue cases.

Verified with CGO + MSYS2 UCRT64: go vet ./... clean, go test -race
./... green (GUI included), full module builds.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 08:22:35 +03:00
mixeme d8ab9acf7e T3.3: Add state-mutating operations to app.Service
Add src/app/operations.go with the seven intents that make the Service
the sole writer of job and runtime state: CreateJob, UpdateJob,
DeleteJob, SetEnabled, RunNow, SetGlobalPause, UpdateSettings. Each
returns error, persists through the store, and announces changes via
RunRecorded/JobChanged/SchedulerStateChanged events.

Extend the Service with a parsed-schedule cache, a global paused flag,
an injectable runJob seam (defaults to runner.RunJob) for testing the
run-now path, and a lifecycle ctx. Run and next-run timing now live in
the Service (duplicating the scheduler temporarily); T3.4 converts the
scheduler to drive the Service and removes the duplication.

Autostart is left to the caller until T5.2's injectable Manager; async
save errors in the run goroutine remain deferred to T5.1. Adds 12 tests
covering create/update/delete, enable/pause, global pause, run-now with
a fake runner, and settings persistence/validation.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 07:48:41 +03:00
mixeme 5e51381b7a T3.2: Add app event types and observer dispatch
Add src/app/events.go: a sealed Event interface with three concrete
types (JobChanged, RunRecorded, SchedulerStateChanged), an Observer
interface plus ObserverFunc adapter, and Subscribe/emit on the Service.
This replaces the scheduler's single onChange callback with typed
events the UI can exhaustively type-switch over.

Dispatch is serialized by a dedicated dispatchMu (separate from the
state lock): observers never run concurrently, emit must be called
without holding s.mu so observers can read Service state, and observers
must not re-enter an emitting method. emit is wired to mutating ops in
T3.3. Adds tests for ordered multi-observer delivery, empty-observer
no-op, and observer-reads-state-without-deadlock.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 07:39:34 +03:00
mixeme 9931ec1237 T3.1: Add app.Service owning state behind a mutex
Create src/app/service.go: the application-service layer that becomes
the single owner of the durable jobs slice and the transient runtime
map, guarded by a non-reentrant sync.Mutex. NewService wires a loaded
store; Open() is the convenience entry point. Read-only accessors
(Jobs/Runtime/Store) take the lock, and Jobs() returns a copy to keep
callers from mutating Service-owned state.

State-mutating intents and the event/observer machinery are deferred to
T3.2-T3.4. Adds no-Fyne unit tests for runtime construction, copy
isolation, and store wiring.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 07:34:34 +03:00
mixeme 98f692658a Bump version to 0.3.4: Phase 2 domain cleanup complete
- Split durable Job from transient JobRuntime
- Extracted Schedule value object
- Made RunJob pure (no job mutation)
- Simplified runtime initialization

No observable behavior changes.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-06-19 00:10:41 +03:00
mixeme b1874845d5 T2.3: Split domain.Job (durable) from domain.JobRuntime (transient)
Move all transient execution state off domain.Job into a new
domain.JobRuntime, keyed by job ID:

- domain: Job now holds only durable YAML fields; remove the yaml:"-"
  fields (LastRun/NextRun/LastState/Logs/Output) and NextDue. Add
  runtime.go with JobRuntime plus NewRuntime/NewRuntimes constructors,
  which now own the runtime-init logic moved out of normalizeJobs.
- runner: RunJob no longer mutates the job; it is pure and returns the
  RunRecord for the caller to fold into the runtime.
- scheduler: take a shared map[int]*JobRuntime and route status/next-run
  bookkeeping through runtimeFor(job); prepareNextRun writes a *JobRuntime.
- storage: normalizeJobs touches only durable config.
- gui: own the runtime map (NewRuntimes), share it with the scheduler,
  and read/write runtime state via runtimeFor; maintain the map by ID on
  add/edit/delete.
- tests: update scheduler/storage tests to the split; tidy a pre-existing
  import-order nit in scheduler.go.

This also satisfies T2.4 (storage load/save only Job, runtime init in
domain.NewRuntime, round-trip tests), since removing the fields forced it.
Runtime-map ownership remains GUI-side glue until T3.1.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 00:08:05 +03:00
mixeme ca673f08f9 T2.2: Migrate scheduler to use domain.Schedule
Parse each job's schedule once on load (resetNextRuns) and on edit
(RefreshSchedule) via the new parseJobSchedule helper, caching the
result in a map[int]domain.Schedule keyed by job ID. prepareNextRun
now looks up the cached Schedule instead of re-parsing the string on
every call. Remove the nextRunTime wrapper that did the per-call
parsing. Drop the three scheduler_test.go tests that duplicated
coverage already in domain/schedule_test.go.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 22:59:46 +03:00
mixeme 0f17782174 T2.1: Add domain.Schedule value object
Introduce src/domain/schedule.go with a Schedule value object that
centralizes schedule parsing and validation: Parse, Validate, and
Next(time.Time). It owns the cron parser and @every handling, moved out
of the scheduler. The scheduler's nextRunTime is kept as a thin wrapper
delegating to domain.Parse for now (T2.2 will parse once on load/edit).

Add unit tests covering invalid specs, @every intervals, five-field cron,
cron descriptors, whitespace trimming, the zero-value Next, and String.

Mark T2.1 complete in REFACTORING.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 22:55:20 +03:00
mixeme 9938164c7a Update changelog for 0.3.3: Phase 1 refactoring complete 2026-06-18 22:42:42 +03:00
mixeme 29c973464c Mark T1.8 complete in REFACTORING.md 2026-06-18 22:40:45 +03:00
mixeme 17c5e15b63 Mark T1.7 complete in REFACTORING.md 2026-06-18 22:38:03 +03:00
mixeme 16d818d03d T1.6: Create src/platform/autostart; move autostart logic
Move autostart_*.go and tests from src/core to src/platform/autostart.
Update src/gui/app.go to call autostart.SetAutostart / autostart.AutostartStatus.
Add quoteDesktopExec to desktop_linux.go (was co-located in autostart_linux.go).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 22:35:51 +03:00
mixeme 06edbfff77 T1.5: Create src/storage; move store/paths logic
Move store.go, paths.go, and store_test.go from src/core into the new
src/storage package. Update src/scheduler and src/gui to import storage
instead of core for Store/Paths/OpenStore. Empty the moved files in core
to preserve the package declaration for the remaining core symbols.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 22:15:40 +03:00
mixeme ad0e45a7dd T1.4: Create src/scheduler; move scheduler logic
Move scheduler.go and scheduler_test.go from src/core to the new
src/scheduler package. The scheduler still takes *[]domain.Job and
*core.Store (storage moves in T1.5). Update src/gui/app.go to import
the new package; rename the local variable to sched to avoid shadowing
the scheduler package name.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 22:03:16 +03:00
mixeme c29d581fb1 T1.3: Create src/runner; move runner logic
Move runner.go, runner_windows.go, runner_other.go, runner_test.go from
src/core to src/runner (package runner). Split helpers into focused files:
invocation*.go, exitcodes.go, logfile.go, cleanup.go. Export LogArguments
and SuccessExitCodesText for use by scheduler's runningOutput helper.
Update src/core/scheduler.go and src/gui/app.go to import src/runner.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 21:47:29 +03:00
mixeme f4fb16c0ed 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>
2026-06-18 21:28:25 +03:00
mixeme 80c76a0cba T1.1: Create src/domain; move Job/RunRecord/Config/JobsFile/StartInTrayArgument
Extracts the five domain types out of src/core/model.go into a new
src/domain package (job.go, record.go, config.go). The unexported
nextDue field is promoted to NextDue so it is accessible from core.
All references across src/core, src/gui, and cmd/gosentry are updated
to use domain.TypeName. src/core/model.go is reduced to a bare package
declaration. Windows and Linux cross-compilation both pass; all tests
remain green.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 21:18:57 +03:00
mixeme 462752f995 Mark T0.2 as complete 2026-06-18 20:25:39 +03:00
mixeme 520a7ef98b Mark T0.1 as complete 2026-06-18 20:14:06 +03:00
mixeme 0038975adc T0.1: Add test scripts and documentation
Add scripts/test.sh and scripts/test.bat to run go vet and go test -race.
Update docs/TESTS.md with test script usage and reorganized manual test commands.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-06-18 20:13:46 +03:00
mixeme f653b1e484 Add task completion checklist to refactoring plan
Track the 30 tasks across 5 phases with checkboxes. Each checkbox can be
marked complete as tasks land and pass review.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-06-18 08:19:02 +03:00
mixeme 4c49104cce Add refactoring plan document
Document a phased plan to restructure GoSentry into focused packages
under src/ (domain, storage, runner, scheduler, platform, app, ui) with
an application-service layer that owns state, and link it from the README.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 08:16:03 +03:00
mixeme 0c2c9f1f67 Add comprehensive test suite documentation
Created docs/TESTS.md documenting all 25 tests across 5 test files:
- store_test.go: YAML serialization tests
- scheduler_test.go: Schedule parsing and invocation output tests
- runner_test.go: Command execution, exit codes, and Windows process tests
- autostart_windows_test.go: Windows startup folder shortcut creation tests
- autostart_linux_test.go: Linux XDG Desktop Entry autostart tests

Includes test descriptions, platform requirements, and usage instructions.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-06-18 00:21:33 +03:00
mixeme 2f7bbe4fca Fix startup timing measurement
Bump the version to 0.3.1 and record startup timing after the main window is actually shown.

Keep autostart launches distinct in History by recording a separate tray-start message when the UI intentionally starts hidden.
2026-06-17 22:57:23 +03:00
mixeme 94033e794f Rename project to GoSentry
Rename the application, Go module path, command package, build artifacts, resource script, and embedded icon assets from PySentry/pysentry to GoSentry/gosentry.

Move portable settings to gosentry.yaml while reading legacy pysentry.yaml during the transition, then rewrite settings under the new name.

Update Windows and Linux autostart integration to use GoSentry names while cleaning up legacy PySentry registry, desktop-entry, and systemd artifacts.

Refresh README, architecture notes, roadmap, changelog, and release examples for version 0.3.0.
2026-06-17 07:29:58 +03:00
mixeme 872cc82c5c Release version 0.2.5
Bump the application version to 0.2.5 and update documented artifact names.

Document the Windows VirtualBox/RDP OpenGL startup failure and the Mesa software OpenGL workaround.

Record the tray-icon double-click limitation in the roadmap for future Fyne or platform-specific tray work.
2026-06-16 22:08:20 +03:00
mixeme b1fe8bd675 Start autostart launches in tray
Add a shared --start-in-tray argument that lets autostart start the scheduler and tray integration without opening the main window.

Write the argument into Windows Startup shortcuts and Linux XDG Autostart desktop entries, and verify existing autostart entries include it.

Keep manual launches unchanged and let a manual second launch reveal an already-running instance while duplicate autostart launches stay hidden.
2026-06-16 21:52:00 +03:00
mixeme 44f24ab3d8 Use Startup shortcut for Windows autostart
Replace the HKCU Run autostart entry with a per-user Startup folder shortcut. A .lnk stores TargetPath separately, which avoids fragile quoting when the executable path contains spaces.

Remove legacy PySentry and GoSentry Run entries when saving autostart settings, and report shortcut status from the actual shortcut target.

Add Windows tests that create and read a temporary shortcut with spaces in the path so the PowerShell/COM invocation remains covered.
2026-06-16 21:40:48 +03:00
mixeme c1bd8c952c Release version 0.2.4
Prevent repeated application launches by using a local single-instance control channel. A second process forwards a show command to the already running instance and exits.

Bump the application version to 0.2.4 and update README artifact examples plus docs/CHANGELOG.md.
2026-06-16 08:01:42 +03:00
mixeme 4a8feb351e Release version 0.2.3
Improve the History tab by keeping records in chronological order, rendering them as a compact table, and allowing the Time column to toggle ascending or descending order.

Use the native Fyne table header so users can resize columns, including Detail and Log, and show only the log file name instead of the full log path.

Bump the application version to 0.2.3 and update README artifact examples plus docs/CHANGELOG.md.
2026-06-15 21:16:57 +03:00
mixeme c644636e57 Release version 0.2.2
Add Linux desktop integration that installs a user-level .desktop file and icon under XDG data directories so taskbars can match the PySentry window to the application icon.

Pass the installed icon path into Linux autostart desktop entries when available, while keeping the Windows and fallback autostart APIs compatible.

Bump the application version to 0.2.2, update README artifact examples, and record the release notes in docs/CHANGELOG.md. Also adjust the Mermaid architecture diagram so Gitea can render it without invalid SVG line-break tags.
2026-06-15 20:57:16 +03:00
mixeme e2464aab0f Move project documentation into docs
Add ARCHITECTURE.md with a Mermaid component interaction diagram and short descriptions of the main runtime flows.

Move CHANGELOG.md and ROADMAP.md under docs/ so project documentation lives in one place, and update README links plus the project layout description.
2026-06-15 20:44:47 +03:00