From f7f333ad529f6d68c60b1c0354ded196caf131ba Mon Sep 17 00:00:00 2001 From: Sergio Rubio Date: Mon, 17 Feb 2025 19:02:40 +0100 Subject: [PATCH 1/8] 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. --- internal/glance/config.go | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/internal/glance/config.go b/internal/glance/config.go index 7b3377b..88479dc 100644 --- a/internal/glance/config.go +++ b/internal/glance/config.go @@ -286,6 +286,20 @@ func configFilesWatcher( } if event.Has(fsnotify.Write) { debouncedCallback() + } else if event.Has(fsnotify.Rename) { + // wait for file to be available + for i := 0; i < 20; i++ { + _, err := os.Stat(mainFileAbsPath) + if err == nil { + break + } + time.Sleep(100 * time.Millisecond) + } + err := watcher.Add(mainFileAbsPath) + if err != nil { + onErr(fmt.Errorf("watching file:", err)) + } + debouncedCallback() } else if event.Has(fsnotify.Remove) { func() { mu.Lock() From 76a80ff034a4c1801ca9e03576f4e24e2f31f517 Mon Sep 17 00:00:00 2001 From: Sergio Rubio Date: Mon, 17 Feb 2025 19:17:49 +0100 Subject: [PATCH 2/8] Add clarifying comment --- internal/glance/config.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/internal/glance/config.go b/internal/glance/config.go index 88479dc..875ce4f 100644 --- a/internal/glance/config.go +++ b/internal/glance/config.go @@ -295,6 +295,9 @@ func configFilesWatcher( } time.Sleep(100 * time.Millisecond) } + // fsnotify removes the file from the watch list on rename events, + // add it back. + // See https://github.com/fsnotify/fsnotify/issues/214 err := watcher.Add(mainFileAbsPath) if err != nil { onErr(fmt.Errorf("watching file:", err)) From 27af0400c0326f8554305175a2da1bb15bf39fcb Mon Sep 17 00:00:00 2001 From: Svilen Markov <7613769+svilenmarkov@users.noreply.github.com> Date: Mon, 17 Feb 2025 22:28:10 +0000 Subject: [PATCH 3/8] Tweak impl for handling config renames --- internal/glance/config.go | 57 ++++++++++++++++++++++----------------- 1 file changed, 32 insertions(+), 25 deletions(-) diff --git a/internal/glance/config.go b/internal/glance/config.go index 875ce4f..0d424a2 100644 --- a/internal/glance/config.go +++ b/internal/glance/config.go @@ -242,7 +242,7 @@ func configFilesWatcher( // needed for lastContents and lastIncludes because they get updated in multiple goroutines mu := sync.Mutex{} - checkForContentChangesBeforeCallback := func() { + parseAndCompareBeforeCallback := func() { currentContents, currentIncludes, err := parseYAMLIncludes(mainFilePath) if err != nil { onErr(fmt.Errorf("parsing main file contents for comparison: %w", err)) @@ -268,15 +268,22 @@ func configFilesWatcher( const debounceDuration = 500 * time.Millisecond var debounceTimer *time.Timer - debouncedCallback := func() { + debouncedParseAndCompareBeforeCallback := func() { if debounceTimer != nil { debounceTimer.Stop() debounceTimer.Reset(debounceDuration) } 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() { for { select { @@ -285,33 +292,33 @@ func configFilesWatcher( return } if event.Has(fsnotify.Write) { - debouncedCallback() + debouncedParseAndCompareBeforeCallback() } else if event.Has(fsnotify.Rename) { - // wait for file to be available - for i := 0; i < 20; i++ { - _, err := os.Stat(mainFileAbsPath) - if err == nil { + // on linux the file will no longer be watched after a rename, on windows + // it will continue to be watched with the new name but we have no access to + // the new name in this event in order to stop watching it manually and match the + // behavior in linux, may lead to weird unintended behaviors on windows as we're + // only handling renames from linux's perspective + // see https://github.com/fsnotify/fsnotify/issues/255 + + // 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(100 * time.Millisecond) + time.Sleep(200 * time.Millisecond) } - // fsnotify removes the file from the watch list on rename events, - // add it back. - // See https://github.com/fsnotify/fsnotify/issues/214 - err := watcher.Add(mainFileAbsPath) - if err != nil { - onErr(fmt.Errorf("watching file:", err)) - } - debouncedCallback() - } else if event.Has(fsnotify.Remove) { - func() { - mu.Lock() - defer mu.Unlock() - fileAbsPath, _ := filepath.Abs(event.Name) - delete(lastIncludes, fileAbsPath) - }() - debouncedCallback() + debouncedParseAndCompareBeforeCallback() + } else if event.Has(fsnotify.Remove) { + deleteLastInclude(event.Name) + debouncedParseAndCompareBeforeCallback() } case err, isOpen := <-watcher.Errors: if !isOpen { From c76a4d4be74701bbf30ece65aa90f62e5ff785ae Mon Sep 17 00:00:00 2001 From: Svilen Markov <7613769+svilenmarkov@users.noreply.github.com> Date: Mon, 17 Feb 2025 23:45:57 +0000 Subject: [PATCH 4/8] Increase docker containers widget timeout --- internal/glance/widget-docker-containers.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/glance/widget-docker-containers.go b/internal/glance/widget-docker-containers.go index 8bd83b1..f38cdeb 100644 --- a/internal/glance/widget-docker-containers.go +++ b/internal/glance/widget-docker-containers.go @@ -241,7 +241,7 @@ func isDockerContainerHidden(container *dockerContainerJsonResponse, hideByDefau func fetchAllDockerContainersFromSock(socketPath string) ([]dockerContainerJsonResponse, error) { client := &http.Client{ - Timeout: 3 * time.Second, + Timeout: 5 * time.Second, Transport: &http.Transport{ DialContext: func(_ context.Context, _, _ string) (net.Conn, error) { return net.Dial("unix", socketPath) From cbf19615108e50be2aad2b843aaaef7c5f839cb8 Mon Sep 17 00:00:00 2001 From: Svilen Markov <7613769+svilenmarkov@users.noreply.github.com> Date: Mon, 17 Feb 2025 23:48:16 +0000 Subject: [PATCH 5/8] Don't try to get sensor info on openbsd --- pkg/sysinfo/sysinfo.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pkg/sysinfo/sysinfo.go b/pkg/sysinfo/sysinfo.go index 09df02f..673b9d2 100644 --- a/pkg/sysinfo/sysinfo.go +++ b/pkg/sysinfo/sysinfo.go @@ -202,7 +202,8 @@ func Collect(req *SystemInfoRequest) (*SystemInfo, []error) { // 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 // 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() if err == nil { if req.CPUTempSensor != "" { From d4565acfe7643fcb053b327d56917893993d21a2 Mon Sep 17 00:00:00 2001 From: Svilen Markov <7613769+svilenmarkov@users.noreply.github.com> Date: Wed, 19 Feb 2025 02:24:32 +0000 Subject: [PATCH 6/8] Markets widget rate limit fix --- internal/glance/widget-markets.go | 1 + internal/glance/widget-utils.go | 12 +++++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/internal/glance/widget-markets.go b/internal/glance/widget-markets.go index 63eda1a..93dd9c6 100644 --- a/internal/glance/widget-markets.go +++ b/internal/glance/widget-markets.go @@ -124,6 +124,7 @@ func fetchMarketsDataFromYahoo(marketRequests []marketRequest) (marketList, erro 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) + setBrowserUserAgentHeader(request) requests = append(requests, request) } diff --git a/internal/glance/widget-utils.go b/internal/glance/widget-utils.go index 77a9d5c..8fb76dd 100644 --- a/internal/glance/widget-utils.go +++ b/internal/glance/widget-utils.go @@ -8,8 +8,11 @@ import ( "errors" "fmt" "io" + "math/rand/v2" "net/http" + "strconv" "sync" + "sync/atomic" "time" ) @@ -35,8 +38,15 @@ type requestDoer interface { Do(*http.Request) (*http.Response, error) } +var userAgentPersistentVersion atomic.Int32 + 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) { From 73198702892f734a45c0650667691be16d991a51 Mon Sep 17 00:00:00 2001 From: DavisYe Date: Thu, 20 Feb 2025 21:30:34 +0800 Subject: [PATCH 7/8] Correct the docker compose environment format of GITHUB_TOKEN in the configuration document. --- docs/configuration.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/configuration.md b/docs/configuration.md index 01afc8e..633d1d8 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -1608,7 +1608,7 @@ services: glance: image: glanceapp/glance environment: - - GITHUB_TOKEN: + - GITHUB_TOKEN= ``` and then use it in your `glance.yml` like this: From 488a1f60703033f578c7d93b1f54fc683ef871ce Mon Sep 17 00:00:00 2001 From: Svilen Markov <7613769+svilenmarkov@users.noreply.github.com> Date: Sat, 22 Feb 2025 13:32:36 +0000 Subject: [PATCH 8/8] Update link to point to repo instead of pkg.go.dev --- docs/configuration.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/configuration.md b/docs/configuration.md index 633d1d8..8944dca 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -1311,7 +1311,7 @@ headers: When set to `true`, removes the border and padding around the widget. ##### `template` -The template that will be used to display the data. It relies on Go's `html/template` package so it's recommended to go through [its documentation](https://pkg.go.dev/text/template) to understand how to do basic things such as conditionals, loops, etc. In addition, it also uses [tidwall's gjson](https://pkg.go.dev/github.com/tidwall/gjson) package to parse the JSON data so it's worth going through its documentation if you want to use more advanced JSON selectors. You can view additional examples with explanations and function definitions [here](custom-api.md). +The template that will be used to display the data. It relies on Go's `html/template` package so it's recommended to go through [its documentation](https://pkg.go.dev/text/template) to understand how to do basic things such as conditionals, loops, etc. In addition, it also uses [tidwall's gjson](https://github.com/tidwall/gjson) package to parse the JSON data so it's worth going through its documentation if you want to use more advanced JSON selectors. You can view additional examples with explanations and function definitions [here](custom-api.md). ### Extension Display a widget provided by an external source (3rd party). If you want to learn more about developing extensions, checkout the [extensions documentation](extensions.md) (WIP).