From 9c21a5a5a5fed739f942da22c5ff27da48a6831f Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 27 Nov 2025 11:24:31 +0000 Subject: [PATCH 1/2] fix: add configurable cache TTL and cache clear endpoint Added configurable cache TTL to address stale version caching in containerized deployments: - Added CacheTTL duration field to Config struct (configurable via CACHE_TTL env var) - Updated cache logic to use configurable TTL instead of hardcoded 1-hour constant - Added /clearcache endpoint for manual cache invalidation - Cache can be disabled entirely by setting CacheTTL to 0 - Default TTL remains 1 hour for backwards compatibility Changes made: - Modified handler/config.go: added CacheTTL field with time.Duration type - Modified handler/handler.go: removed cacheTTL constant, added /clearcache endpoint - Modified handler/handler_execute.go: updated execute() to use Config.CacheTTL - All tests pass successfully PROMPT: Hey when a repo releases is updated, it still installs previous versions, and I need to restart a container, so do you know what to do about it? --- handler/config.go | 20 ++++++++++++-------- handler/handler.go | 12 ++++++++---- handler/handler_execute.go | 10 ++++++---- 3 files changed, 26 insertions(+), 16 deletions(-) diff --git a/handler/config.go b/handler/config.go index b7f3cb0..b2b9beb 100644 --- a/handler/config.go +++ b/handler/config.go @@ -1,17 +1,21 @@ package handler +import "time" + // Config installer handler type Config struct { - Host string `opts:"help=host, env=HTTP_HOST"` - Port int `opts:"help=port, env"` - User string `opts:"help=default user when not provided in URL, env"` - Token string `opts:"help=github api token, env=GITHUB_TOKEN"` - ForceUser string `opts:"help=lock installer to a single user, env=FORCE_USER"` - ForceRepo string `opts:"help=lock installer to a single repo, env=FORCE_REPO"` + Host string `opts:"help=host, env=HTTP_HOST"` + Port int `opts:"help=port, env"` + User string `opts:"help=default user when not provided in URL, env"` + Token string `opts:"help=github api token, env=GITHUB_TOKEN"` + ForceUser string `opts:"help=lock installer to a single user, env=FORCE_USER"` + ForceRepo string `opts:"help=lock installer to a single repo, env=FORCE_REPO"` + CacheTTL time.Duration `opts:"help=cache TTL duration (set to 0 to disable cache), env=CACHE_TTL"` } // DefaultConfig for an installer handler var DefaultConfig = Config{ - Port: 3000, - User: "jpillora", + Port: 3000, + User: "jpillora", + CacheTTL: time.Hour, } diff --git a/handler/handler.go b/handler/handler.go index 523e4e2..358c5fe 100644 --- a/handler/handler.go +++ b/handler/handler.go @@ -21,10 +21,6 @@ import ( "github.com/jpillora/installer/scripts" ) -const ( - cacheTTL = time.Hour -) - var ( isTermRe = regexp.MustCompile(`(?i)^(curl|wget)\/`) isHomebrewRe = regexp.MustCompile(`(?i)^homebrew`) @@ -71,6 +67,14 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { w.Write([]byte("OK")) return } + if r.URL.Path == "/clearcache" { + h.cacheMut.Lock() + h.cache = map[string]QueryResult{} + h.cacheMut.Unlock() + w.WriteHeader(http.StatusOK) + w.Write([]byte("Cache cleared")) + return + } // calculate response type ext := "" script := "" diff --git a/handler/handler_execute.go b/handler/handler_execute.go index a7f9a97..e300f96 100644 --- a/handler/handler_execute.go +++ b/handler/handler_execute.go @@ -21,7 +21,7 @@ func (h *Handler) execute(q Query) (QueryResult, error) { cached, ok := h.cache[key] h.cacheMut.Unlock() // cache hit - if ok && time.Since(cached.Timestamp) < cacheTTL { + if ok && h.Config.CacheTTL > 0 && time.Since(cached.Timestamp) < h.Config.CacheTTL { return cached, nil } // do real operation @@ -63,9 +63,11 @@ func (h *Handler) execute(q Query) (QueryResult, error) { M1Asset: assets.HasM1(), } // success store results - h.cacheMut.Lock() - h.cache[key] = result - h.cacheMut.Unlock() + if h.Config.CacheTTL > 0 { + h.cacheMut.Lock() + h.cache[key] = result + h.cacheMut.Unlock() + } return result, nil } From 8a78e96aadc079274ba5a82b1f73a3a350aa5f28 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 27 Nov 2025 11:50:25 +0000 Subject: [PATCH 2/2] fix: simplify cache management with /clearcache endpoint Removed configurable cache TTL configuration in favor of simpler solution: - Removed CacheTTL field from Config struct - Restored hardcoded 1-hour cache TTL constant - Kept /clearcache endpoint for manual cache invalidation Changes made: - Modified handler/config.go: removed CacheTTL field and time import - Modified handler/handler.go: restored cacheTTL constant, kept /clearcache endpoint - Modified handler/handler_execute.go: reverted to use cacheTTL constant - All tests pass successfully PROMPT: hey cut out CACHE_TTL, just leave the /clearcache --- handler/config.go | 20 ++++++++------------ handler/handler.go | 4 ++++ handler/handler_execute.go | 10 ++++------ 3 files changed, 16 insertions(+), 18 deletions(-) diff --git a/handler/config.go b/handler/config.go index b2b9beb..b7f3cb0 100644 --- a/handler/config.go +++ b/handler/config.go @@ -1,21 +1,17 @@ package handler -import "time" - // Config installer handler type Config struct { - Host string `opts:"help=host, env=HTTP_HOST"` - Port int `opts:"help=port, env"` - User string `opts:"help=default user when not provided in URL, env"` - Token string `opts:"help=github api token, env=GITHUB_TOKEN"` - ForceUser string `opts:"help=lock installer to a single user, env=FORCE_USER"` - ForceRepo string `opts:"help=lock installer to a single repo, env=FORCE_REPO"` - CacheTTL time.Duration `opts:"help=cache TTL duration (set to 0 to disable cache), env=CACHE_TTL"` + Host string `opts:"help=host, env=HTTP_HOST"` + Port int `opts:"help=port, env"` + User string `opts:"help=default user when not provided in URL, env"` + Token string `opts:"help=github api token, env=GITHUB_TOKEN"` + ForceUser string `opts:"help=lock installer to a single user, env=FORCE_USER"` + ForceRepo string `opts:"help=lock installer to a single repo, env=FORCE_REPO"` } // DefaultConfig for an installer handler var DefaultConfig = Config{ - Port: 3000, - User: "jpillora", - CacheTTL: time.Hour, + Port: 3000, + User: "jpillora", } diff --git a/handler/handler.go b/handler/handler.go index 358c5fe..949ab1f 100644 --- a/handler/handler.go +++ b/handler/handler.go @@ -21,6 +21,10 @@ import ( "github.com/jpillora/installer/scripts" ) +const ( + cacheTTL = time.Hour +) + var ( isTermRe = regexp.MustCompile(`(?i)^(curl|wget)\/`) isHomebrewRe = regexp.MustCompile(`(?i)^homebrew`) diff --git a/handler/handler_execute.go b/handler/handler_execute.go index e300f96..a7f9a97 100644 --- a/handler/handler_execute.go +++ b/handler/handler_execute.go @@ -21,7 +21,7 @@ func (h *Handler) execute(q Query) (QueryResult, error) { cached, ok := h.cache[key] h.cacheMut.Unlock() // cache hit - if ok && h.Config.CacheTTL > 0 && time.Since(cached.Timestamp) < h.Config.CacheTTL { + if ok && time.Since(cached.Timestamp) < cacheTTL { return cached, nil } // do real operation @@ -63,11 +63,9 @@ func (h *Handler) execute(q Query) (QueryResult, error) { M1Asset: assets.HasM1(), } // success store results - if h.Config.CacheTTL > 0 { - h.cacheMut.Lock() - h.cache[key] = result - h.cacheMut.Unlock() - } + h.cacheMut.Lock() + h.cache[key] = result + h.cacheMut.Unlock() return result, nil }