diff --git a/controller/appcontroller.go b/controller/appcontroller.go index 2b5b9e0801..9a80c4998d 100644 --- a/controller/appcontroller.go +++ b/controller/appcontroller.go @@ -1508,8 +1508,18 @@ func (ctrl *ApplicationController) processRequestedAppOperation(app *appv1.Appli // if we just completed an operation, force a refresh so that UI will report up-to-date // sync/health information if _, err := cache.MetaNamespaceKeyFunc(app); err == nil { - // force app refresh with using CompareWithLatest comparison type and trigger app reconciliation loop - ctrl.requestAppRefresh(app.QualifiedName(), CompareWithLatestForceResolve.Pointer(), nil) + var compareWith CompareWith + if state.Operation.InitiatedBy.Automated { + // Do not force revision resolution on automated operations because + // this would cause excessive Ls-Remote requests on monorepo commits + compareWith = CompareWithLatest + } else { + // Force app refresh with using most recent resolved revision after sync, + // so UI won't show a just synced application being out of sync if it was + // synced after commit but before app. refresh (see #18153) + compareWith = CompareWithLatestForceResolve + } + ctrl.requestAppRefresh(app.QualifiedName(), compareWith.Pointer(), nil) } else { logCtx.Warnf("Fails to requeue application: %v", err) } diff --git a/controller/appcontroller_test.go b/controller/appcontroller_test.go index 95079ec10a..723eb1f8e7 100644 --- a/controller/appcontroller_test.go +++ b/controller/appcontroller_test.go @@ -2321,6 +2321,41 @@ func TestProcessRequestedAppOperation_Successful(t *testing.T) { assert.Equal(t, CompareWithLatestForceResolve, level) } +func TestProcessRequestedAppAutomatedOperation_Successful(t *testing.T) { + app := newFakeApp() + app.Spec.Project = "default" + app.Operation = &v1alpha1.Operation{ + Sync: &v1alpha1.SyncOperation{}, + InitiatedBy: v1alpha1.OperationInitiator{ + Automated: true, + }, + } + ctrl := newFakeController(&fakeData{ + apps: []runtime.Object{app, &defaultProj}, + manifestResponses: []*apiclient.ManifestResponse{{ + Manifests: []string{}, + }}, + }, nil) + fakeAppCs := ctrl.applicationClientset.(*appclientset.Clientset) + receivedPatch := map[string]any{} + fakeAppCs.PrependReactor("patch", "*", func(action kubetesting.Action) (handled bool, ret runtime.Object, err error) { + if patchAction, ok := action.(kubetesting.PatchAction); ok { + require.NoError(t, json.Unmarshal(patchAction.GetPatch(), &receivedPatch)) + } + return true, &v1alpha1.Application{}, nil + }) + + ctrl.processRequestedAppOperation(app) + + phase, _, _ := unstructured.NestedString(receivedPatch, "status", "operationState", "phase") + message, _, _ := unstructured.NestedString(receivedPatch, "status", "operationState", "message") + assert.Equal(t, string(synccommon.OperationSucceeded), phase) + assert.Equal(t, "successfully synced (no more tasks)", message) + ok, level := ctrl.isRefreshRequested(ctrl.toAppKey(app.Name)) + assert.True(t, ok) + assert.Equal(t, CompareWithLatest, level) +} + func TestProcessRequestedAppOperation_SyncTimeout(t *testing.T) { testCases := []struct { name string