84e81371c1
Four documents asserted things the code contradicts. ARCHITECTURE's component diagram had the UI calling the autostart Manager directly. It does not, and must not: src/ui holds no reference to the package at all — Settings reads svc.AutostartStatus(), like everything else it reads. The edge is folded into the existing ui→Service one, so the diagram no longer draws the exception to the project's own rule. platform/desktop was described in both ARCHITECTURE and DEVELOPMENT as a "display-scale helper". It installs the .desktop entry and icon under XDG data home; there is no scale helper in it. The ~250-line file guideline was written as though the jobs_view and settings_view splits had settled it. Both files are over it again and history_view.go has never been split, so the guideline is now stated as the target it is, with the current state named rather than implied. STANDARDS pointed at a "CI coverage gate" item that ROADMAP does not have, while omitting the two it does. README's gosentry.json sample was three keys short of what the app writes on first run — default_timeout_seconds, theme and job_list_view — which made the one file the user is invited to hand-edit the least accurate thing in the document. The sample is now the real default (verified by marshalling DefaultConfig), with the keys explained, including why a zero timeout is written out and an unset one is not. The per-job overrides for overlap policy and timeout were undocumented despite being in the job dialog, and the feature list had not caught up with the timeout, the theme, or the compact job list. Version numbers in example output paths are now <version>, matching how the CI section already wrote them, so they cannot go stale again. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
278 lines
10 KiB
Markdown
278 lines
10 KiB
Markdown
<p align="center">
|
|
<picture>
|
|
<source media="(prefers-color-scheme: dark)" srcset="assets/logo/gosentry-logo-dark.svg">
|
|
<img src="assets/logo/gosentry-logo.svg" alt="GoSentry" width="420">
|
|
</picture>
|
|
</p>
|
|
|
|
# GoSentry
|
|
|
|
GoSentry is a cross-platform desktop scheduler. It provides a native GUI for
|
|
creating, grouping, pausing, running, and monitoring scheduled shell commands.
|
|
|
|
## Screenshots
|
|
|
|
<table>
|
|
<tr>
|
|
<td align="center"><img src="images/screenshot_jobs.PNG" alt="Jobs tab"><br><em>Jobs tab — job list with details panel and run statistics.</em></td>
|
|
<td align="center"><img src="images/screenshot_settings.PNG" alt="Settings tab"><br><em>Settings tab — application, queue, storage, and version info.</em></td>
|
|
</tr>
|
|
</table>
|
|
|
|
## Features
|
|
|
|
- Native desktop GUI built with [Fyne](https://fyne.io/).
|
|
- Job definitions stored in a clean, hand-editable `jobs.json`.
|
|
- `@every` intervals and standard 5-field cron expressions.
|
|
- Manual and scheduled command runs.
|
|
- Parallel or sequential execution mode; overlap policy (skip or queue) set globally or per job.
|
|
- Run timeout, off by default, set globally or per job.
|
|
- Per-run `.log` files with stdout/stderr capture.
|
|
- Log cleanup by maximum file count and maximum age.
|
|
- Global pause/resume for scheduled job execution (manual runs remain available).
|
|
- Desktop notifications on job failure.
|
|
- Windows tray icon: left-click to show the window, right-click for the menu.
|
|
- Autostart on login (Windows shortcut; Linux XDG desktop entry).
|
|
- Detailed or compact job list, and a default or branded theme; both are remembered.
|
|
|
|
## Platforms
|
|
|
|
GoSentry is built and tested on **Windows** and **Linux**:
|
|
|
|
| Platform | Status | Notes |
|
|
|----------|--------|-------|
|
|
| Windows | Supported | Tray icon, autostart shortcut (`.lnk`), desktop integration. |
|
|
| Linux | Supported | Autostart via XDG desktop entry; desktop integration on X11/Wayland. |
|
|
| macOS | Not supported | The Fyne GUI may build, but autostart and desktop integration are not implemented. |
|
|
|
|
## Documentation
|
|
|
|
- [Changelog](docs/CHANGELOG.md) — record of notable changes by version
|
|
- [Roadmap](docs/ROADMAP.md) — planned work larger than a single bug fix
|
|
- [Architecture](docs/ARCHITECTURE.md) — component interaction model
|
|
- [Standards](docs/STANDARDS.md) — quality rules and intentional behavior
|
|
- [Review](docs/REVIEW.md) — what a whole-project review looks at
|
|
- [Development](docs/DEVELOPMENT.md) — build instructions, project layout, dependencies
|
|
- [Tests](docs/TESTS.md) — test suite layout and how to run it
|
|
- [Performance](docs/PERFORMANCE.md) — measured performance findings
|
|
|
|
## Storage
|
|
|
|
GoSentry stores its files next to the executable by default, making it a
|
|
portable application: moving the program folder also moves its configuration.
|
|
|
|
`gosentry.json` stores application settings:
|
|
|
|
```json
|
|
{
|
|
"jobs_file": "jobs.json",
|
|
"logs_dir": "logs",
|
|
"max_log_files": 100,
|
|
"max_log_age_days": 30,
|
|
"keep_running_in_tray": true,
|
|
"notify_on_failure": true,
|
|
"execution_mode": "parallel",
|
|
"overlap_policy": "skip",
|
|
"default_timeout_seconds": 0,
|
|
"theme": "default",
|
|
"job_list_view": "detailed"
|
|
}
|
|
```
|
|
|
|
That is the file GoSentry writes on first run. `default_timeout_seconds` is the
|
|
run timeout applied to jobs that do not set their own; `0` means no timeout, and
|
|
it is written out even though it is zero, because a missing value and a
|
|
deliberate "no timeout" have to stay distinguishable in a hand-edited file.
|
|
`theme` is `default` or `gosentry` (the branded teal/amber look), and
|
|
`job_list_view` is `detailed` or `compact` — both are remembered from the
|
|
choices made in the app. Keys left at their off value (`start_on_login`,
|
|
`paused`) are omitted until they are turned on.
|
|
|
|
`jobs.json` stores job definitions:
|
|
|
|
```json
|
|
{
|
|
"jobs": [
|
|
{
|
|
"id": 1,
|
|
"name": "Hello scheduler",
|
|
"folder": "Examples",
|
|
"schedule": "@every 1m",
|
|
"command": "echo GoSentry test job: scheduler is alive",
|
|
"enabled": true
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
`jobs_file` is the file GoSentry reads job definitions from, file name included,
|
|
so the file can be named anything. The default `"jobs.json"` is relative and
|
|
resolves to the executable's folder. An absolute path can be used when jobs
|
|
should live elsewhere, such as a shared network drive.
|
|
|
|
A `gosentry.json` from an earlier version that carries `jobs_dir` instead keeps
|
|
working: the directory is combined with `jobs.json` on load, and the file is
|
|
rewritten with `jobs_file`.
|
|
|
|
`logs_dir` is relative to the program folder when it does not start with a
|
|
drive letter or `/`.
|
|
|
|
Command output is written to separate files under `logs_dir`. File names
|
|
include the run timestamp and job name:
|
|
|
|
```text
|
|
20260614-224306_Hello_scheduler.log
|
|
```
|
|
|
|
## Schedules
|
|
|
|
Interval schedules using Go duration syntax:
|
|
|
|
```text
|
|
@every 10s
|
|
@every 5m
|
|
@every 1h30m
|
|
```
|
|
|
|
Standard 5-field cron expressions:
|
|
|
|
```text
|
|
*/5 * * * * every five minutes
|
|
0 2 * * * every day at 02:00
|
|
30 9 * * 1-5 weekdays at 09:30
|
|
```
|
|
|
|
## Using The App
|
|
|
|
1. Start GoSentry.
|
|
2. Use **New job** to create a scheduled command.
|
|
3. Set **Schedule**, **Command**, optional **Arguments**, **Folder**, and **Enabled**.
|
|
4. Use **Run now** for a one-off manual run without waiting for the schedule.
|
|
5. Use **Pause** on a single job to suspend it without deleting it.
|
|
6. Use **Pause all** as a global stop switch for all scheduled runs.
|
|
7. Open **History** to see past runs, their trigger (`Manual`, `Schedule`, or `UI`), state, and log file.
|
|
8. Open **Settings** to change the storage paths, log cleanup limits, queue behavior, and notifications.
|
|
|
|
The **Jobs file** row picks the file itself: **Browse** lists `.json` files, and
|
|
a path can also be typed to name a file that does not exist yet. What Save does
|
|
depends on whether that file is already there:
|
|
|
|
- **The file exists** — its jobs are loaded and replace the current list, so
|
|
selecting a jobs file switches to it (another machine's file, a shared one on
|
|
a network drive). History records how many jobs were loaded and from where.
|
|
- **The file does not exist** — the current jobs are written to it, which is how
|
|
the jobs file is renamed or moved somewhere else.
|
|
|
|
Switching to a different jobs file is refused while a job is running, because
|
|
loading a new list discards the run state of the old one.
|
|
|
|
The **Start on login** checkbox shows an `OK` or `Problem` status. Saving with
|
|
it enabled writes an autostart entry using the current executable path.
|
|
Autostart entries include `--start-in-tray` so scheduled jobs run after sign-in
|
|
without opening the main window.
|
|
|
|
## Queue Settings
|
|
|
|
Three settings in the **Queue** group of the Settings tab control how
|
|
simultaneous, overlapping, and over-long runs are handled.
|
|
|
|
**Execution mode** — applies when multiple jobs become due at the same tick:
|
|
|
|
| Value | Behaviour |
|
|
|-------|-----------|
|
|
| `parallel` (default) | All due jobs start at the same time. |
|
|
| `sequential` | Due jobs are started one after another, in the order they appear in the list. |
|
|
|
|
**Default overlap policy** — applies when a job's next scheduled run fires while
|
|
its previous run is still active:
|
|
|
|
| Value | Behaviour |
|
|
|-------|-----------|
|
|
| `skip` (default) | The new run is discarded; the running instance continues. |
|
|
| `queue` | The new run is held and starts immediately after the current run finishes. |
|
|
|
|
**Default timeout (s)** — how long a run may take before it is killed. `0` (the
|
|
default) means no limit.
|
|
|
|
The last two are defaults: a job's own dialog has an **Overlap policy** and a
|
|
**Timeout (s)** field that override them. A job that overrides nothing follows
|
|
whatever the Settings tab says, so changing a default moves every such job with
|
|
it. In `jobs.json` an override is an `overlap_policy` or `timeout_seconds` key
|
|
on the job; absent means inherit. A `"timeout_seconds": 0` on a job is an
|
|
override too — it means that job has no timeout even when the global default
|
|
sets one.
|
|
|
|
## Notifications
|
|
|
|
When **Notify on failure** is enabled in Settings, GoSentry sends a desktop
|
|
notification whenever a scheduled or manual run exits with a non-zero exit code.
|
|
The notification shows the job name and the exit code.
|
|
|
|
## Autostart
|
|
|
|
GoSentry is a user desktop application, not a system daemon, so autostart is
|
|
configured per user.
|
|
|
|
Linux:
|
|
|
|
```ini
|
|
# GoSentry writes an XDG Autostart desktop entry when Start on login is enabled.
|
|
~/.config/autostart/gosentry.desktop
|
|
|
|
[Desktop Entry]
|
|
Type=Application
|
|
Name=GoSentry
|
|
Exec=/opt/gosentry/gosentry-<version>-linux-amd64 --start-in-tray
|
|
Terminal=false
|
|
```
|
|
|
|
Windows:
|
|
|
|
```text
|
|
# GoSentry writes a shortcut to the current user's Startup folder.
|
|
# A .lnk stores the executable path as TargetPath and --start-in-tray as
|
|
# Arguments, so paths with spaces do not need fragile command-line quoting.
|
|
%APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup\GoSentry.lnk
|
|
```
|
|
|
|
## Troubleshooting
|
|
|
|
### Windows, VirtualBox, RDP, And OpenGL
|
|
|
|
GoSentry uses [Fyne](https://fyne.io/), and Fyne uses GLFW/OpenGL to create the
|
|
desktop window. In a Windows virtual machine, especially when accessed through
|
|
RDP inside VirtualBox, the available video driver can fail OpenGL initialization.
|
|
|
|
Typical error:
|
|
|
|
```text
|
|
Fyne error: window creation error
|
|
Cause: APIUnavailable: WGL: The driver does not appear to support OpenGL
|
|
```
|
|
|
|
Known workaround:
|
|
|
|
1. Download a Windows Mesa build from
|
|
[mesa-dist-win](https://github.com/pal1000/mesa-dist-win/releases). Use the
|
|
archive named like `mesa3d-<version>-release-mingw.7z` — this matches the
|
|
MSYS2 GCC toolchain used to build GoSentry. The `devel`, `debug-info`,
|
|
`tests`, and checksum files are not needed.
|
|
2. Open the archive and use the `x64` build.
|
|
3. Copy the Mesa OpenGL DLL files from `x64` into the same directory as the
|
|
GoSentry `.exe`:
|
|
|
|
```text
|
|
dist\windows\
|
|
gosentry-<version>-windows-amd64.exe
|
|
opengl32.dll
|
|
...
|
|
```
|
|
|
|
Mesa's software OpenGL implementation lets the Fyne window start even when the
|
|
VirtualBox/RDP driver does not provide usable OpenGL.
|
|
|
|
## Development assistance
|
|
|
|
Parts of this project were developed with assistance from [Cursor](https://cursor.com/) AI (Composer agent)
|
|
and [Claude Code](https://claude.com/claude-code).
|