Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Second run of the latest composer install results in error: Call to undefined method Composer\Util\Http\ProxyManager::needsTransitionWarning() #11940

Closed
lubikx opened this issue Apr 20, 2024 · 40 comments · Fixed by #11955
Labels
Milestone

Comments

@lubikx
Copy link

lubikx commented Apr 20, 2024

My composer.json:

{
    "name": "laravel/laravel",
    "type": "project",
    "description": "The Laravel Framework.",
    "keywords": [
        "framework",
        "laravel"
    ],
    "license": "MIT",
    "require": {
        "arcanedev/log-viewer": "^8.0",
        "doctrine/dbal": "^2.12.1",
        "fideloper/proxy": "^4.4.1",
        "guzzlehttp/guzzle": "^7.0.1",
        "inspector-apm/inspector-laravel": "^4.7",
        "laravel/framework": "^8.0",
        "laravel/tinker": "^3.0",
        "laravel/ui": "^3.0",
        "laravelcollective/html": "^6.2.0",
        "maatwebsite/excel": "^3.1",
        "sentry/sentry-laravel": "*",
        "yoeunes/notify": "^1.0.6"
    },
    "require-dev": {
        "barryvdh/laravel-debugbar": "^3.5",
        "barryvdh/laravel-ide-helper": "^2.12",
        "beyondcode/laravel-dump-server": "^1.6",
        "filp/whoops": "^2.9",
        "fzaninotto/faker": "*",
        "mockery/mockery": "^1.4",
        "nunomaduro/collision": "^5.0",
        "nunomaduro/larastan": "^0.6.2",
        "phpstan/phpstan": "^0.12",
        "phpunit/phpunit": "^9.0"
    },
    "config": {
        "optimize-autoloader": true,
        "preferred-install": "dist",
        "sort-packages": true
    },
    "extra": {
        "laravel": {
            "dont-discover": []
        }
    },
    "autoload": {
        "psr-4": {
            "App\\": "app/"
        },
        "classmap": [
            "database/seeds",
            "database/factories"
        ]
    },
    "autoload-dev": {
        "psr-4": {
            "Tests\\": "tests/"
        }
    },
    "minimum-stability": "dev",
    "prefer-stable": true,
    "scripts": {
        "post-autoload-dump": [
            "Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
            "@php artisan package:discover --ansi"
        ],
        "post-root-package-install": [
            "@php -r \"file_exists('.env') || copy('.env.example', '.env');\""
        ],
        "post-create-project-cmd": [
            "@php artisan key:generate --ansi"
        ]
    }
}

Output of composer diagnose:

Checking composer.json: WARNING
require.sentry/sentry-laravel : unbound version constraints (*) should be avoided
Checking platform settings: OK
Checking git settings: OK git version 2.34.1
Checking http connectivity to packagist: OK
Checking https connectivity to packagist: OK
Checking github.com rate limit: OK
Checking disk free space: OK
Checking pubkeys: FAIL
Missing pubkey for tags verification
Missing pubkey for dev verification
Run composer self-update --update-keys to set them up
Checking Composer version: OK
Checking Composer and its dependencies for vulnerabilities: OK
Composer version: 2.7.3
PHP version: 8.1.27
PHP binary path: /usr/bin/php8.1
OpenSSL version: OpenSSL 3.0.2 15 Mar 2022
cURL version: 7.81.0 libz 1.2.11 ssl OpenSSL/3.0.2
zip: extension present, unzip present, 7-Zip not available

When I run this command:

rm -rf vendor
# first run works correctly
composer install --prefer-dist --optimize-autoloader -vvv
# second run will give error described below
composer install --prefer-dist --optimize-autoloader -vvv

I get the following output:

Running 2.7-dev+9f84f0c32bdf15bce9e6cf14a96dec8b2bd443c4 (2024-04-19 19:40:58) with PHP 8.1.27 on Linux / 5.15.0-105-generic
Reading ./composer.json (/home/ploi/xxx/composer.json)
Loading config file ./composer.json (/home/ploi/xxx/composer.json)
Checked CA file /etc/pki/tls/certs/ca-bundle.crt does not exist or it is not a file.
Checked directory /etc/pki/tls/certs/ca-bundle.crt does not exist or it is not a directory.
Checked CA file /etc/ssl/certs/ca-certificates.crt: valid
Executing command (/home/ploi/xxx): 'git' 'branch' '-a' '--no-color' '--no-abbrev' '-v'
Failed to initialize global composer: Composer could not find the config file: /home/ploi/.config/composer/composer.json

Reading ./composer.lock (/home/ploi/xxx/composer.lock)
Reading /home/ploi/xxx/vendor/composer/installed.json
Installing dependencies from lock file (including require-dev)
Verifying lock file contents can be installed on current platform.
Reading ./composer.lock (/home/ploi/xxx/composer.lock)
Built pool.
Generating rules
Resolving dependencies through SAT
Looking at all rules.

Dependency resolution completed in 0.000 seconds
Nothing to install, update or remove
Package swiftmailer/swiftmailer is abandoned, you should avoid using it. Use symfony/mailer instead.
Package yoeunes/notify is abandoned, you should avoid using it. Use php-flasher/flasher-laravel instead.
Package fzaninotto/faker is abandoned, you should avoid using it. No replacement was suggested.
Generating optimized autoload files
> post-autoload-dump: Illuminate\Foundation\ComposerScripts::postAutoloadDump
> post-autoload-dump: @php artisan package:discover --ansi
Executing command (CWD): '/usr/bin/php8.1' -d allow_url_fopen='1' -d disable_functions='' -d memory_limit='-1' artisan package:discover --ansi
Discovered Package: arcanedev/log-viewer
Discovered Package: barryvdh/laravel-debugbar
Discovered Package: barryvdh/laravel-ide-helper
Discovered Package: beyondcode/laravel-dump-server
Discovered Package: fideloper/proxy
Discovered Package: inspector-apm/inspector-laravel
Discovered Package: laravel/tinker
Discovered Package: laravel/ui
Discovered Package: laravelcollective/html
Discovered Package: maatwebsite/excel
Discovered Package: nesbot/carbon
Discovered Package: nunomaduro/collision
Discovered Package: sentry/sentry-laravel
Discovered Package: yoeunes/notify
Package manifest generated successfully.
105 packages you are using are looking for funding.
Use the `composer fund` command to find out more!

In Application.php line 403:
                                                                                      
  [Error]                                                                             
  Call to undefined method Composer\Util\Http\ProxyManager::needsTransitionWarning()  
                                                                                      

Exception trace:
  at phar:///usr/local/bin/composer/src/Composer/Console/Application.php:403
 Composer\Console\Application->doRun() at phar:///usr/local/bin/composer/vendor/symfony/console/Application.php:171
 Symfony\Component\Console\Application->run() at phar:///usr/local/bin/composer/src/Composer/Console/Application.php:147
 Composer\Console\Application->run() at phar:///usr/local/bin/composer/bin/composer:93
 require() at /usr/local/bin/composer:30

And I expected this to happen:

I expect the second run to also end without error. Tried the snapshot composer version and the error is still present.

The error is on this line: https://github.com/composer/composer/blame/9f84f0c32bdf15bce9e6cf14a96dec8b2bd443c4/src/Composer/Console/Application.php#L403

And we can see that the bug was introduced 3 days ago in a big pull request: #11915

@lubikx
Copy link
Author

lubikx commented Apr 20, 2024

@Seldaek and/or @johnstevenson maybe you could spot the problem immediately?

@cgrisar
Copy link

cgrisar commented Apr 20, 2024

I concur.

Having the same issues when deploying with laravel forge.

Checked the log files and it seems there was a commit yesterday on ProxyManager?

I could still deploy yesterday

@lubikx
Copy link
Author

lubikx commented Apr 20, 2024

UPDATE: Ignore this comment, I misread the commit while reading it directly on github

I think the problem is that without any proxy set the ProxyManager::getProxyForScheme method returns null and that's why needsTransitionWarning is not defined.

So maybe just checking for null at the offending line will suffice?

https://github.com/composer/composer/blame/9f84f0c32bdf15bce9e6cf14a96dec8b2bd443c4/src/Composer/Console/Application.php#L403

@cgrisar
Copy link

cgrisar commented Apr 20, 2024

I returned to composer 2.7.2 and it did the trick.

I had my moments of cold sweat though...

@johnstevenson
Copy link
Member

@lubikx ProxyManager::getProxyForScheme returns a RequestProxy instance, but the issue is that the ProxyManager::needsTransitionWarning() method is undefined, which should not be the case:

public function needsTransitionWarning(): bool

@xolf
Copy link

xolf commented Apr 20, 2024

Important: Please apply the fix of @Seldaek, see #11940 (comment). My suggested solution to roll back to the previous composer version is not sustainable and only a quick fix. If you are interested in a dirty quick fix, checkout my solution below.

In my case the readme/metrics package was requiring composer itself, which lead to the deployment issues.

$ composer why composer/composer -t
composer/composer 2.7.3 Composer helps you declare, manage and install dependencies of PHP projects. It ensures you have the right stack everywhere.
└──readme/metrics 11.0.2 (requires composer/composer ^2.0)
   └──spreadly/spreadly.app (requires readme/metrics ^11.0)

@cgrisar wrote, (see comment)

I returned to composer 2.7.2 and it did the trick

I had my moments of cold sweat though...

For all other Laravel Forge user stumbling across this problem. Create a Recipe with the script /usr/local/bin/composer self-update --rollback and run it on all affected servers to rollback your Composer version.

@lubikx
Copy link
Author

lubikx commented Apr 20, 2024

Also temporarily solved by downgrading.

For ploi.io users you can use this command because rollback was not available on my ploi machines:

sudo /usr/local/bin/composer self-update 2.7.2

@lubikx
Copy link
Author

lubikx commented Apr 20, 2024

Rather interesting find.

If I remove composer.lock and run the install command twice with version 2.7.3 (which basically performs update and then install from lock file), the error is no longer there even if I delete vendor directory and run install again twice from the new lock file.

So it is somehow connected to the contents of a lock file and potentially to Laravel because that's what @xolf and me are using.

@xolf can you also try?

@Seldaek
Copy link
Member

Seldaek commented Apr 20, 2024

I'm guessing this happens because you have composer itself in the vendor dir, in an older version than 2.7.3.. So first install happens fine but second one it ends up loading an outdated ProxyManager file from vendor and that one has the new method missing?

@Seldaek
Copy link
Member

Seldaek commented Apr 20, 2024

And if that's indeed the issue then I would think a solution is to run: rm -rf vendor/composer/composer && composer update composer/composer

Sorry so many composer in that cmd but that should ensure update can run without failure as the package is gone from vendor and then it can update it to latest.

@cgrisar
Copy link

cgrisar commented Apr 20, 2024 via email

@cgrisar
Copy link

cgrisar commented Apr 21, 2024

@cgrisar wrote, (see comment)

I returned to composer 2.7.2 and it did the trick
I had my moments of cold sweat though...

For all other Laravel Forge user stumbling across this problem. Create a Recipe with the script /usr/local/bin/composer self-update --rollback and run it on all affected servers to rollback your Composer version.

Doesn't work for me.
I specifically need to instruct the last working version number

In my case it is /usr/local/bin/composer self-update 2.7.2

BEWARE!
Forge's schedulers updates composer on a daily basis.
You can pause the scheduler

@lubikx
Copy link
Author

lubikx commented Apr 22, 2024

I'm guessing this happens because you have composer itself in the vendor dir, in an older version than 2.7.3.. So first install happens fine but second one it ends up loading an outdated ProxyManager file from vendor and that one has the new method missing?

@Seldaek, it looks like you are right, but then this is a systemic problem of the composer.

This would mean it can anytime mix different composer class versions even though it should only use what is in /usr/local/bin/composer phar.

I have tried deleting vendor/composer directory, running composer install against original lock file and it was successful.

Running composer install again after this leads to the problem described in this issue.

@Seldaek
Copy link
Member

Seldaek commented Apr 22, 2024

Have you tried the proposed fix in #11940 (comment)

The systemic issue imo is people requiring composer in their dependencies.. This is usually a bad idea. I'm not sure why you have it. You can run and share output of composer why composer/composer -t

Then maybe we can look at why this is there and if it could be removed.

@SteJW
Copy link

SteJW commented Apr 22, 2024

I also downgraded to 2.7.2 and it works again. I'm using ploi server management.

@CoolGoose
Copy link

+1 on rolling back to 2.7.2 makes it work again (basic ci/cd setup with chialab/php:8.2 and laravel 8 php artisan testing

@Seldaek
Copy link
Member

Seldaek commented Apr 22, 2024

Please stop with the +1 it's just noise. Of course downgrading works but it's a workaround it doesn't fix anything and doesn't let you upgrade which is not a long term solution. A better workaround would be upgrading composer in your dependencies, but even better as I just wrote above is figuring out why it's in the dependencies and how to get rid of it.

xolf added a commit to xolf/metrics-sdks that referenced this issue Apr 22, 2024
Requiring composer itself might lead to a difference between the project composer version and the locally installed version. By this no composer actions can be performed any more in case of breaking changes.

See composer/composer#11940
@CoolGoose
Copy link

@Seldaek I apologies for the noise. Seems in our case it's an old version of larastan.

$ composer why composer/composer -t
composer/composer 2.7.2 Composer helps you declare, manage and install dependencies of PHP projects. It ensures you have the right stack everywhere.
`--nunomaduro/larastan 1.0.4 (requires composer/composer ^1.0 || ^2.0)
   `--laravel/laravel dev-main (requires (for development) nunomaduro/larastan ^1.0)

@cgrisar
Copy link

cgrisar commented Apr 22, 2024 via email

@jbrooksuk
Copy link
Contributor

jbrooksuk commented Apr 22, 2024

We've received several reports of this on Laravel Forge.

In some cases, it looks like the offending package is barryvdh/laravel-ide-helper which requires composer/composer on version 2.7.0 and above. 3.0.0 no longer requires Composer itself. In others, it's older versions of nunomaduro/larastan.

Looping @barryvdh into this thread.

@Seldaek
Copy link
Member

Seldaek commented Apr 22, 2024

For all packages which were fixed and already don't require composer anymore the fix is clear: upgrade these packages to a newer version.

@lubikx
Copy link
Author

lubikx commented Apr 22, 2024

@Seldaek, I still think you should treat this as an issue of the composer itself.

As seen above, the composer can appear in the vendor directory just because of other dependencies, and solving this in those packages every time is cumbersome and, in the end, not a long-term solution (it can reappear anytime).

Also, invisible problems from using different code versions and even security issues can go completely unnoticed if there is no visible error in the command itself.

I really think the correct behaviour is to not autoload classes from the composer in the vendor directory when it is running from another place.

@barryvdh
Copy link
Sponsor Contributor

We've received several reports of this on Laravel Forge.

In some cases, it looks like the offending package is barryvdh/laravel-ide-helper which requires composer/composer on version 2.7.0 and above. 3.0.0 no longer requires Composer itself. In others, it's older versions of nunomaduro/larastan.

Looping @barryvdh into this thread.

The composer dependency has been removed since v2.12.2, in feb 2022, so if they updated in the last 2 years, that should not be a problem.
Not really sure if I can do anything about it (now)

@Seldaek
Copy link
Member

Seldaek commented Apr 22, 2024

Yes I'll try to work on a fix because of the amount of people affected here but I'm also on vacation this week so I'll do that when i manage.. In the meantime trying to help out from my phone.

And autoloading always from the phar is not as easy as it sounds but yes it'd be nice to be able to do that i agree. For composer/composer itself it might be doable but for dependencies of composer it gets trickier as plugins/scripts might rely on the exact version that's installed but composer might be more flexible and allow running with all versions.. I guess ideally we would autoload dependencies we are compatible with from vendor and the rest from the phar to ensure composer runs fine.

@barryvdh
Copy link
Sponsor Contributor

A short-term fix for this specific case might be to check the method exists?

            $proxyManager = ProxyManager::getInstance();
            if (method_exists($proxyManager, 'needsTransitionWarning') && $proxyManager->needsTransitionWarning()) {
                $io->writeError('');
                $io->writeError('<warning>Composer now requires separate proxy environment variables for HTTP and HTTPS requests.</warning>');
...

If you want I can PR this.

Or if it's because the order of autoloading, instantiate the proxymanager earlier?

I tried replicating it by running from source, but it doesn't seem to trigger the error then.

@barryvdh
Copy link
Sponsor Contributor

barryvdh commented Apr 22, 2024

Or if it's because the order of autoloading, instantiate the proxymanager earlier?

I tried replicating it by running from source, but it doesn't seem to trigger the error then.

My bad, I could replicate it. If I move the loading of the instance before the running of the command, it seems to be fixed (for this case).

Could you try #11943 ?

(So clone composer to a directory, run composer install there and try with and without my patch by running php <dir to your composer>/bin/composer install --prefer-dist --optimize-autoloader -vvv)

@hivokas
Copy link

hivokas commented Apr 22, 2024

I use Statamic which uses composer package under the hood.

This command fixed the issue for me:

rm -rf vendor/composer/composer && composer update composer/composer

Thanks @duncanmcclean for posting the solution here statamic/cms#9945 (comment)

@Seldaek
Copy link
Member

Seldaek commented Apr 22, 2024

Can someone please post an easy way to reproduce this with an open source repo I can clone, or a complete composer.json that just works without anything else posted here? No Laravel Forge or other stuff in the loop please.

I tried with the original post's composer.json but no luck reproducing this..

I have a feeling for how this happens and I'm sure #11943 will work around this particular instance of the problem, but I'd still like to have a conclusive way to repro so I can analyse it further later and try to find a more future proof solution.

@Seldaek
Copy link
Member

Seldaek commented Apr 22, 2024

Composer 2.7.4 is now out which hopefully fixes it. I'd still appreciate a repro case though as per my post above.

@Seldaek Seldaek added this to the 2.7 milestone Apr 22, 2024
@Seldaek Seldaek added the Bug label Apr 22, 2024
@barryvdh
Copy link
Sponsor Contributor

barryvdh commented Apr 22, 2024

Can someone please post an easy way to reproduce this with an open source repo I can clone, or a complete composer.json that just works without anything else posted here? No Laravel Forge or other stuff in the loop please.

composer create-project laravel/laravel:^9 laravel9
cd laravel9
composer require nunomaduro/larastan:1.x composer/composer:2.5.x --dev
composer  install --prefer-dist --optimize-autoloader -vvv

You need a package that install composer/composer, but need an older version (eg 2.5).
(If you update to the latest versions, it doesn't happen)

@barryvdh
Copy link
Sponsor Contributor

Composer 2.7.4 is now out which hopefully fixes it. I'd still appreciate a repro case though as per my post above.

Can confirm that this fixes the above test case. Thanks for the effort while you're on vacation! Hope you can enjoy the rest of it ;)

@Seldaek
Copy link
Member

Seldaek commented Apr 22, 2024

Ah ok, yeah I tried that already but it wasn't triggering, because I was missing some script handlers.. Your example works fine if you run the last composer install with --no-scripts.

It seems the problem is that:

    "scripts": {
        "post-autoload-dump": [
            "Illuminate\\Foundation\\ComposerScripts::postAutoloadDump"
        ]
    },

Calls

        require_once $event->getComposer()->getConfig()->get('vendor-dir').'/autoload.php';

To ensure that all vendor dir packages are usable in the process I guess.. and then that registers the project's autoloader, which prepends it and thus overrides Composer's native autoloader..

I don't really see a way to fix this without dirty hackery in vendor/composer/autoload_real.php, but I'll give it some more thought.

@barryvdh
Copy link
Sponsor Contributor

barryvdh commented Apr 22, 2024

Maybe it would be possible to sandbox the event scripts? I think you're using a separate process for php/other scripts already, so maybe you could fire of a composer command to execute callables also? Might be slower, but doesn't have any side effects on the main process probably?

@Seldaek
Copy link
Member

Seldaek commented Apr 22, 2024

That'd for sure cause breakage tho as some php scripts are used to hook into and modify composer settings or behavior.

But I'd argue those laravel scripts running a full command would be better done as an actual command instead of php script handler.

The problem tho is preventing misuse is very difficult and even if it gets fixed old versions linger on as we saw above.. So a solution on the composer side would be nice.

@barryvdh
Copy link
Sponsor Contributor

Hmm I was looking why Laravel was doing a callback instead of a script, but it appears it might have been myself who was responsible for this change.. laravel/laravel#3699

At the time, Laravel used a 'compiled' file which included all the actual files (not just classmap) so when an update was ran, it needed to clear the compiled file. It couldn't be cleared before the update, because the vendor dir was not always available: #5066 so it was moved to post-update; laravel/laravel#3687
But this would cause a mismatch between versions in the compiled.php vs new vendor files so instead of running an artisan script, it just called the clear scripts directly (laravel/framework#12827), bypassing any compiled bootstrapping.

But Laravel doesn't use the compiled file anymore (since a very long time). It would be possible to revert the changes now probably, to just use php artisan clear-compiled. But this would require every existing app to update their application composer.json.
Alternatively, the Laravel composer scripts could be modified to not load the entire application, but would also require updates (although for the framework, which would be easier to upgrade but still would leave lots of legacy available.

From the Composer side, we could compare autoloaders before/after running the callbacks. Eg. #11948
This could still lead to issues if the application actually loads classes (because I don't think you can unload them after use), or when the application tries to use a class that is already loaded by composer, but that would not impact Composer directly.

@buddy94
Copy link

buddy94 commented Apr 23, 2024

Rather interesting find.

If I remove composer.lock and run the install command twice with version 2.7.3 (which basically performs update and then install from lock file), the error is no longer there even if I delete vendor directory and run install again twice from the new lock file.

So it is somehow connected to the contents of a lock file and potentially to Laravel because that's what @xolf and me are using.

@xolf can you also try?

For me just removing the composer.lock worked fine!

@Seldaek
Copy link
Member

Seldaek commented Apr 28, 2024

@barryvdh thanks for the idea and the help looking into this on the Laravel end, see #11955 - it's an alternative impl of the same, which I was able to validate in various conditions. For sure it's not 100% fool proof but it should help prevent most accidental issues like this one.

@mxr576
Copy link
Contributor

mxr576 commented Apr 29, 2024

How about resurrecting my previous idea about generating scoped versions of Composer that package maintainers can require?
#11884

As you may know, Drupal's new Automated updates feature highly relies on Composer therefore installs its a non-dev dependency.
https://git.drupalcode.org/project/drupal/-/blob/10.2.5/composer.json?ref_type=tags#L22

@Seldaek
Copy link
Member

Seldaek commented Apr 29, 2024

My opinion on that hasn't changed sorry :)

Note that having composer in the deps is not really a problem in itself. It was triggering further issues in Laravel there because they include the application's autoloader in the script handler, and that case will be fixed as well. So we harden ourselves against these problems more and more as we find them, making scoping less and less needed.

@mxr576
Copy link
Contributor

mxr576 commented Apr 29, 2024

🤞 and fair enough :) and I'll keep an eye on these kinds of issues to collect ammunition for the scoped use case ;)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.