From d0976b49be5566dae4fbdc91bd803262e69ecd3c Mon Sep 17 00:00:00 2001
From: Stefano Arlandini <sarlandini@alice.it>
Date: Wed, 12 Mar 2025 12:20:27 +0100
Subject: [PATCH] feat(manager/composer): support updates with minimal changes
 (#34218)

Co-authored-by: Rhys Arkins <rhys@arkins.net>
---
 lib/modules/manager/composer/artifacts.ts  |  3 +-
 lib/modules/manager/composer/utils.spec.ts | 43 ++++++++++++++++++++++
 lib/modules/manager/composer/utils.ts      | 16 ++++++++
 3 files changed, 61 insertions(+), 1 deletion(-)

diff --git a/lib/modules/manager/composer/artifacts.ts b/lib/modules/manager/composer/artifacts.ts
index 23dba464cd..06cccb1f26 100644
--- a/lib/modules/manager/composer/artifacts.ts
+++ b/lib/modules/manager/composer/artifacts.ts
@@ -33,6 +33,7 @@ import type { AuthJson } from './types';
 import {
   extractConstraints,
   getComposerArguments,
+  getComposerUpdateArguments,
   getPhpConstraint,
   isArtifactAuthEnabled,
   requireComposerDependencyInstallation,
@@ -191,7 +192,7 @@ export async function updateArtifacts({
             .join(' ')
         ).trim() + ' --with-dependencies';
     }
-    args += getComposerArguments(config, composerToolConstraint);
+    args += getComposerUpdateArguments(config, composerToolConstraint);
     logger.trace({ cmd, args }, 'composer command');
     commands.push(`${cmd} ${args}`);
 
diff --git a/lib/modules/manager/composer/utils.spec.ts b/lib/modules/manager/composer/utils.spec.ts
index f779434481..ac67489ac6 100644
--- a/lib/modules/manager/composer/utils.spec.ts
+++ b/lib/modules/manager/composer/utils.spec.ts
@@ -5,6 +5,7 @@ import { Lockfile, PackageFile } from './schema';
 import {
   extractConstraints,
   getComposerArguments,
+  getComposerUpdateArguments,
   requireComposerDependencyInstallation,
 } from './utils';
 
@@ -316,6 +317,48 @@ describe('modules/manager/composer/utils', () => {
     });
   });
 
+  describe('getComposerUpdateArguments', () => {
+    it.each`
+      constraint
+      ${'2.6.0'}
+      ${'<=2.6'}
+      ${'<2.7'}
+    `(
+      'does not request an update with minimal changes with $constraint',
+      ({ constraint }) => {
+        expect(
+          getComposerUpdateArguments({}, { toolName: 'composer', constraint }),
+        ).toBe(
+          ' --no-ansi --no-interaction --no-scripts --no-autoloader --no-plugins',
+        );
+      },
+    );
+
+    it.each`
+      constraint
+      ${'2.7.0'}
+      ${'2.8.0'}
+      ${'3.0.0'}
+      ${'^2.6'}
+      ${'^2.7'}
+      ${'^2.8'}
+      ${'^3.0'}
+      ${'>=2.6'}
+      ${'>=2.7'}
+      ${'>=2.8'}
+      ${'>=3.0'}
+    `(
+      'requests an update with minimal changes with $constraint',
+      ({ constraint }) => {
+        expect(
+          getComposerUpdateArguments({}, { toolName: 'composer', constraint }),
+        ).toBe(
+          ' --no-ansi --no-interaction --no-scripts --no-autoloader --no-plugins --minimal-changes',
+        );
+      },
+    );
+  });
+
   describe('requireComposerDependencyInstallation', () => {
     it('returns true when symfony/flex has been installed', () => {
       const lockfile = Lockfile.parse({
diff --git a/lib/modules/manager/composer/utils.ts b/lib/modules/manager/composer/utils.ts
index 2e6a2d1a3c..0a0c53ad1d 100644
--- a/lib/modules/manager/composer/utils.ts
+++ b/lib/modules/manager/composer/utils.ts
@@ -49,6 +49,22 @@ export function getComposerArguments(
   return args;
 }
 
+export function getComposerUpdateArguments(
+  config: UpdateArtifactsConfig,
+  toolConstraint: ToolConstraint,
+): string {
+  let args = getComposerArguments(config, toolConstraint);
+
+  if (
+    is.string(toolConstraint.constraint) &&
+    api.intersects!(toolConstraint.constraint, '>=2.7')
+  ) {
+    args += ' --minimal-changes';
+  }
+
+  return args;
+}
+
 export function getPhpConstraint(
   constraints: Record<string, string>,
 ): string | null {
-- 
GitLab