Compare commits

...
7 Commits
Author SHA1 Message Date
Svilen Markov d4565acfe7 Markets widget rate limit fix
Create release / release (push) Has been cancelled
2025-02-19 02:25:07 +00:00
Svilen Markov 16129c53bd Merge pull request #358 from rubiojr/rubiojr/reload-on-rename2
Auto-reload config file on RENAME
2025-02-19 01:25:08 +00:00
Svilen Markov cbf1961510 Don't try to get sensor info on openbsd 2025-02-17 23:48:16 +00:00
Svilen Markov c76a4d4be7 Increase docker containers widget timeout 2025-02-17 23:45:57 +00:00
Svilen Markov 27af0400c0 Tweak impl for handling config renames 2025-02-17 22:28:10 +00:00
Sergio Rubio 76a80ff034 Add clarifying comment 2025-02-17 19:17:49 +01:00
Sergio Rubio f7f333ad52 Auto-reload config file on RENAME
Some editors (like Vim), create a temp file when saving, then replace
the file being edited with the temp file. This causes the FS notify
event to be RENAME, not WRITE, so auto-reload misses this.

In addition to that, the file is removed from the watcher and the
auto-reload functionality stops working entirely after the first RENAME.

https://github.com/fsnotify/fsnotify/issues/255 describes this.
2025-02-17 19:08:54 +01:00
5 changed files with 51 additions and 15 deletions
+36 -12
View File
@@ -242,7 +242,7 @@ func configFilesWatcher(
// needed for lastContents and lastIncludes because they get updated in multiple goroutines // needed for lastContents and lastIncludes because they get updated in multiple goroutines
mu := sync.Mutex{} mu := sync.Mutex{}
checkForContentChangesBeforeCallback := func() { parseAndCompareBeforeCallback := func() {
currentContents, currentIncludes, err := parseYAMLIncludes(mainFilePath) currentContents, currentIncludes, err := parseYAMLIncludes(mainFilePath)
if err != nil { if err != nil {
onErr(fmt.Errorf("parsing main file contents for comparison: %w", err)) onErr(fmt.Errorf("parsing main file contents for comparison: %w", err))
@@ -268,15 +268,22 @@ func configFilesWatcher(
const debounceDuration = 500 * time.Millisecond const debounceDuration = 500 * time.Millisecond
var debounceTimer *time.Timer var debounceTimer *time.Timer
debouncedCallback := func() { debouncedParseAndCompareBeforeCallback := func() {
if debounceTimer != nil { if debounceTimer != nil {
debounceTimer.Stop() debounceTimer.Stop()
debounceTimer.Reset(debounceDuration) debounceTimer.Reset(debounceDuration)
} else { } else {
debounceTimer = time.AfterFunc(debounceDuration, checkForContentChangesBeforeCallback) debounceTimer = time.AfterFunc(debounceDuration, parseAndCompareBeforeCallback)
} }
} }
deleteLastInclude := func(filePath string) {
mu.Lock()
defer mu.Unlock()
fileAbsPath, _ := filepath.Abs(filePath)
delete(lastIncludes, fileAbsPath)
}
go func() { go func() {
for { for {
select { select {
@@ -285,16 +292,33 @@ func configFilesWatcher(
return return
} }
if event.Has(fsnotify.Write) { if event.Has(fsnotify.Write) {
debouncedCallback() debouncedParseAndCompareBeforeCallback()
} else if event.Has(fsnotify.Remove) { } else if event.Has(fsnotify.Rename) {
func() { // on linux the file will no longer be watched after a rename, on windows
mu.Lock() // it will continue to be watched with the new name but we have no access to
defer mu.Unlock() // the new name in this event in order to stop watching it manually and match the
fileAbsPath, _ := filepath.Abs(event.Name) // behavior in linux, may lead to weird unintended behaviors on windows as we're
delete(lastIncludes, fileAbsPath) // only handling renames from linux's perspective
}() // see https://github.com/fsnotify/fsnotify/issues/255
debouncedCallback() // remove the old file from our manually tracked includes, calling
// debouncedParseAndCompareBeforeCallback will re-add it if it's still
// required after it triggers
deleteLastInclude(event.Name)
// wait for file to maybe get created again
// see https://github.com/glanceapp/glance/pull/358
for i := 0; i < 10; i++ {
if _, err := os.Stat(event.Name); err == nil {
break
}
time.Sleep(200 * time.Millisecond)
}
debouncedParseAndCompareBeforeCallback()
} else if event.Has(fsnotify.Remove) {
deleteLastInclude(event.Name)
debouncedParseAndCompareBeforeCallback()
} }
case err, isOpen := <-watcher.Errors: case err, isOpen := <-watcher.Errors:
if !isOpen { if !isOpen {
+1 -1
View File
@@ -241,7 +241,7 @@ func isDockerContainerHidden(container *dockerContainerJsonResponse, hideByDefau
func fetchAllDockerContainersFromSock(socketPath string) ([]dockerContainerJsonResponse, error) { func fetchAllDockerContainersFromSock(socketPath string) ([]dockerContainerJsonResponse, error) {
client := &http.Client{ client := &http.Client{
Timeout: 3 * time.Second, Timeout: 5 * time.Second,
Transport: &http.Transport{ Transport: &http.Transport{
DialContext: func(_ context.Context, _, _ string) (net.Conn, error) { DialContext: func(_ context.Context, _, _ string) (net.Conn, error) {
return net.Dial("unix", socketPath) return net.Dial("unix", socketPath)
+1
View File
@@ -124,6 +124,7 @@ func fetchMarketsDataFromYahoo(marketRequests []marketRequest) (marketList, erro
for i := range marketRequests { for i := range marketRequests {
request, _ := http.NewRequest("GET", fmt.Sprintf("https://query1.finance.yahoo.com/v8/finance/chart/%s?range=1mo&interval=1d", marketRequests[i].Symbol), nil) request, _ := http.NewRequest("GET", fmt.Sprintf("https://query1.finance.yahoo.com/v8/finance/chart/%s?range=1mo&interval=1d", marketRequests[i].Symbol), nil)
setBrowserUserAgentHeader(request)
requests = append(requests, request) requests = append(requests, request)
} }
+11 -1
View File
@@ -8,8 +8,11 @@ import (
"errors" "errors"
"fmt" "fmt"
"io" "io"
"math/rand/v2"
"net/http" "net/http"
"strconv"
"sync" "sync"
"sync/atomic"
"time" "time"
) )
@@ -35,8 +38,15 @@ type requestDoer interface {
Do(*http.Request) (*http.Response, error) Do(*http.Request) (*http.Response, error)
} }
var userAgentPersistentVersion atomic.Int32
func setBrowserUserAgentHeader(request *http.Request) { func setBrowserUserAgentHeader(request *http.Request) {
request.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:123.0) Gecko/20100101 Firefox/123.0") if rand.IntN(2000) == 0 {
userAgentPersistentVersion.Store(rand.Int32N(5))
}
version := strconv.Itoa(130 + int(userAgentPersistentVersion.Load()))
request.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:"+version+".0) Gecko/20100101 Firefox/"+version+".0")
} }
func decodeJsonFromRequest[T any](client requestDoer, request *http.Request) (T, error) { func decodeJsonFromRequest[T any](client requestDoer, request *http.Request) (T, error) {
+2 -1
View File
@@ -202,7 +202,8 @@ func Collect(req *SystemInfoRequest) (*SystemInfo, []error) {
// keeps returning a single sensor with key "ACPI\\ThermalZone\\TZ00_0" which // keeps returning a single sensor with key "ACPI\\ThermalZone\\TZ00_0" which
// doesn't seem to be the CPU sensor or correspond to anything useful when // doesn't seem to be the CPU sensor or correspond to anything useful when
// compared against the temperatures Libre Hardware Monitor reports // compared against the temperatures Libre Hardware Monitor reports
if runtime.GOOS != "windows" { // also disabled on openbsd because it's not implemented by go-psutil
if runtime.GOOS != "windows" && runtime.GOOS != "openbsd" {
sensorReadings, err := sensors.SensorsTemperatures() sensorReadings, err := sensors.SensorsTemperatures()
if err == nil { if err == nil {
if req.CPUTempSensor != "" { if req.CPUTempSensor != "" {