Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6fcaa46f7a | |||
| 2505181291 | |||
| 8e67177c85 | |||
| 172cacb5fe | |||
| db9d5adb06 | |||
| a7faccef8a |
+3
-3
@@ -2,7 +2,7 @@
|
||||
bin
|
||||
dist
|
||||
logs
|
||||
gosentry.yaml
|
||||
pysentry.yaml
|
||||
jobs.yaml
|
||||
gosentry.json
|
||||
jobs.json
|
||||
*.yaml
|
||||
*.exe
|
||||
|
||||
+3
-3
@@ -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.
|
||||
|
||||
@@ -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.**
|
||||
|
||||
@@ -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
@@ -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"
|
||||
|
||||
@@ -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")
|
||||
|
||||
Reference in New Issue
Block a user