From 44bb82b50f7d6920c43ea705ae94dbc6498176b0 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 10 Mar 2021 14:34:26 +0100 Subject: [PATCH 1/5] Make full functional test output more reliable --- .../Test/Fixtures/functional/installed-versions/Hooks.php | 8 ++++---- .../functional/installed-versions/plugin-a/PluginA.php | 8 ++++---- .../functional/installed-versions/plugin-b/PluginB.php | 8 ++++---- .../functional/installed-versions2/plugin-a/PluginA.php | 8 ++++---- .../functional/installed-versions2/plugin-b/PluginB.php | 8 ++++---- 5 files changed, 20 insertions(+), 20 deletions(-) diff --git a/tests/Composer/Test/Fixtures/functional/installed-versions/Hooks.php b/tests/Composer/Test/Fixtures/functional/installed-versions/Hooks.php index b215d1e52..31faaae16 100644 --- a/tests/Composer/Test/Fixtures/functional/installed-versions/Hooks.php +++ b/tests/Composer/Test/Fixtures/functional/installed-versions/Hooks.php @@ -8,13 +8,13 @@ class Hooks { public static function preUpdate(Event $event) { - echo '!!PreUpdate:'.JsonFile::encode(InstalledVersions::getInstalledPackages(), 320)."\n"; - echo '!!Versions:console:'.InstalledVersions::getVersion('symfony/console').';process:'.InstalledVersions::getVersion('symfony/process').';filesystem:'.InstalledVersions::getVersion('symfony/filesystem')."\n"; + fwrite(STDERR, '!!PreUpdate:'.JsonFile::encode(InstalledVersions::getInstalledPackages(), 320)."\n"); + fwrite(STDERR, '!!Versions:console:'.InstalledVersions::getVersion('symfony/console').';process:'.InstalledVersions::getVersion('symfony/process').';filesystem:'.InstalledVersions::getVersion('symfony/filesystem')."\n"); } public static function postUpdate(Event $event) { - echo '!!PostUpdate:'.JsonFile::encode(InstalledVersions::getInstalledPackages(), 320)."\n"; - echo '!!Versions:console:'.InstalledVersions::getVersion('symfony/console').';process:'.InstalledVersions::getVersion('symfony/process').';filesystem:'.InstalledVersions::getVersion('symfony/filesystem')."\n"; + fwrite(STDERR, '!!PostUpdate:'.JsonFile::encode(InstalledVersions::getInstalledPackages(), 320)."\n"); + fwrite(STDERR, '!!Versions:console:'.InstalledVersions::getVersion('symfony/console').';process:'.InstalledVersions::getVersion('symfony/process').';filesystem:'.InstalledVersions::getVersion('symfony/filesystem')."\n"); } } diff --git a/tests/Composer/Test/Fixtures/functional/installed-versions/plugin-a/PluginA.php b/tests/Composer/Test/Fixtures/functional/installed-versions/plugin-a/PluginA.php index 224012c6f..8e40ac318 100644 --- a/tests/Composer/Test/Fixtures/functional/installed-versions/plugin-a/PluginA.php +++ b/tests/Composer/Test/Fixtures/functional/installed-versions/plugin-a/PluginA.php @@ -10,10 +10,10 @@ class PluginA implements PluginInterface { public function activate(Composer $composer, IOInterface $io) { - echo '!!PluginAInit'.JsonFile::encode(InstalledVersions::getInstalledPackages(), 320)."\n"; - echo '!!PluginA:'.(InstalledVersions::isInstalled('plugin/a') ? InstalledVersions::getVersion('plugin/a') : 'null')."\n"; - echo '!!PluginB:'.(InstalledVersions::isInstalled('plugin/b') ? InstalledVersions::getVersion('plugin/b') : 'null')."\n"; - echo '!!Versions:console:'.InstalledVersions::getVersion('symfony/console').';process:'.InstalledVersions::getVersion('symfony/process').';filesystem:'.InstalledVersions::getVersion('symfony/filesystem')."\n"; + fwrite(STDERR, '!!PluginAInit'.JsonFile::encode(InstalledVersions::getInstalledPackages(), 320)."\n"); + fwrite(STDERR, '!!PluginA:'.(InstalledVersions::isInstalled('plugin/a') ? InstalledVersions::getVersion('plugin/a') : 'null')."\n"); + fwrite(STDERR, '!!PluginB:'.(InstalledVersions::isInstalled('plugin/b') ? InstalledVersions::getVersion('plugin/b') : 'null')."\n"); + fwrite(STDERR, '!!Versions:console:'.InstalledVersions::getVersion('symfony/console').';process:'.InstalledVersions::getVersion('symfony/process').';filesystem:'.InstalledVersions::getVersion('symfony/filesystem')."\n"); } public function deactivate(Composer $composer, IOInterface $io) diff --git a/tests/Composer/Test/Fixtures/functional/installed-versions/plugin-b/PluginB.php b/tests/Composer/Test/Fixtures/functional/installed-versions/plugin-b/PluginB.php index 1315f52c5..65a8c81d8 100644 --- a/tests/Composer/Test/Fixtures/functional/installed-versions/plugin-b/PluginB.php +++ b/tests/Composer/Test/Fixtures/functional/installed-versions/plugin-b/PluginB.php @@ -10,10 +10,10 @@ class PluginB implements PluginInterface { public function activate(Composer $composer, IOInterface $io) { - echo '!!PluginBInit'.JsonFile::encode(InstalledVersions::getInstalledPackages(), 320)."\n"; - echo '!!PluginA:'.(InstalledVersions::isInstalled('plugin/a') ? InstalledVersions::getVersion('plugin/a') : 'null')."\n"; - echo '!!PluginB:'.(InstalledVersions::isInstalled('plugin/b') ? InstalledVersions::getVersion('plugin/b') : 'null')."\n"; - echo '!!Versions:console:'.InstalledVersions::getVersion('symfony/console').';process:'.InstalledVersions::getVersion('symfony/process').';filesystem:'.InstalledVersions::getVersion('symfony/filesystem')."\n"; + fwrite(STDERR, '!!PluginBInit'.JsonFile::encode(InstalledVersions::getInstalledPackages(), 320)."\n"); + fwrite(STDERR, '!!PluginA:'.(InstalledVersions::isInstalled('plugin/a') ? InstalledVersions::getVersion('plugin/a') : 'null')."\n"); + fwrite(STDERR, '!!PluginB:'.(InstalledVersions::isInstalled('plugin/b') ? InstalledVersions::getVersion('plugin/b') : 'null')."\n"); + fwrite(STDERR, '!!Versions:console:'.InstalledVersions::getVersion('symfony/console').';process:'.InstalledVersions::getVersion('symfony/process').';filesystem:'.InstalledVersions::getVersion('symfony/filesystem')."\n"); } public function deactivate(Composer $composer, IOInterface $io) diff --git a/tests/Composer/Test/Fixtures/functional/installed-versions2/plugin-a/PluginA.php b/tests/Composer/Test/Fixtures/functional/installed-versions2/plugin-a/PluginA.php index 224012c6f..8e40ac318 100644 --- a/tests/Composer/Test/Fixtures/functional/installed-versions2/plugin-a/PluginA.php +++ b/tests/Composer/Test/Fixtures/functional/installed-versions2/plugin-a/PluginA.php @@ -10,10 +10,10 @@ class PluginA implements PluginInterface { public function activate(Composer $composer, IOInterface $io) { - echo '!!PluginAInit'.JsonFile::encode(InstalledVersions::getInstalledPackages(), 320)."\n"; - echo '!!PluginA:'.(InstalledVersions::isInstalled('plugin/a') ? InstalledVersions::getVersion('plugin/a') : 'null')."\n"; - echo '!!PluginB:'.(InstalledVersions::isInstalled('plugin/b') ? InstalledVersions::getVersion('plugin/b') : 'null')."\n"; - echo '!!Versions:console:'.InstalledVersions::getVersion('symfony/console').';process:'.InstalledVersions::getVersion('symfony/process').';filesystem:'.InstalledVersions::getVersion('symfony/filesystem')."\n"; + fwrite(STDERR, '!!PluginAInit'.JsonFile::encode(InstalledVersions::getInstalledPackages(), 320)."\n"); + fwrite(STDERR, '!!PluginA:'.(InstalledVersions::isInstalled('plugin/a') ? InstalledVersions::getVersion('plugin/a') : 'null')."\n"); + fwrite(STDERR, '!!PluginB:'.(InstalledVersions::isInstalled('plugin/b') ? InstalledVersions::getVersion('plugin/b') : 'null')."\n"); + fwrite(STDERR, '!!Versions:console:'.InstalledVersions::getVersion('symfony/console').';process:'.InstalledVersions::getVersion('symfony/process').';filesystem:'.InstalledVersions::getVersion('symfony/filesystem')."\n"); } public function deactivate(Composer $composer, IOInterface $io) diff --git a/tests/Composer/Test/Fixtures/functional/installed-versions2/plugin-b/PluginB.php b/tests/Composer/Test/Fixtures/functional/installed-versions2/plugin-b/PluginB.php index 1315f52c5..65a8c81d8 100644 --- a/tests/Composer/Test/Fixtures/functional/installed-versions2/plugin-b/PluginB.php +++ b/tests/Composer/Test/Fixtures/functional/installed-versions2/plugin-b/PluginB.php @@ -10,10 +10,10 @@ class PluginB implements PluginInterface { public function activate(Composer $composer, IOInterface $io) { - echo '!!PluginBInit'.JsonFile::encode(InstalledVersions::getInstalledPackages(), 320)."\n"; - echo '!!PluginA:'.(InstalledVersions::isInstalled('plugin/a') ? InstalledVersions::getVersion('plugin/a') : 'null')."\n"; - echo '!!PluginB:'.(InstalledVersions::isInstalled('plugin/b') ? InstalledVersions::getVersion('plugin/b') : 'null')."\n"; - echo '!!Versions:console:'.InstalledVersions::getVersion('symfony/console').';process:'.InstalledVersions::getVersion('symfony/process').';filesystem:'.InstalledVersions::getVersion('symfony/filesystem')."\n"; + fwrite(STDERR, '!!PluginBInit'.JsonFile::encode(InstalledVersions::getInstalledPackages(), 320)."\n"); + fwrite(STDERR, '!!PluginA:'.(InstalledVersions::isInstalled('plugin/a') ? InstalledVersions::getVersion('plugin/a') : 'null')."\n"); + fwrite(STDERR, '!!PluginB:'.(InstalledVersions::isInstalled('plugin/b') ? InstalledVersions::getVersion('plugin/b') : 'null')."\n"); + fwrite(STDERR, '!!Versions:console:'.InstalledVersions::getVersion('symfony/console').';process:'.InstalledVersions::getVersion('symfony/process').';filesystem:'.InstalledVersions::getVersion('symfony/filesystem')."\n"); } public function deactivate(Composer $composer, IOInterface $io) From 76524088298f3e31a5f68a5c04d851e26b990c0d Mon Sep 17 00:00:00 2001 From: Brandon Kelly Date: Thu, 11 Mar 2021 15:18:41 -0800 Subject: [PATCH 2/5] Only call sapi_windows_set_ctrl_handler() for CLI requests (#9771) --- src/Composer/Command/CreateProjectCommand.php | 2 +- src/Composer/Installer/InstallationManager.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Composer/Command/CreateProjectCommand.php b/src/Composer/Command/CreateProjectCommand.php index b029b1c6d..d7cfd1039 100644 --- a/src/Composer/Command/CreateProjectCommand.php +++ b/src/Composer/Command/CreateProjectCommand.php @@ -405,7 +405,7 @@ EOT } } // handler Ctrl+C for Windows on PHP 7.4+ - if (function_exists('sapi_windows_set_ctrl_handler')) { + if (function_exists('sapi_windows_set_ctrl_handler') && PHP_SAPI === 'cli') { @mkdir($directory, 0777, true); if ($realDir = realpath($directory)) { sapi_windows_set_ctrl_handler(function () use ($realDir) { diff --git a/src/Composer/Installer/InstallationManager.php b/src/Composer/Installer/InstallationManager.php index 8cbf62668..6b42dd8ed 100644 --- a/src/Composer/Installer/InstallationManager.php +++ b/src/Composer/Installer/InstallationManager.php @@ -208,7 +208,7 @@ class InstallationManager }; $handleInterruptsUnix = function_exists('pcntl_async_signals') && function_exists('pcntl_signal'); - $handleInterruptsWindows = function_exists('sapi_windows_set_ctrl_handler'); + $handleInterruptsWindows = function_exists('sapi_windows_set_ctrl_handler') && PHP_SAPI === 'cli'; $prevHandler = null; $windowsHandler = null; if ($handleInterruptsUnix) { From 2f4b99eacdb3172d6a7ef33c3c14a3c61376abd0 Mon Sep 17 00:00:00 2001 From: Stephan Date: Mon, 15 Mar 2021 12:35:16 +0000 Subject: [PATCH 3/5] MaxFileSizeException should reject download job (#9778) --- src/Composer/Downloader/FileDownloader.php | 4 +++ src/Composer/Util/Http/CurlDownloader.php | 29 +++++++++++++--------- 2 files changed, 21 insertions(+), 12 deletions(-) diff --git a/src/Composer/Downloader/FileDownloader.php b/src/Composer/Downloader/FileDownloader.php index 06c080374..0c84276de 100644 --- a/src/Composer/Downloader/FileDownloader.php +++ b/src/Composer/Downloader/FileDownloader.php @@ -225,6 +225,10 @@ class FileDownloader implements DownloaderInterface, ChangeReportInterface throw $e; } + if ($e instanceof MaxFileSizeExceededException) { + throw $e; + } + if ($e instanceof TransportException) { // if we got an http response with a proper code, then requesting again will probably not help, abort if ((0 !== $e->getCode() && !in_array($e->getCode(), array(500, 502, 503, 504))) || !$retries) { diff --git a/src/Composer/Util/Http/CurlDownloader.php b/src/Composer/Util/Http/CurlDownloader.php index 7e4753496..83c55f945 100644 --- a/src/Composer/Util/Http/CurlDownloader.php +++ b/src/Composer/Util/Http/CurlDownloader.php @@ -377,16 +377,7 @@ class CurlDownloader $e->setResponseInfo($progress); } - if (is_resource($job['headerHandle'])) { - fclose($job['headerHandle']); - } - if (is_resource($job['bodyHandle'])) { - fclose($job['bodyHandle']); - } - if ($job['filename']) { - @unlink($job['filename'].'~'); - } - call_user_func($job['reject'], $e); + $this->rejectJob($job, $e); } } @@ -403,12 +394,12 @@ class CurlDownloader if (isset($this->jobs[$i]['options']['max_file_size'])) { // Compare max_file_size with the content-length header this value will be -1 until the header is parsed if ($this->jobs[$i]['options']['max_file_size'] < $progress['download_content_length']) { - throw new MaxFileSizeExceededException('Maximum allowed download size reached. Content-length header indicates ' . $progress['download_content_length'] . ' bytes. Allowed ' . $this->jobs[$i]['options']['max_file_size'] . ' bytes'); + $this->rejectJob($this->jobs[$i], new MaxFileSizeExceededException('Maximum allowed download size reached. Content-length header indicates ' . $progress['download_content_length'] . ' bytes. Allowed ' . $this->jobs[$i]['options']['max_file_size'] . ' bytes')); } // Compare max_file_size with the download size in bytes if ($this->jobs[$i]['options']['max_file_size'] < $progress['size_download']) { - throw new MaxFileSizeExceededException('Maximum allowed download size reached. Downloaded ' . $progress['size_download'] . ' of allowed ' . $this->jobs[$i]['options']['max_file_size'] . ' bytes'); + $this->rejectJob($this->jobs[$i], new MaxFileSizeExceededException('Maximum allowed download size reached. Downloaded ' . $progress['size_download'] . ' of allowed ' . $this->jobs[$i]['options']['max_file_size'] . ' bytes')); } } @@ -521,6 +512,20 @@ class CurlDownloader return new TransportException('The "'.$job['url'].'" file could not be downloaded ('.$errorMessage.')' . $details, $response->getStatusCode()); } + private function rejectJob(array $job, \Exception $e) + { + if (is_resource($job['headerHandle'])) { + fclose($job['headerHandle']); + } + if (is_resource($job['bodyHandle'])) { + fclose($job['bodyHandle']); + } + if ($job['filename']) { + @unlink($job['filename'].'~'); + } + call_user_func($job['reject'], $e); + } + private function checkCurlResult($code) { if ($code != CURLM_OK && $code != CURLM_CALL_MULTI_PERFORM) { From 3b89a9c3b4e57e00cc862cb98bd257f427cf3ff4 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 16 Mar 2021 20:29:57 +0100 Subject: [PATCH 4/5] Also attempt working around Vagrant filesystem issues when installing plugins initially, refs #9627 --- src/Composer/Installer/PluginInstaller.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Composer/Installer/PluginInstaller.php b/src/Composer/Installer/PluginInstaller.php index e41938816..0cbe1ab12 100644 --- a/src/Composer/Installer/PluginInstaller.php +++ b/src/Composer/Installer/PluginInstaller.php @@ -76,6 +76,7 @@ class PluginInstaller extends LibraryInstaller return $promise->then(function () use ($self, $pluginManager, $package, $repo) { try { + Platform::workaroundFilesystemIssues(); $pluginManager->registerPackage($package, true); } catch (\Exception $e) { $self->rollbackInstall($e, $repo, $package); From 63c086c208542db8d8846c01dbc6bb367de83a60 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 16 Mar 2021 20:30:53 +0100 Subject: [PATCH 5/5] Add source package name to debug info when enabling plugins --- src/Composer/Plugin/PluginManager.php | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/Composer/Plugin/PluginManager.php b/src/Composer/Plugin/PluginManager.php index 1505d2e3d..aa9aec082 100644 --- a/src/Composer/Plugin/PluginManager.php +++ b/src/Composer/Plugin/PluginManager.php @@ -229,7 +229,7 @@ class PluginManager $this->registeredPlugins[$package->getName()] = $installer; } elseif (class_exists($class)) { $plugin = new $class(); - $this->addPlugin($plugin, $isGlobalPlugin); + $this->addPlugin($plugin, $isGlobalPlugin, $package); $this->registeredPlugins[$package->getName()] = $plugin; } elseif ($failOnMissingClasses) { throw new \UnexpectedValueException('Plugin '.$package->getName().' could not be initialized, class not found: '.$class); @@ -319,11 +319,19 @@ class PluginManager * programmatically and want to register a plugin class directly this is a valid way * to do it. * - * @param PluginInterface $plugin plugin instance + * @param PluginInterface $plugin plugin instance + * @param ?PackageInterface $sourcePackage Package from which the plugin comes from */ - public function addPlugin(PluginInterface $plugin, $isGlobalPlugin = false) + public function addPlugin(PluginInterface $plugin, $isGlobalPlugin = false, PackageInterface $sourcePackage = null) { - $this->io->writeError('Loading plugin '.get_class($plugin).($isGlobalPlugin ? ' (installed globally)' : ''), true, IOInterface::DEBUG); + $details = array(); + if ($sourcePackage) { + $details[] = 'from '.$sourcePackage->getName(); + } + if ($isGlobalPlugin) { + $details[] = 'installed globally'; + } + $this->io->writeError('Loading plugin '.get_class($plugin).($details ? ' ('.implode(', ', $details).')' : ''), true, IOInterface::DEBUG); $this->plugins[] = $plugin; $plugin->activate($this->composer, $this->io);