From e3af4ee606023079d8af222da82a5b6f36ca879f Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 24 Feb 2021 15:46:54 +0100 Subject: [PATCH 1/4] Include stdout as well as stderr if git stash/diff/.. fails, fixes #9720 --- src/Composer/Downloader/GitDownloader.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Composer/Downloader/GitDownloader.php b/src/Composer/Downloader/GitDownloader.php index b5bd73db6..54b67983e 100644 --- a/src/Composer/Downloader/GitDownloader.php +++ b/src/Composer/Downloader/GitDownloader.php @@ -519,7 +519,7 @@ class GitDownloader extends VcsDownloader implements DvcsDownloaderInterface { $path = $this->normalizePath($path); if (0 !== $this->process->execute('git clean -df && git reset --hard', $output, $path)) { - throw new \RuntimeException("Could not reset changes\n\n:".$this->process->getErrorOutput()); + throw new \RuntimeException("Could not reset changes\n\n:".$output); } $this->hasDiscardedChanges[$path] = true; @@ -533,7 +533,7 @@ class GitDownloader extends VcsDownloader implements DvcsDownloaderInterface { $path = $this->normalizePath($path); if (0 !== $this->process->execute('git stash --include-untracked', $output, $path)) { - throw new \RuntimeException("Could not stash changes\n\n:".$this->process->getErrorOutput()); + throw new \RuntimeException("Could not stash changes\n\n:".$output); } $this->hasStashedChanges[$path] = true; @@ -547,7 +547,7 @@ class GitDownloader extends VcsDownloader implements DvcsDownloaderInterface { $path = $this->normalizePath($path); if (0 !== $this->process->execute('git diff HEAD', $output, $path)) { - throw new \RuntimeException("Could not view diff\n\n:".$this->process->getErrorOutput()); + throw new \RuntimeException("Could not view diff\n\n:".$output); } $this->io->writeError($output); From 07f59a91624611b3c91477d2a1e6f6a2ce45aa5e Mon Sep 17 00:00:00 2001 From: John Stevenson Date: Fri, 5 Mar 2021 18:57:47 +0000 Subject: [PATCH 2/4] Preserve file permissions on Windows self-update (#9733) Windows file operations result in different file permissions depending if the file is copied or moved. A copy operation applies permissions from the destination folder (or file if it already exists and does not use inheritance), while a move operation generally preserves the source file permissions. Windows PHP `rename` uses MoveFileEx so if the user is running as an admin and the destination is in a common (non-user) location, then the permission for other users will be replaced by the admin user. Likewise for the UAC elevation feature, which uses the cmd.exe `move` command. This fix uses copy and delete operations on Windows, so that other users can continue to run composer.phar --- src/Composer/Command/SelfUpdateCommand.php | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/src/Composer/Command/SelfUpdateCommand.php b/src/Composer/Command/SelfUpdateCommand.php index ea0831818..54750d4ad 100644 --- a/src/Composer/Command/SelfUpdateCommand.php +++ b/src/Composer/Command/SelfUpdateCommand.php @@ -425,7 +425,14 @@ TAGSPUBKEY } try { - rename($newFilename, $localFilename); + if (Platform::isWindows()) { + // use copy to apply permissions from the destination directory + // as rename uses source permissions and may block other users + copy($newFilename, $localFilename); + @unlink($newFilename); + } else { + rename($newFilename, $localFilename); + } return true; } catch (\Exception $e) { @@ -534,7 +541,7 @@ TAGSPUBKEY /** * Invokes a UAC prompt to update composer.phar as an admin * - * Uses a .vbs script to elevate and run the cmd.exe move command. + * Uses a .vbs script to elevate and run the cmd.exe copy command. * * @param string $localFilename The composer.phar location * @param string $newFilename The downloaded or backup phar @@ -560,13 +567,13 @@ TAGSPUBKEY $checksum = hash_file('sha256', $newFilename); - // cmd's internal move is fussy about backslashes + // cmd's internal copy is fussy about backslashes $source = str_replace('/', '\\', $newFilename); $destination = str_replace('/', '\\', $localFilename); $vbs = <<writeError('Operation succeeded.'); + @unlink($newFilename); } else { - $io->writeError('Operation failed (file not written). '.$helpMessage.''); + $io->writeError('Operation failed.'.$helpMessage.''); } return $result; From 4f47a4c6679f6cedada38704f35f725bb8f5511d Mon Sep 17 00:00:00 2001 From: John Stevenson Date: Fri, 5 Mar 2021 19:05:36 +0000 Subject: [PATCH 3/4] Proxy handling docs and tweaks (#9735) --- src/Composer/Util/Http/CurlDownloader.php | 1 - src/Composer/Util/Http/ProxyHelper.php | 2 ++ src/Composer/Util/RemoteFilesystem.php | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Composer/Util/Http/CurlDownloader.php b/src/Composer/Util/Http/CurlDownloader.php index 5e9494aed..7e4753496 100644 --- a/src/Composer/Util/Http/CurlDownloader.php +++ b/src/Composer/Util/Http/CurlDownloader.php @@ -183,7 +183,6 @@ class CurlDownloader } $options['http']['header'] = $this->authHelper->addAuthenticationHeader($options['http']['header'], $origin, $url); - // Merge in headers - we don't get any proxy values $options = StreamContextFactory::initOptions($url, $options, true); foreach (self::$options as $type => $curlOptions) { diff --git a/src/Composer/Util/Http/ProxyHelper.php b/src/Composer/Util/Http/ProxyHelper.php index 4ca7a4bc8..e291f5b81 100644 --- a/src/Composer/Util/Http/ProxyHelper.php +++ b/src/Composer/Util/Http/ProxyHelper.php @@ -131,12 +131,14 @@ class ProxyHelper $error = sprintf('malformed %s url', $envName); $proxy = parse_url($proxyUrl); + // We need parse_url to have identified a host if (!isset($proxy['host'])) { throw new \RuntimeException($error); } $proxyUrl = self::formatParsedUrl($proxy, true); + // We need a port because streams and curl use different defaults if (!parse_url($proxyUrl, PHP_URL_PORT)) { throw new \RuntimeException($error); } diff --git a/src/Composer/Util/RemoteFilesystem.php b/src/Composer/Util/RemoteFilesystem.php index ea899b312..8b8e0c3e0 100644 --- a/src/Composer/Util/RemoteFilesystem.php +++ b/src/Composer/Util/RemoteFilesystem.php @@ -254,7 +254,7 @@ class RemoteFilesystem $ctx = StreamContextFactory::getContext($fileUrl, $options, array('notification' => array($this, 'callbackGet'))); - $proxy = ProxyManager::getInstance()->getProxyForRequest($fileUrl); + $proxy = $this->proxyManager->getProxyForRequest($fileUrl); $usingProxy = $proxy->getFormattedUrl(' using proxy (%s)'); $this->io->writeError((strpos($origFileUrl, 'http') === 0 ? 'Downloading ' : 'Reading ') . Url::sanitize($origFileUrl) . $usingProxy, true, IOInterface::DEBUG); unset($origFileUrl, $proxy, $usingProxy); From 0879e80d56f5a0f5df63f5f3dc2b133aeaf52083 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 9 Mar 2021 09:54:06 +0100 Subject: [PATCH 4/4] Fix functional tests to use the same PHP version as PHPUnit runs with --- tests/Composer/Test/AllFunctionalTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Composer/Test/AllFunctionalTest.php b/tests/Composer/Test/AllFunctionalTest.php index c29b4f279..75f942c1a 100644 --- a/tests/Composer/Test/AllFunctionalTest.php +++ b/tests/Composer/Test/AllFunctionalTest.php @@ -78,7 +78,7 @@ class AllFunctionalTest extends TestCase } } - $proc = new Process('php -dphar.readonly=0 '.escapeshellarg('./bin/compile'), $target); + $proc = new Process((defined('PHP_BINARY') ? escapeshellcmd(PHP_BINARY) : 'php').' -dphar.readonly=0 '.escapeshellarg('./bin/compile'), $target); $exitcode = $proc->run(); if ($exitcode !== 0 || trim($proc->getOutput())) { @@ -110,7 +110,7 @@ class AllFunctionalTest extends TestCase 'COMPOSER_CACHE_DIR' => $this->testDir.'cache', ); - $cmd = 'php '.escapeshellarg(self::$pharPath).' --no-ansi '.$testData['RUN']; + $cmd = (defined('PHP_BINARY') ? escapeshellcmd(PHP_BINARY) : 'php') .' '.escapeshellarg(self::$pharPath).' --no-ansi '.$testData['RUN']; $proc = new Process($cmd, $this->testDir, $env, null, 300); $output = '';