Jean85/pretty-package-versions

ReplacedPackageException thrown for packages that are listen as "replaced", yet not desired

Closed this issue · 7 comments

I'm experiencing an unexpected behaviour, after upgrading to your v2.x. The ReplacedPackageException exception is thrown when a target package is listed in the replace section of my root composer.json. I fail to understand why this is needed...

Thus, when I attempt to obtain the version of one of my packages, e.g. aedart/athenaeum-console, from my unit or integration tests, the mentioned exception is thrown.

Additional Context

I am working on upgrading several packages in my Athenaeum project, which is a mono-repository. As such, all packages that are offered, are listed inside my root composer.json in the replacesection.
E.g.:

{
    "replace": {
        "aedart/athenaeum-acl": "self.version",
        "aedart/athenaeum-audit": "self.version",
        "aedart/athenaeum-circuits": "self.version",
        "aedart/athenaeum-collections": "self.version",
    }
}

(Entire composer.json content is not shown in the above example.)

The replace section is not something that I manually maintain, but rather something that Monorepo Builder does for me.

I'm sure that you have a good reason / edge-case when you must throw the mentioned exception, yet I have no idea how to avoid it in my particular case...

Umh that's an interesting corner case. Normally, when replacing, you shouldn't have a clear version available, because in that row of composer.json you have a constraint (or often a *).

In your case, you're basically proxying toward the root package, so maybe we could handle this in some way. The exceptions are thrown here:

protected static function checkReplacedPackages(string $packageName): void
{
if (! method_exists(InstalledVersions::class, 'getAllRawData')) {
if (isset(InstalledVersions::getRawData()['versions'][$packageName]['replaced'])) {
throw ReplacedPackageException::create($packageName);
}
return;
}
foreach (InstalledVersions::getAllRawData() as $installed) {
if (isset($installed['versions'][$packageName]['replaced'])) {
throw ReplacedPackageException::create($packageName);
}
}
}

I would need to dig further on what Composer returns if you set the replace with self.version but, if that can be intercepted, we could rework and avoid throwing in that case.

Happy to review a PR if you care to open one!

Perhaps expand the if statement and check the value if replace, that it does not match self.version (or actual version of root package)?

Yet, before I create a PR for such, I too must understand the inner-works of composer better - do you think that my proposed solution cause issues for someone else?

No I don't, because in other cases it would have thrown the exception.

I'll try to spent some time on a PR towards the weekend. In the meantime, I hope someone else reviews this issue and provides some more feedback.

Hi @Jean85

I spent some time trying to understand the core data structure that you use and found that it actually lacks enough meta information. As it stands right now, any proposed solution - changes in your code - would only amount to a strange work-around. Therefore, I have submitted an issue at composer, asking for improvement of the vendor/composer/installed.php data structure.
Please review composer/composer#10478, and leave this issue standing, until an appropriate solution has been found at composer (hopefully the maintainers will agree to either of my proposed solutions).

Hi again.
I have created a PR for composer (composer/composer#10494), which will provide more meta information about replaced and provided packages. I hope they will accept it, because it will be a starting point for solving this edge-case.

Sadly, I'm not going to be able to solve this in any good way. I will try to find a different solution.