From 99888d6f6b50f8688db6f1e1f14cb61e337019f5 Mon Sep 17 00:00:00 2001 From: Martin Herndl <martin.herndl@icis.com> Date: Thu, 23 Dec 2021 09:12:14 +0100 Subject: [PATCH] feat(composer): ignore extension and library platform requirements only (#13154) Co-authored-by: Michael Kriese <michael.kriese@visualon.de> Co-authored-by: HonkingGoose <34918129+HonkingGoose@users.noreply.github.com> --- docs/usage/configuration-options.md | 10 ++- lib/manager/composer/artifacts.ts | 5 +- lib/manager/composer/utils.spec.ts | 97 ++++++++++++++++++++++------- lib/manager/composer/utils.ts | 12 +++- 4 files changed, 95 insertions(+), 29 deletions(-) diff --git a/docs/usage/configuration-options.md b/docs/usage/configuration-options.md index 54759d9f5c..5e98e7db6d 100644 --- a/docs/usage/configuration-options.md +++ b/docs/usage/configuration-options.md @@ -363,10 +363,14 @@ The "topic" is usually refers to the dependency being updated, e.g. `"dependency ## composerIgnorePlatformReqs -By default, Renovate will run Composer with `--ignore-platform-reqs` as the PHP platform used by Renovate most probably won't match with the required PHP environment of your project as configured in your `composer.json` file. -However, this also means that all platform constraints (including PHP version) will be ignored by default, which can result in updated dependencies that are not compatible with your platform. +By default, Renovate will ignore Composer platform requirements as the PHP platform used by Renovate most probably won't match the required PHP environment of your project as configured in your `composer.json` file. -To solve this, you should configure explicit ignored platform requirements (for example `ext-zip`) by setting them separately in this array. +Composer `2.2` and up will be run with `--ignore-platform-req='ext-*' --ignore-platform-req='lib-*'`, which ignores extension and library platform requirements but not the PHP version itself and should work in most cases. + +Older Composer versions will be run with `--ignore-platform-reqs`, which means that all platform constraints (including the PHP version) will be ignored by default. +This can result in updated dependencies that are not compatible with your platform. + +To customize this behaviour, you can explicitly ignore platform requirements (for example `ext-zip`) by setting them separately in this array. Each item will be added to the Composer command with `--ignore-platform-req`, resulting in it being ignored during its invocation. Note that this requires your project to use Composer V2, as V1 doesn't support excluding single platform requirements. The used PHP version will be guessed automatically from your `composer.json` definition, so `php` should not be added as explicit dependency. diff --git a/lib/manager/composer/artifacts.ts b/lib/manager/composer/artifacts.ts index 78cfa37d0f..4e6c0c6489 100644 --- a/lib/manager/composer/artifacts.ts +++ b/lib/manager/composer/artifacts.ts @@ -125,7 +125,8 @@ export async function updateArtifacts({ // Determine whether install is required before update if (requireComposerDependencyInstallation(existingLockFile)) { const preCmd = 'composer'; - const preArgs = 'install' + getComposerArguments(config); + const preArgs = + 'install' + getComposerArguments(config, composerToolConstraint); logger.debug({ preCmd, preArgs }, 'composer pre-update command'); commands.push(`${preCmd} ${preArgs}`); } @@ -140,7 +141,7 @@ export async function updateArtifacts({ 'update ' + updatedDeps.map((dep) => quote(dep.depName)).join(' ') ).trim() + ' --with-dependencies'; } - args += getComposerArguments(config); + args += getComposerArguments(config, composerToolConstraint); logger.debug({ cmd, args }, 'composer command'); commands.push(`${cmd} ${args}`); diff --git a/lib/manager/composer/utils.spec.ts b/lib/manager/composer/utils.spec.ts index 76b3efb091..563ff991fc 100644 --- a/lib/manager/composer/utils.spec.ts +++ b/lib/manager/composer/utils.spec.ts @@ -62,33 +62,80 @@ describe('manager/composer/utils', () => { }); it('disables scripts and plugins by default', () => { - expect(getComposerArguments({})).toBe( + expect( + getComposerArguments({}, { toolName: 'composer', constraint: '1.*' }) + ).toBe( ' --no-ansi --no-interaction --no-scripts --no-autoloader --no-plugins' ); }); it('disables platform requirements', () => { expect( - getComposerArguments({ - composerIgnorePlatformReqs: [], - }) + getComposerArguments( + { + composerIgnorePlatformReqs: [], + }, + { toolName: 'composer', constraint: '1.*' } + ) + ).toBe( + ' --ignore-platform-reqs --no-ansi --no-interaction --no-scripts --no-autoloader --no-plugins' + ); + }); + it('disables all platform requirements with 2.1.0', () => { + expect( + getComposerArguments( + { + composerIgnorePlatformReqs: [], + }, + { toolName: 'composer', constraint: '2.1.0' } + ) ).toBe( ' --ignore-platform-reqs --no-ansi --no-interaction --no-scripts --no-autoloader --no-plugins' ); }); + it('disables only extension and library platform requirements with 2.2.0', () => { + expect( + getComposerArguments( + { + composerIgnorePlatformReqs: [], + }, + { toolName: 'composer', constraint: '2.2.0' } + ) + ).toBe( + " --ignore-platform-req='ext-*' --ignore-platform-req='lib-*' --no-ansi --no-interaction --no-scripts --no-autoloader --no-plugins" + ); + }); + it('disables only extension and library platform requirements with ^2.2', () => { + expect( + getComposerArguments( + { + composerIgnorePlatformReqs: [], + }, + { toolName: 'composer', constraint: '^2.2' } + ) + ).toBe( + " --ignore-platform-req='ext-*' --ignore-platform-req='lib-*' --no-ansi --no-interaction --no-scripts --no-autoloader --no-plugins" + ); + }); it('disables single platform requirement', () => { expect( - getComposerArguments({ - composerIgnorePlatformReqs: ['ext-intl'], - }) + getComposerArguments( + { + composerIgnorePlatformReqs: ['ext-intl'], + }, + { toolName: 'composer', constraint: '1.*' } + ) ).toBe( ' --ignore-platform-req ext-intl --no-ansi --no-interaction --no-scripts --no-autoloader --no-plugins' ); }); it('disables multiple platform requirement', () => { expect( - getComposerArguments({ - composerIgnorePlatformReqs: ['ext-intl', 'ext-icu'], - }) + getComposerArguments( + { + composerIgnorePlatformReqs: ['ext-intl', 'ext-icu'], + }, + { toolName: 'composer', constraint: '1.*' } + ) ).toBe( ' --ignore-platform-req ext-intl --ignore-platform-req ext-icu --no-ansi --no-interaction --no-scripts --no-autoloader --no-plugins' ); @@ -97,18 +144,21 @@ describe('manager/composer/utils', () => { GlobalConfig.set({ allowScripts: true, }); - expect(getComposerArguments({})).toBe( - ' --no-ansi --no-interaction --no-plugins' - ); + expect( + getComposerArguments({}, { toolName: 'composer', constraint: '1.*' }) + ).toBe(' --no-ansi --no-interaction --no-plugins'); }); it('disables scripts when configured locally', () => { GlobalConfig.set({ allowScripts: true, }); expect( - getComposerArguments({ - ignoreScripts: true, - }) + getComposerArguments( + { + ignoreScripts: true, + }, + { toolName: 'composer', constraint: '1.*' } + ) ).toBe( ' --no-ansi --no-interaction --no-scripts --no-autoloader --no-plugins' ); @@ -117,18 +167,21 @@ describe('manager/composer/utils', () => { GlobalConfig.set({ allowPlugins: true, }); - expect(getComposerArguments({})).toBe( - ' --no-ansi --no-interaction --no-scripts --no-autoloader' - ); + expect( + getComposerArguments({}, { toolName: 'composer', constraint: '1.*' }) + ).toBe(' --no-ansi --no-interaction --no-scripts --no-autoloader'); }); it('disables plugins when configured locally', () => { GlobalConfig.set({ allowPlugins: true, }); expect( - getComposerArguments({ - ignorePlugins: true, - }) + getComposerArguments( + { + ignorePlugins: true, + }, + { toolName: 'composer', constraint: '1.*' } + ) ).toBe( ' --no-ansi --no-interaction --no-scripts --no-autoloader --no-plugins' ); diff --git a/lib/manager/composer/utils.ts b/lib/manager/composer/utils.ts index 98f34bf2b4..d17fe86a9d 100644 --- a/lib/manager/composer/utils.ts +++ b/lib/manager/composer/utils.ts @@ -1,6 +1,7 @@ import { quote } from 'shlex'; import { GlobalConfig } from '../../config/global'; import { logger } from '../../logger'; +import type { ToolConstraint } from '../../util/exec/types'; import { api, id as composerVersioningId } from '../../versioning/composer'; import type { UpdateArtifactsConfig } from '../types'; import type { ComposerConfig, ComposerLock } from './types'; @@ -9,12 +10,19 @@ export { composerVersioningId }; const depRequireInstall = new Set(['symfony/flex']); -export function getComposerArguments(config: UpdateArtifactsConfig): string { +export function getComposerArguments( + config: UpdateArtifactsConfig, + toolConstraint: ToolConstraint +): string { let args = ''; if (config.composerIgnorePlatformReqs) { if (config.composerIgnorePlatformReqs.length === 0) { - args += ' --ignore-platform-reqs'; + const major = api.getMajor(toolConstraint.constraint); + const minor = api.getMinor(toolConstraint.constraint); + args += api.matches(`${major}.${minor}`, '^2.2') + ? " --ignore-platform-req='ext-*' --ignore-platform-req='lib-*'" + : ' --ignore-platform-reqs'; } else { config.composerIgnorePlatformReqs.forEach((req) => { args += ' --ignore-platform-req ' + quote(req); -- GitLab