Flex: Composer update don't limit version to expected one

Created on 12 Dec 2018  Â·  16Comments  Â·  Source: symfony/flex

See https://github.com/symfony/symfony/issues/29581

Symfony version(s) affected: 4.0, 4.1 and 4.2

Description
I want to create a project with 4.1.* Symfony version, and don't use any 4.2 package.
Same bug when i want to create a project with 4.0.* Symfony version.
This problem will be the same for 4.2, when 4.3 will be released.

How to reproduce

composer create-project symfony/skeleton sf-4-1-9 4.1.9.1

(I have removed some dependencies to make output more readable)

Installing symfony/skeleton (v4.1.9.1)
  - Installing symfony/skeleton (v4.1.9.1): Downloading (100%)         
Created project in sf-4-1-9
Loading composer repositories with package information
Installing dependencies (including require-dev) from lock file
Package operations: 20 installs, 0 updates, 0 removals
  - Installing symfony/flex (v1.1.8): Loading from cache

Prefetching 11 packages 🎶 💨
  - Downloading (100%)

  - Installing symfony/console (v4.1.9): Loading from cache
  - Installing symfony/dotenv (v4.1.9): Loading from cache
  - Installing symfony/routing (v4.1.9): Loading from cache
  - Installing symfony/http-foundation (v4.1.9): Loading from cache
  - Installing symfony/event-dispatcher (v4.1.9): Loading from cache
  - Installing symfony/debug (v4.1.9): Loading from cache
  - Installing symfony/http-kernel (v4.1.9): Loading from cache
  - Installing symfony/finder (v4.1.9): Loading from cache
  - Installing symfony/filesystem (v4.1.9): Loading from cache
  - Installing symfony/dependency-injection (v4.1.9): Loading from cache
  - Installing symfony/config (v4.1.9): Loading from cache
  - Installing symfony/cache (v4.1.9): Loading from cache
  - Installing symfony/framework-bundle (v4.1.9): Loading from cache
  - Installing symfony/yaml (v4.1.9): Loading from cache

4.1.9 is installed, with it's dependencies as 4.1.9 too.
But if i do this :

rm -rf vendor && composer up

Dependencies i don't have in my composer.json will be installed as latest version (4.2.1 today):

Loading composer repositories with package information
Updating dependencies (including require-dev)
Package operations: 22 installs, 0 updates, 0 removals
  - Installing symfony/flex (v1.1.8): Loading from cache
  - Installing symfony/polyfill-mbstring (v1.10.0): Loading from cache
  - Installing symfony/console (v4.1.9): Loading from cache
  - Installing symfony/dotenv (v4.1.9): Loading from cache
  - Installing symfony/routing (v4.2.1): Loading from cache
  - Installing symfony/http-foundation (v4.2.1): Loading from cache
  - Installing symfony/contracts (v1.0.2): Loading from cache
  - Installing symfony/event-dispatcher (v4.2.1): Loading from cache
  - Installing symfony/debug (v4.2.1): Loading from cache
  - Installing symfony/http-kernel (v4.2.1): Loading from cache
  - Installing symfony/finder (v4.2.1): Loading from cache
  - Installing symfony/filesystem (v4.2.1): Loading from cache
  - Installing symfony/dependency-injection (v4.2.1): Loading from cache
  - Installing symfony/config (v4.2.1): Loading from cache
  - Installing symfony/var-exporter (v4.2.1): Loading from cache
  - Installing symfony/cache (v4.2.1): Loading from cache
  - Installing symfony/framework-bundle (v4.1.9): Loading from cache
  - Installing symfony/yaml (v4.1.9): Loading from cache

I've tried to use extra.symfony.require, but it didn't change anything.

help wanted

Most helpful comment

After installing Flex as global composer plugin, i've found this problem:

Without extra.symfony.require config:

composer up
Loading composer repositories with package information
Updating dependencies (including require-dev)
Package operations: 2 installs, 16 updates, 0 removals
  - Installing symfony/contracts (v1.0.2): Loading from cache
  - Updating symfony/dependency-injection (v4.1.9 => v4.2.1): Loading from cache
  - Updating symfony/doctrine-bridge (v4.1.9 => v4.2.1): Loading from cache
  - Installing symfony/var-exporter (v4.2.1): Loading from cache
  - Updating symfony/cache (v4.1.9 => v4.2.1): Loading from cache
  - Updating symfony/filesystem (v4.1.9 => v4.2.1): Loading from cache
  - Updating symfony/config (v4.1.9 => v4.2.1): Loading from cache
  - Updating symfony/finder (v4.1.9 => v4.2.1): Loading from cache
  - Updating symfony/routing (v4.1.9 => v4.2.1): Loading from cache
  - Updating symfony/options-resolver (v4.1.9 => v4.2.1): Loading from cache
  - Updating symfony/intl (v4.1.9 => v4.2.1): Loading from cache
  - Updating symfony/inflector (v4.1.9 => v4.2.1): Loading from cache
  - Updating symfony/property-access (v4.1.9 => v4.2.1): Loading from cache
  - Updating symfony/http-foundation (v4.1.9 => v4.2.1): Loading from cache
  - Updating symfony/event-dispatcher (v4.1.9 => v4.2.1): Loading from cache
  - Updating symfony/security (v4.1.9 => v4.2.1): Loading from cache
  - Updating symfony/monolog-bridge (v4.1.9 => v4.2.1): Loading from cache
  - Updating symfony/stopwatch (v4.1.9 => v4.2.1): Loading from cache
Writing lock file
Generating autoload files
> steevanb\ComposerOverloadClass\OverloadClass::overload
ocramius/package-versions:  Generating version class...
ocramius/package-versions: ...done generating version class
Symfony recipes are disabled: "symfony/flex" not found in the root composer.json

What about running composer global require symfony/thanks && composer thanks now?
This will spread some 💖  by sending a ★  to the GitHub repositories of your fellow package maintainers.

Executing script cache:clear [OK]
Executing script assets:install --symlink --relative public [OK]

Add extra.symfony.require: 4.1, to downgrade 4.2 installed packages to 4.1

composer up
Loading composer repositories with package information
Updating dependencies (including require-dev)
Restricting packages listed in "symfony/symfony" to "4.1.*"
Nothing to install or update
Writing lock file
Generating autoload files
> steevanb\ComposerOverloadClass\OverloadClass::overload
ocramius/package-versions:  Generating version class...
ocramius/package-versions: ...done generating version class
Symfony recipes are disabled: "symfony/flex" not found in the root composer.json
Executing script cache:clear [OK]
Executing script assets:install --symlink --relative public [OK]

It's not working, 4.2 installed packages are not downgraded to 4.1.

I have to remove vendor dir, just add extra.symfony.require when vendor are already installed as 4.2 (instead of 4.1) is not enough to downgrade 4.2 packages to 4.1.
After removing vendor dir, symfony dependencies are installed in 4.1, it's ok.

Another problem: with Flex as global plugin only, recipes are disabled, as you can see above:

Symfony recipes are disabled: "symfony/flex" not found in the root composer.json

### Finally, to make it works:

  • composer global require symfony/flex ^1.0, to restrict packages who are not listed in your composer.json to the version you want
  • add symfony/flex: ^1.0 in your composer.json, to make recipes work
  • add extra.symfony.require: 4.1.* in your composer.json, not added by default on a lot of symfony/skeleton versions
  • don't do composer up alone, do rm -rf vendor && composer up, or you will not downgrade installed packages to 4.1

All 16 comments

(from linked issue)

This might be solvable at flex level but it's going to be tricky:

  • Flex would need to detect when it has been just installed but didn't yet enforce extra.symfony.require. This should be "relatively simple" in a POST_PACKAGE_INSTALL hook.
  • When this situation occurs, flex could disable all pending operations by calling:
    $composer->getInstallationManager()->addInstaller(new NoopInstaller()); which would terminate the current installation immediately in a proper way.
  • Then, flex should hook into POST_UPDATE_CMD|POST_INSTALL_CMD to restart composer and do a "composer update" internally.

Since that's quite a challenge to implement (writing a composer plugin is always a challenge), I don't expect this to be fixed in the near future. Please prove me wrong ;)

hooking into POST_INSTALL_CMD to run a composer update when composer makes an install from lock looks wrong to me.

sure - the logic should only trigger when an update has been run - either implicitly (ins with no lock) or explicitly.

And in both cases, that's POST_UPDATE_CMD. If composer implicitly runs an update instead of an install due to the missing lock file, it runs the POST_UPDATE scripts

And if composer install plugins before dependencies ? Is should fix the problem no ?

And if composer install plugins before dependencies ? Is should fix the problem no ?

Composer already installs plugins before deps. But that's not enough here as the deps need to be recomputed.

After installing Flex as global composer plugin, i've found this problem:

Without extra.symfony.require config:

composer up
Loading composer repositories with package information
Updating dependencies (including require-dev)
Package operations: 2 installs, 16 updates, 0 removals
  - Installing symfony/contracts (v1.0.2): Loading from cache
  - Updating symfony/dependency-injection (v4.1.9 => v4.2.1): Loading from cache
  - Updating symfony/doctrine-bridge (v4.1.9 => v4.2.1): Loading from cache
  - Installing symfony/var-exporter (v4.2.1): Loading from cache
  - Updating symfony/cache (v4.1.9 => v4.2.1): Loading from cache
  - Updating symfony/filesystem (v4.1.9 => v4.2.1): Loading from cache
  - Updating symfony/config (v4.1.9 => v4.2.1): Loading from cache
  - Updating symfony/finder (v4.1.9 => v4.2.1): Loading from cache
  - Updating symfony/routing (v4.1.9 => v4.2.1): Loading from cache
  - Updating symfony/options-resolver (v4.1.9 => v4.2.1): Loading from cache
  - Updating symfony/intl (v4.1.9 => v4.2.1): Loading from cache
  - Updating symfony/inflector (v4.1.9 => v4.2.1): Loading from cache
  - Updating symfony/property-access (v4.1.9 => v4.2.1): Loading from cache
  - Updating symfony/http-foundation (v4.1.9 => v4.2.1): Loading from cache
  - Updating symfony/event-dispatcher (v4.1.9 => v4.2.1): Loading from cache
  - Updating symfony/security (v4.1.9 => v4.2.1): Loading from cache
  - Updating symfony/monolog-bridge (v4.1.9 => v4.2.1): Loading from cache
  - Updating symfony/stopwatch (v4.1.9 => v4.2.1): Loading from cache
Writing lock file
Generating autoload files
> steevanb\ComposerOverloadClass\OverloadClass::overload
ocramius/package-versions:  Generating version class...
ocramius/package-versions: ...done generating version class
Symfony recipes are disabled: "symfony/flex" not found in the root composer.json

What about running composer global require symfony/thanks && composer thanks now?
This will spread some 💖  by sending a ★  to the GitHub repositories of your fellow package maintainers.

Executing script cache:clear [OK]
Executing script assets:install --symlink --relative public [OK]

Add extra.symfony.require: 4.1, to downgrade 4.2 installed packages to 4.1

composer up
Loading composer repositories with package information
Updating dependencies (including require-dev)
Restricting packages listed in "symfony/symfony" to "4.1.*"
Nothing to install or update
Writing lock file
Generating autoload files
> steevanb\ComposerOverloadClass\OverloadClass::overload
ocramius/package-versions:  Generating version class...
ocramius/package-versions: ...done generating version class
Symfony recipes are disabled: "symfony/flex" not found in the root composer.json
Executing script cache:clear [OK]
Executing script assets:install --symlink --relative public [OK]

It's not working, 4.2 installed packages are not downgraded to 4.1.

I have to remove vendor dir, just add extra.symfony.require when vendor are already installed as 4.2 (instead of 4.1) is not enough to downgrade 4.2 packages to 4.1.
After removing vendor dir, symfony dependencies are installed in 4.1, it's ok.

Another problem: with Flex as global plugin only, recipes are disabled, as you can see above:

Symfony recipes are disabled: "symfony/flex" not found in the root composer.json

### Finally, to make it works:

  • composer global require symfony/flex ^1.0, to restrict packages who are not listed in your composer.json to the version you want
  • add symfony/flex: ^1.0 in your composer.json, to make recipes work
  • add extra.symfony.require: 4.1.* in your composer.json, not added by default on a lot of symfony/skeleton versions
  • don't do composer up alone, do rm -rf vendor && composer up, or you will not downgrade installed packages to 4.1

I am trying to limit my application in Symfony 4.1.* but it is not working as expected.

"extra": {
        "symfony": {
            "allow-contrib": false,
            "require": "4.1.*"
        }
    }

I had removed vendor dir, added extra.symfony.require : 4.1.* and then ran composer update. This is output

Loading composer repositories with package information
Updating dependencies (including require-dev)
Package operations: 227 installs, 0 updates, 0 removals
  - Installing symfony/flex (v1.1.8): Loading from cache
  - Installing ocramius/package-versions (1.3.0): Loading from cache
  - Installing doctrine/common (v2.10.0): Loading from cache
  - Installing symfony/inflector (v4.2.2): Loading from cache
  - Installing symfony/http-foundation (v4.2.2): Loading from cache
  - Installing symfony/contracts (v1.0.2): Loading from cache
  - Installing symfony/event-dispatcher (v4.2.2): Loading from cache
  - Installing symfony/debug (v4.2.2): Loading from cache
  - Installing symfony/property-access (v4.2.2): Loading from cache
  - Installing symfony/http-kernel (v4.2.2): Loading from cache
  - Installing symfony/security-core (v4.2.2): Loading from cache
  - Installing symfony/intl (v4.2.2): Loading from cache

Clearly its impossible to limit Symfony version to 4.1. Worse, we are not able to update any other packages as well because if we do composer update it will update symfony too.

@hanishsingla Take care, if you remove vendor dir: you have to install Flex as global too, not only for your project.

composer global require symfony/flex ^1.0, to restrict packages who are not listed in your composer.json to the version you want
add symfony/flex: ^1.0 in your composer.json, to make recipes work

It worked now. Had to do step exactly like @steevanb mentioned. Specially, (don't do composer up alone, do rm -rf vendor && composer up)

Let's wait for next release of flex and hopefully this issue will be fixed soon.

Any news about this bug ?

Status: help wanted - ie PR welcome if anyone is willing to give it a try.
See https://github.com/symfony/flex/issues/450#issuecomment-446743049

@nicolas-grekas I have tried to use 'extra' in a symfony-pack file, but it seems to get ignored. According to composer documentation, extra is not required to be in the root composer.json file.

Do I understand this correctly? Shouldn't it work from non root composer.json file or is this a bug?

Here is example:

"extra": { "symfony": { "allow-contrib": true, "require": "4.1.*" } }

Symfony 4.2.7 was installed and the allow-contrib was also ignored as I was prompted to ok contrib recipes install.

Kind regards, Dave Spiegel

@dspiegel this extra has to be in the root composer.json

Regarding the downgrading not working, I suspect that this is related to the fact that Composer 1.x injects installed packages as candidates in the solver too (Composer 2 will not do that anymore).
As Flex works by restricting candidates coming from registered repositories to only include whitelisted versions, this won't help if the installed package is at a higher version, as it will get selected.

Fixed in #520

Was this page helpful?
0 / 5 - 0 ratings