From a77e2fb093db7600c95e61e2133ac8a425d7524c Mon Sep 17 00:00:00 2001 From: bogdan Date: Thu, 4 Feb 2016 03:11:18 +0200 Subject: [PATCH 1/3] Removed code duplication with abstract hasMetadataRepository method which checks if vcs metadata is present in the package Fixed a bug with -vvv mode. When .git folder is missing from package in normal mode, -v mode, -vv mode the program throws the exception about missing .git folder in -vvv mode an exception about inability to read logs --- src/Composer/Downloader/GitDownloader.php | 21 +++++++++---- src/Composer/Downloader/HgDownloader.php | 16 ++++++++-- .../Downloader/PerforceDownloader.php | 12 ++++++++ src/Composer/Downloader/SvnDownloader.php | 18 +++++++++-- src/Composer/Downloader/VcsDownloader.php | 27 ++++++++++++----- .../VcsMissingMetadataException.php | 30 +++++++++++++++++++ 6 files changed, 107 insertions(+), 17 deletions(-) create mode 100644 src/Composer/Downloader/VcsMissingMetadataException.php diff --git a/src/Composer/Downloader/GitDownloader.php b/src/Composer/Downloader/GitDownloader.php index 7db561889..d3eb6509c 100644 --- a/src/Composer/Downloader/GitDownloader.php +++ b/src/Composer/Downloader/GitDownloader.php @@ -72,9 +72,8 @@ class GitDownloader extends VcsDownloader public function doUpdate(PackageInterface $initial, PackageInterface $target, $path, $url) { GitUtil::cleanEnv(); - $path = $this->normalizePath($path); - if (!is_dir($path.'/.git')) { - throw new \RuntimeException('The .git directory is missing from '.$path.', see https://getcomposer.org/commit-deps for more information'); + if (!$this->hasMetadataRepository($path)) { + throw new VcsMissingMetadataException('The .git directory is missing from '.$path.', see https://getcomposer.org/commit-deps for more information'); } $ref = $target->getSourceReference(); @@ -100,8 +99,7 @@ class GitDownloader extends VcsDownloader public function getLocalChanges(PackageInterface $package, $path) { GitUtil::cleanEnv(); - $path = $this->normalizePath($path); - if (!is_dir($path.'/.git')) { + if (!$this->hasMetadataRepository($path)) { return; } @@ -371,4 +369,17 @@ class GitDownloader extends VcsDownloader return $path; } + + /** + * Checks if VCS metadata repository has been initialized + * repository example: .git|.svn|.hg + * + * @param string $path + * @return bool + */ + protected function hasMetadataRepository($path) + { + $path = $this->normalizePath($path); + return is_dir($path.'/.git'); + } } diff --git a/src/Composer/Downloader/HgDownloader.php b/src/Composer/Downloader/HgDownloader.php index bec59175d..06fc4fbe9 100644 --- a/src/Composer/Downloader/HgDownloader.php +++ b/src/Composer/Downloader/HgDownloader.php @@ -47,8 +47,8 @@ class HgDownloader extends VcsDownloader $ref = ProcessExecutor::escape($target->getSourceReference()); $this->io->writeError(" Updating to ".$target->getSourceReference()); - if (!is_dir($path.'/.hg')) { - throw new \RuntimeException('The .hg directory is missing from '.$path.', see https://getcomposer.org/commit-deps for more information'); + if (!$this->hasMetadataRepository($path)) { + throw new VcsMissingMetadataException('The .hg directory is missing from '.$path.', see https://getcomposer.org/commit-deps for more information'); } $command = sprintf('hg pull %s && hg up %s', $url, $ref); @@ -84,4 +84,16 @@ class HgDownloader extends VcsDownloader return $output; } + + /** + * Checks if VCS metadata repository has been initialized + * repository example: .git|.svn|.hg + * + * @param string $path + * @return bool + */ + protected function hasMetadataRepository($path) + { + return is_dir($path . '/.hg'); + } } diff --git a/src/Composer/Downloader/PerforceDownloader.php b/src/Composer/Downloader/PerforceDownloader.php index d99fc7997..77f82ac1e 100644 --- a/src/Composer/Downloader/PerforceDownloader.php +++ b/src/Composer/Downloader/PerforceDownloader.php @@ -104,4 +104,16 @@ class PerforceDownloader extends VcsDownloader { $this->perforce = $perforce; } + + /** + * Checks if VCS metadata repository has been initialized + * repository example: .git|.svn|.hg + * + * @param string $path + * @return bool + */ + protected function hasMetadataRepository($path) + { + return true; + } } diff --git a/src/Composer/Downloader/SvnDownloader.php b/src/Composer/Downloader/SvnDownloader.php index 0b4afa9a4..a8a856a33 100644 --- a/src/Composer/Downloader/SvnDownloader.php +++ b/src/Composer/Downloader/SvnDownloader.php @@ -52,8 +52,8 @@ class SvnDownloader extends VcsDownloader SvnUtil::cleanEnv(); $ref = $target->getSourceReference(); - if (!is_dir($path.'/.svn')) { - throw new \RuntimeException('The .svn directory is missing from '.$path.', see https://getcomposer.org/commit-deps for more information'); + if (!$this->hasMetadataRepository($path)) { + throw new VcsMissingMetadataException('The .svn directory is missing from '.$path.', see https://getcomposer.org/commit-deps for more information'); } $flags = ""; @@ -72,7 +72,7 @@ class SvnDownloader extends VcsDownloader */ public function getLocalChanges(PackageInterface $package, $path) { - if (!is_dir($path.'/.svn')) { + if (!$this->hasMetadataRepository($path)) { return; } @@ -188,4 +188,16 @@ class SvnDownloader extends VcsDownloader throw new \RuntimeException("Could not reset changes\n\n:".$this->process->getErrorOutput()); } } + + /** + * Checks if VCS metadata repository has been initialized + * repository example: .git|.svn|.hg + * + * @param string $path + * @return bool + */ + protected function hasMetadataRepository($path) + { + return is_dir($path.'/.svn'); + } } diff --git a/src/Composer/Downloader/VcsDownloader.php b/src/Composer/Downloader/VcsDownloader.php index fe6c9b40f..be6391046 100644 --- a/src/Composer/Downloader/VcsDownloader.php +++ b/src/Composer/Downloader/VcsDownloader.php @@ -111,6 +111,8 @@ abstract class VcsDownloader implements DownloaderInterface, ChangeReportInterfa $this->cleanChanges($initial, $path, true); $urls = $target->getSourceUrls(); + + $exception = null; while ($url = array_shift($urls)) { try { if (Filesystem::isLocalPath($url)) { @@ -118,22 +120,24 @@ abstract class VcsDownloader implements DownloaderInterface, ChangeReportInterfa } $this->doUpdate($initial, $target, $path, $url); break; - } catch (\Exception $e) { + } catch (\Exception $exception) { if ($this->io->isDebug()) { - $this->io->writeError('Failed: ['.get_class($e).'] '.$e->getMessage()); + $this->io->writeError('Failed: ['.get_class($exception).'] '.$exception->getMessage()); } elseif (count($urls)) { $this->io->writeError(' Failed, trying the next URL'); - } else { - // in case of failed update, try to reapply the changes before aborting - $this->reapplyChanges($path); - - throw $e; } } } $this->reapplyChanges($path); + if (!$this->hasMetadataRepository($path) && !$urls && $exception) { + if (!$this->io->isDebug()) { + $this->io->write(' The VCS directory is missing from vendor package, see https://getcomposer.org/commit-deps for more information'); + } + throw $exception; + } + // print the commit logs if in verbose mode if ($this->io->isVerbose()) { $message = 'Pulling in changes:'; @@ -236,4 +240,13 @@ abstract class VcsDownloader implements DownloaderInterface, ChangeReportInterfa * @return string */ abstract protected function getCommitLogs($fromReference, $toReference, $path); + + /** + * Checks if VCS metadata repository has been initialized + * repository example: .git|.svn|.hg + * + * @param string $path + * @return bool + */ + abstract protected function hasMetadataRepository($path); } diff --git a/src/Composer/Downloader/VcsMissingMetadataException.php b/src/Composer/Downloader/VcsMissingMetadataException.php new file mode 100644 index 000000000..f450edcf5 --- /dev/null +++ b/src/Composer/Downloader/VcsMissingMetadataException.php @@ -0,0 +1,30 @@ + + */ +class VcsMissingMetadataException extends \RuntimeException +{ + /** + * Construct the exception. Note: The message is NOT binary safe. + * @link http://php.net/manual/en/exception.construct.php + * @param string $message [optional] The Exception message to throw. + * @param int $code [optional] The Exception code. + * @param \Exception $previous [optional] The previous exception used for the exception chaining. Since 5.3.0 + * @since 5.1.0 + */ + public function __construct($message = '', $code = 0, \Exception $previous = null) + { + parent::__construct("Missing VSC metadata exception: \n".$message, $code, $previous); + } +} \ No newline at end of file From 581ce91f9020b208b558802deaab34f6cbbaed17 Mon Sep 17 00:00:00 2001 From: bogdan Date: Thu, 4 Feb 2016 03:16:39 +0200 Subject: [PATCH 2/3] Implemented new option (ignore-missing-metadata) for composer install command The command allows to slightly change how repository updates are handled during install In the previous version composer failed to updated if .git|.svn|.hg folder was missing from the package In the current version, with the option enabled, if the update fails for exactly this reason, it'll try to remove the package completely and install it from remote --- src/Composer/Command/InstallCommand.php | 2 ++ src/Composer/Downloader/DownloadManager.php | 29 ++++++++++++++++++--- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/src/Composer/Command/InstallCommand.php b/src/Composer/Command/InstallCommand.php index 3ae00c228..6763d24d6 100644 --- a/src/Composer/Command/InstallCommand.php +++ b/src/Composer/Command/InstallCommand.php @@ -48,6 +48,7 @@ class InstallCommand extends Command new InputOption('optimize-autoloader', 'o', InputOption::VALUE_NONE, 'Optimize autoloader during autoloader dump'), new InputOption('classmap-authoritative', 'a', InputOption::VALUE_NONE, 'Autoload classes from the classmap only. Implicitly enables `--optimize-autoloader`.'), new InputOption('ignore-platform-reqs', null, InputOption::VALUE_NONE, 'Ignore platform requirements (php & ext- packages).'), + new InputOption('ignore-missing-metadata', null, InputOption::VALUE_NONE, 'Ignore missing .git|.svn|.hg metadata repositories (when updating or reinstalling).'), new InputArgument('packages', InputArgument::IS_ARRAY | InputArgument::OPTIONAL, 'Should not be provided, use composer require instead to add a given package to composer.json.'), )) ->setHelp(<<getComposer(true, $input->getOption('no-plugins')); $composer->getDownloadManager()->setOutputProgress(!$input->getOption('no-progress')); + $composer->getDownloadManager()->setForceUpdate($input->getOption('ignore-missing-metadata')); $commandEvent = new CommandEvent(PluginEvents::COMMAND, 'install', $input, $output); $composer->getEventDispatcher()->dispatch($commandEvent->getName(), $commandEvent); diff --git a/src/Composer/Downloader/DownloadManager.php b/src/Composer/Downloader/DownloadManager.php index 4d2e9c8f4..684b64111 100644 --- a/src/Composer/Downloader/DownloadManager.php +++ b/src/Composer/Downloader/DownloadManager.php @@ -26,6 +26,7 @@ class DownloadManager private $io; private $preferDist = false; private $preferSource = false; + private $forceUpdate = false; private $filesystem; private $downloaders = array(); @@ -69,6 +70,20 @@ class DownloadManager return $this; } + /** + * set force update mode + * forces to update the repository event when missing metadata + * + * @param $forceUpdate + * @return DownloadManager + */ + public function setForceUpdate($forceUpdate) + { + $this->forceUpdate = (boolean) $forceUpdate; + + return $this; + } + /** * Sets whether to output download progress information for all registered * downloaders @@ -250,11 +265,17 @@ class DownloadManager if ($initialType === $targetType) { $target->setInstallationSource($installationSource); - $downloader->update($initial, $target, $targetDir); - } else { - $downloader->remove($initial, $targetDir); - $this->download($target, $targetDir, 'source' === $installationSource); + try { + $downloader->update($initial, $target, $targetDir); + return; + } catch (VcsMissingMetadataException $ex) { + if ($this->forceUpdate === false) { + throw $ex; + } + } } + $downloader->remove($initial, $targetDir); + $this->download($target, $targetDir, 'source' === $installationSource); } /** From e0fad1f55b147c7f8a93fe746aa5b46ecd3e929b Mon Sep 17 00:00:00 2001 From: bogdan Date: Thu, 4 Feb 2016 23:01:21 +0200 Subject: [PATCH 3/3] Implemented interactive user confirmation on source package update failure Removed unnecessary options for installation Removed unnecessary exception --- src/Composer/Command/InstallCommand.php | 2 -- src/Composer/Downloader/DownloadManager.php | 20 ++----------- src/Composer/Downloader/GitDownloader.php | 2 +- src/Composer/Downloader/HgDownloader.php | 2 +- src/Composer/Downloader/SvnDownloader.php | 2 +- src/Composer/Downloader/VcsDownloader.php | 16 +++++----- .../VcsMissingMetadataException.php | 30 ------------------- 7 files changed, 13 insertions(+), 61 deletions(-) delete mode 100644 src/Composer/Downloader/VcsMissingMetadataException.php diff --git a/src/Composer/Command/InstallCommand.php b/src/Composer/Command/InstallCommand.php index 6763d24d6..3ae00c228 100644 --- a/src/Composer/Command/InstallCommand.php +++ b/src/Composer/Command/InstallCommand.php @@ -48,7 +48,6 @@ class InstallCommand extends Command new InputOption('optimize-autoloader', 'o', InputOption::VALUE_NONE, 'Optimize autoloader during autoloader dump'), new InputOption('classmap-authoritative', 'a', InputOption::VALUE_NONE, 'Autoload classes from the classmap only. Implicitly enables `--optimize-autoloader`.'), new InputOption('ignore-platform-reqs', null, InputOption::VALUE_NONE, 'Ignore platform requirements (php & ext- packages).'), - new InputOption('ignore-missing-metadata', null, InputOption::VALUE_NONE, 'Ignore missing .git|.svn|.hg metadata repositories (when updating or reinstalling).'), new InputArgument('packages', InputArgument::IS_ARRAY | InputArgument::OPTIONAL, 'Should not be provided, use composer require instead to add a given package to composer.json.'), )) ->setHelp(<<getComposer(true, $input->getOption('no-plugins')); $composer->getDownloadManager()->setOutputProgress(!$input->getOption('no-progress')); - $composer->getDownloadManager()->setForceUpdate($input->getOption('ignore-missing-metadata')); $commandEvent = new CommandEvent(PluginEvents::COMMAND, 'install', $input, $output); $composer->getEventDispatcher()->dispatch($commandEvent->getName(), $commandEvent); diff --git a/src/Composer/Downloader/DownloadManager.php b/src/Composer/Downloader/DownloadManager.php index 684b64111..902e8beaf 100644 --- a/src/Composer/Downloader/DownloadManager.php +++ b/src/Composer/Downloader/DownloadManager.php @@ -26,7 +26,6 @@ class DownloadManager private $io; private $preferDist = false; private $preferSource = false; - private $forceUpdate = false; private $filesystem; private $downloaders = array(); @@ -70,20 +69,6 @@ class DownloadManager return $this; } - /** - * set force update mode - * forces to update the repository event when missing metadata - * - * @param $forceUpdate - * @return DownloadManager - */ - public function setForceUpdate($forceUpdate) - { - $this->forceUpdate = (boolean) $forceUpdate; - - return $this; - } - /** * Sets whether to output download progress information for all registered * downloaders @@ -268,8 +253,9 @@ class DownloadManager try { $downloader->update($initial, $target, $targetDir); return; - } catch (VcsMissingMetadataException $ex) { - if ($this->forceUpdate === false) { + } catch (\RuntimeException $ex) { + if (!$this->io->isInteractive() || + !$this->io->askConfirmation(' Updating failed. Would you like to try reinstalling instead [yes]? ', true)) { throw $ex; } } diff --git a/src/Composer/Downloader/GitDownloader.php b/src/Composer/Downloader/GitDownloader.php index d3eb6509c..84d1f735f 100644 --- a/src/Composer/Downloader/GitDownloader.php +++ b/src/Composer/Downloader/GitDownloader.php @@ -73,7 +73,7 @@ class GitDownloader extends VcsDownloader { GitUtil::cleanEnv(); if (!$this->hasMetadataRepository($path)) { - throw new VcsMissingMetadataException('The .git directory is missing from '.$path.', see https://getcomposer.org/commit-deps for more information'); + throw new \RuntimeException('The .git directory is missing from '.$path.', see https://getcomposer.org/commit-deps for more information'); } $ref = $target->getSourceReference(); diff --git a/src/Composer/Downloader/HgDownloader.php b/src/Composer/Downloader/HgDownloader.php index 06fc4fbe9..9c379a9e5 100644 --- a/src/Composer/Downloader/HgDownloader.php +++ b/src/Composer/Downloader/HgDownloader.php @@ -48,7 +48,7 @@ class HgDownloader extends VcsDownloader $this->io->writeError(" Updating to ".$target->getSourceReference()); if (!$this->hasMetadataRepository($path)) { - throw new VcsMissingMetadataException('The .hg directory is missing from '.$path.', see https://getcomposer.org/commit-deps for more information'); + throw new \RuntimeException('The .hg directory is missing from '.$path.', see https://getcomposer.org/commit-deps for more information'); } $command = sprintf('hg pull %s && hg up %s', $url, $ref); diff --git a/src/Composer/Downloader/SvnDownloader.php b/src/Composer/Downloader/SvnDownloader.php index a8a856a33..42ebf758e 100644 --- a/src/Composer/Downloader/SvnDownloader.php +++ b/src/Composer/Downloader/SvnDownloader.php @@ -53,7 +53,7 @@ class SvnDownloader extends VcsDownloader $ref = $target->getSourceReference(); if (!$this->hasMetadataRepository($path)) { - throw new VcsMissingMetadataException('The .svn directory is missing from '.$path.', see https://getcomposer.org/commit-deps for more information'); + throw new \RuntimeException('The .svn directory is missing from '.$path.', see https://getcomposer.org/commit-deps for more information'); } $flags = ""; diff --git a/src/Composer/Downloader/VcsDownloader.php b/src/Composer/Downloader/VcsDownloader.php index be6391046..37aa9042f 100644 --- a/src/Composer/Downloader/VcsDownloader.php +++ b/src/Composer/Downloader/VcsDownloader.php @@ -131,15 +131,9 @@ abstract class VcsDownloader implements DownloaderInterface, ChangeReportInterfa $this->reapplyChanges($path); - if (!$this->hasMetadataRepository($path) && !$urls && $exception) { - if (!$this->io->isDebug()) { - $this->io->write(' The VCS directory is missing from vendor package, see https://getcomposer.org/commit-deps for more information'); - } - throw $exception; - } - - // print the commit logs if in verbose mode - if ($this->io->isVerbose()) { + // print the commit logs if in verbose mode and VCS metadata is present + // because in case of missing metadata code would trigger another exception + if ($this->io->isVerbose() && $this->hasMetadataRepository($path)) { $message = 'Pulling in changes:'; $logs = $this->getCommitLogs($initial->getSourceReference(), $target->getSourceReference(), $path); @@ -161,6 +155,10 @@ abstract class VcsDownloader implements DownloaderInterface, ChangeReportInterfa } } + if (!$urls && $exception) { + throw $exception; + } + $this->io->writeError(''); } diff --git a/src/Composer/Downloader/VcsMissingMetadataException.php b/src/Composer/Downloader/VcsMissingMetadataException.php deleted file mode 100644 index f450edcf5..000000000 --- a/src/Composer/Downloader/VcsMissingMetadataException.php +++ /dev/null @@ -1,30 +0,0 @@ - - */ -class VcsMissingMetadataException extends \RuntimeException -{ - /** - * Construct the exception. Note: The message is NOT binary safe. - * @link http://php.net/manual/en/exception.construct.php - * @param string $message [optional] The Exception message to throw. - * @param int $code [optional] The Exception code. - * @param \Exception $previous [optional] The previous exception used for the exception chaining. Since 5.3.0 - * @since 5.1.0 - */ - public function __construct($message = '', $code = 0, \Exception $previous = null) - { - parent::__construct("Missing VSC metadata exception: \n".$message, $code, $previous); - } -} \ No newline at end of file