1
0
Fork 0

Remove code duplication in Svn classes

pull/498/head
Jordi Boggiano 2012-03-25 00:29:14 +01:00
parent cd2e5bf2e0
commit 5a7abfd84f
4 changed files with 96 additions and 133 deletions

View File

@ -22,16 +22,6 @@ use Composer\Util\Svn as SvnUtil;
*/ */
class SvnDownloader extends VcsDownloader class SvnDownloader extends VcsDownloader
{ {
/**
* @var bool
*/
protected $useAuth = false;
/**
* @var \Composer\Util\Svn
*/
protected $util;
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@ -40,12 +30,8 @@ class SvnDownloader extends VcsDownloader
$url = $package->getSourceUrl(); $url = $package->getSourceUrl();
$ref = $package->getSourceReference(); $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->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(); $url = $target->getSourceUrl();
$ref = $target->getSourceReference(); $ref = $target->getSourceReference();
$util = $this->getUtil($url);
$command = $util->getCommand("svn switch", sprintf("%s/%s", $url, $ref));
$this->io->write(" Checking out " . $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 string $baseUrl Base URL of the repository
* @param SvnUtil $util * @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 * @return string
*/ */
protected function execute($command, SvnUtil $util) protected function execute($baseUrl, $command, $url, $cwd = null, $path = null)
{ {
$status = $this->process->execute($command, $output); $util = new SvnUtil($baseUrl, $this->io);
if (0 === $status) { try {
return $output; return $util->execute($command, $url, $cwd, $path);
} catch (\RuntimeException $e) {
throw new \RuntimeException(
'Package could not be downloaded, '.$e->getMessage()
);
} }
// this could be any failure, since SVN exits with 1 always
if (empty($output)) {
$output = $this->process->getErrorOutput();
}
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 = $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);
} }
} }

View File

@ -29,28 +29,6 @@ class SvnDriver extends VcsDriver
protected $branches; protected $branches;
protected $infoCache = array(); 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 * @var \Composer\Util\Svn
*/ */
@ -71,13 +49,12 @@ class SvnDriver extends VcsDriver
if (false !== ($pos = strrpos($url, '/trunk'))) { if (false !== ($pos = strrpos($url, '/trunk'))) {
$this->baseUrl = substr($url, 0, $pos); $this->baseUrl = substr($url, 0, $pos);
} }
$this->util = new SvnUtil($this->baseUrl, $io); $this->util = new SvnUtil($this->baseUrl, $io, $this->process);
$this->useAuth = $this->util->hasAuth();
} }
/** /**
* Execute an SVN command and try to fix up the process with credentials * 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 $command The svn command to run.
* @param string $url The SVN URL. * @param string $url The SVN URL.
@ -86,37 +63,13 @@ class SvnDriver extends VcsDriver
*/ */
protected function execute($command, $url) protected function execute($command, $url)
{ {
$svnCommand = $this->util->getCommand($command, $url); try {
return $this->util->execute($command, $url);
$status = $this->process->execute( } catch (\RuntimeException $e) {
$svnCommand, throw new \RuntimeException(
$output 'Repository '.$this->url.' could not be processed, '.$e->getMessage()
); );
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}");
}
return $output;
} }
/** /**

View File

@ -45,16 +45,71 @@ class Svn
*/ */
protected $cacheCredentials = true; protected $cacheCredentials = true;
/**
* @var ProcessExecutor
*/
protected $process;
/** /**
* @param string $url * @param string $url
* @param \Composer\IO\IOInterface $io * @param \Composer\IO\IOInterface $io
* * @param ProcessExecutor $process
* @return \Composer\Util\Svn
*/ */
public function __construct($url, IOInterface $io) public function __construct($url, IOInterface $io, ProcessExecutor $process = null)
{ {
$this->url = $url; $this->url = $url;
$this->io = $io; $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 * @return \Composer\Util\Svn
*/ */
public function doAuthDance() protected function doAuthDance()
{ {
$this->io->write("The Subversion server ({$this->url}) requested credentials:"); $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 $cmd Usually 'svn ls' or something like that.
* @param string $url Repo URL. * @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 * @return string
*/ */
public function getCommand($cmd, $url, $path = null) protected function getCommand($cmd, $url, $path = null)
{ {
$cmd = sprintf('%s %s%s %s', $cmd = sprintf('%s %s%s %s',
$cmd, $cmd,
@ -107,7 +162,7 @@ class Svn
* *
* @return string * @return string
*/ */
public function getCredentialString() protected function getCredentialString()
{ {
if (!$this->hasAuth()) { if (!$this->hasAuth()) {
return ''; return '';
@ -127,7 +182,7 @@ class Svn
* @return string * @return string
* @throws \LogicException * @throws \LogicException
*/ */
public function getPassword() protected function getPassword()
{ {
if ($this->credentials === null) { if ($this->credentials === null) {
throw new \LogicException("No svn auth detected."); throw new \LogicException("No svn auth detected.");
@ -142,7 +197,7 @@ class Svn
* @return string * @return string
* @throws \LogicException * @throws \LogicException
*/ */
public function getUsername() protected function getUsername()
{ {
if ($this->credentials === null) { if ($this->credentials === null) {
throw new \LogicException("No svn auth detected."); throw new \LogicException("No svn auth detected.");
@ -158,7 +213,7 @@ class Svn
* *
* @return Boolean * @return Boolean
*/ */
public function hasAuth() protected function hasAuth()
{ {
if (null !== $this->hasAuth) { if (null !== $this->hasAuth) {
return $this->hasAuth; return $this->hasAuth;

View File

@ -18,12 +18,10 @@ use Composer\IO\NullIO;
class SvnDriverTest extends \PHPUnit_Framework_TestCase 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 = $this->getMock('Composer\IO\IOInterface');
$console->expects($this->once()) $console->expects($this->once())
->method('isInteractive') ->method('isInteractive')
@ -37,9 +35,12 @@ class SvnDriverTest extends \PHPUnit_Framework_TestCase
$process->expects($this->once()) $process->expects($this->once())
->method('execute') ->method('execute')
->will($this->returnValue(1)); ->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 = 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) private function getCmd($cmd)