fix(extension-manager): stop re-checking for updates in the same process that ran composer update (#4764) - #5043
Open
wakqasahmed wants to merge 1 commit into
Conversation
…ess that ran composer update GlobalUpdateHandler, MinorUpdateHandler, and MajorUpdateHandler all run composer update in-process (ComposerAdapter runs Composer's own Application::run() directly, not a subprocess), then synchronously dispatch FlarumUpdated. ReCheckForUpdates' listener responds to that by immediately dispatching CheckForUpdates, which constructor-injects a Guzzle Client -- constructing it touches whatever HTTP handler class Guzzle picks, and if composer just bumped Guzzle itself to a version with a new class (e.g. GuzzleHttp\Handler\CurlVersion, added in 7.12.0), the still-running process's autoloader has no idea that class exists, and it fatals. This isn't fixable by making the recheck a queued job: Job\Dispatcher only defers to the queue when it isn't the sync driver, and Flarum defaults to SyncQueue for any install that hasn't configured Redis or a database queue -- which just runs the job immediately, in the same process, same as today. The one process boundary that's actually reliable regardless of queue config is a fresh request: the frontend already does a full window.location.reload() after a synchronous global update succeeds. So this skips the recheck entirely for FlarumUpdated (all three types share the exact same in-process-composer-then-synchronous-dispatch shape), while leaving the Updated event (single extension installs) untouched -- that path wasn't reported broken and is a narrower composer operation. LastUpdateRun still records a successful run; the limitedPackages diff it used to compute from the now-skipped check is left empty, since there's no safe way to compute it without touching Guzzle in this same process.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #4764.
GlobalUpdateHandler,MinorUpdateHandler, andMajorUpdateHandlerall runcomposer updatein-process —ComposerAdapter::run()calls Composer's ownApplication::run()directly rather than shelling out — then synchronously dispatchFlarumUpdated.ReCheckForUpdatesresponds to that by immediately dispatchingCheckForUpdates, whose handler constructor-injects a GuzzleClient. Constructing that client touches whatever HTTP handler class Guzzle picks, and if the update just bumped Guzzle itself onto a version with a new class —GuzzleHttp\Handler\CurlVersion, added in 7.12.0 — the still-running process's autoloader has no idea that class exists yet, and it fatals mid-request. That's exactly the reported crash upgrading to v2.0.0-rc.4.I looked at just queuing the recheck instead, since
Job\Dispatcheralready exists and is used for the outerGlobalUpdate/CheckForUpdatescommands. It doesn't actually solve this:Job\Dispatcher::dispatch()only defers to the queue when the configured queue isn'tSyncQueue, and Flarum defaults toSyncQueuefor any install that hasn't set up Redis or a database queue — which just runs the job immediately, in the same process, identical to today. So for the common case (no queue configured), queuing wouldn't have changed anything.The one process boundary that's reliable regardless of queue configuration is a fresh HTTP request. The frontend already does a full
window.location.reload()after a synchronous global update succeeds (ControlSectionState.ts,updateGlobally()), which is a genuinely new PHP-FPM/php-cgi worker in essentially every real deployment.So this skips the recheck entirely when handling
FlarumUpdated— global, minor, and major core updates all share the identical in-process-composer-then-synchronous-dispatch shape, confirmed by reading all three handlers. TheUpdatedevent (single-extension installs) is untouched; that path wasn't reported broken and is a narrower composer operation on its own.LastUpdateRunstill records the run as successful; thelimitedPackagesdiff it used to compute by comparing before/after update-check results is left empty, since there's no safe way to get that comparison without constructing the Guzzle client in this same process. Happy to revisit if there's a way to compute it I'm missing, or if you'd rather see this handled differently — e.g. an explicit "recovery/recheck" action surfaced in the panel after a global update, or genuinely running composer in a subprocess instead of in-process, which would be a much bigger change.Added a unit test covering all three
FlarumUpdatedtypes (asserting the bus is never dispatched for any of them, whileLastUpdateRunstill records success) and confirming theUpdatedpath is untouched, and updated the one existing integration test that asserted on the oldlimitedPackagesbehavior.Verified with a disposable
php:8.3-clicontainer: the new unit test suite passes (4 tests, 14 assertions), andphp -lon all three changed files. I couldn't get the actual integration-level "run composer update against a live fixture" test working in this environment — its fixture pins dependencies that only declare support through PHP 8.1, and Composer's current security-advisory check blocks installing that combination under PHP 8.3 here. That's a pre-existing limitation of the test fixture/environment, not something this change introduces, but I want to flag it rather than claim more verification than I actually got.