From 3eab5f940c920862a03d18b94141aa27b949cd47 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 2 Apr 2026 12:38:14 +0000 Subject: [PATCH 1/3] Initial plan From cb9fec76184d740f44dda2711dbd0bf733dfbfb9 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 2 Apr 2026 12:42:12 +0000 Subject: [PATCH 2/3] fix: wait for completion cache update before cleanup Agent-Logs-Url: https://github.com/Jguer/yay/sessions/a5e04c9b-5487-45ce-89ae-1d649ec8eb4d Co-authored-by: Jguer <8071073+Jguer@users.noreply.github.com> --- pkg/sync/sync.go | 38 +++++++++++--- pkg/sync/sync_test.go | 114 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 144 insertions(+), 8 deletions(-) create mode 100644 pkg/sync/sync_test.go diff --git a/pkg/sync/sync.go b/pkg/sync/sync.go index 4d0e27a1a..2b597160a 100644 --- a/pkg/sync/sync.go +++ b/pkg/sync/sync.go @@ -2,6 +2,7 @@ package sync import ( "context" + "sync" "github.com/Jguer/yay/v12/pkg/completion" "github.com/Jguer/yay/v12/pkg/db" @@ -18,6 +19,11 @@ import ( "github.com/leonelquinteros/gotext" ) +var ( + completionNeedsUpdate = completion.NeedsUpdate + completionUpdateCache = completion.UpdateCache +) + type OperationService struct { ctx context.Context cfg *settings.Configuration @@ -69,14 +75,9 @@ func (o *OperationService) Run(ctx context.Context, run *runtime.Runtime, installer.AddPostInstallHook(cleanAURDirsFunc) } - if completion.NeedsUpdate(o.cfg.CompletionPath, o.cfg.CompletionInterval, false) { - go func() { - errComp := completion.UpdateCache(ctx, run.HTTPClient, o.dbExecutor, - o.cfg.AURURL, o.cfg.CompletionPath, o.logger) - if errComp != nil { - o.logger.Warnln(errComp) - } - }() + waitForCompletionUpdate := o.startCompletionUpdate(ctx, run) + if waitForCompletionUpdate != nil { + defer waitForCompletionUpdate() } srcInfo, errInstall := srcinfo.NewService(o.dbExecutor, o.cfg, @@ -123,6 +124,27 @@ func (o *OperationService) Run(ctx context.Context, run *runtime.Runtime, return multiErr.Return() } +func (o *OperationService) startCompletionUpdate(ctx context.Context, run *runtime.Runtime) func() { + if !completionNeedsUpdate(o.cfg.CompletionPath, o.cfg.CompletionInterval, false) { + return nil + } + + var wg sync.WaitGroup + wg.Add(1) + + go func() { + defer wg.Done() + + errComp := completionUpdateCache(ctx, run.HTTPClient, o.dbExecutor, + o.cfg.AURURL, o.cfg.CompletionPath, o.logger) + if errComp != nil { + o.logger.Warnln(errComp) + } + }() + + return wg.Wait +} + func (o *OperationService) manualConfirmRequired(cmdArgs *parser.Arguments) bool { return (!cmdArgs.ExistsArg("u", "sysupgrade") && cmdArgs.Op != "Y") || o.cfg.DoubleConfirm } diff --git a/pkg/sync/sync_test.go b/pkg/sync/sync_test.go new file mode 100644 index 000000000..89594af3b --- /dev/null +++ b/pkg/sync/sync_test.go @@ -0,0 +1,114 @@ +//go:build !integration +// +build !integration + +package sync + +import ( + "context" + "io" + "net/http" + "strings" + "testing" + "time" + + "github.com/Jguer/yay/v12/pkg/completion" + "github.com/Jguer/yay/v12/pkg/download" + "github.com/Jguer/yay/v12/pkg/runtime" + "github.com/Jguer/yay/v12/pkg/settings" + "github.com/Jguer/yay/v12/pkg/text" + + "github.com/stretchr/testify/require" +) + +func TestStartCompletionUpdateSkipsWhenCacheIsFresh(t *testing.T) { + originalNeedsUpdate := completionNeedsUpdate + originalUpdateCache := completionUpdateCache + t.Cleanup(func() { + completionNeedsUpdate = originalNeedsUpdate + completionUpdateCache = originalUpdateCache + }) + + updateCalled := false + completionNeedsUpdate = func(string, int, bool) bool { return false } + completionUpdateCache = func(context.Context, download.HTTPRequestDoer, completion.PkgSynchronizer, + string, string, *text.Logger, + ) error { + updateCalled = true + return nil + } + + service, run := newTestOperationService() + + wait := service.startCompletionUpdate(context.Background(), run) + + require.Nil(t, wait) + require.False(t, updateCalled) +} + +func TestStartCompletionUpdateWaitsForBackgroundUpdate(t *testing.T) { + originalNeedsUpdate := completionNeedsUpdate + originalUpdateCache := completionUpdateCache + t.Cleanup(func() { + completionNeedsUpdate = originalNeedsUpdate + completionUpdateCache = originalUpdateCache + }) + + started := make(chan struct{}) + release := make(chan struct{}) + + completionNeedsUpdate = func(string, int, bool) bool { return true } + completionUpdateCache = func(context.Context, download.HTTPRequestDoer, completion.PkgSynchronizer, + string, string, *text.Logger, + ) error { + close(started) + <-release + return nil + } + + service, run := newTestOperationService() + + wait := service.startCompletionUpdate(context.Background(), run) + require.NotNil(t, wait) + + select { + case <-started: + case <-time.After(time.Second): + t.Fatal("completion update did not start") + } + + waitReturned := make(chan struct{}) + go func() { + wait() + close(waitReturned) + }() + + select { + case <-waitReturned: + t.Fatal("wait returned before completion update finished") + case <-time.After(50 * time.Millisecond): + } + + close(release) + + select { + case <-waitReturned: + case <-time.After(time.Second): + t.Fatal("wait did not return after completion update finished") + } +} + +func newTestOperationService() (*OperationService, *runtime.Runtime) { + cfg := &settings.Configuration{ + AURURL: "https://aur.archlinux.org", + CompletionPath: "/tmp/completion", + CompletionInterval: 7, + } + logger := text.NewLogger(io.Discard, io.Discard, strings.NewReader(""), false, "test") + run := &runtime.Runtime{ + Cfg: cfg, + HTTPClient: &http.Client{}, + Logger: logger, + } + + return NewOperationService(context.Background(), nil, run), run +} From 04c2d711fc241ebc6a29bf01d88621ce88913508 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 2 Apr 2026 12:43:59 +0000 Subject: [PATCH 3/3] test: clarify sync wait timing constant Agent-Logs-Url: https://github.com/Jguer/yay/sessions/a5e04c9b-5487-45ce-89ae-1d649ec8eb4d Co-authored-by: Jguer <8071073+Jguer@users.noreply.github.com> --- pkg/sync/sync_test.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pkg/sync/sync_test.go b/pkg/sync/sync_test.go index 89594af3b..5781cb999 100644 --- a/pkg/sync/sync_test.go +++ b/pkg/sync/sync_test.go @@ -20,6 +20,8 @@ import ( "github.com/stretchr/testify/require" ) +const waitGracePeriod = 50 * time.Millisecond + func TestStartCompletionUpdateSkipsWhenCacheIsFresh(t *testing.T) { originalNeedsUpdate := completionNeedsUpdate originalUpdateCache := completionUpdateCache @@ -85,7 +87,7 @@ func TestStartCompletionUpdateWaitsForBackgroundUpdate(t *testing.T) { select { case <-waitReturned: t.Fatal("wait returned before completion update finished") - case <-time.After(50 * time.Millisecond): + case <-time.After(waitGracePeriod): } close(release)