diff --git a/src/Composer/Downloader/SvnDownloader.php b/src/Composer/Downloader/SvnDownloader.php index 20c9384f4..451126023 100644 --- a/src/Composer/Downloader/SvnDownloader.php +++ b/src/Composer/Downloader/SvnDownloader.php @@ -22,16 +22,6 @@ use Composer\Util\Svn as SvnUtil; */ class SvnDownloader extends VcsDownloader { - /** - * @var bool - */ - protected $useAuth = false; - - /** - * @var \Composer\Util\Svn - */ - protected $util; - /** * {@inheritDoc} */ @@ -40,12 +30,8 @@ class SvnDownloader extends VcsDownloader $url = $package->getSourceUrl(); $ref = $package->getSourceReference(); - $util = $this->getUtil($url); - - $command = $util->getCommand("svn co", sprintf("%s/%s", $url, $ref), $path); - $this->io->write(" Checking out ".$package->getSourceReference()); - $this->execute($command, $util); + $this->execute($url, "svn co", sprintf("%s/%s", $url, $ref), null, $path); } /** @@ -56,11 +42,8 @@ class SvnDownloader extends VcsDownloader $url = $target->getSourceUrl(); $ref = $target->getSourceReference(); - $util = $this->getUtil($url); - $command = $util->getCommand("svn switch", sprintf("%s/%s", $url, $ref)); - $this->io->write(" Checking out " . $ref); - $this->execute(sprintf('cd %s && %s', $path, $command), $util); + $this->execute($url, "svn switch", sprintf("%s/%s", $url, $ref), $path); } /** @@ -75,55 +58,26 @@ class SvnDownloader extends VcsDownloader } /** - * Wrap {@link \Composer\Util\ProcessExecutor::execute(). + * Execute an SVN command and try to fix up the process with credentials + * if necessary. * - * @param string $cmd - * @param SvnUtil $util + * @param string $baseUrl Base URL of the repository + * @param string $command SVN command to run + * @param string $url SVN url + * @param string $cwd Working directory + * @param string $path Target for a checkout * * @return string */ - protected function execute($command, SvnUtil $util) + protected function execute($baseUrl, $command, $url, $cwd = null, $path = null) { - $status = $this->process->execute($command, $output); - if (0 === $status) { - return $output; - } - - // this could be any failure, since SVN exits with 1 always - if (empty($output)) { - $output = $this->process->getErrorOutput(); - } - - if (!$this->io->isInteractive()) { - return $output; + $util = new SvnUtil($baseUrl, $this->io); + try { + return $util->execute($command, $url, $cwd, $path); + } catch (\RuntimeException $e) { + throw new \RuntimeException( + 'Package could not be downloaded, '.$e->getMessage() + ); } - - // the error is not auth-related - if (false === stripos($output, 'authorization failed:')) { - return $output; - } - - // no authorization has been detected so far - if (!$this->useAuth) { - $this->useAuth = $util->doAuthDance()->hasAuth(); - $credentials = $util->getCredentialString(); - - // restart the process - $output = $this->execute($command . ' ' . $credentials, $util); - } else { - $this->io->write("Authorization failed: {$command}"); - } - - return $output; - } - - /** - * @param string $url - * - * @return \Composer\Util\Svn - */ - protected function getUtil($url) - { - return new SvnUtil($url, $this->io); } } diff --git a/src/Composer/Repository/Vcs/SvnDriver.php b/src/Composer/Repository/Vcs/SvnDriver.php index 2e9c71798..4d6d895b5 100644 --- a/src/Composer/Repository/Vcs/SvnDriver.php +++ b/src/Composer/Repository/Vcs/SvnDriver.php @@ -29,28 +29,6 @@ class SvnDriver extends VcsDriver protected $branches; protected $infoCache = array(); - /** - * Contains credentials, or not? - * @var boolean - */ - protected $useAuth = false; - - /** - * To determine if we should cache the credentials supplied by the user. By default: no cache. - * @var boolean - */ - protected $useCache = false; - - /** - * @var string - */ - protected $svnUsername = ''; - - /** - * @var string - */ - protected $svnPassword = ''; - /** * @var \Composer\Util\Svn */ @@ -71,13 +49,12 @@ class SvnDriver extends VcsDriver if (false !== ($pos = strrpos($url, '/trunk'))) { $this->baseUrl = substr($url, 0, $pos); } - $this->util = new SvnUtil($this->baseUrl, $io); - $this->useAuth = $this->util->hasAuth(); + $this->util = new SvnUtil($this->baseUrl, $io, $this->process); } /** * Execute an SVN command and try to fix up the process with credentials - * if necessary. The command is 'fixed up' with {@link self::getSvnCommand()}. + * if necessary. * * @param string $command The svn command to run. * @param string $url The SVN URL. @@ -86,37 +63,13 @@ class SvnDriver extends VcsDriver */ protected function execute($command, $url) { - $svnCommand = $this->util->getCommand($command, $url); - - $status = $this->process->execute( - $svnCommand, - $output - ); - - if (0 === $status) { - return $output; - } - - // this could be any failure, since SVN exits with 1 always - if (!$this->io->isInteractive()) { - return $output; - } - - // the error is not auth-related - if (false === stripos($output, 'authorization failed:')) { - return $output; - } - - // no authorization has been detected so far - if (!$this->useAuth) { - $this->useAuth = $this->util->doAuthDance()->hasAuth(); - - // restart the process - $output = $this->execute($command, $url); - } else { - $this->io->write("Authorization failed: {$svnCommand}"); + try { + return $this->util->execute($command, $url); + } catch (\RuntimeException $e) { + throw new \RuntimeException( + 'Repository '.$this->url.' could not be processed, '.$e->getMessage() + ); } - return $output; } /** diff --git a/src/Composer/Util/Svn.php b/src/Composer/Util/Svn.php index 7de84ef85..b8372dd1a 100644 --- a/src/Composer/Util/Svn.php +++ b/src/Composer/Util/Svn.php @@ -45,16 +45,71 @@ class Svn */ protected $cacheCredentials = true; + /** + * @var ProcessExecutor + */ + protected $process; + /** * @param string $url * @param \Composer\IO\IOInterface $io - * - * @return \Composer\Util\Svn + * @param ProcessExecutor $process */ - public function __construct($url, IOInterface $io) + public function __construct($url, IOInterface $io, ProcessExecutor $process = null) { $this->url = $url; $this->io = $io; + $this->process = $process ?: new ProcessExecutor; + } + + /** + * Execute an SVN command and try to fix up the process with credentials + * if necessary. + * + * @param string $command SVN command to run + * @param string $url SVN url + * @param string $cwd Working directory + * @param string $path Target for a checkout + * + * @return string + * + * @throws \RuntimeException + */ + public function execute($command, $url, $cwd = null, $path = null) + { + $svnCommand = $this->getCommand($command, $url, $path); + $status = $this->process->execute($svnCommand, $output, $cwd); + if (0 === $status) { + return $output; + } + + if (empty($output)) { + $output = $this->process->getErrorOutput(); + } + + // the error is not auth-related + if (false === stripos($output, 'authorization failed:')) { + throw new \RuntimeException('Package could not be downloaded: '.$output); + } + + // no auth supported for non interactive calls + if (!$this->io->isInteractive()) { + throw new \RuntimeException( + 'Package could not be downloaded, can not ask for authentication in non interactive mode ('.$output.')' + ); + } + + // try to authenticate + if (!$this->hasAuth()) { + $this->doAuthDance(); + + // restart the process + return $this->execute($command, $url, $cwd, $path); + } + + throw new \RuntimeException( + 'Repository '.$this->url.' could not be processed, wrong credentials provided ('.$output.')' + ); } /** @@ -62,7 +117,7 @@ class Svn * * @return \Composer\Util\Svn */ - public function doAuthDance() + protected function doAuthDance() { $this->io->write("The Subversion server ({$this->url}) requested credentials:"); @@ -80,11 +135,11 @@ class Svn * * @param string $cmd Usually 'svn ls' or something like that. * @param string $url Repo URL. - * @param string $path The path to run this against (e.g. a 'co' into) + * @param string $path Target for a checkout * * @return string */ - public function getCommand($cmd, $url, $path = null) + protected function getCommand($cmd, $url, $path = null) { $cmd = sprintf('%s %s%s %s', $cmd, @@ -107,7 +162,7 @@ class Svn * * @return string */ - public function getCredentialString() + protected function getCredentialString() { if (!$this->hasAuth()) { return ''; @@ -127,7 +182,7 @@ class Svn * @return string * @throws \LogicException */ - public function getPassword() + protected function getPassword() { if ($this->credentials === null) { throw new \LogicException("No svn auth detected."); @@ -142,7 +197,7 @@ class Svn * @return string * @throws \LogicException */ - public function getUsername() + protected function getUsername() { if ($this->credentials === null) { throw new \LogicException("No svn auth detected."); @@ -158,7 +213,7 @@ class Svn * * @return Boolean */ - public function hasAuth() + protected function hasAuth() { if (null !== $this->hasAuth) { return $this->hasAuth; diff --git a/tests/Composer/Test/Repository/Vcs/SvnDriverTest.php b/tests/Composer/Test/Repository/Vcs/SvnDriverTest.php index 3265ab311..6dff2f113 100644 --- a/tests/Composer/Test/Repository/Vcs/SvnDriverTest.php +++ b/tests/Composer/Test/Repository/Vcs/SvnDriverTest.php @@ -18,12 +18,10 @@ use Composer\IO\NullIO; class SvnDriverTest extends \PHPUnit_Framework_TestCase { /** - * Test the execute method. + * @expectedException RuntimeException */ - public function testExecute() + public function testWrongCredentialsInUrl() { - $this->markTestIncomplete("Currently no way to mock the output value which is passed by reference."); - $console = $this->getMock('Composer\IO\IOInterface'); $console->expects($this->once()) ->method('isInteractive') @@ -37,9 +35,12 @@ class SvnDriverTest extends \PHPUnit_Framework_TestCase $process->expects($this->once()) ->method('execute') ->will($this->returnValue(1)); + $process->expects($this->once()) + ->method('getErrorOutput') + ->will($this->returnValue($output)); $svn = new SvnDriver('http://till:secret@corp.svn.local/repo', $console, $process); - $svn->execute('svn ls', 'http://corp.svn.local/repo'); + $svn->getTags(); } private function getCmd($cmd)