wikimedia / composer-merge-plugin

Merge one or more additional composer.json files at Composer runtime
MIT License
933 stars 161 forks source link

Add support for Composer v2 #189

Closed mcaskill closed 3 years ago

mcaskill commented 3 years ago

Latest Update 2021-01-25 14:25 EST

Notes:

  1. Avoid unnecessary issue bumping. Please test this pull request and report issues or approve working state.
    See comment.
  2. Stability of this pull request's changeset needs additional testing from the community, such as unintentional updates upon requiring this plugin.
    See comment.
  3. Project is stuck in the process of transferring ownership to a new team within the Wikimedia Foundation.
    See comment.
  4. Since v1.4 of the plugin does not support Composer v2, the plugin is ignored when updating to v1.5. As a result, Composer is unaware of the merged dependencies and removes them. Either update the plugin with Composer v1 first or run composer update again.
    See comment.
  5. Unintentional lock file updates caused by isFirstInstall() on Composer v2. Listening only to post-update-cmd instead of post-install-cmd might be more sensible. See comment and followup.
  6. Please be patient, we don't want to push broken changes.

How to test:

{
    "repositories": [
        {
            "type":"vcs",
            "url":"https://github.com/mcaskill/composer-merge-plugin"
        }
    ],
    "require": {
        "wikimedia/composer-merge-plugin": "dev-feature/composer-v2 as 1.5.0"
    }
}

Tasks:


Replaces #187, #185, as fix for #184 (as per composer/composer#8726)

I've tested using the example and the unit tests and it works in Composer v1 and v2.

This alternative proposal forgoes PRE_DEPENDENCIES_SOLVING entirely to avoid over-complicating the codebase and streamline the plugin's merge logic.

The issue with the previous attempts (as well as attempts on other plugins) stems from a miscommunication from the Composer team of the major difference in how v1 and v2 resolve and install dependencies.

[…] Roughly speaking:

  • Composer v1: Composer resolves dependencies (dispatching *_DEPENDENCIES_SOLVING), iterates packages (while dispatching PRE_PACKAGE_* before PRE_FILE_DOWNLOAD), then finally writes the lock file.
  • Composer v2: Composer resolves dependencies (dispatching PRE_POOL_CREATE), writes the lock file, dispatches PRE_OPERATIONS_EXEC, downloads the packages (while dispatching PRE_FILE_DOWNLOAD), then iterates packages (while dispatching PRE_PACKAGE_*).

In particular, people are assuming that PRE_OPERATIONS_EXEC is a replacement for PRE_DEPENDENCIES_SOLVING since they are dispatched in a similar-looking routine. The closest event to the latter would actually be PRE_POOL_CREATE. — composer/composer#8726

Back to my proposal.

Currently, the use of PRE_DEPENDENCIES_SOLVING in the plugin is used to inject duplicate requirements (usually with a different version constraints) into the solver (while distinguishing between require and require-dev).

What I propose replaces the duplicate links tracking in ExtraPackage by back porting Composer 2's new static MultiConstraint::create() method which can be used to resolve complex-constraints early on. In turn, this makes the need for PRE_DEPENDENCIES_SOLVING obsolete.

I hope this PR will, at the very least, help to figure out how to support Composer v2.

Seldaek commented 3 years ago

@b-sharpe that's a tricky problem to solve I think unless the plugin stops listening to post-install-cmd entirely, but that would then not work for people only running composer install (which without a lock file is the equivalent of an update). Maybe Composer should in that case trigger a post-update-cmd instead as that's kinda what is happening, we need to think about this some more. But even if we'd fix that I am not entirely sure things would work 100%.

Seldaek commented 3 years ago

Ah never mind, composer already triggers post-update-cmd if there is no lock file and you run install, so yes listening only to post-update-cmd might be more sensible here.

lvidal1 commented 3 years ago

Hi there,

I am getting the following error after 3 successful cloning process.

 [RuntimeException]                                                                                                                                                       
  Failed to execute git clone --mirror 'git@github.com:mcaskill/composer-merge-plugin.git' '/root/.composer/cache/vcs/git-github.com-mcaskill-composer-merge-plugin.git/'  

  Cloning into bare repository '/root/.composer/cache/vcs/git-github.com-mcaskill-composer-merge-plugin.git'...                                                            
  error: cannot run ssh: No such file or directory                                                                                                                         
  fatal: unable to fork 
reedy commented 3 years ago

Hi there,

I am getting the following error after 3 successful cloning process.

 [RuntimeException]                                                                                                                                                       
  Failed to execute git clone --mirror 'git@github.com:mcaskill/composer-merge-plugin.git' '/root/.composer/cache/vcs/git-github.com-mcaskill-composer-merge-plugin.git/'  

  Cloning into bare repository '/root/.composer/cache/vcs/git-github.com-mcaskill-composer-merge-plugin.git'...                                                            
  error: cannot run ssh: No such file or directory                                                                                                                         
  fatal: unable to fork 

This is almost certainly a local issue to you. Plenty of results if you have a google - https://rtfm.co.ua/en/git-git-clone-fatal-unable-to-fork-and-rsa-key-fingerprint/ for example

yalsicor commented 3 years ago

Hi there,

My setup breaks after updating to composer v2.0.8 and using the proposed pull-request from this thread:

Running composer update to apply merge settings
Loading composer repositories with package information
PHP Fatal error:  Uncaught TypeError: Argument 4 passed to Composer\Repository\Vcs\VcsDriver::__construct() must be an instance of Composer\Util\ProcessExecutor or null, instance of Composer\Util\HttpDownloader given, called in phar:///usr/local/bin/composer/src/Composer/Repository/VcsRepository.php on line 121 and defined in /home/yalsicor/data/html/project_base/vendor/composer/composer/src/Composer/Repository/Vcs/VcsDriver.php:60
Stack trace:
#0 phar:///usr/local/bin/composer/src/Composer/Repository/VcsRepository.php(121): Composer\Repository\Vcs\VcsDriver->__construct()
#1 phar:///usr/local/bin/composer/src/Composer/Repository/VcsRepository.php(160): Composer\Repository\VcsRepository->getDriver()
#2 phar:///usr/local/bin/composer/src/Composer/Repository/ArrayRepository.php(266): Composer\Repository\VcsRepository->initialize()
#3 phar:///usr/local/bin/composer/src/Composer/Repository/ArrayRepository.php(55): Composer\Repository\ArrayRepository->getPackages()
#4 phar:///usr/local/bin/composer/src/Composer/ in /home/spokk/data/html/openforests/of_projectexplorer_base/vendor/composer/composer/src/Composer/Repository/Vcs/VcsDriver.php on line 60

This only happens when I start without a vendor folder. The second time I do composer install, it doesn't throw the error. Since I use a PaaS for hosting, unfortunately this happens for every deployment, breaking it.

Is this related to the merge plugin or some other composer bug?

Edit: With composer debug (-vvv):

Loading plugin apiato\installer\ContainerInstallerPlugin_composer_tmp0
Loading plugin Wikimedia\Composer\MergePlugin_composer_tmp1
> init: Wikimedia\Composer\MergePlugin_composer_tmp1->onInit
  [merge-plugin] Loading app/Ship/composer.json...
  [merge-plugin] Prepending vcs repository
  [merge-plugin] Prepending vcs repository
  [merge-plugin] Prepending vcs repository
  [merge-plugin] Adding apiato/core
  [merge-plugin] Loading app/Containers/Authentication/composer.json...
  [merge-plugin] Adding laravel/passport
  [merge-plugin] Loading app/Containers/Authorization/composer.json...
  [merge-plugin] Adding spatie/laravel-permission
  [merge-plugin] Loading app/Containers/Debugger/composer.json...
  [merge-plugin] Merging jenssegers/agent
  [merge-plugin] Loading app/Containers/Documentation/composer.json...
  [merge-plugin] Loading app/Containers/Feedback/composer.json...
  [merge-plugin] Loading app/Containers/Geo/composer.json...
  [merge-plugin] Adding mstaack/laravel-postgis
  [merge-plugin] Loading app/Containers/Localization/composer.json...
  [merge-plugin] Adding ext-intl
  [merge-plugin] Loading app/Containers/Logging/composer.json...
  [merge-plugin] Adding spatie/laravel-activitylog
  [merge-plugin] Loading app/Containers/Media/composer.json...
  [merge-plugin] Adding intervention/image
  [merge-plugin] Loading app/Containers/Payment/composer.json...
  [merge-plugin] Loading app/Containers/Settings/composer.json...
  [merge-plugin] Loading app/Containers/SocialAuth/composer.json...
  [merge-plugin] Adding laravel/socialite
  [merge-plugin] Loading app/Containers/Stripe/composer.json...
  [merge-plugin] Adding cartalyst/stripe-laravel
  [merge-plugin] Loading app/Containers/User/composer.json...
  [merge-plugin] Loading app/Containers/Welcome/composer.json...
  [merge-plugin] Loading app/Containers/composer.json...
> pre-update-cmd: Wikimedia\Composer\MergePlugin_composer_tmp1->onInstallUpdateOrDump
  [merge-plugin] Loading -dev sections of app/Ship/composer.json...
  [merge-plugin] Adding barryvdh/laravel-ide-helper
  [merge-plugin] Loading -dev sections of app/Containers/Authentication/composer.json...
  [merge-plugin] Loading -dev sections of app/Containers/Authorization/composer.json...
  [merge-plugin] Loading -dev sections of app/Containers/Debugger/composer.json...
  [merge-plugin] Adding barryvdh/laravel-debugbar
  [merge-plugin] Loading -dev sections of app/Containers/Documentation/composer.json...
  [merge-plugin] Loading -dev sections of app/Containers/Feedback/composer.json...
  [merge-plugin] Loading -dev sections of app/Containers/Geo/composer.json...
  [merge-plugin] Loading -dev sections of app/Containers/Localization/composer.json...
  [merge-plugin] Loading -dev sections of app/Containers/Logging/composer.json...
  [merge-plugin] Loading -dev sections of app/Containers/Media/composer.json...
  [merge-plugin] Loading -dev sections of app/Containers/Payment/composer.json...
  [merge-plugin] Loading -dev sections of app/Containers/Settings/composer.json...
  [merge-plugin] Loading -dev sections of app/Containers/SocialAuth/composer.json...
  [merge-plugin] Loading -dev sections of app/Containers/Stripe/composer.json...
  [merge-plugin] Loading -dev sections of app/Containers/User/composer.json...
  [merge-plugin] Loading -dev sections of app/Containers/Welcome/composer.json...
  [merge-plugin] Loading -dev sections of app/Containers/composer.json...
Reading ./composer.lock
Loading composer repositories with package information
PHP Fatal error:  Uncaught TypeError: Argument 4 passed to Composer\Repository\Vcs\VcsDriver::__construct() must be an instance of Composer\Util\ProcessExecutor or null, instance of Composer\Util\HttpDownloader given, called in phar:///usr/local/bin/composer/src/Composer/Repository/VcsRepository.php on line 121 and defined in /home/yalsicor/data/html/project_base/vendor/composer/composer/src/Composer/Repository/Vcs/VcsDriver.php:60
Stack trace:
#0 phar:///usr/local/bin/composer/src/Composer/Repository/VcsRepository.php(121): Composer\Repository\Vcs\VcsDriver->__construct()
#1 phar:///usr/local/bin/composer/src/Composer/Repository/VcsRepository.php(160): Composer\Repository\VcsRepository->getDriver()
#2 phar:///usr/local/bin/composer/src/Composer/Repository/ArrayRepository.php(266): Composer\Repository\VcsRepository->initialize()
#3 phar:///usr/local/bin/composer/src/Composer/Repository/ArrayRepository.php(55): Composer\Repository\ArrayRepository->getPackages()
#4 phar:///usr/local/bin/composer/src/Composer/ in /home/spokk/data/html/openforests/of_projectexplorer_base/vendor/composer/composer/src/Composer/Repository/Vcs/VcsDriver.php on line 60
hexmode commented 3 years ago

This works for me, every time. Thank you, @mcaskill !!

@yalsicor, could you paste exactly what you're doing to install this?

mcaskill commented 3 years ago

Hi everyone, I'll try to get back on this pull request during the weekend to rebase latest changes to master.

Regarding the following:

Stability of this pull request's changeset needs additional testing from the community, such as unintentional updates upon requiring this plugin.

Thank you to all who have been testing this out in your projects across various frameworks. Based on the feedback, it appears to work good enough for most (see latter point).

Project is stuck in the process of transferring ownership to a new team within the Wikimedia Foundation.

@bd808 How's that coming along?

Since v1.4 of the plugin does not support Composer v2, the plugin is ignored when updating to v1.5. As a result, Composer is unaware of the merged dependencies and removes them. Either update the plugin with Composer v1 first or run composer update again.

I will add a notice to the README indicating the risks of updating this plugin and how to do so without temporarily losing all merged dependencies.

Unintentional lock file updates caused by isFirstInstall() on Composer v2. Listening only to post-update-cmd instead of post-install-cmd might be more sensible.

I'll do some tests to see if we can't prevent inconsistent lock files.

bd808 commented 3 years ago

Project is stuck in the process of transferring ownership to a new team within the Wikimedia Foundation.

@bd808 How's that coming along?

See https://phabricator.wikimedia.org/T248908 for any progress. The last action was apparently "AMooney moved this task from Inbox to Later on the Platform Team Workboards (Clinic Duty Team) board." taken on 2020-11-19.

LukeTowers commented 3 years ago

@bd808 @mcaskill if @wikimedia doesn't get their act together and take over ownership, then we at @octobercms are willing to do so to move this forward. This plugin is an important part of how our ecosystem works, so we're fine with taking over maintaining it if we need to.

addshore commented 3 years ago

@bd808 @mcaskill if @wikimedia doesn't get their act together and take over ownership, then we at @octobercms are willing to do so to move this forward. This plugin is an important part of how our ecosystem works, so we're fine with taking over maintaining it if we need to.

I just poked some people

reedy commented 3 years ago

FWIW, I migrated the repo to using GitHub actions rather than GitHub... So this probably wants rebasing at some point, and some of the changes from .travis.yml porting across

mcaskill commented 3 years ago

I've rebased this pull request to include the switch from Travis CI to GH Actions. I've updated php.yml to test Composer 1 and 2.


Regarding the update of dependencies from the extra composer update after the initial installation of the plugin (isFirstInstall) [1][2]:

I will not be addressing it in this pull request, after all. This is an existing issue and falls outside of this request's primary responsibility (it's also a much more complex issue to resolve).

This could maybe be resolved using setUpdateAllowList() with setAllowListTransitiveDependencies() in Composer 1 and setUpdateAllowTransitiveDependencies() in Composer 2.

Discussion

tstarling commented 3 years ago

I was asked to look at this on behalf of the platform team. I can do a code review, but because I'm not familiar with the source of either composer-merge-plugin or composer itself, I'm not necessarily going to see every bug. The quality target is going to be "seems mostly harmless" or "better than nothing" rather than "will definitely work perfectly for everyone". Note 6 in the summary seems overly cautious in any case given @mcaskill's later comments and the community testing we've had.

Dmi3yy commented 3 years ago

maybe help some one, delete this plugin and start use this solution: https://getcomposer.org/doc/05-repositories.md#path
this way use in Drupal, and now in Evolution CMS

mcaskill commented 3 years ago

It still has {"require-dev": {"composer/composer": "^1.1"}} in composer.json, is it possible to update this? I see in the test logs that PHPUnit tests are being run against Composer 1.10, regardless of the matrix variable, which doesn't seem ideal. They pass for me when I force Composer 2.

Thanks! I evidently did not pay attention to what I was doing when updating the workflow.

There are unused use statements InstallerEvent and InstallerEvents due to the removal of onDependencySolve. […]

Thanks!

[…] Evidently there is no Phan, because we use Phan to catch that sort of thing in other projects.

I'm not very happy about the use of codeCoverageIgnore, but I suppose it is an existing issue. I think if code coverage is e.g. 75%, then the status button should just say that, rather than faking 100% coverage.

👍 I agree. We can eventually create a new pull request to change code coverage, add Phan, and update PHPUnit.

CR status: first pass done, now I'm trying to test it. I'd ideally like to verify that the use cases supported by PRE_DEPENDENCIES_SOLVING are indeed replaced by MultiConstraint. I'm wondering whether we should have additional tests for that, replacing the assertions which were removed from MergePluginTest.php.

I can try to import the MultiConstraint test from composer/semver. As for other assertions, the way the class works and how to improve it is a little beyond my abilities.

mcaskill commented 3 years ago

maybe help some one, delete this plugin and start use this solution: getcomposer.org/doc/05-repositories.md#path this way use in Drupal, and now in Evolution CMS

This is indeed a viable solution for most use cases—I use this—but it doesn't work in situations where a local package can not be symlinked or copied (like a WordPress project) or if the project has many nested dependencies.

Dmi3yy commented 3 years ago

maybe help some one, delete this plugin and start use this solution: getcomposer.org/doc/05-repositories.md#path this way use in Drupal, and now in Evolution CMS

This is indeed a viable solution for most use cases—I use this—but it doesn't work in situations where a local package can not be symlinked or copied (like a WordPress project) or if the project has many nested dependencies.

Yep, after full test its not work like this plugin.

—path Can’t work if not exist file ;(

prudloff-insite commented 3 years ago

I will not be addressing it in this pull request, after all. This is an existing issue and falls outside of this request's primary responsibility (it's also a much more complex issue to resolve).

This is not really an existing issue since we did not have this problem with Composer 1.

It is caused by the fact you force isLocked() here: https://github.com/wikimedia/composer-merge-plugin/blob/fad0518b770650b65920c700c563d4d0d2e0bb08/src/MergePlugin.php#L300

We had to patch it back on our setup in order to get the expected behavior:

diff --git a/src/MergePlugin.php b/src/MergePlugin.php
index 676b158..94e51e8 100644
--- a/src/MergePlugin.php
+++ b/src/MergePlugin.php
@@ -292,13 +292,9 @@ class MergePlugin implements PluginInterface, EventSubscriberInterface
             if ($package === self::PACKAGE_NAME) {
                 $this->logger->info('composer-merge-plugin installed');
                 $this->state->setFirstInstall(true);
-                if ($this->state->isComposer1()) {
-                    $this->state->setLocked(
-                        $event->getComposer()->getLocker()->isLocked()
-                    );
-                } else {
-                    $this->state->setLocked(false);
-                }
+                $this->state->setLocked(
+                    $event->getComposer()->getLocker()->isLocked()
+                );
             }
         }
     }

IMHO it would be better to display a warning explaining that running composer update again might be needed after updating from Composer 1 to Composer 2 than forcing it on every first install and breaking every CI using this plugin.

mcaskill commented 3 years ago

IMHO it would be better to display a warning explaining that running composer update again might be needed after updating from Composer 1 to Composer 2 than forcing it on every first install and breaking every CI using this plugin.

Ok. I'll revert that section and display a message to that effect as well as update the README.

reedy commented 3 years ago

Just confirmation of this method working "fine" for the MediaWiki workflow without that mass removal of libraries:

If I try and do the upgrade of composer and the plugin in one go, we get the previous behaviour of things being mass uninstalled. Another update fixes that.

Yay, edge cases.

@tstarling Probably something to think about in terms of documentation/announcements. I can just see the "I upgraded and things broke!"... Of course documentation updates isn't going to prevent some of those. Luckily the fix is "simple", just telling them to run composer update again...

Guess we can include the plugin update first (into master), and then allow the newer composer api version (a few commits) later to save some potential dev env breakages. But of course, that doesn't help for releases and people upgrading between release versions.

Ideally we obviously want 1.36 (and 1.35 through backports) to work with composer 2.0 too. So maybe documentation updates and the MediaWiki handling is enough...

Saying that, can our composer checks (during update.php) confirm that merge-plugin brought in libraries are actually there (I guess that's kind of "are all requirements met?")/up to date in some way? That way we can probably prevent a few broken installs.

checkComposerLockUpToDate.php alone doesn't seem to be enough to do this, unfortunately. ComposerJson::getRequiredDependencies only checks "require", so it's not really a surprise. I'll file a downstream task about improving things. It seems more generally useful if people have added composer.json files to composer.local.json, and haven't run composer update, it will potentially cause errors.

reedy commented 3 years ago

https://phabricator.wikimedia.org/T273570 filed for that

tstarling commented 3 years ago

@reedy Regarding MediaWiki working fine: I tried running composer2 update on my test wiki, but it failed with an error saying that my symlinked path repos don't have a high enough priority anymore.

Root composer.json requires wikimedia/shellbox *, it is satisfiable by wikimedia/shellbox[0.0.3] from lock repo but wikimedia/shellbox[dev-master] from composer repo (https://repo.packagist.org) has higher repository priority. The packages with higher priority do not match your minimum-stability and are therefore not installable. See https://getcomposer.org/repoprio for details and assistance.

ExtraPackage::prependRepositories() is apparently doing what it is meant to do, with path repos being listed before packagist in the final array. I think I'll need to dig into composer a bit more to sort it out. Not sure if this will affect other people.

reedy commented 3 years ago

What do you have symlinked where? Do I want to know? :)

I have skins/extensions symlinked in, but am running it in the actual path of the git clone of MW Core

tstarling commented 3 years ago

My current composer.local.json is https://paste.tstarling.com/p/YlxoBI.html . So for example composer makes a symlink from /srv/mw/core/vendor/wikimedia/shellbox to /srv/shellbox, the latter being the git working copy I'm using for development. It's almost as good as "npm link". Not sure where I got the idea from, it doesn't seem to be well documented. Probably legoktm suggested it.

tstarling commented 3 years ago

Never mind, PEBCAK. Path repos installed in this way do in fact work and have the correct priority.

tstarling commented 3 years ago

It's possible to put a hook at the top level which checks if composer-merge-plugin was activated and throws an exception if it was not. That avoids having composer delete all the packages. https://gerrit.wikimedia.org/r/c/mediawiki/core/+/660984

junaidpv commented 3 years ago

Continuing here as @mcaskill suggested in #184.

To answer, I am using this v1.5 without mixing with v1.4.

BTW, I verified this version of the merge plugin is no longer working, though it not giving any errors.

I created two files

composer.json

{
  "name": "test/test",
  "description": "To test merge plugin for the Composer 2",
  "type": "project",
  "license": "GPL-2.0-or-later",
  "repositories": [
    {
      "type": "composer",
      "url": "https://packages.drupal.org/8"
    },
    {
      "type": "composer",
      "url": "https://asset-packagist.org"
    },
    {
      "type": "vcs",
      "url": "https://github.com/mcaskill/composer-merge-plugin"
    }
  ],
  "require": {
    "wikimedia/composer-merge-plugin": "dev-feature/composer-v2 as 1.5.0"
  },
  "extra": {
    "merge-plugin": {
      "require": [
        "common.json"
      ],
      "recurse": true,
      "replace": false,
      "ignore-duplicates": false,
      "merge-dev": true,
      "merge-extra": true,
      "merge-extra-deep": true,
      "merge-scripts": true
    }
  }
}

common.json

{
  "require": {
    "guzzlehttp/guzzle": "7.2.0"
  }
}

Running composer install --no-dev does not get the guzzle package.

reedy commented 3 years ago

So re the above... It doesn't install guzzle et al the first time, but if you run it again immediately after, it does.

$ php ../composer-2.0.phar --version
Composer version 2.0.9 2021-01-27 16:09:27
$ php ../composer-2.0.phar update
Loading composer repositories with package information
Updating dependencies                                 
Lock file operations: 1 install, 0 updates, 0 removals
  - Locking wikimedia/composer-merge-plugin (dev-feature/composer-v2 3f4c1e9)
Writing lock file
Installing dependencies from lock file (including require-dev)
Package operations: 1 install, 0 updates, 0 removals
  - Syncing wikimedia/composer-merge-plugin (dev-feature/composer-v2 3f4c1e9) into cache
  - Installing wikimedia/composer-merge-plugin (dev-feature/composer-v2 3f4c1e9): Cloning 3f4c1e914b from cache
Generating autoload files

Running composer update to apply merge settings
You may need to manually run composer update to apply merge settings
Installing dependencies from lock file (including require-dev)
Verifying lock file contents can be installed on current platform.
Package operations: 0 installs, 0 updates, 0 removals
Generating autoload files
$ php ../composer-2.0.phar update
Loading composer repositories with package information
Updating dependencies                                 
Lock file operations: 6 installs, 0 updates, 0 removals
  - Locking guzzlehttp/guzzle (7.2.0)
  - Locking guzzlehttp/promises (1.4.0)
  - Locking guzzlehttp/psr7 (1.7.0)
  - Locking psr/http-client (1.0.1)
  - Locking psr/http-message (1.0.1)
  - Locking ralouphie/getallheaders (3.0.3)
Writing lock file
Installing dependencies from lock file (including require-dev)
Package operations: 6 installs, 0 updates, 0 removals
  - Installing psr/http-message (1.0.1): Extracting archive
  - Installing psr/http-client (1.0.1): Extracting archive
  - Installing ralouphie/getallheaders (3.0.3): Extracting archive
  - Installing guzzlehttp/psr7 (1.7.0): Extracting archive
  - Installing guzzlehttp/promises (1.4.0): Extracting archive
  - Installing guzzlehttp/guzzle (7.2.0): Extracting archive
2 package suggestions were added by new dependencies, use `composer suggest` to see details.
Generating autoload files
1 package you are using is looking for funding.
Use the `composer fund` command to find out more!
$ 

If we do the same with the old version of the plugin (1.4.1) and composer 1.x, it does do it all in one step

Composer version 1.10.20 2021-01-27 15:41:06
$ php ../composer.phar update
Loading composer repositories with package information
Updating dependencies (including require-dev)
Package operations: 1 install, 0 updates, 0 removals
  - Installing wikimedia/composer-merge-plugin (v1.4.1): Loading from cache
Writing lock file
Generating autoload files
Loading composer repositories with package information
Updating dependencies (including require-dev)
Package operations: 6 installs, 0 updates, 0 removals
  - Installing psr/http-message (1.0.1): Loading from cache
  - Installing psr/http-client (1.0.1): Loading from cache
  - Installing ralouphie/getallheaders (3.0.3): Loading from cache
  - Installing guzzlehttp/psr7 (1.7.0): Loading from cache
  - Installing guzzlehttp/promises (1.4.0): Loading from cache
  - Installing guzzlehttp/guzzle (7.2.0): Loading from cache
guzzlehttp/psr7 suggests installing laminas/laminas-httphandlerrunner (Emit PSR-7 responses)
guzzlehttp/guzzle suggests installing psr/log (Required for using the Log middleware)
Writing lock file
Generating autoload files
1 package you are using is looking for funding.
Use the `composer fund` command to find out more!
$

So that's definitely a change in behaviour... Potentially a regression depending on how you look at it.

But is this a side effect of 3f4c1e9 and the quoted comments below?

IMHO it would be better to display a warning explaining that running composer update again might be needed after updating from Composer 1 to Composer 2 than forcing it on every first install and breaking every CI using this plugin.

Ok. I'll revert that section and display a message to that effect as well as update the README.

mcaskill commented 3 years ago

That's what I thought the issue was going to be.

When I initially submitted this pull request, I was trying to preserve the first-install behavior of Merge Plugin 1.4 + Composer 1 but since Composer 2 locks the lock file before installing, I forced a unlocked state on the lock file to get the plugin to update the lock file with the merged dependencies during first-install. But this has the side effect of updating everything else.

So I reverted that portion—because it is potentially damaging—and replaced it with an notice:

Running composer update to apply merge settings
You may need to manually run composer update to apply merge settings
if ($this->state->forceUpdate()) {
    // Force update mode so that new packages are processed rather
    // than just telling the user that composer.json and
    // composer.lock don't match.
    $installer->setUpdate(true);
} else {
    $this->logger->log('You may need to manually run composer update to apply merge settings');
}

To resolve this regression in Composer 2 we would need to refactor the first-install to (I think) use setUpdateAllowList() with setAllowListTransitiveDependencies() in Composer 1 and setUpdateAllowTransitiveDependencies() in Composer 2, giving it the list of nested dependencies to install. I haven't tried this solution yet because it's a little out of my depth and I didn't want to delay this pull request any further. But it might be a deal breaker and a required change.

I can give it a try and I'd welcome anyone else with experience with it.

junaidpv commented 3 years ago

So re the above... It doesn't install guzzle et al the first time, but if you run it again immediately after, it does.

Thanks! It helped. Actually, we are having an automated script. I added an additional composer update command which helped to fix it for us.

mcaskill commented 3 years ago

I've implemented Installer::setUpdateAllowList(), this should provide consistent first-install between Composer 1 and 2.

@prudloff-insite Can you test this out on your end? Thanks.

I used the Composer files you shared with me, back in November, and things appear to be working as intended.

reedy commented 3 years ago

Trying to update to composer 2.0 and plugin 1.5 in one go (still needs two composer update as discussed):

$ php ../composer-2.0.phar update
The "wikimedia/composer-merge-plugin" plugin was skipped because it requires a Plugin API version ("^1.0") that does not match your Composer installation ("2.0.0"). You may need to run composer update with the "--no-plugins" option.
> ComposerHookHandler::onPreUpdate
Loading composer repositories with package information
Updating dependencies                                 
Lock file operations: 1 install, 1 update, 24 removals
  - Removing beberlei/assert (v3.3.0)
  - Removing brick/math (0.9.2)
  - Removing christian-riesen/base32 (1.4.0)
  - Removing defuse/php-encryption (v2.2.1)
  - Removing fgrosse/phpasn1 (v2.2.0)
  - Removing firebase/php-jwt (v5.2.0)
  - Removing jakobo/hotp-php (v2.0.0)
  - Removing lcobucci/jwt (3.4.3)
  - Removing league/event (2.2.0)
  - Removing league/oauth2-server (dev-v9.0.0-alpha a00cc3c)
  - Removing league/uri (6.4.0)
  - Removing league/uri-components (2.3.0)
  - Removing league/uri-interfaces (2.2.0)
  - Removing psr/http-factory (1.0.1)
  - Removing ramsey/collection (1.1.3)
  - Removing ramsey/uuid (4.1.1)
  - Removing spomky-labs/base64url (v2.0.4)
  - Removing spomky-labs/cbor-php (v2.0.1)
  - Removing symfony/process (v5.2.3)
  - Removing web-auth/cose-lib (v3.2.10)
  - Removing web-auth/metadata-service (v3.2.10)
  - Removing web-auth/webauthn-lib (v3.2.10)
  - Removing wikimedia/css-sanitizer (v3.0.1)
  - Removing wikimedia/equivset (1.4.2)
  - Upgrading wikimedia/composer-merge-plugin (v1.4.1 => dev-feature/composer-v2 4195d45)
  - Locking wikimedia/shellbox (1.0.1)
Writing lock file
Installing dependencies from lock file (including require-dev)
Package operations: 1 install, 1 update, 24 removals
  - Syncing wikimedia/composer-merge-plugin (dev-feature/composer-v2 4195d45) into cache
  - Downloading wikimedia/shellbox (1.0.1)
  - Removing wikimedia/equivset (1.4.2)
  - Removing wikimedia/css-sanitizer (v3.0.1)
  - Removing web-auth/webauthn-lib (v3.2.10)
  - Removing web-auth/metadata-service (v3.2.10)
  - Removing web-auth/cose-lib (v3.2.10)
  - Removing symfony/process (v5.2.3)
  - Removing spomky-labs/cbor-php (v2.0.1)
  - Removing spomky-labs/base64url (v2.0.4)
  - Removing ramsey/uuid (4.1.1)
  - Removing ramsey/collection (1.1.3)
  - Removing psr/http-factory (1.0.1)
  - Removing league/uri-interfaces (2.2.0)
  - Removing league/uri-components (2.3.0)
  - Removing league/uri (6.4.0)
  - Removing league/oauth2-server (dev-v9.0.0-alpha a00cc3c)
  - Removing league/event (2.2.0)
  - Removing lcobucci/jwt (3.4.3)
  - Removing jakobo/hotp-php (v2.0.0)
  - Removing firebase/php-jwt (v5.2.0)
  - Removing fgrosse/phpasn1 (v2.2.0)
  - Removing defuse/php-encryption (v2.2.1)
  - Removing christian-riesen/base32 (1.4.0)
  - Removing brick/math (0.9.2)
  - Removing beberlei/assert (v3.3.0)
  - Upgrading wikimedia/composer-merge-plugin (v1.4.1 => dev-feature/composer-v2 4195d45): Checking out 4195d45d1c from cache
  - Installing wikimedia/shellbox (1.0.1): Extracting archive
Package phpunit/php-token-stream is abandoned, you should avoid using it. No replacement was suggested.
Generating optimized autoload files
composer/package-versions-deprecated: Generating version class...
composer/package-versions-deprecated: ...done generating version class
39 packages you are using are looking for funding.
Use the `composer fund` command to find out more!
> ComposerVendorHtaccessCreator::onEvent
$ php ../composer-2.0.phar update
> ComposerHookHandler::onPreUpdate
Loading composer repositories with package information
Updating dependencies                                 
Lock file operations: 24 installs, 0 updates, 0 removals
  - Locking beberlei/assert (v3.3.0)
  - Locking brick/math (0.9.2)
  - Locking christian-riesen/base32 (1.4.0)
  - Locking defuse/php-encryption (v2.2.1)
  - Locking fgrosse/phpasn1 (v2.2.0)
  - Locking firebase/php-jwt (v5.2.0)
  - Locking jakobo/hotp-php (v2.0.0)
  - Locking lcobucci/jwt (3.4.3)
  - Locking league/event (2.2.0)
  - Locking league/oauth2-server (dev-v9.0.0-alpha a00cc3c)
  - Locking league/uri (6.4.0)
  - Locking league/uri-components (2.3.0)
  - Locking league/uri-interfaces (2.2.0)
  - Locking psr/http-factory (1.0.1)
  - Locking ramsey/collection (1.1.3)
  - Locking ramsey/uuid (4.1.1)
  - Locking spomky-labs/base64url (v2.0.4)
  - Locking spomky-labs/cbor-php (v2.0.1)
  - Locking symfony/process (v5.2.3)
  - Locking web-auth/cose-lib (v3.2.10)
  - Locking web-auth/metadata-service (v3.2.10)
  - Locking web-auth/webauthn-lib (v3.2.10)
  - Locking wikimedia/css-sanitizer (v3.0.1)
  - Locking wikimedia/equivset (1.4.2)
Writing lock file
Installing dependencies from lock file (including require-dev)
Package operations: 24 installs, 0 updates, 0 removals
  - Syncing league/oauth2-server (dev-v9.0.0-alpha a00cc3c) into cache
  - Installing brick/math (0.9.2): Extracting archive
  - Installing christian-riesen/base32 (1.4.0): Extracting archive
  - Installing fgrosse/phpasn1 (v2.2.0): Extracting archive
  - Installing firebase/php-jwt (v5.2.0): Extracting archive
  - Installing jakobo/hotp-php (v2.0.0): Extracting archive
  - Installing league/event (2.2.0): Extracting archive
  - Installing lcobucci/jwt (3.4.3): Extracting archive
  - Installing defuse/php-encryption (v2.2.1): Extracting archive
  - Installing league/oauth2-server (dev-v9.0.0-alpha a00cc3c): Cloning a00cc3c274 from cache
  - Installing league/uri-interfaces (2.2.0): Extracting archive
  - Installing league/uri (6.4.0): Extracting archive
  - Installing league/uri-components (2.3.0): Extracting archive
  - Installing ramsey/collection (1.1.3): Extracting archive
  - Installing psr/http-factory (1.0.1): Extracting archive
  - Installing beberlei/assert (v3.3.0): Extracting archive
  - Installing web-auth/metadata-service (v3.2.10): Extracting archive
  - Installing web-auth/cose-lib (v3.2.10): Extracting archive
  - Installing symfony/process (v5.2.3): Extracting archive
  - Installing spomky-labs/cbor-php (v2.0.1): Extracting archive
  - Installing spomky-labs/base64url (v2.0.4): Extracting archive
  - Installing ramsey/uuid (4.1.1): Extracting archive
  - Installing web-auth/webauthn-lib (v3.2.10): Extracting archive
  - Installing wikimedia/css-sanitizer (v3.0.1): Extracting archive
  - Installing wikimedia/equivset (1.4.2): Extracting archive
12 package suggestions were added by new dependencies, use `composer suggest` to see details.
Package phpunit/php-token-stream is abandoned, you should avoid using it. No replacement was suggested.
Generating optimized autoload files
composer/package-versions-deprecated: Generating version class...
composer/package-versions-deprecated: ...done generating version class
51 packages you are using are looking for funding.
Use the `composer fund` command to find out more!
> ComposerVendorHtaccessCreator::onEvent

No vendor repo, 1.4.1 and composer 1.x

$ php ../composer.phar update
> ComposerHookHandler::onPreUpdate
Loading composer repositories with package information
Updating dependencies (including require-dev)
Package operations: 121 installs, 0 updates, 0 removals
  - Installing wikimedia/composer-merge-plugin (v1.4.1): Loading from cache
  - Installing composer/package-versions-deprecated (1.11.99.1): Loading from cache
  - Installing cssjanus/cssjanus (v1.3.0): Loading from cache
  - Installing oojs/oojs-ui (v0.41.1): Loading from cache
  - Installing pear/pear_exception (v1.0.1): Loading from cache
  - Installing pear/console_getopt (v1.4.3): Loading from cache
  - Installing pear/pear-core-minimal (v1.10.10): Loading from cache
  - Installing pear/mail (v1.4.1): Loading from cache
  - Installing pear/mail_mime (1.10.9): Loading from cache
  - Installing pear/net_socket (v1.2.2): Loading from cache
  - Installing pear/net_smtp (1.9.2): Loading from cache
  - Installing pear/net_url2 (v2.2.2): Loading from cache
  - Installing wikimedia/base-convert (v2.0.1): Loading from cache
  - Installing wikimedia/cdb (1.4.1): Loading from cache
  - Installing wikimedia/cldr-plural-rule-parser (v1.0.0): Loading from cache
  - Installing pleonasm/bloom-filter (1.0.2): Loading from cache
  - Installing wikimedia/common-passwords (0.2.0): Loading from cache
  - Installing wikimedia/html-formatter (2.0.1): Loading from cache
  - Installing wikimedia/at-ease (v2.0.0): Loading from cache
  - Installing wikimedia/ip-set (2.1.0): Loading from cache
  - Installing wikimedia/less.php (v3.1.0): Loading from cache
  - Installing psr/log (1.1.3): Loading from cache
  - Installing wikimedia/php-session-serializer (v1.0.7): Loading from cache
  - Installing wikimedia/purtle (v1.0.7): Loading from cache
  - Installing wikimedia/relpath (2.1.1): Loading from cache
  - Installing wikimedia/running-stat (v1.2.1): Loading from cache
  - Installing wikimedia/scoped-callback (v3.0.0): Loading from cache
  - Installing wikimedia/assert (v0.5.0): Loading from cache
  - Installing psr/container (1.0.0): Loading from cache
  - Installing wikimedia/services (2.0.1): Loading from cache
  - Installing wikimedia/wikipeg (2.0.5): Loading from cache
  - Installing monolog/monolog (2.2.0): Loading from cache
  - Installing psr/http-message (1.0.1): Loading from cache
  - Installing psr/http-client (1.0.1): Loading from cache
  - Installing ralouphie/getallheaders (3.0.3): Loading from cache
  - Installing guzzlehttp/psr7 (1.7.0): Loading from cache
  - Installing guzzlehttp/promises (1.4.0): Loading from cache
  - Installing guzzlehttp/guzzle (7.2.0): Loading from cache
  - Installing wikimedia/shellbox (1.0.1): Loading from cache
  - Installing wikimedia/utfnormal (3.0.1): Loading from cache
  - Installing wikimedia/wait-condition-loop (v1.0.1): Loading from cache
  - Installing wikimedia/wrappedstring (v3.2.0): Loading from cache
  - Installing wikimedia/timestamp (v3.0.0): Loading from cache
  - Installing wikimedia/xmp-reader (0.8.0): Loading from cache
  - Installing zordius/lightncandy (v1.2.5): Loading from cache
  - Installing doctrine/sql-formatter (1.1.1): Loading from cache
  - Installing giorgiosironi/eris (0.10.0): Loading from cache
  - Installing squizlabs/php_codesniffer (3.5.8): Loading from cache
  - Installing composer/spdx-licenses (1.5.4): Loading from cache
  - Installing composer/semver (3.2.4): Loading from cache
  - Installing mediawiki/mediawiki-codesniffer (v35.0.0): Loading from cache
  - Installing symfony/polyfill-php80 (v1.22.0): Loading from cache
  - Installing symfony/polyfill-intl-normalizer (v1.22.0): Loading from cache
  - Installing symfony/polyfill-intl-grapheme (v1.22.0): Loading from cache
  - Installing symfony/string (v5.2.3): Loading from cache
  - Installing symfony/service-contracts (v2.2.0): Loading from cache
  - Installing symfony/polyfill-php73 (v1.22.0): Loading from cache
  - Installing symfony/console (v5.2.3): Loading from cache
  - Installing sabre/event (5.1.2): Loading from cache
  - Installing netresearch/jsonmapper (v2.1.0): Loading from cache
  - Installing microsoft/tolerant-php-parser (v0.0.23): Loading from cache
  - Installing phpdocumentor/reflection-common (2.2.0): Loading from cache
  - Installing webmozart/assert (1.9.1): Loading from cache
  - Installing phpdocumentor/type-resolver (1.4.0): Loading from cache
  - Installing phpdocumentor/reflection-docblock (5.2.2): Loading from cache
  - Installing felixfbecker/advanced-json-rpc (v3.2.0): Loading from cache
  - Installing composer/xdebug-handler (1.4.5): Loading from cache
  - Installing phan/phan (3.2.6): Loading from cache
  - Installing mediawiki/phan-taint-check-plugin (3.2.1): Loading from cache
  - Installing mediawiki/mediawiki-phan-config (0.10.6): Loading from cache
  - Installing nmred/kafka-php (v0.1.5): Loading from cache
  - Installing php-parallel-lint/php-console-color (v0.3): Loading from cache
  - Installing php-parallel-lint/php-console-highlighter (v0.5): Loading from cache
  - Installing php-parallel-lint/php-parallel-lint (v1.2.0): Loading from cache
  - Installing pimple/pimple (v3.3.1): Loading from cache
  - Installing dnoegel/php-xdg-base-dir (v0.1.1): Loading from cache
  - Installing nikic/php-parser (v4.10.2): Loading from cache
  - Installing symfony/var-dumper (v5.2.3): Loading from cache
  - Installing psy/psysh (v0.10.5): Loading from cache
  - Installing seld/jsonlint (1.8.3): Loading from cache
  - Installing wikimedia/avro (v1.9.0): Loading from cache
  - Installing wikimedia/testing-access-wrapper (2.0.0): Loading from cache
  - Installing wikimedia/zest-css (1.1.4): Loading from cache
  - Installing wikimedia/remex-html (2.2.2): Loading from cache
  - Installing wikimedia/object-factory (v3.0.0): Loading from cache
  - Installing wikimedia/ip-utils (3.0.1): Loading from cache
  - Installing liuggio/statsd-php-client (v1.0.18): Loading from cache
  - Installing wikimedia/parsoid (v0.13.0-a24): Loading from cache
  - Installing doctrine/event-manager (1.1.1): Loading from cache
  - Installing doctrine/cache (1.10.2): Loading from cache
  - Installing doctrine/dbal (3.0.0): Loading from cache
  - Installing sebastian/version (2.0.1): Loading from cache
  - Installing sebastian/type (1.1.4): Loading from cache
  - Installing sebastian/resource-operations (2.0.2): Loading from cache
  - Installing sebastian/recursion-context (3.0.1): Loading from cache
  - Installing sebastian/object-reflector (1.1.2): Loading from cache
  - Installing sebastian/object-enumerator (3.0.4): Loading from cache
  - Installing sebastian/global-state (3.0.1): Loading from cache
  - Installing sebastian/exporter (3.1.3): Loading from cache
  - Installing sebastian/environment (4.2.4): Loading from cache
  - Installing sebastian/diff (3.0.3): Loading from cache
  - Installing sebastian/comparator (3.0.3): Loading from cache
  - Installing phpunit/php-timer (2.1.3): Loading from cache
  - Installing phpunit/php-text-template (1.2.1): Loading from cache
  - Installing phpunit/php-file-iterator (2.0.3): Loading from cache
  - Installing theseer/tokenizer (1.2.0): Loading from cache
  - Installing sebastian/code-unit-reverse-lookup (1.0.2): Loading from cache
  - Installing phpunit/php-token-stream (4.0.4): Loading from cache
  - Installing phpunit/php-code-coverage (7.0.14): Loading from cache
  - Installing doctrine/instantiator (1.4.0): Loading from cache
  - Installing phpspec/prophecy (1.12.2): Loading from cache
  - Installing myclabs/deep-copy (1.10.2): Loading from cache
  - Installing phar-io/version (3.0.4): Loading from cache
  - Installing phar-io/manifest (2.0.1): Loading from cache
  - Installing phpunit/phpunit (8.5.14): Loading from cache
  - Installing johnkary/phpunit-speedtrap (v3.3.0): Loading from cache
  - Installing justinrainbow/json-schema (5.2.10): Loading from cache
  - Installing symfony/deprecation-contracts (v2.2.0): Loading from cache
  - Installing symfony/yaml (v5.2.3): Loading from cache
  - Installing hamcrest/hamcrest-php (v2.0.1): Loading from cache
  - Installing wmde/hamcrest-html-matchers (v0.1.1): Loading from cache
pear/net_smtp suggests installing pear/auth_sasl (Install optionally via your project's composer.json)
monolog/monolog suggests installing graylog2/gelf-php (Allow sending log messages to a GrayLog2 server)
monolog/monolog suggests installing doctrine/couchdb (Allow sending log messages to a CouchDB server)
monolog/monolog suggests installing ruflin/elastica (Allow sending log messages to an Elastic Search server)
monolog/monolog suggests installing elasticsearch/elasticsearch (Allow sending log messages to an Elasticsearch server via official client)
monolog/monolog suggests installing php-amqplib/php-amqplib (Allow sending log messages to an AMQP server using php-amqplib)
monolog/monolog suggests installing ext-amqp (Allow sending log messages to an AMQP server (1.0+ required))
monolog/monolog suggests installing ext-mongodb (Allow sending log messages to a MongoDB server (via driver))
monolog/monolog suggests installing mongodb/mongodb (Allow sending log messages to a MongoDB server (via library))
monolog/monolog suggests installing aws/aws-sdk-php (Allow sending log messages to AWS services like DynamoDB)
monolog/monolog suggests installing rollbar/rollbar (Allow sending log messages to Rollbar)
monolog/monolog suggests installing php-console/php-console (Allow sending log messages to Google Chrome)
guzzlehttp/psr7 suggests installing laminas/laminas-httphandlerrunner (Emit PSR-7 responses)
giorgiosironi/eris suggests installing icomefromthenet/reverse-regex (v0.0.6.3 for the regex() Generator)
symfony/service-contracts suggests installing symfony/service-implementation
symfony/console suggests installing symfony/event-dispatcher
symfony/console suggests installing symfony/lock
symfony/console suggests installing symfony/process
psy/psysh suggests installing ext-pdo-sqlite (The doc command requires SQLite to work.)
psy/psysh suggests installing hoa/console (A pure PHP readline implementation. You'll want this if your PHP install doesn't already support readline or libedit.)
wikimedia/parsoid suggests installing wikimedia/langconv (Provides script conversion support)
doctrine/cache suggests installing alcaeus/mongo-php-adapter (Required to use legacy MongoDB driver)
sebastian/global-state suggests installing ext-uopz (*)
phpunit/phpunit suggests installing phpunit/php-invoker (^2.0.0)
phpunit/phpunit suggests installing ext-soap (*)
Package phpunit/php-token-stream is abandoned, you should avoid using it. No replacement was suggested.
Writing lock file
Generating optimized autoload files
composer/package-versions-deprecated: Generating version class...
composer/package-versions-deprecated: ...done generating version class
39 packages you are using are looking for funding.
Use the `composer fund` command to find out more!
> ComposerHookHandler::onPreUpdate
Loading composer repositories with package information
Updating dependencies (including require-dev)         
Package operations: 24 installs, 0 updates, 0 removals
  - Installing firebase/php-jwt (v5.2.0): Loading from cache
  - Installing defuse/php-encryption (v2.2.1): Loading from cache
  - Installing lcobucci/jwt (3.4.3): Loading from cache
  - Installing league/event (2.2.0): Loading from cache
  - Installing league/oauth2-server (dev-v9.0.0-alpha a00cc3c): Cloning a00cc3c274 from cache
  - Installing christian-riesen/base32 (1.4.0): Loading from cache
  - Installing jakobo/hotp-php (v2.0.0): Loading from cache
  - Installing psr/http-factory (1.0.1): Loading from cache
  - Installing league/uri-interfaces (2.2.0): Loading from cache
  - Installing league/uri-components (2.3.0): Loading from cache
  - Installing league/uri (6.4.0): Loading from cache
  - Installing beberlei/assert (v3.3.0): Loading from cache
  - Installing web-auth/metadata-service (v3.2.10): Loading from cache
  - Installing fgrosse/phpasn1 (v2.2.0): Loading from cache
  - Installing web-auth/cose-lib (v3.2.10): Loading from cache
  - Installing symfony/process (v5.2.3): Loading from cache
  - Installing brick/math (0.9.2): Loading from cache
  - Installing spomky-labs/cbor-php (v2.0.1): Loading from cache
  - Installing spomky-labs/base64url (v2.0.4): Loading from cache
  - Installing ramsey/collection (1.1.3): Loading from cache
  - Installing ramsey/uuid (4.1.1): Loading from cache
  - Installing web-auth/webauthn-lib (v3.2.10): Loading from cache
  - Installing wikimedia/equivset (1.4.2): Loading from cache
  - Installing wikimedia/css-sanitizer (v3.0.1): Loading from cache
lcobucci/jwt suggests installing lcobucci/clock (*)
league/uri-components suggests installing jeremykendall/php-domain-parser (Public Suffix and Top Level Domain parsing implemented in PHP)
web-auth/metadata-service suggests installing web-token/jwt-key-mgmt (Mandatory for fetching Metadata Statement from distant sources)
web-auth/metadata-service suggests installing web-token/jwt-signature-algorithm-ecdsa (Mandatory for fetching Metadata Statement from distant sources)
fgrosse/phpasn1 suggests installing phpseclib/bcmath_compat (BCmath polyfill for servers where neither GMP nor BCmath is available)
ramsey/uuid suggests installing ext-uuid (Enables the use of PeclUuidTimeGenerator and PeclUuidRandomGenerator.)
ramsey/uuid suggests installing ramsey/uuid-doctrine (Allows the use of Ramsey\Uuid\Uuid as Doctrine field type.)
ramsey/uuid suggests installing paragonie/random-lib (Provides RandomLib for use with the RandomLibAdapter)
web-auth/webauthn-lib suggests installing web-token/jwt-key-mgmt (Mandatory for the AndroidSafetyNet Attestation Statement support)
web-auth/webauthn-lib suggests installing web-token/jwt-signature-algorithm-rsa (Mandatory for the AndroidSafetyNet Attestation Statement support)
web-auth/webauthn-lib suggests installing web-token/jwt-signature-algorithm-ecdsa (Recommended for the AndroidSafetyNet Attestation Statement support)
web-auth/webauthn-lib suggests installing web-token/jwt-signature-algorithm-eddsa (Recommended for the AndroidSafetyNet Attestation Statement support)
Package phpunit/php-token-stream is abandoned, you should avoid using it. No replacement was suggested.
Writing lock file
Generating optimized autoload files
composer/package-versions-deprecated: Generating version class...
composer/package-versions-deprecated: ...done generating version class
51 packages you are using are looking for funding.
Use the `composer fund` command to find out more!
> ComposerVendorHtaccessCreator::onEvent
> ComposerVendorHtaccessCreator::onEvent

ComposerVendorHtaccessCreator::onEvent running twice at the end looks funny

Upgrade the plugin on 1.x, run update on 1.x, run update on 2.x (effectively a no-op)

$ php ../composer.phar update
> ComposerHookHandler::onPreUpdate
Loading composer repositories with package information
Updating dependencies (including require-dev)         
Package operations: 0 installs, 1 update, 0 removals
  - Removing wikimedia/composer-merge-plugin (v1.4.1)
  - Installing wikimedia/composer-merge-plugin (dev-feature/composer-v2 4195d45): Cloning 4195d45d1c from cache
Package phpunit/php-token-stream is abandoned, you should avoid using it. No replacement was suggested.
Writing lock file
Generating optimized autoload files
composer/package-versions-deprecated: Generating version class...
composer/package-versions-deprecated: ...done generating version class
51 packages you are using are looking for funding.
Use the `composer fund` command to find out more!
> ComposerVendorHtaccessCreator::onEvent
$ php ../composer.phar update
> ComposerHookHandler::onPreUpdate
Loading composer repositories with package information
Updating dependencies (including require-dev)         
Package operations: 0 installs, 0 updates, 0 removals
Package phpunit/php-token-stream is abandoned, you should avoid using it. No replacement was suggested.
Generating optimized autoload files
composer/package-versions-deprecated: Generating version class...
composer/package-versions-deprecated: ...done generating version class
51 packages you are using are looking for funding.
Use the `composer fund` command to find out more!
> ComposerVendorHtaccessCreator::onEvent
$ php ../composer-2.0.phar update
> ComposerHookHandler::onPreUpdate
Loading composer repositories with package information
Updating dependencies                                 
Lock file operations: 0 installs, 0 updates, 0 removals
Writing lock file
Installing dependencies from lock file (including require-dev)
Package operations: 0 installs, 0 updates, 0 removals
Package phpunit/php-token-stream is abandoned, you should avoid using it. No replacement was suggested.
Generating optimized autoload files
composer/package-versions-deprecated: Generating version class...
composer/package-versions-deprecated: ...done generating version class
51 packages you are using are looking for funding.
Use the `composer fund` command to find out more!
> ComposerVendorHtaccessCreator::onEvent
$ php ../composer-2.0.phar update
> ComposerHookHandler::onPreUpdate
Loading composer repositories with package information
Updating dependencies                                 
Nothing to modify in lock file
Installing dependencies from lock file (including require-dev)
Package operations: 0 installs, 0 updates, 0 removals
Package phpunit/php-token-stream is abandoned, you should avoid using it. No replacement was suggested.
Generating optimized autoload files
composer/package-versions-deprecated: Generating version class...
composer/package-versions-deprecated: ...done generating version class
51 packages you are using are looking for funding.
Use the `composer fund` command to find out more!
> ComposerVendorHtaccessCreator::onEvent

No vendor repo, 1.5, composer 2.x

$ php ../composer-2.0.phar update
> ComposerHookHandler::onPreUpdate
Loading composer repositories with package information
Updating dependencies                                 
Lock file operations: 121 installs, 0 updates, 0 removals
  - Locking composer/package-versions-deprecated (1.11.99.1)
  - Locking composer/semver (3.2.4)
  - Locking composer/spdx-licenses (1.5.4)
  - Locking composer/xdebug-handler (1.4.5)
  - Locking cssjanus/cssjanus (v1.3.0)
  - Locking dnoegel/php-xdg-base-dir (v0.1.1)
  - Locking doctrine/cache (1.10.2)
  - Locking doctrine/dbal (3.0.0)
  - Locking doctrine/event-manager (1.1.1)
  - Locking doctrine/instantiator (1.4.0)
  - Locking doctrine/sql-formatter (1.1.1)
  - Locking felixfbecker/advanced-json-rpc (v3.2.0)
  - Locking giorgiosironi/eris (0.10.0)
  - Locking guzzlehttp/guzzle (7.2.0)
  - Locking guzzlehttp/promises (1.4.0)
  - Locking guzzlehttp/psr7 (1.7.0)
  - Locking hamcrest/hamcrest-php (v2.0.1)
  - Locking johnkary/phpunit-speedtrap (v3.3.0)
  - Locking justinrainbow/json-schema (5.2.10)
  - Locking liuggio/statsd-php-client (v1.0.18)
  - Locking mediawiki/mediawiki-codesniffer (v35.0.0)
  - Locking mediawiki/mediawiki-phan-config (0.10.6)
  - Locking mediawiki/phan-taint-check-plugin (3.2.1)
  - Locking microsoft/tolerant-php-parser (v0.0.23)
  - Locking monolog/monolog (2.2.0)
  - Locking myclabs/deep-copy (1.10.2)
  - Locking netresearch/jsonmapper (v2.1.0)
  - Locking nikic/php-parser (v4.10.2)
  - Locking nmred/kafka-php (v0.1.5)
  - Locking oojs/oojs-ui (v0.41.1)
  - Locking pear/console_getopt (v1.4.3)
  - Locking pear/mail (v1.4.1)
  - Locking pear/mail_mime (1.10.9)
  - Locking pear/net_smtp (1.9.2)
  - Locking pear/net_socket (v1.2.2)
  - Locking pear/net_url2 (v2.2.2)
  - Locking pear/pear-core-minimal (v1.10.10)
  - Locking pear/pear_exception (v1.0.1)
  - Locking phan/phan (3.2.6)
  - Locking phar-io/manifest (2.0.1)
  - Locking phar-io/version (3.0.4)
  - Locking php-parallel-lint/php-console-color (v0.3)
  - Locking php-parallel-lint/php-console-highlighter (v0.5)
  - Locking php-parallel-lint/php-parallel-lint (v1.2.0)
  - Locking phpdocumentor/reflection-common (2.2.0)
  - Locking phpdocumentor/reflection-docblock (5.2.2)
  - Locking phpdocumentor/type-resolver (1.4.0)
  - Locking phpspec/prophecy (1.12.2)
  - Locking phpunit/php-code-coverage (7.0.14)
  - Locking phpunit/php-file-iterator (2.0.3)
  - Locking phpunit/php-text-template (1.2.1)
  - Locking phpunit/php-timer (2.1.3)
  - Locking phpunit/php-token-stream (4.0.4)
  - Locking phpunit/phpunit (8.5.14)
  - Locking pimple/pimple (v3.3.1)
  - Locking pleonasm/bloom-filter (1.0.2)
  - Locking psr/container (1.0.0)
  - Locking psr/http-client (1.0.1)
  - Locking psr/http-message (1.0.1)
  - Locking psr/log (1.1.3)
  - Locking psy/psysh (v0.10.5)
  - Locking ralouphie/getallheaders (3.0.3)
  - Locking sabre/event (5.1.2)
  - Locking sebastian/code-unit-reverse-lookup (1.0.2)
  - Locking sebastian/comparator (3.0.3)
  - Locking sebastian/diff (3.0.3)
  - Locking sebastian/environment (4.2.4)
  - Locking sebastian/exporter (3.1.3)
  - Locking sebastian/global-state (3.0.1)
  - Locking sebastian/object-enumerator (3.0.4)
  - Locking sebastian/object-reflector (1.1.2)
  - Locking sebastian/recursion-context (3.0.1)
  - Locking sebastian/resource-operations (2.0.2)
  - Locking sebastian/type (1.1.4)
  - Locking sebastian/version (2.0.1)
  - Locking seld/jsonlint (1.8.3)
  - Locking squizlabs/php_codesniffer (3.5.8)
  - Locking symfony/console (v5.2.3)
  - Locking symfony/deprecation-contracts (v2.2.0)
  - Locking symfony/polyfill-intl-grapheme (v1.22.0)
  - Locking symfony/polyfill-intl-normalizer (v1.22.0)
  - Locking symfony/polyfill-php73 (v1.22.0)
  - Locking symfony/polyfill-php80 (v1.22.0)
  - Locking symfony/service-contracts (v2.2.0)
  - Locking symfony/string (v5.2.3)
  - Locking symfony/var-dumper (v5.2.3)
  - Locking symfony/yaml (v5.2.3)
  - Locking theseer/tokenizer (1.2.0)
  - Locking webmozart/assert (1.9.1)
  - Locking wikimedia/assert (v0.5.0)
  - Locking wikimedia/at-ease (v2.0.0)
  - Locking wikimedia/avro (v1.9.0)
  - Locking wikimedia/base-convert (v2.0.1)
  - Locking wikimedia/cdb (1.4.1)
  - Locking wikimedia/cldr-plural-rule-parser (v1.0.0)
  - Locking wikimedia/common-passwords (0.2.0)
  - Locking wikimedia/composer-merge-plugin (dev-feature/composer-v2 4195d45)
  - Locking wikimedia/html-formatter (2.0.1)
  - Locking wikimedia/ip-set (2.1.0)
  - Locking wikimedia/ip-utils (3.0.1)
  - Locking wikimedia/less.php (v3.1.0)
  - Locking wikimedia/object-factory (v3.0.0)
  - Locking wikimedia/parsoid (v0.13.0-a24)
  - Locking wikimedia/php-session-serializer (v1.0.7)
  - Locking wikimedia/purtle (v1.0.7)
  - Locking wikimedia/relpath (2.1.1)
  - Locking wikimedia/remex-html (2.2.2)
  - Locking wikimedia/running-stat (v1.2.1)
  - Locking wikimedia/scoped-callback (v3.0.0)
  - Locking wikimedia/services (2.0.1)
  - Locking wikimedia/shellbox (1.0.1)
  - Locking wikimedia/testing-access-wrapper (2.0.0)
  - Locking wikimedia/timestamp (v3.0.0)
  - Locking wikimedia/utfnormal (3.0.1)
  - Locking wikimedia/wait-condition-loop (v1.0.1)
  - Locking wikimedia/wikipeg (2.0.5)
  - Locking wikimedia/wrappedstring (v3.2.0)
  - Locking wikimedia/xmp-reader (0.8.0)
  - Locking wikimedia/zest-css (1.1.4)
  - Locking wmde/hamcrest-html-matchers (v0.1.1)
  - Locking zordius/lightncandy (v1.2.5)
Writing lock file
Installing dependencies from lock file (including require-dev)
Package operations: 121 installs, 0 updates, 0 removals
  - Syncing wikimedia/composer-merge-plugin (dev-feature/composer-v2 4195d45) into cache
  - Installing composer/package-versions-deprecated (1.11.99.1): Extracting archive
  - Installing wikimedia/composer-merge-plugin (dev-feature/composer-v2 4195d45): Cloning 4195d45d1c from cache
  - Installing cssjanus/cssjanus (v1.3.0): Extracting archive
  - Installing doctrine/event-manager (1.1.1): Extracting archive
  - Installing doctrine/cache (1.10.2): Extracting archive
  - Installing doctrine/dbal (3.0.0): Extracting archive
  - Installing doctrine/sql-formatter (1.1.1): Extracting archive
  - Installing giorgiosironi/eris (0.10.0): Extracting archive
  - Installing guzzlehttp/promises (1.4.0): Extracting archive
  - Installing ralouphie/getallheaders (3.0.3): Extracting archive
  - Installing psr/http-message (1.0.1): Extracting archive
  - Installing guzzlehttp/psr7 (1.7.0): Extracting archive
  - Installing sebastian/version (2.0.1): Extracting archive
  - Installing sebastian/type (1.1.4): Extracting archive
  - Installing sebastian/resource-operations (2.0.2): Extracting archive
  - Installing sebastian/recursion-context (3.0.1): Extracting archive
  - Installing sebastian/object-reflector (1.1.2): Extracting archive
  - Installing sebastian/object-enumerator (3.0.4): Extracting archive
  - Installing sebastian/global-state (3.0.1): Extracting archive
  - Installing sebastian/exporter (3.1.3): Extracting archive
  - Installing sebastian/environment (4.2.4): Extracting archive
  - Installing sebastian/diff (3.0.3): Extracting archive
  - Installing sebastian/comparator (3.0.3): Extracting archive
  - Installing phpunit/php-timer (2.1.3): Extracting archive
  - Installing phpunit/php-text-template (1.2.1): Extracting archive
  - Installing phpunit/php-file-iterator (2.0.3): Extracting archive
  - Installing theseer/tokenizer (1.2.0): Extracting archive
  - Installing sebastian/code-unit-reverse-lookup (1.0.2): Extracting archive
  - Installing phpunit/php-token-stream (4.0.4): Extracting archive
  - Installing phpunit/php-code-coverage (7.0.14): Extracting archive
  - Installing webmozart/assert (1.9.1): Extracting archive
  - Installing phpdocumentor/reflection-common (2.2.0): Extracting archive
  - Installing phpdocumentor/type-resolver (1.4.0): Extracting archive
  - Installing phpdocumentor/reflection-docblock (5.2.2): Extracting archive
  - Installing doctrine/instantiator (1.4.0): Extracting archive
  - Installing phpspec/prophecy (1.12.2): Extracting archive
  - Installing phar-io/version (3.0.4): Extracting archive
  - Installing phar-io/manifest (2.0.1): Extracting archive
  - Installing myclabs/deep-copy (1.10.2): Extracting archive
  - Installing phpunit/phpunit (8.5.14): Extracting archive
  - Installing johnkary/phpunit-speedtrap (v3.3.0): Extracting archive
  - Installing justinrainbow/json-schema (5.2.10): Extracting archive
  - Installing squizlabs/php_codesniffer (3.5.8): Extracting archive
  - Installing composer/spdx-licenses (1.5.4): Extracting archive
  - Installing composer/semver (3.2.4): Extracting archive
  - Installing mediawiki/mediawiki-codesniffer (v35.0.0): Extracting archive
  - Installing symfony/polyfill-php80 (v1.22.0): Extracting archive
  - Installing symfony/polyfill-intl-normalizer (v1.22.0): Extracting archive
  - Installing symfony/polyfill-intl-grapheme (v1.22.0): Extracting archive
  - Installing symfony/string (v5.2.3): Extracting archive
  - Installing psr/container (1.0.0): Extracting archive
  - Installing symfony/service-contracts (v2.2.0): Extracting archive
  - Installing symfony/polyfill-php73 (v1.22.0): Extracting archive
  - Installing symfony/console (v5.2.3): Extracting archive
  - Installing sabre/event (5.1.2): Extracting archive
  - Installing netresearch/jsonmapper (v2.1.0): Extracting archive
  - Installing microsoft/tolerant-php-parser (v0.0.23): Extracting archive
  - Installing felixfbecker/advanced-json-rpc (v3.2.0): Extracting archive
  - Installing psr/log (1.1.3): Extracting archive
  - Installing composer/xdebug-handler (1.4.5): Extracting archive
  - Installing phan/phan (3.2.6): Extracting archive
  - Installing mediawiki/phan-taint-check-plugin (3.2.1): Extracting archive
  - Installing mediawiki/mediawiki-phan-config (0.10.6): Extracting archive
  - Installing nmred/kafka-php (v0.1.5): Extracting archive
  - Installing oojs/oojs-ui (v0.41.1): Extracting archive
  - Installing pear/pear_exception (v1.0.1): Extracting archive
  - Installing pear/console_getopt (v1.4.3): Extracting archive
  - Installing pear/pear-core-minimal (v1.10.10): Extracting archive
  - Installing pear/mail (v1.4.1): Extracting archive
  - Installing pear/mail_mime (1.10.9): Extracting archive
  - Installing pear/net_socket (v1.2.2): Extracting archive
  - Installing pear/net_smtp (1.9.2): Extracting archive
  - Installing pear/net_url2 (v2.2.2): Extracting archive
  - Installing php-parallel-lint/php-console-color (v0.3): Extracting archive
  - Installing php-parallel-lint/php-console-highlighter (v0.5): Extracting archive
  - Installing php-parallel-lint/php-parallel-lint (v1.2.0): Extracting archive
  - Installing pimple/pimple (v3.3.1): Extracting archive
  - Installing psr/http-client (1.0.1): Extracting archive
  - Installing symfony/var-dumper (v5.2.3): Extracting archive
  - Installing nikic/php-parser (v4.10.2): Extracting archive
  - Installing dnoegel/php-xdg-base-dir (v0.1.1): Extracting archive
  - Installing psy/psysh (v0.10.5): Extracting archive
  - Installing seld/jsonlint (1.8.3): Extracting archive
  - Installing symfony/deprecation-contracts (v2.2.0): Extracting archive
  - Installing symfony/yaml (v5.2.3): Extracting archive
  - Installing wikimedia/avro (v1.9.0): Extracting archive
  - Installing wikimedia/base-convert (v2.0.1): Extracting archive
  - Installing wikimedia/cdb (1.4.1): Extracting archive
  - Installing wikimedia/cldr-plural-rule-parser (v1.0.0): Extracting archive
  - Installing pleonasm/bloom-filter (1.0.2): Extracting archive
  - Installing wikimedia/common-passwords (0.2.0): Extracting archive
  - Installing wikimedia/html-formatter (2.0.1): Extracting archive
  - Installing wikimedia/at-ease (v2.0.0): Extracting archive
  - Installing wikimedia/ip-set (2.1.0): Extracting archive
  - Installing wikimedia/less.php (v3.1.0): Extracting archive
  - Installing wikimedia/zest-css (1.1.4): Extracting archive
  - Installing wikimedia/wikipeg (2.0.5): Extracting archive
  - Installing wikimedia/scoped-callback (v3.0.0): Extracting archive
  - Installing wikimedia/utfnormal (3.0.1): Extracting archive
  - Installing wikimedia/remex-html (2.2.2): Extracting archive
  - Installing wikimedia/object-factory (v3.0.0): Extracting archive
  - Installing wikimedia/ip-utils (3.0.1): Extracting archive
  - Installing wikimedia/assert (v0.5.0): Extracting archive
  - Installing liuggio/statsd-php-client (v1.0.18): Extracting archive
  - Installing wikimedia/parsoid (v0.13.0-a24): Extracting archive
  - Installing wikimedia/php-session-serializer (v1.0.7): Extracting archive
  - Installing wikimedia/purtle (v1.0.7): Extracting archive
  - Installing wikimedia/relpath (2.1.1): Extracting archive
  - Installing wikimedia/running-stat (v1.2.1): Extracting archive
  - Installing wikimedia/services (2.0.1): Extracting archive
  - Installing monolog/monolog (2.2.0): Extracting archive
  - Installing guzzlehttp/guzzle (7.2.0): Extracting archive
  - Installing wikimedia/shellbox (1.0.1): Extracting archive
  - Installing wikimedia/testing-access-wrapper (2.0.0): Extracting archive
  - Installing wikimedia/wait-condition-loop (v1.0.1): Extracting archive
  - Installing wikimedia/wrappedstring (v3.2.0): Extracting archive
  - Installing wikimedia/timestamp (v3.0.0): Extracting archive
  - Installing wikimedia/xmp-reader (0.8.0): Extracting archive
  - Installing hamcrest/hamcrest-php (v2.0.1): Extracting archive
  - Installing wmde/hamcrest-html-matchers (v0.1.1): Extracting archive
  - Installing zordius/lightncandy (v1.2.5): Extracting archive
26 package suggestions were added by new dependencies, use `composer suggest` to see details.
Package phpunit/php-token-stream is abandoned, you should avoid using it. No replacement was suggested.
Generating optimized autoload files
composer/package-versions-deprecated: Generating version class...
composer/package-versions-deprecated: ...done generating version class
39 packages you are using are looking for funding.
Use the `composer fund` command to find out more!

Running composer update to apply merge settings
> ComposerHookHandler::onPreUpdate
Loading composer repositories with package information
Updating dependencies                                 
Lock file operations: 24 installs, 0 updates, 0 removals
  - Locking beberlei/assert (v3.3.0)
  - Locking brick/math (0.9.2)
  - Locking christian-riesen/base32 (1.4.0)
  - Locking defuse/php-encryption (v2.2.1)
  - Locking fgrosse/phpasn1 (v2.2.0)
  - Locking firebase/php-jwt (v5.2.0)
  - Locking jakobo/hotp-php (v2.0.0)
  - Locking lcobucci/jwt (3.4.3)
  - Locking league/event (2.2.0)
  - Locking league/oauth2-server (dev-v9.0.0-alpha a00cc3c)
  - Locking league/uri (6.4.0)
  - Locking league/uri-components (2.3.0)
  - Locking league/uri-interfaces (2.2.0)
  - Locking psr/http-factory (1.0.1)
  - Locking ramsey/collection (1.1.3)
  - Locking ramsey/uuid (4.1.1)
  - Locking spomky-labs/base64url (v2.0.4)
  - Locking spomky-labs/cbor-php (v2.0.1)
  - Locking symfony/process (v5.2.3)
  - Locking web-auth/cose-lib (v3.2.10)
  - Locking web-auth/metadata-service (v3.2.10)
  - Locking web-auth/webauthn-lib (v3.2.10)
  - Locking wikimedia/css-sanitizer (v3.0.1)
  - Locking wikimedia/equivset (1.4.2)
Writing lock file
Installing dependencies from lock file (including require-dev)
Package operations: 24 installs, 0 updates, 0 removals
  - Syncing league/oauth2-server (dev-v9.0.0-alpha a00cc3c) into cache
  - Installing brick/math (0.9.2): Extracting archive
  - Installing christian-riesen/base32 (1.4.0): Extracting archive
  - Installing fgrosse/phpasn1 (v2.2.0): Extracting archive
  - Installing firebase/php-jwt (v5.2.0): Extracting archive
  - Installing jakobo/hotp-php (v2.0.0): Extracting archive
  - Installing league/event (2.2.0): Extracting archive
  - Installing lcobucci/jwt (3.4.3): Extracting archive
  - Installing defuse/php-encryption (v2.2.1): Extracting archive
  - Installing league/oauth2-server (dev-v9.0.0-alpha a00cc3c): Cloning a00cc3c274 from cache
  - Installing league/uri-interfaces (2.2.0): Extracting archive
  - Installing league/uri (6.4.0): Extracting archive
  - Installing league/uri-components (2.3.0): Extracting archive
  - Installing ramsey/collection (1.1.3): Extracting archive
  - Installing psr/http-factory (1.0.1): Extracting archive
  - Installing beberlei/assert (v3.3.0): Extracting archive
  - Installing web-auth/metadata-service (v3.2.10): Extracting archive
  - Installing web-auth/cose-lib (v3.2.10): Extracting archive
  - Installing symfony/process (v5.2.3): Extracting archive
  - Installing spomky-labs/cbor-php (v2.0.1): Extracting archive
  - Installing spomky-labs/base64url (v2.0.4): Extracting archive
  - Installing ramsey/uuid (4.1.1): Extracting archive
  - Installing web-auth/webauthn-lib (v3.2.10): Extracting archive
  - Installing wikimedia/css-sanitizer (v3.0.1): Extracting archive
  - Installing wikimedia/equivset (1.4.2): Extracting archive
12 package suggestions were added by new dependencies, use `composer suggest` to see details.
Package phpunit/php-token-stream is abandoned, you should avoid using it. No replacement was suggested.
Generating optimized autoload files
composer/package-versions-deprecated: Generating version class...
composer/package-versions-deprecated: ...done generating version class
51 packages you are using are looking for funding.
Use the `composer fund` command to find out more!
> ComposerVendorHtaccessCreator::onEvent
> ComposerVendorHtaccessCreator::onEvent
$ php ../composer-2.0.phar update
> ComposerHookHandler::onPreUpdate
Loading composer repositories with package information
Updating dependencies                                 
Nothing to modify in lock file
Installing dependencies from lock file (including require-dev)
Package operations: 0 installs, 0 updates, 0 removals
Package phpunit/php-token-stream is abandoned, you should avoid using it. No replacement was suggested.
Generating optimized autoload files
composer/package-versions-deprecated: Generating version class...
composer/package-versions-deprecated: ...done generating version class
51 packages you are using are looking for funding.
Use the `composer fund` command to find out more!
> ComposerVendorHtaccessCreator::onEvent

This LGTM, on the whole. The 1.x -> 2.x and plugin update in the same go not working is the only workflow that isn't great, but easily "fixed" on the local install by running composer again if necessary. The other install/upgrade/setup paths seems good.

Is there any other way we can improve that from the composer/composer plugin side? Maybe a question for composer upstream. I'm sure there won't just be MW installs where someone might upgrade the plugin (by upgrading MW, in this case; similar upgrades for other software distributions) and composer all in one go. It's obviously a bit of an edge case though

https://gerrit.wikimedia.org/r/c/mediawiki/core/+/660984 works for MediaWiki at least, and should visibly notify people that their MW install might be broken till something is remedied.

mcaskill commented 3 years ago

Trying to update to composer 2.0 and plugin 1.5 in one go (still needs two composer update as discussed)

With the latest commit, 4195d45, the upgrade process was working fine for me (merged dependencies get uninstalled under 1.4 but are reinstall at the end of the update to 1.5).

Can you provide me with your composer.json and composer.lock so I can test it out.

This LGTM, on the whole. The 1.x -> 2.x and plugin update in the same go not working is the only workflow that isn't great, but easily "fixed" on the local install by running composer again if necessary. The other install/upgrade/setup paths seems good.

Is there any other way we can improve that from the composer/composer plugin side? Maybe a question for composer upstream. I'm sure there won't just be MW installs where someone might upgrade the plugin (by upgrading MW, in this case; similar upgrades for other software distributions) and composer all in one go. It's obviously a bit of an edge case though

I've left the new instruction in the README regarding updating with Composer 1 first but this section might benefit from additional details.

gerrit.wikimedia.org/r/c/mediawiki/core/+/660984 works for MediaWiki at least, and should visibly notify people that their MW install might be broken till something is remedied.

👍

reedy commented 3 years ago

Trying to update to composer 2.0 and plugin 1.5 in one go (still needs two composer update as discussed)

With the latest commit, 4195d45, the upgrade process was working fine for me (merged dependencies get uninstalled under 1.4 but are reinstall at the end of the update to 1.5).

As per the output, that is the version it upgraded to. But it required a second composer run to do it

  • Upgrading wikimedia/composer-merge-plugin (v1.4.1 => dev-feature/composer-v2 4195d45)

I'll get the files together and post them.

reedy commented 3 years ago

Ok, to be fun... There's a composer.json and composer.lock, and there's a composer.local.json which then includes 6 other composer.json files from a tree. I imagine you don't want to close numerous large repos :)

So I've made a tarzip (github doesn't allow upload of tar in comments) of this... I noticed it doesn't actually install the dependancies from the other composer.json files included via composer.local.json config...

mwcomposer.zip

But confirmed, if I setup vendor on 1.x, composer update on plugin 1.5/composer 2.x it does remove everything. A second run brings them back...

$ php /var/www/wiki/mediawiki/composer-2.0.phar update
The "wikimedia/composer-merge-plugin" plugin was skipped because it requires a Plugin API version ("^1.0") that does not match your Composer installation ("2.0.0"). You may need to run composer update with the "--no-plugins" option.
Class ComposerHookHandler is not autoloadable, can not call pre-update-cmd script
Loading composer repositories with package information
Updating dependencies                                 
Lock file operations: 0 installs, 1 update, 24 removals
  - Removing beberlei/assert (v3.3.0)
  - Removing brick/math (0.9.2)
  - Removing christian-riesen/base32 (1.4.0)
  - Removing defuse/php-encryption (v2.2.1)
  - Removing fgrosse/phpasn1 (v2.2.0)
  - Removing firebase/php-jwt (v5.2.0)
  - Removing jakobo/hotp-php (v2.0.0)
  - Removing lcobucci/jwt (3.4.3)
  - Removing league/event (2.2.0)
  - Removing league/oauth2-server (dev-v9.0.0-alpha a00cc3c)
  - Removing league/uri (6.4.0)
  - Removing league/uri-components (2.3.0)
  - Removing league/uri-interfaces (2.2.0)
  - Removing psr/http-factory (1.0.1)
  - Removing ramsey/collection (1.1.3)
  - Removing ramsey/uuid (4.1.1)
  - Removing spomky-labs/base64url (v2.0.4)
  - Removing spomky-labs/cbor-php (v2.0.1)
  - Removing symfony/process (v5.2.3)
  - Removing web-auth/cose-lib (v3.2.10)
  - Removing web-auth/metadata-service (v3.2.10)
  - Removing web-auth/webauthn-lib (v3.2.10)
  - Removing wikimedia/css-sanitizer (v3.0.1)
  - Removing wikimedia/equivset (1.4.2)
  - Upgrading wikimedia/composer-merge-plugin (v1.4.1 => dev-feature/composer-v2 4195d45)
Writing lock file
Installing dependencies from lock file (including require-dev)
Package operations: 0 installs, 1 update, 24 removals
  - Syncing wikimedia/composer-merge-plugin (dev-feature/composer-v2 4195d45) into cache
  - Removing wikimedia/equivset (1.4.2)
  - Removing wikimedia/css-sanitizer (v3.0.1)
  - Removing web-auth/webauthn-lib (v3.2.10)
  - Removing web-auth/metadata-service (v3.2.10)
  - Removing web-auth/cose-lib (v3.2.10)
  - Removing symfony/process (v5.2.3)
  - Removing spomky-labs/cbor-php (v2.0.1)
  - Removing spomky-labs/base64url (v2.0.4)
  - Removing ramsey/uuid (4.1.1)
  - Removing ramsey/collection (1.1.3)
  - Removing psr/http-factory (1.0.1)
  - Removing league/uri-interfaces (2.2.0)
  - Removing league/uri-components (2.3.0)
  - Removing league/uri (6.4.0)
  - Removing league/oauth2-server (dev-v9.0.0-alpha a00cc3c)
  - Removing league/event (2.2.0)
  - Removing lcobucci/jwt (3.4.3)
  - Removing jakobo/hotp-php (v2.0.0)
  - Removing firebase/php-jwt (v5.2.0)
  - Removing fgrosse/phpasn1 (v2.2.0)
  - Removing defuse/php-encryption (v2.2.1)
  - Removing christian-riesen/base32 (1.4.0)
  - Removing brick/math (0.9.2)
  - Removing beberlei/assert (v3.3.0)
  - Removing wikimedia/composer-merge-plugin (v1.4.1)
  - Installing wikimedia/composer-merge-plugin (dev-feature/composer-v2 4195d45): Cloning 4195d45d1c from cache
Package phpunit/php-token-stream is abandoned, you should avoid using it. No replacement was suggested.
Generating optimized autoload files
composer/package-versions-deprecated: Generating version class...
composer/package-versions-deprecated: ...done generating version class
39 packages you are using are looking for funding.
Use the `composer fund` command to find out more!
Class ComposerVendorHtaccessCreator is not autoloadable, can not call post-update-cmd script
$ php /var/www/wiki/mediawiki/composer-2.0.phar update
Class ComposerHookHandler is not autoloadable, can not call pre-update-cmd script
Loading composer repositories with package information
Updating dependencies                                 
Lock file operations: 24 installs, 0 updates, 0 removals
  - Locking beberlei/assert (v3.3.0)
  - Locking brick/math (0.9.2)
  - Locking christian-riesen/base32 (1.4.0)
  - Locking defuse/php-encryption (v2.2.1)
  - Locking fgrosse/phpasn1 (v2.2.0)
  - Locking firebase/php-jwt (v5.2.0)
  - Locking jakobo/hotp-php (v2.0.0)
  - Locking lcobucci/jwt (3.4.3)
  - Locking league/event (2.2.0)
  - Locking league/oauth2-server (dev-v9.0.0-alpha a00cc3c)
  - Locking league/uri (6.4.0)
  - Locking league/uri-components (2.3.0)
  - Locking league/uri-interfaces (2.2.0)
  - Locking psr/http-factory (1.0.1)
  - Locking ramsey/collection (1.1.3)
  - Locking ramsey/uuid (4.1.1)
  - Locking spomky-labs/base64url (v2.0.4)
  - Locking spomky-labs/cbor-php (v2.0.1)
  - Locking symfony/process (v5.2.3)
  - Locking web-auth/cose-lib (v3.2.10)
  - Locking web-auth/metadata-service (v3.2.10)
  - Locking web-auth/webauthn-lib (v3.2.10)
  - Locking wikimedia/css-sanitizer (v3.0.1)
  - Locking wikimedia/equivset (1.4.2)
Writing lock file
Installing dependencies from lock file (including require-dev)
Package operations: 24 installs, 0 updates, 0 removals
  - Syncing league/oauth2-server (dev-v9.0.0-alpha a00cc3c) into cache
  - Installing brick/math (0.9.2): Extracting archive
  - Installing christian-riesen/base32 (1.4.0): Extracting archive
  - Installing fgrosse/phpasn1 (v2.2.0): Extracting archive
  - Installing firebase/php-jwt (v5.2.0): Extracting archive
  - Installing jakobo/hotp-php (v2.0.0): Extracting archive
  - Installing league/event (2.2.0): Extracting archive
  - Installing lcobucci/jwt (3.4.3): Extracting archive
  - Installing defuse/php-encryption (v2.2.1): Extracting archive
  - Installing league/oauth2-server (dev-v9.0.0-alpha a00cc3c): Cloning a00cc3c274 from cache
  - Installing league/uri-interfaces (2.2.0): Extracting archive
  - Installing league/uri (6.4.0): Extracting archive
  - Installing league/uri-components (2.3.0): Extracting archive
  - Installing ramsey/collection (1.1.3): Extracting archive
  - Installing psr/http-factory (1.0.1): Extracting archive
  - Installing beberlei/assert (v3.3.0): Extracting archive
  - Installing web-auth/metadata-service (v3.2.10): Extracting archive
  - Installing web-auth/cose-lib (v3.2.10): Extracting archive
  - Installing symfony/process (v5.2.3): Extracting archive
  - Installing spomky-labs/cbor-php (v2.0.1): Extracting archive
  - Installing spomky-labs/base64url (v2.0.4): Extracting archive
  - Installing ramsey/uuid (4.1.1): Extracting archive
  - Installing web-auth/webauthn-lib (v3.2.10): Extracting archive
  - Installing wikimedia/css-sanitizer (v3.0.1): Extracting archive
  - Installing wikimedia/equivset (1.4.2): Extracting archive
12 package suggestions were added by new dependencies, use `composer suggest` to see details.
Package phpunit/php-token-stream is abandoned, you should avoid using it. No replacement was suggested.
Generating optimized autoload files
composer/package-versions-deprecated: Generating version class...
composer/package-versions-deprecated: ...done generating version class
51 packages you are using are looking for funding.
Use the `composer fund` command to find out more!
Class ComposerVendorHtaccessCreator is not autoloadable, can not call post-update-cmd script
prudloff-insite commented 3 years ago

@mcaskill I tried 4195d45d1c6aceef57a1658dca9ace09fcd8a311. It seems to work correctly with Composer 2. But with Composer 1, it crashes with this error:

PHP Fatal error:  Uncaught Error: Call to undefined method Composer\Installer::setUpdateAllowList() in /mnt/data/workspace/drupal/vendor/wikimedia/composer-merge-plugin/src/MergePlugin.php:374
Stack trace:
#0 [internal function]: Wikimedia\Composer\MergePlugin->onPostInstallOrUpdate()
#1 /usr/share/php/Composer/EventDispatcher/EventDispatcher.php(164): call_user_func()
#2 /usr/share/php/Composer/EventDispatcher/EventDispatcher.php(96): Composer\EventDispatcher\EventDispatcher->doDispatch()
#3 /usr/share/php/Composer/Installer.php(338): Composer\EventDispatcher\EventDispatcher->dispatchScript()
#4 /usr/share/php/Composer/Command/InstallCommand.php(122): Composer\Installer->run()
#5 /usr/share/php/Symfony/Component/Console/Command/Command.php(255): Composer\Command\InstallCommand->execute()
#6 /usr/share/php/Symfony/Component/Console/Application.php(934): Symfony\Component\Console\Command\Command->run()
#7 /usr/share/php/Symfony/Component/Console/Application.php(273): Symfony\Component\Console\Application->doRunCommand()
#8 /usr in /mnt/data/workspace/drupal/vendor/wikimedia/composer-merge-plugin/src/MergePlugin.php on line 374
mcaskill commented 3 years ago

Oh, yeah, I should take into account that Installer::setUpdateAllowList() was only added in Composer 1.10.8 replace Installer::setUpdateWhitelist(). I'll fix that.

mcaskill commented 3 years ago

This morning while updating a client project, I stumbled upon the same bug as @AntoninSlejska and @chrissnyder2337:

Fatal error: Uncaught Error: Call to undefined method Wikimedia\Composer\Merge\ExtraPackage::getMergedRequirements() in phar:///usr/local/bin/composer/src/Composer/Plugin/PluginManager.php(220) : eval()'d code:287
Stack trace:
#0 phar:///usr/local/bin/composer/src/Composer/Plugin/PluginManager.php(220) : eval()'d code(254): Wikimedia\Composer\MergePlugin_composer_tmp0->mergeFile(Object(Composer\Package\RootPackage), 'composer.local....')
#1 phar:///usr/local/bin/composer/src/Composer/Plugin/PluginManager.php(220) : eval()'d code(215): Wikimedia\Composer\MergePlugin_composer_tmp0->mergeFiles(Array, false)
#2 [internal function]: Wikimedia\Composer\MergePlugin_composer_tmp0->onInstallUpdateOrDump(Object(Composer\Script\Event))
#3 phar:///usr/local/bin/composer/src/Composer/EventDispatcher/EventDispatcher.php(172): call_user_func(Array, Object(Composer\Script\Event))
#4 phar:///usr/local/bin/composer/src/Composer/EventDispatcher/EventDispatcher.php(101): Composer\EventDispatcher\EventDispatcher->doDispatch(Object(Composer\Sc in phar:///usr/local/bin/composer/src/Composer/Plugin/PluginManager.php(220) : eval()'d code on line 287

When it occurred, the plugin was being updated from a8c5a89 (pre-getMergedRequirements()) to b8b8cb4. After this error was raised, I consulted the vendor directory and it contained the correct copy of files.

@Seldaek Sorry to bother you. Do you have any idea why there would be a discrepancy?

Maybe it has something to do when composer update is initially executed, the plugin at a8c5a89 is loaded into memory, packages are updated including the plugin (now at b8b8cb4) which is then loaded into memory causing a conflict.

Seldaek commented 3 years ago

Yeah Composer makes an effort to load the new version of the plugin class after an upgrade, by renaming the class in memory and loading that, but if the plugin has dependencies that are already loaded those do not get updated and there you may get trouble. This works well for plugins which are in a self-contained class. See https://github.com/composer/composer/blob/40095b20dc58772b7b630e1ca41f024fda7eae44/src/Composer/Plugin/PluginManager.php#L204-L223 for details but this won't really help you here.

The solution would be to make the new code check for method existence I guess and abort if it's missing, to make the upgrade smoother. Or put all the relevant/required logic in that one class..

tstarling commented 3 years ago

It looks like this class renaming issue has been there since before composer 2.0, so there's no obvious need to deal with it in the same PR.

mcaskill commented 3 years ago

I'm thinking of adding a method exists check on ExtraPackage::getMergedRequirements() from MergePlugin and display a message to that effect.

tstarling commented 3 years ago

I was thinking we could change the namespace to Wikimedia\Composer\Merge2. We'd increment the namespace version number every time there is a similar breaking change.

mcaskill commented 3 years ago

Thanks for merging.

I'm not satisfied with the last outstanding issue of the outdated ExtraPackage. I'm surprised we had not stumbled upon the issue sooner with the addition of PluginState::isComposer1() that is used in most classes.

We could introduce a similar class-swapping mechanism as Composer's PluginManager or introducing a versioned namespace to the Merge Plugin.

Instead of Wikimedia\Composer\Merge2, maybe Wikimedia\Composer\Merge\V2 and move MergePlugin into it as well?

reedy commented 3 years ago

Sounds reasonable to me. PR welcome ;)

It's probably easiest to just start a PR (if you're willing to work on this issue too), and then it's a bit easier to make a decision about naming when we have a more concrete example. Renaming/moving things around is easy enough while it's still WIP

mcaskill commented 3 years ago

I might not be able to start work on this over the weekend. If someone else jumps on the occasion, I am available to contribute to that pull request.

This is indeed turning into a major update (1.4 → 2.0) rather than a minor update (1.4 → 1.5).

reedy commented 3 years ago

I don't think needing to bump the major version is a particular issue. There's some that would argue (and some that wouldn't!) that bumping the required PHP version alone does that.

mcaskill commented 3 years ago

Of course. I'm bringing this up in relation to @tstarling proposing to rename the namespace to force Composer to load the new classes once it boots the new MergePlugin class.

hexmode commented 3 years ago

This is indeed turning into a major update (1.4 → 2.0) rather than a minor update (1.4 → 1.5).

Agreed.

Since this is distributed on Packagist and packagist.org states under Managing package versions: "The use of Semantic Versioning is strongly encouraged", it makes sense to use semantic versioning.

SemVer says "increment the... MAJOR version when you make incompatible API changes" and since you need to use composer2 to use this update, bumping the major version makes sense.

reedy commented 3 years ago

Since this is distributed on Packagist and packagist.org states under Managing package versions: "The use of Semantic Versioning is strongly encouraged", it makes sense to use semantic versioning.

SemVer says "increment the... MAJOR version when you make incompatible API changes" and since you need to use composer2 to use this update, bumping the major version makes sense.

Unfortunately, many projects don't see it that way.

hexmode commented 3 years ago

Unfortunately, many projects don't see it that way.

Two of the links you provide talk about specific situations, but don't really make broader recommendations relevant to this situation. The first link is about the problems caused when including composer.lock files. We're talking about version numbers.

The second, Composer, semver, and underlying dependency changes, does offer some good insights into what we might do here. The post makes an attempt at offering some best practices.

The third link ostensibly refutes the second link, but is really just talking about some of the problems they ran into. I think this sentence is telling:

So who's to blame? Let's point the finger at myself first: we should have updated league/commonmark sooner.

This is another best practice, keep your dependencies up to date (which is kind of what this whole "Add support for Composer v2" is about).

Best practices (what the second post offered) are not a panacea. Composer isn't perfect, but if used with best practices, suffering is reduced.

But, with all this in mind and after reading the "Composer, semver, and underlying dependency changes" link, I think only a minor version bump is appropriate.