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.
This commit is contained in:
@@ -86,7 +86,7 @@ The binary is written to:
|
||||
|
||||
```text
|
||||
# GUI executable produced by scripts\build-windows.bat.
|
||||
dist\windows\pysentry-0.2.3-windows-amd64.exe
|
||||
dist\windows\pysentry-0.2.4-windows-amd64.exe
|
||||
```
|
||||
|
||||
Linux:
|
||||
@@ -101,7 +101,7 @@ The binary is written to:
|
||||
|
||||
```text
|
||||
# Linux executable produced by scripts/build-linux.sh.
|
||||
dist/linux/pysentry-0.2.3-linux-amd64
|
||||
dist/linux/pysentry-0.2.4-linux-amd64
|
||||
```
|
||||
|
||||
Linux using Docker:
|
||||
@@ -118,7 +118,7 @@ The binary is copied to:
|
||||
|
||||
```text
|
||||
# Linux executable copied out of the Docker build image.
|
||||
dist\linux\pysentry-0.2.3-linux-amd64
|
||||
dist\linux\pysentry-0.2.4-linux-amd64
|
||||
```
|
||||
|
||||
Release build from Linux:
|
||||
@@ -143,13 +143,13 @@ The binaries are copied to:
|
||||
|
||||
```text
|
||||
# Linux artifact.
|
||||
dist/linux/pysentry-0.2.3-linux-amd64
|
||||
dist/linux/pysentry-0.2.4-linux-amd64
|
||||
|
||||
# Linux arm64 artifact.
|
||||
dist/linux/pysentry-0.2.3-linux-arm64
|
||||
dist/linux/pysentry-0.2.4-linux-arm64
|
||||
|
||||
# Windows artifact cross-compiled from Linux.
|
||||
dist/windows/pysentry-0.2.3-windows-amd64.exe
|
||||
dist/windows/pysentry-0.2.4-windows-amd64.exe
|
||||
```
|
||||
|
||||
## Run From Source
|
||||
@@ -292,7 +292,7 @@ Linux:
|
||||
[Desktop Entry]
|
||||
Type=Application
|
||||
Name=PySentry
|
||||
Exec=/opt/pysentry/pysentry-0.2.3-linux-amd64
|
||||
Exec=/opt/pysentry/pysentry-0.2.4-linux-amd64
|
||||
Terminal=false
|
||||
```
|
||||
|
||||
|
||||
@@ -2,6 +2,11 @@
|
||||
|
||||
All notable PySentry changes are recorded in this file.
|
||||
|
||||
## 0.2.4 - 2026-06-16
|
||||
|
||||
- Prevented repeated application launches by forwarding a second start attempt to the already running instance.
|
||||
- A second instance now asks the first instance to show and focus the existing window, then exits.
|
||||
|
||||
## 0.2.3 - 2026-06-15
|
||||
|
||||
- Changed History to use chronological ordering with new records appended at the bottom.
|
||||
|
||||
+1
-1
@@ -3,4 +3,4 @@ package core
|
||||
// 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.2.3"
|
||||
var Version = "0.2.4"
|
||||
|
||||
@@ -2,6 +2,8 @@ package gui
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/url"
|
||||
"runtime"
|
||||
"runtime/debug"
|
||||
@@ -31,6 +33,8 @@ const settingsLabelWidth float32 = 140
|
||||
const settingsControlWidth float32 = 330
|
||||
const settingsStatusWidth float32 = 280
|
||||
const projectRepositoryURL = "https://gitea.mixdep.ru/mix/gosentry"
|
||||
const singleInstanceAddress = "127.0.0.1:37653"
|
||||
const singleInstanceShowCommand = "show"
|
||||
|
||||
// The GUI package aliases core types to keep widget callbacks short. The actual
|
||||
// durable model still lives in src/core, so GUI code does not define a second
|
||||
@@ -40,6 +44,14 @@ type event = core.RunRecord
|
||||
|
||||
func Run() {
|
||||
started := time.Now()
|
||||
instanceListener, primary := acquireSingleInstance()
|
||||
if !primary {
|
||||
return
|
||||
}
|
||||
if instanceListener != nil {
|
||||
defer instanceListener.Close()
|
||||
}
|
||||
|
||||
// A stable app ID lets Fyne persist desktop preferences consistently across
|
||||
// launches and gives tray/window integration a predictable identity.
|
||||
a := app.NewWithID(appID)
|
||||
@@ -49,6 +61,7 @@ func Run() {
|
||||
configureSystemTray(a, w)
|
||||
w.Resize(fyne.NewSize(1120, 720))
|
||||
w.SetContent(newMainView(w, started))
|
||||
serveSingleInstance(instanceListener, w)
|
||||
w.ShowAndRun()
|
||||
}
|
||||
|
||||
@@ -83,6 +96,47 @@ func configureSystemTray(a fyne.App, w fyne.Window) {
|
||||
})
|
||||
}
|
||||
|
||||
func acquireSingleInstance() (net.Listener, bool) {
|
||||
listener, err := net.Listen("tcp", singleInstanceAddress)
|
||||
if err == nil {
|
||||
return listener, true
|
||||
}
|
||||
|
||||
connection, dialErr := net.DialTimeout("tcp", singleInstanceAddress, time.Second)
|
||||
if dialErr == nil {
|
||||
_, _ = io.WriteString(connection, singleInstanceShowCommand)
|
||||
_ = connection.Close()
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// If the port is unavailable but does not answer as GoSentry, continue
|
||||
// startup instead of making the application impossible to open because of an
|
||||
// unrelated local listener. In the normal duplicate-start case the dial above
|
||||
// succeeds and this process exits after waking the first instance.
|
||||
return nil, true
|
||||
}
|
||||
|
||||
func serveSingleInstance(listener net.Listener, w fyne.Window) {
|
||||
if listener == nil {
|
||||
return
|
||||
}
|
||||
go func() {
|
||||
for {
|
||||
connection, err := listener.Accept()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
command, _ := io.ReadAll(io.LimitReader(connection, 32))
|
||||
_ = connection.Close()
|
||||
if strings.TrimSpace(string(command)) != singleInstanceShowCommand {
|
||||
continue
|
||||
}
|
||||
w.Show()
|
||||
w.RequestFocus()
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func newMainView(w fyne.Window, started time.Time) fyne.CanvasObject {
|
||||
store, jobs, err := core.OpenStore()
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user