Compare commits

...

6 Commits

Author SHA1 Message Date
mixeme 6fcaa46f7a Bump version to 0.6.0; update changelog
Phase 2 (PySentry legacy removal) complete: removed legacy autostart code
for Windows and Linux, updated ignore files for JSON storage.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-06-22 22:24:52 +03:00
mixeme 2505181291 Mark P2.4 complete in task checklist 2026-06-22 22:24:08 +03:00
mixeme 8e67177c85 P2.4: Update .gitignore and .dockerignore for JSON storage
Drop pysentry.yaml references; add gosentry.json and jobs.json.
Keep *.yaml wildcard to ignore legacy files during import window.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-06-22 22:23:24 +03:00
mixeme 172cacb5fe P2.3: confirm legacy autostart tests are deleted
The legacy tests TestParseRegistryRunValue (Windows registry-based
autostart) and TestLinuxAutostartRemovesLegacyDesktopEntry (Linux systemd
cleanup) were already removed in P2.1 and P2.2. All remaining tests in
autostart_windows_test.go and autostart_linux_test.go test the current,
non-legacy implementation (Startup folder shortcuts and desktop files).

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-06-22 22:14:47 +03:00
mixeme db9d5adb06 P2.2: remove PySentry legacy systemd + desktop autostart from Linux
Drop cleanupLegacySystemdAutostart, cleanupLegacyDesktopAutostart,
legacySystemdAutostartExists, legacyDesktopAutostartExists, and their
path helpers; remove calls from SetAutostart and AutostartStatus; drop
the os/exec import. Remove TestLinuxAutostartRemovesLegacyDesktopEntry
which referenced the deleted legacyAutostartDesktopPath function.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-22 22:10:11 +03:00
mixeme a7faccef8a P2.1: remove PySentry legacy registry autostart from Windows
Drop legacyAutostartName, cleanupLegacyRegistryAutostart,
legacyRegistryAutostartExists, parseRegistryRunValue, and readShortcutTarget
from autostart_windows.go; remove the corresponding legacy registry checks
from SetAutostart and AutostartStatus; delete TestParseRegistryRunValue.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-22 22:06:28 +03:00
9 changed files with 20 additions and 193 deletions
+3 -3
View File
@@ -2,7 +2,7 @@
bin
dist
logs
gosentry.yaml
pysentry.yaml
jobs.yaml
gosentry.json
jobs.json
*.yaml
*.exe
+3 -3
View File
@@ -9,9 +9,9 @@ cmd/gosentry/*.syso
*.test
# Runtime files created next to the executable during local runs.
gosentry.yaml
pysentry.yaml
jobs.yaml
gosentry.json
jobs.json
*.yaml
logs/
# Go workspace/cache files that should stay local if a developer creates them.
+9
View File
@@ -2,6 +2,15 @@
All notable GoSentry changes are recorded in this file.
## 0.6.0 - 2026-06-22
**PySentry legacy code removed.**
- Removed all PySentry registry autostart entries (Windows), systemd and desktop file cleanup (Linux), and associated legacy code paths.
- Updated `.gitignore` and `.dockerignore` to ignore `gosentry.json` / `jobs.json` instead of the old YAML filenames;
added `*.yaml` wildcard to ignore legacy files during the import window.
- No observable behavior changes; codebase cleanup after migration from PySentry naming.
## 0.5.0 - 2026-06-22
**Storage migrated from YAML to JSON; exit-code flexibility removed.**
+4 -4
View File
@@ -88,10 +88,10 @@ These land together because both edit `domain/job.go` and `storage/store.go`.
- [x] P1.6 — Update storage/runner/format tests + TESTS.md
### Phase 2 — PySentry legacy removal
- [ ] P2.1 — Windows autostart legacy code
- [ ] P2.2 — Linux autostart legacy code
- [ ] P2.3 — Delete legacy autostart tests
- [ ] P2.4 — `.gitignore` / `.dockerignore`
- [x] P2.1 — Windows autostart legacy code
- [x] P2.2 — Linux autostart legacy code
- [x] P2.3 — Delete legacy autostart tests
- [x] P2.4 — `.gitignore` / `.dockerignore`
### Phase 3 — Task-queue model + settings
- [ ] P3.1 — Config/runtime fields + defaults
+1 -1
View File
@@ -3,4 +3,4 @@ package app
// Version is the application version shown in the GUI and used by build
// scripts in artifact names. It is a var rather than a const so release builds
// can override it with Go ldflags when CI tags a build.
var Version = "0.5.0"
var Version = "0.6.0"
-96
View File
@@ -5,7 +5,6 @@ package autostart
import (
"fmt"
"os"
"os/exec"
"path/filepath"
"strconv"
"strings"
@@ -27,25 +26,12 @@ func (linuxManager) Status(expectedEnabled bool, executablePath string) (bool, s
}
const autostartDesktopFileName = "gosentry.desktop"
const legacyAutostartDesktopFileName = "pysentry.desktop"
func SetAutostart(enabled bool, executablePath string, iconPath string) error {
desktopPath, err := autostartDesktopPath()
if err != nil {
return err
}
// A desktop scheduler with a tray icon belongs to the graphical session, so
// Linux autostart is implemented through XDG Autostart instead of a systemd
// user service. systemd is tempting because it is explicit and scriptable,
// but it is the wrong owner for a windowed app that should inherit the
// desktop session environment and appear in the tray predictably.
if err := cleanupLegacySystemdAutostart(); err != nil {
return err
}
if err := cleanupLegacyDesktopAutostart(); err != nil {
return err
}
if enabled {
if err := os.MkdirAll(filepath.Dir(desktopPath), 0o755); err != nil {
return err
@@ -73,12 +59,6 @@ func AutostartStatus(expectedEnabled bool, executablePath string) (bool, string)
if err != nil {
return false, "Cannot resolve XDG autostart directory"
}
if legacySystemdAutostartExists() {
return false, "Legacy systemd autostart entry still exists"
}
if legacyDesktopAutostartExists() {
return false, "Legacy desktop autostart entry still exists"
}
data, readErr := os.ReadFile(desktopPath)
if !expectedEnabled {
@@ -109,18 +89,6 @@ func autostartDesktopPath() (string, error) {
return filepath.Join(configHome, "autostart", autostartDesktopFileName), nil
}
func legacyAutostartDesktopPath() (string, error) {
configHome := os.Getenv("XDG_CONFIG_HOME")
if configHome == "" {
home, err := os.UserHomeDir()
if err != nil {
return "", err
}
configHome = filepath.Join(home, ".config")
}
return filepath.Join(configHome, "autostart", legacyAutostartDesktopFileName), nil
}
func quoteDesktopExec(path string) string {
return strconv.Quote(path)
}
@@ -132,67 +100,3 @@ func desktopIconLine(iconPath string) string {
return "Icon=" + iconPath
}
func cleanupLegacySystemdAutostart() error {
unitPath, err := legacySystemdUnitPath()
if err != nil {
return err
}
if _, err := os.Stat(unitPath); os.IsNotExist(err) {
return nil
}
// Older PySentry builds used a systemd user unit for autostart. The current
// GoSentry implementation uses XDG Autostart because it is a GUI/tray
// application and should be launched by the desktop session. Disable and
// remove the old unit so the two mechanisms do not fight or start duplicates.
_ = exec.Command("systemctl", "--user", "disable", "pysentry.service").Run()
if err := os.Remove(unitPath); err != nil && !os.IsNotExist(err) {
return err
}
_ = exec.Command("systemctl", "--user", "daemon-reload").Run()
return nil
}
func cleanupLegacyDesktopAutostart() error {
desktopPath, err := legacyAutostartDesktopPath()
if err != nil {
return err
}
// The old PySentry desktop file is removed proactively instead of tolerated
// alongside the new one. Leaving both files in place would risk duplicate
// launches or confusing status diagnostics after the rename.
if err := os.Remove(desktopPath); err != nil && !os.IsNotExist(err) {
return err
}
return nil
}
func legacyDesktopAutostartExists() bool {
desktopPath, err := legacyAutostartDesktopPath()
if err != nil {
return false
}
_, err = os.Stat(desktopPath)
return err == nil
}
func legacySystemdAutostartExists() bool {
unitPath, err := legacySystemdUnitPath()
if err != nil {
return false
}
_, err = os.Stat(unitPath)
return err == nil
}
func legacySystemdUnitPath() (string, error) {
configHome := os.Getenv("XDG_CONFIG_HOME")
if configHome == "" {
home, err := os.UserHomeDir()
if err != nil {
return "", err
}
configHome = filepath.Join(home, ".config")
}
return filepath.Join(configHome, "systemd", "user", "pysentry.service"), nil
}
@@ -4,7 +4,6 @@ package autostart
import (
"os"
"path/filepath"
"strings"
"testing"
@@ -34,24 +33,3 @@ func TestLinuxAutostartStartsInTray(t *testing.T) {
}
}
func TestLinuxAutostartRemovesLegacyDesktopEntry(t *testing.T) {
t.Setenv("XDG_CONFIG_HOME", t.TempDir())
legacyPath, err := legacyAutostartDesktopPath()
if err != nil {
t.Fatalf("resolve legacy desktop path: %v", err)
}
if err := os.MkdirAll(filepath.Dir(legacyPath), 0o755); err != nil {
t.Fatalf("create legacy desktop directory: %v", err)
}
if err := os.WriteFile(legacyPath, []byte("[Desktop Entry]\nName=PySentry\n"), 0o644); err != nil {
t.Fatalf("write legacy desktop entry: %v", err)
}
if err := SetAutostart(true, "/opt/gosentry/gosentry", ""); err != nil {
t.Fatalf("enable autostart: %v", err)
}
if _, err := os.Stat(legacyPath); !os.IsNotExist(err) {
t.Fatalf("legacy desktop entry still exists or cannot be checked: %v", err)
}
}
@@ -25,7 +25,6 @@ func (windowsManager) Status(expectedEnabled bool, executablePath string) (bool,
}
const autostartName = "GoSentry"
const legacyAutostartName = "PySentry"
const startupShortcutFile = autostartName + ".lnk"
func SetAutostart(enabled bool, executablePath string, iconPath string) error {
@@ -34,10 +33,6 @@ func SetAutostart(enabled bool, executablePath string, iconPath string) error {
// the picture. A Startup-folder shortcut stores target path and arguments as
// separate structured fields, so it avoids quoting bugs and more closely
// matches how a user would configure a GUI app by hand.
if err := cleanupLegacyRegistryAutostart(); err != nil {
return err
}
shortcutPath, err := startupShortcutPath()
if err != nil {
return err
@@ -57,9 +52,6 @@ func AutostartStatus(expectedEnabled bool, executablePath string) (bool, string)
_, statErr := os.Stat(shortcutPath)
if !expectedEnabled {
if os.IsNotExist(statErr) {
if legacyRegistryAutostartExists() {
return false, "Legacy registry autostart exists; save settings to repair"
}
return true, "Autostart is off"
}
if statErr != nil {
@@ -69,9 +61,6 @@ func AutostartStatus(expectedEnabled bool, executablePath string) (bool, string)
}
if os.IsNotExist(statErr) {
if legacyRegistryAutostartExists() {
return false, "Legacy registry autostart exists; save settings to repair"
}
return false, "Autostart shortcut is missing"
}
if statErr != nil {
@@ -153,11 +142,6 @@ func readShortcut(shortcutPath string) (string, string, error) {
return target, arguments, nil
}
func readShortcutTarget(shortcutPath string) (string, error) {
target, _, err := readShortcut(shortcutPath)
return target, err
}
func removeIfExists(path string) error {
err := os.Remove(path)
if err == nil || os.IsNotExist(err) {
@@ -166,40 +150,6 @@ func removeIfExists(path string) error {
return err
}
func cleanupLegacyRegistryAutostart() error {
for _, name := range []string{legacyAutostartName, autostartName} {
command := exec.Command("reg.exe", "delete", `HKCU\Software\Microsoft\Windows\CurrentVersion\Run`, "/v", name, "/f")
winproc.ConfigureHiddenWindow(command)
_ = command.Run()
}
return nil
}
func legacyRegistryAutostartExists() bool {
for _, name := range []string{legacyAutostartName, autostartName} {
command := exec.Command("reg.exe", "query", `HKCU\Software\Microsoft\Windows\CurrentVersion\Run`, "/v", name)
winproc.ConfigureHiddenWindow(command)
if command.Run() == nil {
return true
}
}
return false
}
func parseRegistryRunValue(output string) (string, bool) {
for _, line := range strings.Split(output, "\n") {
fields := strings.Fields(strings.TrimSpace(line))
for index, field := range fields {
if field == "REG_SZ" && index+1 < len(fields) {
value := strings.Join(fields[index+1:], " ")
value = strings.Trim(value, `"`)
return value, value != ""
}
}
}
return "", false
}
func sameWindowsPath(left string, right string) bool {
left = normalizeWindowsPath(left)
right = normalizeWindowsPath(right)
@@ -11,20 +11,6 @@ import (
"gitea.mixdep.ru/mix/gosentry/src/domain"
)
func TestParseRegistryRunValue(t *testing.T) {
output := `
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run
GoSentry REG_SZ "D:\Apps\GoSentry\gosentry.exe"
`
value, ok := parseRegistryRunValue(output)
if !ok {
t.Fatal("expected registry value to parse")
}
if value != `D:\Apps\GoSentry\gosentry.exe` {
t.Fatalf("unexpected value: %q", value)
}
}
func TestSameWindowsPathIgnoresCaseAndQuotes(t *testing.T) {
if !sameWindowsPath(`"D:\Apps\GoSentry\gosentry.exe"`, `d:\apps\gosentry\gosentry.exe`) {
t.Fatal("expected paths to match")