From 6417a86651efd6c0f8a38afa43261ee03d3fcb07 Mon Sep 17 00:00:00 2001 From: till Date: Thu, 8 Mar 2012 15:25:44 +0100 Subject: [PATCH 01/47] * fix docblocks --- src/Composer/Repository/Vcs/SvnDriver.php | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/Composer/Repository/Vcs/SvnDriver.php b/src/Composer/Repository/Vcs/SvnDriver.php index 2d428f6d4..8d1972d73 100644 --- a/src/Composer/Repository/Vcs/SvnDriver.php +++ b/src/Composer/Repository/Vcs/SvnDriver.php @@ -31,6 +31,16 @@ class SvnDriver extends VcsDriver implements VcsDriverInterface */ protected $svnPassword = ''; + /** + * __construct + * + * @param string $url + * @param IOInterface $io + * @param ProcessExecutor $process + * + * @return $this + * @uses self::detectSvnAuth() + */ public function __construct($url, IOInterface $io, ProcessExecutor $process = null) { parent::__construct($this->baseUrl = rtrim($url, '/'), $io, $process); @@ -209,9 +219,10 @@ class SvnDriver extends VcsDriver implements VcsDriverInterface /** * Return the credential string for the svn command. * - * --no-auth-cache when credentials are present + * Adds --no-auth-cache when credentials are present. * * @return string + * @uses self::$useAuth */ public function getSvnCredentialString() { From d1482bfa3cf77d259710df855c574cc06390479a Mon Sep 17 00:00:00 2001 From: till Date: Thu, 8 Mar 2012 15:39:22 +0100 Subject: [PATCH 02/47] * refactor svn command 'creation' into a single method: getSvnCommand() --- src/Composer/Repository/Vcs/SvnDriver.php | 63 ++++++++++++----------- 1 file changed, 32 insertions(+), 31 deletions(-) diff --git a/src/Composer/Repository/Vcs/SvnDriver.php b/src/Composer/Repository/Vcs/SvnDriver.php index 8d1972d73..c72a85b9d 100644 --- a/src/Composer/Repository/Vcs/SvnDriver.php +++ b/src/Composer/Repository/Vcs/SvnDriver.php @@ -109,27 +109,21 @@ class SvnDriver extends VcsDriver implements VcsDriverInterface } $this->process->execute( - sprintf( - 'svn cat --non-interactive %s %s', - $this->getSvnCredentialString(), - escapeshellarg($this->baseUrl.$identifier.'composer.json'.$rev) - ), + $this->getSvnCommand('svn cat', $this->baseUrl . $identifier . 'composer.json' . $rev), $composer ); if (!trim($composer)) { - throw new \UnexpectedValueException('Failed to retrieve composer information for identifier '.$identifier.' in '.$this->getUrl()); + throw new \UnexpectedValueException( + 'Failed to retrieve composer information for identifier ' . $identifier . ' in ' . $this->getUrl() + ); } $composer = JsonFile::parseJson($composer); if (!isset($composer['time'])) { $this->process->execute( - sprintf( - 'svn info %s %s', - $this->getSvnCredentialString(), - escapeshellarg($this->baseUrl.$identifier.$rev) - ), + $this->getSvnCommand('svn info', $this->baseUrl . $identifier . $rev), $output ); foreach ($this->process->splitLines($output) as $line) { @@ -153,11 +147,7 @@ class SvnDriver extends VcsDriver implements VcsDriverInterface { if (null === $this->tags) { $this->process->execute( - sprintf( - 'svn ls --non-interactive %s %s', - $this->getSvnCredentialString(), - escapeshellarg($this->baseUrl.'/tags') - ), + $this->getSvnCommand('svn ls', $this->baseUrl . '/tags'), $output ); $this->tags = array(); @@ -178,11 +168,7 @@ class SvnDriver extends VcsDriver implements VcsDriverInterface { if (null === $this->branches) { $this->process->execute( - sprintf( - 'svn ls --verbose --non-interactive %s %s', - $this->getSvnCredentialString(), - escapeshellarg($this->baseUrl.'/') - ), + $this->getSvnCommand('svn ls --verbose', $this->baseUrl . '/'), $output ); @@ -197,11 +183,7 @@ class SvnDriver extends VcsDriver implements VcsDriverInterface unset($output); $this->process->execute( - sprintf( - 'svn ls --verbose --non-interactive %s', - $this->getSvnCredentialString(), - escapeshellarg($this->baseUrl.'/branches') - ), + $this->getSvnCommand('svn ls --verbose', $this->baseUrl . '/branches'), $output ); foreach ($this->process->splitLines(trim($output)) as $line) { @@ -216,6 +198,29 @@ class SvnDriver extends VcsDriver implements VcsDriverInterface return $this->branches; } + /** + * A method to create the svn commands run. + * + * @string $cmd Usually 'svn ls' or something like that. + * @string $url Repo URL. + * @string $pipe Optional pipe for the output. + * + * @return string + */ + public function getSvnCommand($cmd, $url, $pipe = null) + { + $cmd = sprintf('%s %s%s %s', + $cmd, + $this->getSvnInteractiveSetting(), + $this->getSvnCredentialString(), + escapeshellarg($url) + ); + if ($pipe !== null) { + $cmd .= ' ' . $pipe; + } + return $cmd; + } + /** * Return the credential string for the svn command. * @@ -267,11 +272,7 @@ class SvnDriver extends VcsDriver implements VcsDriverInterface $processExecutor = new ProcessExecutor(); $exit = $processExecutor->execute( - sprintf( - 'svn info --non-interactive %s %s 2>/dev/null', - $this->getSvnCredentialString(), - escapeshellarg($url) - ), + $this->getSvnCommand('svn info', $url, '2>/dev/null'), $ignored ); return $exit === 0; From 3f665e8bbbf6d4dec9e2eb1d59724c519bbdbda8 Mon Sep 17 00:00:00 2001 From: till Date: Thu, 8 Mar 2012 15:44:29 +0100 Subject: [PATCH 03/47] * method to gather if this session is 'interactive' or 'non interactive' --- src/Composer/Repository/Vcs/SvnDriver.php | 22 +++++++++++++++++++ .../Test/Repository/Vcs/SvnDriverTest.php | 6 +++++ 2 files changed, 28 insertions(+) diff --git a/src/Composer/Repository/Vcs/SvnDriver.php b/src/Composer/Repository/Vcs/SvnDriver.php index c72a85b9d..bccd1e000 100644 --- a/src/Composer/Repository/Vcs/SvnDriver.php +++ b/src/Composer/Repository/Vcs/SvnDriver.php @@ -242,6 +242,28 @@ class SvnDriver extends VcsDriver implements VcsDriverInterface ); } + /** + * Determine if composer was called with --no-interaction/-n + * and return the option string for the command. + * + * @return string + * @uses parent::$io + * @see IOInterface::isInteractive() + */ + public function getSvnInteractiveSetting() + { + static $option; + if ($option !== null) { + return $option; + } + if ($this->io->isInteractive() === false) { + $option = '--non-interactive '; + } else { + $option = ''; + } + return $option; + } + /** * {@inheritDoc} */ diff --git a/tests/Composer/Test/Repository/Vcs/SvnDriverTest.php b/tests/Composer/Test/Repository/Vcs/SvnDriverTest.php index 7d4578930..12e931eef 100644 --- a/tests/Composer/Test/Repository/Vcs/SvnDriverTest.php +++ b/tests/Composer/Test/Repository/Vcs/SvnDriverTest.php @@ -44,4 +44,10 @@ class SvnDriverTest extends \PHPUnit_Framework_TestCase $this->assertEquals($expect, $svn->getSvnCredentialString()); } + + public function testInteractiveString() + { + $io = new \Composer\IO\NullIO; // non-interactive by design + $svn = new SvnDriver('http://svn.example.org', $io); + } } From 953de35a0b4978fc2b307d54070a9d36a839270b Mon Sep 17 00:00:00 2001 From: till Date: Thu, 8 Mar 2012 15:47:26 +0100 Subject: [PATCH 04/47] * basic testcase --- tests/Composer/Test/Repository/Vcs/SvnDriverTest.php | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/tests/Composer/Test/Repository/Vcs/SvnDriverTest.php b/tests/Composer/Test/Repository/Vcs/SvnDriverTest.php index 12e931eef..5b1549daa 100644 --- a/tests/Composer/Test/Repository/Vcs/SvnDriverTest.php +++ b/tests/Composer/Test/Repository/Vcs/SvnDriverTest.php @@ -47,7 +47,14 @@ class SvnDriverTest extends \PHPUnit_Framework_TestCase public function testInteractiveString() { + $url = 'http://svn.example.org'; + $io = new \Composer\IO\NullIO; // non-interactive by design - $svn = new SvnDriver('http://svn.example.org', $io); + $svn = new SvnDriver($url, $io); + + $this->assertEquals( + "svn ls --non-interactive 'http://svn.example.org'", + $svn->getSvnCommand('svn ls', $url) + ); } } From 99eb18d0fff7a2b6d8f5b9fb267494615ccdcef7 Mon Sep 17 00:00:00 2001 From: till Date: Thu, 8 Mar 2012 15:56:52 +0100 Subject: [PATCH 05/47] * rename var from $composer to $output (for consistency) --- src/Composer/Repository/Vcs/SvnDriver.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Composer/Repository/Vcs/SvnDriver.php b/src/Composer/Repository/Vcs/SvnDriver.php index bccd1e000..c780179f5 100644 --- a/src/Composer/Repository/Vcs/SvnDriver.php +++ b/src/Composer/Repository/Vcs/SvnDriver.php @@ -110,16 +110,16 @@ class SvnDriver extends VcsDriver implements VcsDriverInterface $this->process->execute( $this->getSvnCommand('svn cat', $this->baseUrl . $identifier . 'composer.json' . $rev), - $composer + $output ); - if (!trim($composer)) { + if (!trim($output)) { throw new \UnexpectedValueException( 'Failed to retrieve composer information for identifier ' . $identifier . ' in ' . $this->getUrl() ); } - $composer = JsonFile::parseJson($composer); + $composer = JsonFile::parseJson($output); if (!isset($composer['time'])) { $this->process->execute( From 907db48bb53a4efa9535a6ad143cb7b49827a7f3 Mon Sep 17 00:00:00 2001 From: till Date: Thu, 8 Mar 2012 16:27:22 +0100 Subject: [PATCH 06/47] * wrap execution of commands into local method --- src/Composer/Repository/Vcs/SvnDriver.php | 46 +++++++++++++++-------- 1 file changed, 30 insertions(+), 16 deletions(-) diff --git a/src/Composer/Repository/Vcs/SvnDriver.php b/src/Composer/Repository/Vcs/SvnDriver.php index c780179f5..05bc9bdda 100644 --- a/src/Composer/Repository/Vcs/SvnDriver.php +++ b/src/Composer/Repository/Vcs/SvnDriver.php @@ -52,6 +52,25 @@ class SvnDriver extends VcsDriver implements VcsDriverInterface $this->detectSvnAuth(); } + /** + * Execute an SVN command and try to fix up the process with credentials + * if necessary. + * + * @param string $command The svn command to run. + * + * @return string + * @uses parent::$process + * @see ProcessExecutor::execute() + */ + public function execute($command) + { + $status = $this->process->execute( + $command, + $output + ); + return $output; + } + /** * {@inheritDoc} */ @@ -108,9 +127,8 @@ class SvnDriver extends VcsDriver implements VcsDriverInterface $rev = ''; } - $this->process->execute( - $this->getSvnCommand('svn cat', $this->baseUrl . $identifier . 'composer.json' . $rev), - $output + $output = $this->execute( + $this->getSvnCommand('svn cat', $this->baseUrl . $identifier . 'composer.json' . $rev) ); if (!trim($output)) { @@ -122,9 +140,8 @@ class SvnDriver extends VcsDriver implements VcsDriverInterface $composer = JsonFile::parseJson($output); if (!isset($composer['time'])) { - $this->process->execute( - $this->getSvnCommand('svn info', $this->baseUrl . $identifier . $rev), - $output + $output = $this->execute( + $this->getSvnCommand('svn info', $this->baseUrl . $identifier . $rev) ); foreach ($this->process->splitLines($output) as $line) { if ($line && preg_match('{^Last Changed Date: ([^(]+)}', $line, $match)) { @@ -146,9 +163,8 @@ class SvnDriver extends VcsDriver implements VcsDriverInterface public function getTags() { if (null === $this->tags) { - $this->process->execute( - $this->getSvnCommand('svn ls', $this->baseUrl . '/tags'), - $output + $output = $this->execute( + $this->getSvnCommand('svn ls', $this->baseUrl . '/tags') ); $this->tags = array(); foreach ($this->process->splitLines($output) as $tag) { @@ -167,9 +183,8 @@ class SvnDriver extends VcsDriver implements VcsDriverInterface public function getBranches() { if (null === $this->branches) { - $this->process->execute( - $this->getSvnCommand('svn ls --verbose', $this->baseUrl . '/'), - $output + $output = $this->execute( + $this->getSvnCommand('svn ls --verbose', $this->baseUrl . '/') ); $this->branches = array(); @@ -182,9 +197,8 @@ class SvnDriver extends VcsDriver implements VcsDriverInterface } unset($output); - $this->process->execute( - $this->getSvnCommand('svn ls --verbose', $this->baseUrl . '/branches'), - $output + $output = $this->execute( + $this->getSvnCommand('svn ls --verbose', $this->baseUrl . '/branches') ); foreach ($this->process->splitLines(trim($output)) as $line) { preg_match('{^\s*(\S+).*?(\S+)\s*$}', $line, $match); @@ -295,7 +309,7 @@ class SvnDriver extends VcsDriver implements VcsDriverInterface $exit = $processExecutor->execute( $this->getSvnCommand('svn info', $url, '2>/dev/null'), - $ignored + $ignoredOutput ); return $exit === 0; } From a3e0a0805cfc114ddb86d0df74dcd1a03a912aaa Mon Sep 17 00:00:00 2001 From: till Date: Thu, 8 Mar 2012 16:42:19 +0100 Subject: [PATCH 07/47] * always run non-interactive --- src/Composer/Repository/Vcs/SvnDriver.php | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/src/Composer/Repository/Vcs/SvnDriver.php b/src/Composer/Repository/Vcs/SvnDriver.php index 05bc9bdda..6140f5b1c 100644 --- a/src/Composer/Repository/Vcs/SvnDriver.php +++ b/src/Composer/Repository/Vcs/SvnDriver.php @@ -257,25 +257,15 @@ class SvnDriver extends VcsDriver implements VcsDriverInterface } /** - * Determine if composer was called with --no-interaction/-n - * and return the option string for the command. + * Always run commands 'non-interactive': + * It's easier to spot issues because then the svn process would fail fast + * and not wait for user input. * * @return string - * @uses parent::$io - * @see IOInterface::isInteractive() */ public function getSvnInteractiveSetting() { - static $option; - if ($option !== null) { - return $option; - } - if ($this->io->isInteractive() === false) { - $option = '--non-interactive '; - } else { - $option = ''; - } - return $option; + return '--non-interactive '; } /** From 44470da804d0665e37f138d6e36302f7dff7cd65 Mon Sep 17 00:00:00 2001 From: till Date: Thu, 8 Mar 2012 16:52:36 +0100 Subject: [PATCH 08/47] * do svnCommand() in execute() to be able to restart the dance in case it failed --- src/Composer/Repository/Vcs/SvnDriver.php | 32 +++++++++-------------- 1 file changed, 13 insertions(+), 19 deletions(-) diff --git a/src/Composer/Repository/Vcs/SvnDriver.php b/src/Composer/Repository/Vcs/SvnDriver.php index 6140f5b1c..6252fef37 100644 --- a/src/Composer/Repository/Vcs/SvnDriver.php +++ b/src/Composer/Repository/Vcs/SvnDriver.php @@ -54,18 +54,22 @@ class SvnDriver extends VcsDriver implements VcsDriverInterface /** * Execute an SVN command and try to fix up the process with credentials - * if necessary. + * if necessary. The command is 'fixed up' with {@link self::getSvnCommand()}. * * @param string $command The svn command to run. + * @param string $url The SVN URL. * * @return string + * @uses self::getSvnCommand() * @uses parent::$process * @see ProcessExecutor::execute() */ - public function execute($command) + public function execute($command, $url) { + $svnCommand = $this->getSvnCommand($command, $url); + $status = $this->process->execute( - $command, + $svnCommand, $output ); return $output; @@ -127,9 +131,7 @@ class SvnDriver extends VcsDriver implements VcsDriverInterface $rev = ''; } - $output = $this->execute( - $this->getSvnCommand('svn cat', $this->baseUrl . $identifier . 'composer.json' . $rev) - ); + $output = $this->execute('svn cat', $this->baseUrl . $identifier . 'composer.json' . $rev); if (!trim($output)) { throw new \UnexpectedValueException( @@ -140,9 +142,7 @@ class SvnDriver extends VcsDriver implements VcsDriverInterface $composer = JsonFile::parseJson($output); if (!isset($composer['time'])) { - $output = $this->execute( - $this->getSvnCommand('svn info', $this->baseUrl . $identifier . $rev) - ); + $output = $this->execute('svn info', $this->baseUrl . $identifier . $rev); foreach ($this->process->splitLines($output) as $line) { if ($line && preg_match('{^Last Changed Date: ([^(]+)}', $line, $match)) { $date = new \DateTime($match[1]); @@ -163,9 +163,7 @@ class SvnDriver extends VcsDriver implements VcsDriverInterface public function getTags() { if (null === $this->tags) { - $output = $this->execute( - $this->getSvnCommand('svn ls', $this->baseUrl . '/tags') - ); + $output = $this->execute('svn ls', $this->baseUrl . '/tags'); $this->tags = array(); foreach ($this->process->splitLines($output) as $tag) { if ($tag) { @@ -183,10 +181,7 @@ class SvnDriver extends VcsDriver implements VcsDriverInterface public function getBranches() { if (null === $this->branches) { - $output = $this->execute( - $this->getSvnCommand('svn ls --verbose', $this->baseUrl . '/') - ); - + $output = $this->execute('svn ls --verbose', $this->baseUrl . '/'); $this->branches = array(); foreach ($this->process->splitLines($output) as $line) { preg_match('{^\s*(\S+).*?(\S+)\s*$}', $line, $match); @@ -197,9 +192,8 @@ class SvnDriver extends VcsDriver implements VcsDriverInterface } unset($output); - $output = $this->execute( - $this->getSvnCommand('svn ls --verbose', $this->baseUrl . '/branches') - ); + $output = $this->execute('svn ls --verbose', $this->baseUrl . '/branches'); + foreach ($this->process->splitLines(trim($output)) as $line) { preg_match('{^\s*(\S+).*?(\S+)\s*$}', $line, $match); if ($match[2] === './') { From 59c1a7ff159190d46ad3a0314ac05039e9151942 Mon Sep 17 00:00:00 2001 From: till Date: Thu, 8 Mar 2012 16:53:41 +0100 Subject: [PATCH 09/47] * when an auth failure is detected and 'auth' is not present, we ask for credentials * ... and repeat --- src/Composer/Repository/Vcs/SvnDriver.php | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/Composer/Repository/Vcs/SvnDriver.php b/src/Composer/Repository/Vcs/SvnDriver.php index 6252fef37..380e565d0 100644 --- a/src/Composer/Repository/Vcs/SvnDriver.php +++ b/src/Composer/Repository/Vcs/SvnDriver.php @@ -72,6 +72,17 @@ class SvnDriver extends VcsDriver implements VcsDriverInterface $svnCommand, $output ); + // this could be any failure, but let's see if it's auth related + if ($status == 1) { + if ($this->useAuth === false && strpos($output, 'authorization failed:') !== false) { + $this->svnUsername = $this->io->ask("What's your svn username?"); + $this->svnPassword = $this->io->ask("What's your svn password?"); + $this->useAuth = true; + + // restart the process + $output = $this->execute($command, $url); + } + } return $output; } From 978d4c145d8054458ca70910f0d05b5ea46935e5 Mon Sep 17 00:00:00 2001 From: till Date: Thu, 8 Mar 2012 17:00:24 +0100 Subject: [PATCH 10/47] * more output --- src/Composer/Repository/Vcs/SvnDriver.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Composer/Repository/Vcs/SvnDriver.php b/src/Composer/Repository/Vcs/SvnDriver.php index 380e565d0..2e602b0cc 100644 --- a/src/Composer/Repository/Vcs/SvnDriver.php +++ b/src/Composer/Repository/Vcs/SvnDriver.php @@ -75,8 +75,9 @@ class SvnDriver extends VcsDriver implements VcsDriverInterface // this could be any failure, but let's see if it's auth related if ($status == 1) { if ($this->useAuth === false && strpos($output, 'authorization failed:') !== false) { - $this->svnUsername = $this->io->ask("What's your svn username?"); - $this->svnPassword = $this->io->ask("What's your svn password?"); + $this->io->write("The Subversion server ({$this->baseUrl}) request credentials:"); + $this->svnUsername = $this->io->ask("Username"); + $this->svnPassword = $this->io->ask("Password"); $this->useAuth = true; // restart the process From a8287118ed8dd99d126eed9817796f5141fa16cc Mon Sep 17 00:00:00 2001 From: till Date: Fri, 9 Mar 2012 13:47:53 +0100 Subject: [PATCH 11/47] * work on no-auth-cache 'creation' --- src/Composer/Repository/Vcs/SvnDriver.php | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/Composer/Repository/Vcs/SvnDriver.php b/src/Composer/Repository/Vcs/SvnDriver.php index 2e602b0cc..82d2d3570 100644 --- a/src/Composer/Repository/Vcs/SvnDriver.php +++ b/src/Composer/Repository/Vcs/SvnDriver.php @@ -218,6 +218,16 @@ class SvnDriver extends VcsDriver implements VcsDriverInterface return $this->branches; } + /** + * Return the no-auth-cache switch. + * + * @return string + */ + public function getSvnAuthCache() + { + return '--no-auth-cache '; + } + /** * A method to create the svn commands run. * @@ -254,9 +264,10 @@ class SvnDriver extends VcsDriver implements VcsDriverInterface if ($this->useAuth !== true) { return ''; } - $str = ' --no-auth-cache --username %s --password %s '; + $str = ' %s--username %s --password %s '; return sprintf( $str, + $this->getSvnAuthCache(), escapeshellarg($this->svnUsername), escapeshellarg($this->svnPassword) ); From cde38f6634696251bb012ee0ab5fae16091d1070 Mon Sep 17 00:00:00 2001 From: till Date: Fri, 9 Mar 2012 13:48:27 +0100 Subject: [PATCH 12/47] * inject iointerface into test * document test cases * add a test-case which should not have ' --no-auth-cache' --- .../Test/Repository/Vcs/SvnDriverTest.php | 33 ++++++++++++++++--- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/tests/Composer/Test/Repository/Vcs/SvnDriverTest.php b/tests/Composer/Test/Repository/Vcs/SvnDriverTest.php index 5b1549daa..9a76051f4 100644 --- a/tests/Composer/Test/Repository/Vcs/SvnDriverTest.php +++ b/tests/Composer/Test/Repository/Vcs/SvnDriverTest.php @@ -23,21 +23,46 @@ class SvnDriverTest extends \PHPUnit_Framework_TestCase /** * Provide some examples for {@self::testCredentials()}. * + * {@link \Composer\IO\NullIO} is always non-interactive. + * * @return array */ public static function urlProvider() { return array( - array('http://till:test@svn.example.org/', " --no-auth-cache --username 'till' --password 'test' "), - array('http://svn.apache.org/', ''), - array('svn://johndoe@example.org', " --no-auth-cache --username 'johndoe' --password '' "), + array( + 'http://till:test@svn.example.org/', + " --no-auth-cache --username 'till' --password 'test' ", + '\Composer\IO\NullIO', + ), + array( + 'http://svn.apache.org/', + '', + '\Composer\IO\NullIO', + ), + array( + 'svn://johndoe@example.org', + " --no-auth-cache --username 'johndoe' --password '' ", + '\Composer\IO\NullIO', + ), + array( + 'https://till:secret@corp.svn.local/project1', + " --username 'till' --password 'secret' ", + '\Composer\IO\ConsoleIO', + ), ); } /** + * Test the credential string. + * + * @param string $url The SVN url. + * @param string $expect The expectation for the test. + * @param string $ioClass The IO interface. + * * @dataProvider urlProvider */ - public function testCredentials($url, $expect) + public function testCredentials($url, $expect, $ioClass) { $io = new \Composer\IO\NullIO; $svn = new SvnDriver($url, $io); From 84bf429795191c28ab0b97a901f0fad7c8d6884c Mon Sep 17 00:00:00 2001 From: till Date: Fri, 9 Mar 2012 13:56:27 +0100 Subject: [PATCH 13/47] * we need symfony console objects for ConsoleIO --- tests/Composer/Test/Repository/Vcs/SvnDriverTest.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/Composer/Test/Repository/Vcs/SvnDriverTest.php b/tests/Composer/Test/Repository/Vcs/SvnDriverTest.php index 9a76051f4..5478d7105 100644 --- a/tests/Composer/Test/Repository/Vcs/SvnDriverTest.php +++ b/tests/Composer/Test/Repository/Vcs/SvnDriverTest.php @@ -29,6 +29,14 @@ class SvnDriverTest extends \PHPUnit_Framework_TestCase */ public static function urlProvider() { + $nullIO = new \Composer\IO\NullIO; + + $input = new \Symfony\Component\Console\Input\ArrayInput(array('install')); + $output = new \Symfony\Component\Console\Output\NullOutput; + $helper = new \Symfony\Component\Console\Helper\HelperSet; + + $consoleInteractiveIO = new \Composer\IO\ConsoleIO($input, $output, $helper); + return array( array( 'http://till:test@svn.example.org/', From bcadfadc8af0b103a1fa108437a7a9fcd3a225f1 Mon Sep 17 00:00:00 2001 From: till Date: Fri, 9 Mar 2012 13:57:03 +0100 Subject: [PATCH 14/47] inject them directly --- tests/Composer/Test/Repository/Vcs/SvnDriverTest.php | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/tests/Composer/Test/Repository/Vcs/SvnDriverTest.php b/tests/Composer/Test/Repository/Vcs/SvnDriverTest.php index 5478d7105..d2a30e69e 100644 --- a/tests/Composer/Test/Repository/Vcs/SvnDriverTest.php +++ b/tests/Composer/Test/Repository/Vcs/SvnDriverTest.php @@ -41,22 +41,22 @@ class SvnDriverTest extends \PHPUnit_Framework_TestCase array( 'http://till:test@svn.example.org/', " --no-auth-cache --username 'till' --password 'test' ", - '\Composer\IO\NullIO', + $nullIO, ), array( 'http://svn.apache.org/', '', - '\Composer\IO\NullIO', + $nullIO, ), array( 'svn://johndoe@example.org', " --no-auth-cache --username 'johndoe' --password '' ", - '\Composer\IO\NullIO', + $nullIO, ), array( 'https://till:secret@corp.svn.local/project1', " --username 'till' --password 'secret' ", - '\Composer\IO\ConsoleIO', + $consoleInteractiveIO, ), ); } @@ -70,9 +70,8 @@ class SvnDriverTest extends \PHPUnit_Framework_TestCase * * @dataProvider urlProvider */ - public function testCredentials($url, $expect, $ioClass) + public function testCredentials($url, $expect, \Composer\IO\IOInterface $io) { - $io = new \Composer\IO\NullIO; $svn = new SvnDriver($url, $io); $this->assertEquals($expect, $svn->getSvnCredentialString()); From 1f03d37a8940bd35c5d70567b2e41098dc3cc14a Mon Sep 17 00:00:00 2001 From: till Date: Fri, 9 Mar 2012 14:07:04 +0100 Subject: [PATCH 15/47] * only 'ask' the user if the session is interactive --- src/Composer/Repository/Vcs/SvnDriver.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Repository/Vcs/SvnDriver.php b/src/Composer/Repository/Vcs/SvnDriver.php index 82d2d3570..d6b6b8daf 100644 --- a/src/Composer/Repository/Vcs/SvnDriver.php +++ b/src/Composer/Repository/Vcs/SvnDriver.php @@ -73,7 +73,7 @@ class SvnDriver extends VcsDriver implements VcsDriverInterface $output ); // this could be any failure, but let's see if it's auth related - if ($status == 1) { + if ($status == 1 && $this->io->isInteractive()) { if ($this->useAuth === false && strpos($output, 'authorization failed:') !== false) { $this->io->write("The Subversion server ({$this->baseUrl}) request credentials:"); $this->svnUsername = $this->io->ask("Username"); From 061d91b4a817938ed7312283b5b6b5198517395f Mon Sep 17 00:00:00 2001 From: till Date: Fri, 9 Mar 2012 14:13:29 +0100 Subject: [PATCH 16/47] whitespace --- src/Composer/Repository/Vcs/SvnDriver.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Composer/Repository/Vcs/SvnDriver.php b/src/Composer/Repository/Vcs/SvnDriver.php index d6b6b8daf..3daea8f55 100644 --- a/src/Composer/Repository/Vcs/SvnDriver.php +++ b/src/Composer/Repository/Vcs/SvnDriver.php @@ -72,10 +72,13 @@ class SvnDriver extends VcsDriver implements VcsDriverInterface $svnCommand, $output ); + // this could be any failure, but let's see if it's auth related if ($status == 1 && $this->io->isInteractive()) { if ($this->useAuth === false && strpos($output, 'authorization failed:') !== false) { + $this->io->write("The Subversion server ({$this->baseUrl}) request credentials:"); + $this->svnUsername = $this->io->ask("Username"); $this->svnPassword = $this->io->ask("Password"); $this->useAuth = true; From a3363a8560bcbe007effa5062b31703004aadcab Mon Sep 17 00:00:00 2001 From: till Date: Fri, 9 Mar 2012 14:24:06 +0100 Subject: [PATCH 17/47] * SvnDriver::$useCache: false by default * setting is adjust in 'interactive' mode when auth fails --- src/Composer/Repository/Vcs/SvnDriver.php | 27 +++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/src/Composer/Repository/Vcs/SvnDriver.php b/src/Composer/Repository/Vcs/SvnDriver.php index 3daea8f55..64334d228 100644 --- a/src/Composer/Repository/Vcs/SvnDriver.php +++ b/src/Composer/Repository/Vcs/SvnDriver.php @@ -21,6 +21,13 @@ class SvnDriver extends VcsDriver implements VcsDriverInterface */ protected $useAuth = false; + /** + * @var boolean $useCache To determine if we should cache the credentials + * supplied by the user. By default: no cache. + * @see self::getSvnAuthCache() + */ + protected $useCache = false; + /** * @var string $svnUsername */ @@ -75,14 +82,23 @@ class SvnDriver extends VcsDriver implements VcsDriverInterface // this could be any failure, but let's see if it's auth related if ($status == 1 && $this->io->isInteractive()) { - if ($this->useAuth === false && strpos($output, 'authorization failed:') !== false) { - - $this->io->write("The Subversion server ({$this->baseUrl}) request credentials:"); + if (strpos($output, 'authorization failed:') === false) { + return $output; + } + if (!$this->useAuth) { + $this->io->write("The Subversion server ({$this->baseUrl}) requested credentials:"); $this->svnUsername = $this->io->ask("Username"); $this->svnPassword = $this->io->ask("Password"); $this->useAuth = true; + $cacheTrueAnswers = array('yes', 'y', 'true', 'ja', 'si', 'da'); + + $cacheAnswer = strtolower($this->io->ask("Should we Subversion cache these credentials?", 'no')); + if (in_array($cacheAnswer, $cacheTrueAnswers)) { + $this->useCache = true; + } + // restart the process $output = $this->execute($command, $url); } @@ -228,7 +244,10 @@ class SvnDriver extends VcsDriver implements VcsDriverInterface */ public function getSvnAuthCache() { - return '--no-auth-cache '; + if (!$this->useCache) { + return '--no-auth-cache '; + } + return ''; } /** From f8d8ccfedc1cf2adf94c2a344e97b16f9ad7e84f Mon Sep 17 00:00:00 2001 From: till Date: Fri, 9 Mar 2012 14:24:49 +0100 Subject: [PATCH 18/47] * in interactive mode: display error message to the user when authorization fails --- src/Composer/Repository/Vcs/SvnDriver.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Composer/Repository/Vcs/SvnDriver.php b/src/Composer/Repository/Vcs/SvnDriver.php index 64334d228..eac32cc0e 100644 --- a/src/Composer/Repository/Vcs/SvnDriver.php +++ b/src/Composer/Repository/Vcs/SvnDriver.php @@ -101,6 +101,8 @@ class SvnDriver extends VcsDriver implements VcsDriverInterface // restart the process $output = $this->execute($command, $url); + } else { + $this->io->write("Authorization failed: {$svnCommand}"); } } return $output; From 4da56ea617f0346273797d0011e5c12bbba1635c Mon Sep 17 00:00:00 2001 From: till Date: Fri, 9 Mar 2012 14:25:19 +0100 Subject: [PATCH 19/47] explain non-interactive --- src/Composer/Repository/Vcs/SvnDriver.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Composer/Repository/Vcs/SvnDriver.php b/src/Composer/Repository/Vcs/SvnDriver.php index eac32cc0e..e71460de6 100644 --- a/src/Composer/Repository/Vcs/SvnDriver.php +++ b/src/Composer/Repository/Vcs/SvnDriver.php @@ -299,8 +299,9 @@ class SvnDriver extends VcsDriver implements VcsDriverInterface /** * Always run commands 'non-interactive': - * It's easier to spot issues because then the svn process would fail fast - * and not wait for user input. + * + * It's easier to spot potential issues (e.g. auth-failure) because + * non-interactive svn fails fast and does not wait for user input. * * @return string */ From c78d9ffece03ca0af62566458e025525857bf0e0 Mon Sep 17 00:00:00 2001 From: till Date: Fri, 9 Mar 2012 14:33:34 +0100 Subject: [PATCH 20/47] more inline documentation --- src/Composer/Repository/Vcs/SvnDriver.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Composer/Repository/Vcs/SvnDriver.php b/src/Composer/Repository/Vcs/SvnDriver.php index e71460de6..f0b399789 100644 --- a/src/Composer/Repository/Vcs/SvnDriver.php +++ b/src/Composer/Repository/Vcs/SvnDriver.php @@ -80,11 +80,15 @@ class SvnDriver extends VcsDriver implements VcsDriverInterface $output ); - // this could be any failure, but let's see if it's auth related + // this could be any failure if ($status == 1 && $this->io->isInteractive()) { + + // the error is not auth-related if (strpos($output, 'authorization failed:') === false) { return $output; } + + // no authorization has been detected so far if (!$this->useAuth) { $this->io->write("The Subversion server ({$this->baseUrl}) requested credentials:"); From 4c2da5714057c7de446b0dad03264cae2c11973c Mon Sep 17 00:00:00 2001 From: till Date: Fri, 9 Mar 2012 14:34:02 +0100 Subject: [PATCH 21/47] make static --- src/Composer/Repository/Vcs/SvnDriver.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Repository/Vcs/SvnDriver.php b/src/Composer/Repository/Vcs/SvnDriver.php index f0b399789..a8bbaf4b6 100644 --- a/src/Composer/Repository/Vcs/SvnDriver.php +++ b/src/Composer/Repository/Vcs/SvnDriver.php @@ -96,7 +96,7 @@ class SvnDriver extends VcsDriver implements VcsDriverInterface $this->svnPassword = $this->io->ask("Password"); $this->useAuth = true; - $cacheTrueAnswers = array('yes', 'y', 'true', 'ja', 'si', 'da'); + static $cacheTrueAnswers = array('yes', 'y', 'true', 'ja', 'si', 'da'); $cacheAnswer = strtolower($this->io->ask("Should we Subversion cache these credentials?", 'no')); if (in_array($cacheAnswer, $cacheTrueAnswers)) { From 8d80969f26b2e49e717b7299d2f9373da1b2c6b5 Mon Sep 17 00:00:00 2001 From: till Date: Fri, 9 Mar 2012 14:34:17 +0100 Subject: [PATCH 22/47] trim input --- src/Composer/Repository/Vcs/SvnDriver.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Repository/Vcs/SvnDriver.php b/src/Composer/Repository/Vcs/SvnDriver.php index a8bbaf4b6..643badcdb 100644 --- a/src/Composer/Repository/Vcs/SvnDriver.php +++ b/src/Composer/Repository/Vcs/SvnDriver.php @@ -98,7 +98,7 @@ class SvnDriver extends VcsDriver implements VcsDriverInterface static $cacheTrueAnswers = array('yes', 'y', 'true', 'ja', 'si', 'da'); - $cacheAnswer = strtolower($this->io->ask("Should we Subversion cache these credentials?", 'no')); + $cacheAnswer = strtolower(trim($this->io->ask("Should Subversion cache these credentials?", 'no'))); if (in_array($cacheAnswer, $cacheTrueAnswers)) { $this->useCache = true; } From 25e8ecc5ba55202992af3c326d5287f592cfa160 Mon Sep 17 00:00:00 2001 From: till Date: Fri, 9 Mar 2012 17:27:06 +0100 Subject: [PATCH 23/47] * trim the testcase: no need to inject nullio --- .../Test/Repository/Vcs/SvnDriverTest.php | 25 ++++--------------- 1 file changed, 5 insertions(+), 20 deletions(-) diff --git a/tests/Composer/Test/Repository/Vcs/SvnDriverTest.php b/tests/Composer/Test/Repository/Vcs/SvnDriverTest.php index d2a30e69e..7bfc985d9 100644 --- a/tests/Composer/Test/Repository/Vcs/SvnDriverTest.php +++ b/tests/Composer/Test/Repository/Vcs/SvnDriverTest.php @@ -31,32 +31,18 @@ class SvnDriverTest extends \PHPUnit_Framework_TestCase { $nullIO = new \Composer\IO\NullIO; - $input = new \Symfony\Component\Console\Input\ArrayInput(array('install')); - $output = new \Symfony\Component\Console\Output\NullOutput; - $helper = new \Symfony\Component\Console\Helper\HelperSet; - - $consoleInteractiveIO = new \Composer\IO\ConsoleIO($input, $output, $helper); - return array( array( 'http://till:test@svn.example.org/', " --no-auth-cache --username 'till' --password 'test' ", - $nullIO, ), array( 'http://svn.apache.org/', '', - $nullIO, ), array( 'svn://johndoe@example.org', " --no-auth-cache --username 'johndoe' --password '' ", - $nullIO, - ), - array( - 'https://till:secret@corp.svn.local/project1', - " --username 'till' --password 'secret' ", - $consoleInteractiveIO, ), ); } @@ -64,15 +50,14 @@ class SvnDriverTest extends \PHPUnit_Framework_TestCase /** * Test the credential string. * - * @param string $url The SVN url. - * @param string $expect The expectation for the test. - * @param string $ioClass The IO interface. - * + * @param string $url The SVN url. + * @param string $expect The expectation for the test. + * * @dataProvider urlProvider */ - public function testCredentials($url, $expect, \Composer\IO\IOInterface $io) + public function testCredentials($url, $expect) { - $svn = new SvnDriver($url, $io); + $svn = new SvnDriver($url, new \Composer\IO\NullIO); $this->assertEquals($expect, $svn->getSvnCredentialString()); } From 364e9613ad3a545a92faa9232c273c1763e9bb93 Mon Sep 17 00:00:00 2001 From: till Date: Fri, 9 Mar 2012 17:41:56 +0100 Subject: [PATCH 24/47] * started on a test case for SvnDriver::execute() (work in progress) --- .../Test/Repository/Vcs/SvnDriverTest.php | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/tests/Composer/Test/Repository/Vcs/SvnDriverTest.php b/tests/Composer/Test/Repository/Vcs/SvnDriverTest.php index 7bfc985d9..fe19d9a59 100644 --- a/tests/Composer/Test/Repository/Vcs/SvnDriverTest.php +++ b/tests/Composer/Test/Repository/Vcs/SvnDriverTest.php @@ -62,6 +62,31 @@ class SvnDriverTest extends \PHPUnit_Framework_TestCase $this->assertEquals($expect, $svn->getSvnCredentialString()); } + /** + * Test the execute method. + */ + public function testExecute() + { + $this->markTestSkipped("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') + ->will($this->returnValue(true)); + + $output = "svn: OPTIONS of 'http://corp.svn.local/repo':"; + $output .= " authorization failed: Could not authenticate to server:"; + $output .= " rejected Basic challenge (http://corp.svn.local/)"; + + $process = $this->getMock('Composer\Util\ProcessExecutor'); + $process->expects($this->once()) + ->method('execute') + ->will($this->returnValue(1)); + + $svn = new SvnDriver('http://till:secret@corp.svn.local/repo', $console, $process); + $svn->execute('svn ls', 'http://corp.svn.local/repo'); + } + public function testInteractiveString() { $url = 'http://svn.example.org'; From 96298a33dc663b5bfa1a0df017a26f4d1c85a078 Mon Sep 17 00:00:00 2001 From: till Date: Mon, 12 Mar 2012 17:24:36 +0100 Subject: [PATCH 25/47] catch all failures --- src/Composer/Repository/Vcs/SvnDriver.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Repository/Vcs/SvnDriver.php b/src/Composer/Repository/Vcs/SvnDriver.php index 643badcdb..83941374d 100644 --- a/src/Composer/Repository/Vcs/SvnDriver.php +++ b/src/Composer/Repository/Vcs/SvnDriver.php @@ -81,7 +81,7 @@ class SvnDriver extends VcsDriver implements VcsDriverInterface ); // this could be any failure - if ($status == 1 && $this->io->isInteractive()) { + if ($status > 0 && $this->io->isInteractive()) { // the error is not auth-related if (strpos($output, 'authorization failed:') === false) { From 244cc2a8c224d3b56472de3615b0b2a3eec99b26 Mon Sep 17 00:00:00 2001 From: till Date: Mon, 12 Mar 2012 17:25:45 +0100 Subject: [PATCH 26/47] hide password input --- src/Composer/Repository/Vcs/SvnDriver.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Repository/Vcs/SvnDriver.php b/src/Composer/Repository/Vcs/SvnDriver.php index 83941374d..8ded8702b 100644 --- a/src/Composer/Repository/Vcs/SvnDriver.php +++ b/src/Composer/Repository/Vcs/SvnDriver.php @@ -93,7 +93,7 @@ class SvnDriver extends VcsDriver implements VcsDriverInterface $this->io->write("The Subversion server ({$this->baseUrl}) requested credentials:"); $this->svnUsername = $this->io->ask("Username"); - $this->svnPassword = $this->io->ask("Password"); + $this->svnPassword = $this->io->askAndHideAnswer("Password"); $this->useAuth = true; static $cacheTrueAnswers = array('yes', 'y', 'true', 'ja', 'si', 'da'); From a1e1a8c7d73664891ff09d806e04cbcff2261276 Mon Sep 17 00:00:00 2001 From: till Date: Mon, 12 Mar 2012 17:28:13 +0100 Subject: [PATCH 27/47] * use askConfirmation() instead --- src/Composer/Repository/Vcs/SvnDriver.php | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/Composer/Repository/Vcs/SvnDriver.php b/src/Composer/Repository/Vcs/SvnDriver.php index 8ded8702b..330eaeeb5 100644 --- a/src/Composer/Repository/Vcs/SvnDriver.php +++ b/src/Composer/Repository/Vcs/SvnDriver.php @@ -96,10 +96,8 @@ class SvnDriver extends VcsDriver implements VcsDriverInterface $this->svnPassword = $this->io->askAndHideAnswer("Password"); $this->useAuth = true; - static $cacheTrueAnswers = array('yes', 'y', 'true', 'ja', 'si', 'da'); - - $cacheAnswer = strtolower(trim($this->io->ask("Should Subversion cache these credentials?", 'no'))); - if (in_array($cacheAnswer, $cacheTrueAnswers)) { + $pleaseCache = $this->io->askConfirmation("Should Subversion cache these credentials?", false); + if ($pleaseCache === true) { $this->useCache = true; } From 50f6445bc9b609f088ecccf238c2517b0d093d92 Mon Sep 17 00:00:00 2001 From: till Date: Mon, 12 Mar 2012 20:45:27 +0100 Subject: [PATCH 28/47] cleanup --- tests/Composer/Test/Repository/Vcs/SvnDriverTest.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/tests/Composer/Test/Repository/Vcs/SvnDriverTest.php b/tests/Composer/Test/Repository/Vcs/SvnDriverTest.php index 42e88d13c..69e34c0aa 100644 --- a/tests/Composer/Test/Repository/Vcs/SvnDriverTest.php +++ b/tests/Composer/Test/Repository/Vcs/SvnDriverTest.php @@ -23,14 +23,10 @@ class SvnDriverTest extends \PHPUnit_Framework_TestCase /** * Provide some examples for {@self::testCredentials()}. * - * {@link \Composer\IO\NullIO} is always non-interactive. - * * @return array */ public function urlProvider() { - $nullIO = new \Composer\IO\NullIO; - return array( array('http://till:test@svn.example.org/', $this->getCmd(" --no-auth-cache --username 'till' --password 'test' ")), array('http://svn.apache.org/', ''), From f06bdcbf1628c7a4d4f1a3c35b5ecc0a861f10b1 Mon Sep 17 00:00:00 2001 From: till Date: Mon, 12 Mar 2012 20:45:45 +0100 Subject: [PATCH 29/47] use shorthand (we have a use statement) --- tests/Composer/Test/Repository/Vcs/SvnDriverTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Composer/Test/Repository/Vcs/SvnDriverTest.php b/tests/Composer/Test/Repository/Vcs/SvnDriverTest.php index 69e34c0aa..28f7b0111 100644 --- a/tests/Composer/Test/Repository/Vcs/SvnDriverTest.php +++ b/tests/Composer/Test/Repository/Vcs/SvnDriverTest.php @@ -44,7 +44,7 @@ class SvnDriverTest extends \PHPUnit_Framework_TestCase */ public function testCredentials($url, $expect) { - $svn = new SvnDriver($url, new \Composer\IO\NullIO); + $svn = new SvnDriver($url, new NullIO); $this->assertEquals($expect, $svn->getSvnCredentialString()); } From 9a60913d815fe60a455a762bcd3b4a88b6997a37 Mon Sep 17 00:00:00 2001 From: till Date: Mon, 12 Mar 2012 20:46:01 +0100 Subject: [PATCH 30/47] incomplete (instead of skipped) --- tests/Composer/Test/Repository/Vcs/SvnDriverTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Composer/Test/Repository/Vcs/SvnDriverTest.php b/tests/Composer/Test/Repository/Vcs/SvnDriverTest.php index 28f7b0111..684681e16 100644 --- a/tests/Composer/Test/Repository/Vcs/SvnDriverTest.php +++ b/tests/Composer/Test/Repository/Vcs/SvnDriverTest.php @@ -54,7 +54,7 @@ class SvnDriverTest extends \PHPUnit_Framework_TestCase */ public function testExecute() { - $this->markTestSkipped("Currently no way to mock the output value which is passed by reference."); + $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()) From 2562755867c9f761910adfe23804997a1a225583 Mon Sep 17 00:00:00 2001 From: till Date: Mon, 12 Mar 2012 21:27:22 +0100 Subject: [PATCH 31/47] * add a test to cover SvnDriver::supports() (three fail) --- .../Test/Repository/Vcs/SvnDriverTest.php | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/tests/Composer/Test/Repository/Vcs/SvnDriverTest.php b/tests/Composer/Test/Repository/Vcs/SvnDriverTest.php index 684681e16..ca9fa8936 100644 --- a/tests/Composer/Test/Repository/Vcs/SvnDriverTest.php +++ b/tests/Composer/Test/Repository/Vcs/SvnDriverTest.php @@ -95,4 +95,30 @@ class SvnDriverTest extends \PHPUnit_Framework_TestCase return $cmd; } + + public static function supportProvider() + { + return array( + array('http://svn.apache.org', true), + array('http://svn.sf.net', true), + array('svn://example.org', true), + array('svn+ssh://example.org', true), + array('file:///d:/repository_name/project', true), + array('file:///repository_name/project', true), + ); + } + + /** + * Nail a bug in {@link SvnDriver::support()}. + * + * @dataProvider supportProvider + */ + public function testSupport($url, $assertion) + { + if ($assertion === true) { + $this->assertTrue(SvnDriver::supports($url)); + } else { + $this->assertFalse(SvnDriver::supports($url)); + } + } } From 66d53aafefa95b861143343fcd6761bb384716e5 Mon Sep 17 00:00:00 2001 From: till Date: Sun, 18 Mar 2012 16:35:09 +0100 Subject: [PATCH 32/47] add more examples to the dataprovider --- tests/Composer/Test/Repository/Vcs/SvnDriverTest.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/Composer/Test/Repository/Vcs/SvnDriverTest.php b/tests/Composer/Test/Repository/Vcs/SvnDriverTest.php index ca9fa8936..9c11330b0 100644 --- a/tests/Composer/Test/Repository/Vcs/SvnDriverTest.php +++ b/tests/Composer/Test/Repository/Vcs/SvnDriverTest.php @@ -100,11 +100,12 @@ class SvnDriverTest extends \PHPUnit_Framework_TestCase { return array( array('http://svn.apache.org', true), - array('http://svn.sf.net', true), + array('https://svn.sf.net', true), array('svn://example.org', true), array('svn+ssh://example.org', true), array('file:///d:/repository_name/project', true), array('file:///repository_name/project', true), + array('/absolute/path', true), ); } From 6d1cdb3e4552608998edf23c4eaf682cb68c5f25 Mon Sep 17 00:00:00 2001 From: till Date: Sun, 18 Mar 2012 16:36:03 +0100 Subject: [PATCH 33/47] * fixSvnUrl(): to prefix absolute paths with file:// --- src/Composer/Repository/Vcs/SvnDriver.php | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/Composer/Repository/Vcs/SvnDriver.php b/src/Composer/Repository/Vcs/SvnDriver.php index d76629c62..f1ab75d42 100644 --- a/src/Composer/Repository/Vcs/SvnDriver.php +++ b/src/Composer/Repository/Vcs/SvnDriver.php @@ -331,6 +331,21 @@ class SvnDriver extends VcsDriver return $exit === 0; } + /** + * An absolute path (leading '/') is converted to a file:// url. + * + * @param string $url + * + * @return string + */ + protected static function fixSvnUrl($url) + { + if (strpos($url, '/', 0) === 0) { + $url = 'file://' . $url; + } + return $url; + } + /** * This is quick and dirty - thoughts? * From c0ec8f16f96bf6d9b2fcfbd4ee003f770c32bc63 Mon Sep 17 00:00:00 2001 From: till Date: Sun, 18 Mar 2012 16:36:30 +0100 Subject: [PATCH 34/47] * fixSvnUrl() * extend regex to match more possible svn hosts --- src/Composer/Repository/Vcs/SvnDriver.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Composer/Repository/Vcs/SvnDriver.php b/src/Composer/Repository/Vcs/SvnDriver.php index f1ab75d42..f6a7de275 100644 --- a/src/Composer/Repository/Vcs/SvnDriver.php +++ b/src/Composer/Repository/Vcs/SvnDriver.php @@ -50,6 +50,7 @@ class SvnDriver extends VcsDriver */ public function __construct($url, IOInterface $io, ProcessExecutor $process = null) { + $url = self::fixSvnUrl($url); parent::__construct($this->baseUrl = rtrim($url, '/'), $io, $process); if (false !== ($pos = strrpos($url, '/trunk'))) { @@ -314,7 +315,8 @@ class SvnDriver extends VcsDriver */ public static function supports($url, $deep = false) { - if (preg_match('#(^svn://|//svn\.)#i', $url)) { + $url = self::fixSvnUrl($url); + if (preg_match('#((^svn://)|(^svn\+ssh://)|(^file:///)|(^http)|(svn\.))#i', $url)) { return true; } From 6f364a85b3af992172df455c9f20de1d965ee5ed Mon Sep 17 00:00:00 2001 From: till Date: Sun, 18 Mar 2012 17:35:32 +0100 Subject: [PATCH 35/47] * do not use getSvnCommand (since we are not in object context) * run non interacive instead of piping output to /dev/null --- src/Composer/Repository/Vcs/SvnDriver.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Repository/Vcs/SvnDriver.php b/src/Composer/Repository/Vcs/SvnDriver.php index f6a7de275..3380b9d41 100644 --- a/src/Composer/Repository/Vcs/SvnDriver.php +++ b/src/Composer/Repository/Vcs/SvnDriver.php @@ -327,7 +327,7 @@ class SvnDriver extends VcsDriver $processExecutor = new ProcessExecutor(); $exit = $processExecutor->execute( - $this->getSvnCommand('svn info', $url, '2>/dev/null'), + "svn info --non-interactive {$url}", $ignoredOutput ); return $exit === 0; From 8eb9584173f5c1fb83dcbd2a0953f2e4c97a887d Mon Sep 17 00:00:00 2001 From: till Date: Thu, 22 Mar 2012 17:16:41 +0100 Subject: [PATCH 36/47] * move tests from Composer\Test\Vcs\SvnDriverTest to this class --- tests/Composer/Test/Util/SvnTest.php | 49 ++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 tests/Composer/Test/Util/SvnTest.php diff --git a/tests/Composer/Test/Util/SvnTest.php b/tests/Composer/Test/Util/SvnTest.php new file mode 100644 index 000000000..2a1839f67 --- /dev/null +++ b/tests/Composer/Test/Util/SvnTest.php @@ -0,0 +1,49 @@ +getCmd(" --no-auth-cache --username 'till' --password 'test' ")), + array('http://svn.apache.org/', ''), + array('svn://johndoe@example.org', $this->getCmd(" --no-auth-cache --username 'johndoe' --password '' ")), + ); + } + + /** + * Test the credential string. + * + * @param string $url The SVN url. + * @param string $expect The expectation for the test. + * + * @dataProvider urlProvider + */ + public function testCredentials($url, $expect) + { + $svn = new Svn($url, new NullIO); + + $this->assertEquals($expect, $svn->getCredentialString()); + } + + public function testInteractiveString() + { + $url = 'http://svn.example.org'; + + $svn = new Svn($url, new NullIO()); + + $this->assertEquals( + "svn ls --non-interactive 'http://svn.example.org'", + $svn->getCommand('svn ls', $url) + ); + } +} \ No newline at end of file From cd2c6dc5d2d74e8ce4099f014221a6653b217c54 Mon Sep 17 00:00:00 2001 From: till Date: Thu, 22 Mar 2012 17:17:11 +0100 Subject: [PATCH 37/47] * ignore vagrant related * ignore phpstorm related --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index b3424784d..0e883e5c1 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,6 @@ /vendor /nbproject phpunit.xml +.vagrant +Vagrantfile +.idea From e0150326151c328b50140061627f5ea77e06a225 Mon Sep 17 00:00:00 2001 From: till Date: Thu, 22 Mar 2012 17:17:55 +0100 Subject: [PATCH 38/47] remove old tests --- .../Test/Repository/Vcs/SvnDriverTest.php | 42 ------------------- 1 file changed, 42 deletions(-) diff --git a/tests/Composer/Test/Repository/Vcs/SvnDriverTest.php b/tests/Composer/Test/Repository/Vcs/SvnDriverTest.php index 9c11330b0..9e85c5129 100644 --- a/tests/Composer/Test/Repository/Vcs/SvnDriverTest.php +++ b/tests/Composer/Test/Repository/Vcs/SvnDriverTest.php @@ -20,35 +20,6 @@ use Composer\IO\NullIO; */ class SvnDriverTest extends \PHPUnit_Framework_TestCase { - /** - * Provide some examples for {@self::testCredentials()}. - * - * @return array - */ - public function urlProvider() - { - return array( - array('http://till:test@svn.example.org/', $this->getCmd(" --no-auth-cache --username 'till' --password 'test' ")), - array('http://svn.apache.org/', ''), - array('svn://johndoe@example.org', $this->getCmd(" --no-auth-cache --username 'johndoe' --password '' ")), - ); - } - - /** - * Test the credential string. - * - * @param string $url The SVN url. - * @param string $expect The expectation for the test. - * - * @dataProvider urlProvider - */ - public function testCredentials($url, $expect) - { - $svn = new SvnDriver($url, new NullIO); - - $this->assertEquals($expect, $svn->getSvnCredentialString()); - } - /** * Test the execute method. */ @@ -74,19 +45,6 @@ class SvnDriverTest extends \PHPUnit_Framework_TestCase $svn->execute('svn ls', 'http://corp.svn.local/repo'); } - public function testInteractiveString() - { - $url = 'http://svn.example.org'; - - $io = new \Composer\IO\NullIO; // non-interactive by design - $svn = new SvnDriver($url, $io); - - $this->assertEquals( - "svn ls --non-interactive 'http://svn.example.org'", - $svn->getSvnCommand('svn ls', $url) - ); - } - private function getCmd($cmd) { if (defined('PHP_WINDOWS_VERSION_BUILD')) { From 17f90f56ebcbfbdfec62018331753d5dc897025d Mon Sep 17 00:00:00 2001 From: till Date: Thu, 22 Mar 2012 17:18:24 +0100 Subject: [PATCH 39/47] * move helper functions to util class --- src/Composer/Repository/Vcs/SvnDriver.php | 110 ++----------- src/Composer/Util/Svn.php | 181 ++++++++++++++++++++++ 2 files changed, 192 insertions(+), 99 deletions(-) create mode 100644 src/Composer/Util/Svn.php diff --git a/src/Composer/Repository/Vcs/SvnDriver.php b/src/Composer/Repository/Vcs/SvnDriver.php index 3380b9d41..5b49b7e77 100644 --- a/src/Composer/Repository/Vcs/SvnDriver.php +++ b/src/Composer/Repository/Vcs/SvnDriver.php @@ -4,6 +4,7 @@ namespace Composer\Repository\Vcs; use Composer\Json\JsonFile; use Composer\Util\ProcessExecutor; +use Composer\Util\Svn as SvnUtil; use Composer\IO\IOInterface; /** @@ -38,6 +39,11 @@ class SvnDriver extends VcsDriver */ protected $svnPassword = ''; + /** + * @var Composer\Util\Svn $util + */ + protected $util; + /** * __construct * @@ -56,8 +62,8 @@ class SvnDriver extends VcsDriver if (false !== ($pos = strrpos($url, '/trunk'))) { $this->baseUrl = substr($url, 0, $pos); } - - $this->detectSvnAuth(); + $this->util = new SvnUtil($this->baseUrl, $io); + $this->useAuth = $this->util->hasAuth(); } /** @@ -68,13 +74,13 @@ class SvnDriver extends VcsDriver * @param string $url The SVN URL. * * @return string - * @uses self::getSvnCommand() + * @uses Composer\Util\Svn::getCommand() * @uses parent::$process * @see ProcessExecutor::execute() */ public function execute($command, $url) { - $svnCommand = $this->getSvnCommand($command, $url); + $svnCommand = $this->util->getCommand($command, $url); $status = $this->process->execute( $svnCommand, @@ -239,77 +245,6 @@ class SvnDriver extends VcsDriver return $this->branches; } - /** - * Return the no-auth-cache switch. - * - * @return string - */ - public function getSvnAuthCache() - { - if (!$this->useCache) { - return '--no-auth-cache '; - } - return ''; - } - - /** - * A method to create the svn commands run. - * - * @string $cmd Usually 'svn ls' or something like that. - * @string $url Repo URL. - * @string $pipe Optional pipe for the output. - * - * @return string - */ - public function getSvnCommand($cmd, $url, $pipe = null) - { - $cmd = sprintf('%s %s%s %s', - $cmd, - $this->getSvnInteractiveSetting(), - $this->getSvnCredentialString(), - escapeshellarg($url) - ); - if ($pipe !== null) { - $cmd .= ' ' . $pipe; - } - return $cmd; - } - - /** - * Return the credential string for the svn command. - * - * Adds --no-auth-cache when credentials are present. - * - * @return string - * @uses self::$useAuth - */ - public function getSvnCredentialString() - { - if ($this->useAuth !== true) { - return ''; - } - $str = ' %s--username %s --password %s '; - return sprintf( - $str, - $this->getSvnAuthCache(), - escapeshellarg($this->svnUsername), - escapeshellarg($this->svnPassword) - ); - } - - /** - * Always run commands 'non-interactive': - * - * It's easier to spot potential issues (e.g. auth-failure) because - * non-interactive svn fails fast and does not wait for user input. - * - * @return string - */ - public function getSvnInteractiveSetting() - { - return '--non-interactive '; - } - /** * {@inheritDoc} */ @@ -330,6 +265,7 @@ class SvnDriver extends VcsDriver "svn info --non-interactive {$url}", $ignoredOutput ); + return $exit === 0; } @@ -347,28 +283,4 @@ class SvnDriver extends VcsDriver } return $url; } - - /** - * This is quick and dirty - thoughts? - * - * @return void - * @uses parent::$baseUrl - * @uses self::$useAuth, self::$svnUsername, self::$svnPassword - * @see self::__construct() - */ - protected function detectSvnAuth() - { - $uri = parse_url($this->baseUrl); - if (empty($uri['user'])) { - return; - } - - $this->svnUsername = $uri['user']; - - if (!empty($uri['pass'])) { - $this->svnPassword = $uri['pass']; - } - - $this->useAuth = true; - } } diff --git a/src/Composer/Util/Svn.php b/src/Composer/Util/Svn.php new file mode 100644 index 000000000..b7b45790b --- /dev/null +++ b/src/Composer/Util/Svn.php @@ -0,0 +1,181 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Util; + +use Composer\IO\IOInterface; + +/** + * @author Till Klampaeckel + */ +class Svn +{ + /** + * @var mixed $credentials + * @see self::hasAuth() + */ + protected $credentials; + + /** + * @var boolean $hasAuth + */ + protected $hasAuth; + + /** + * @var \Composer\IO\IOInterface $io + */ + protected $io; + + /** + * @var string $url + */ + protected $url; + + /** + * __construct + * + * @param string $url + * @param \Composer\IO\IOInterface $io + * + * @return \Composer\Util\Svn + */ + public function __construct($url, IOInterface $io) + { + $this->url = $url; + $this->io = $io; + } + + /** + * Return the no-auth-cache switch. + * + * @return string + */ + public function getAuthCache() + { + if (!$this->hasCache) { + return '--no-auth-cache '; + } + return ''; + } + + /** + * A method to create the svn commands run. + * + * @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 mixed $pipe Optional pipe for the output. + * + * @return string + */ + public function getCommand($cmd, $url, $path = '', $pipe = null) + { + $cmd = sprintf('%s %s%s %s', + $cmd, + '--non-interactive ', + $this->getCredentialString(), + escapeshellarg($url) + ); + if (!empty($path)) { + $cmd .= ' ' . escapeshellarg($path); + } + if ($pipe !== null) { + $cmd .= ' ' . $pipe; + } + return $cmd; + } + + /** + * Return the credential string for the svn command. + * + * Adds --no-auth-cache when credentials are present. + * + * @return string + * @uses self::$useAuth + */ + public function getCredentialString() + { + if ($this->hasAuth === null) { + $this->hasAuth(); + } + if (!$this->hasAuth) { + return ''; + } + return sprintf( + ' %s--username %s --password %s ', + $this->getAuthCache(), + escapeshellarg($this->getUsername()), + escapeshellarg($this->getPassword()) + ); + } + + /** + * Get the password for the svn command. Can be empty. + * + * @return string + * @throws \LogicException + */ + public function getPassword() + { + if ($this->credentials === null) { + throw new \LogicException("No auth detected."); + } + if (isset($this->credentials->password)) { + return $this->credentials->password; + } + return ''; // could be empty + } + + /** + * Get the username for the svn command. + * + * @return string + * @throws \LogicException + */ + public function getUsername() + { + if ($this->credentials === null) { + throw new \LogicException("No auth detected."); + } + return $this->credentials->username; + } + + /** + * Detect Svn Auth. + * + * @param string $url + * + * @return \stdClass + */ + public function hasAuth() + { + if ($this->hasAuth !== null) { + return $this->hasAuth; + } + + $uri = parse_url($this->url); + if (empty($uri['user'])) { + $this->hasAuth = false; + return $this->hasAuth; + } + + $this->hasAuth = true; + $this->credentials = new \stdClass(); + + $this->credentials->username = $uri['user']; + + if (!empty($uri['pass'])) { + $this->credentials->password = $uri['pass']; + } + + return $this->hasAuth; + } +} \ No newline at end of file From 3de8d66a82581d9056e02cf365f48ca876435495 Mon Sep 17 00:00:00 2001 From: till Date: Thu, 22 Mar 2012 17:19:10 +0100 Subject: [PATCH 40/47] * refactor SvnDownloader to use new Util Class * now supports auth all over * svn command generation is proxied through one place * still needs the 'interactive' settings and an execute method --- src/Composer/Downloader/SvnDownloader.php | 46 ++++++++++++++++++----- 1 file changed, 36 insertions(+), 10 deletions(-) diff --git a/src/Composer/Downloader/SvnDownloader.php b/src/Composer/Downloader/SvnDownloader.php index 28d3e2101..5a47a755d 100644 --- a/src/Composer/Downloader/SvnDownloader.php +++ b/src/Composer/Downloader/SvnDownloader.php @@ -14,22 +14,32 @@ namespace Composer\Downloader; use Composer\Package\PackageInterface; use Composer\Util\ProcessExecutor; +use Composer\Util\Svn as SvnUtil; /** * @author Ben Bieker */ class SvnDownloader extends VcsDownloader { + /** + * @var \Composer\Util\Svn $util + */ + protected $util; + /** * {@inheritDoc} */ public function doDownload(PackageInterface $package, $path) { - $url = escapeshellarg($package->getSourceUrl()); - $ref = escapeshellarg($package->getSourceReference()); - $path = escapeshellarg($path); + $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->process->execute(sprintf('svn co %s/%s %s', $url, $ref, $path)); + $this->process->execute($command); } /** @@ -37,11 +47,14 @@ class SvnDownloader extends VcsDownloader */ public function doUpdate(PackageInterface $initial, PackageInterface $target, $path) { - $ref = escapeshellarg($target->getSourceReference()); - $path = escapeshellarg($path); - $url = escapeshellarg($target->getSourceUrl()); - $this->io->write(" Checking out ".$target->getSourceReference()); - $this->process->execute(sprintf('cd %s && svn switch %s/%s', $path, $url, $ref)); + $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->process->execute(sprintf('cd %s && %s', $path, $command)); } /** @@ -54,4 +67,17 @@ class SvnDownloader extends VcsDownloader throw new \RuntimeException('Source directory ' . $path . ' has uncommitted changes'); } } -} \ No newline at end of file + + /** + * This is heavy - recreating Util often. + * + * @param string $url + * + * @return \Composer\Util\Svn + */ + protected function getUtil($url) + { + $util = new SvnUtil($url, $this->io); + return $util; + } +} From 0d6297f2350ccc62484eeb77fecf2ade96e606a2 Mon Sep 17 00:00:00 2001 From: till Date: Thu, 22 Mar 2012 18:40:18 +0100 Subject: [PATCH 41/47] * add 'doAuthDance()' to allow code reuse from SvnDownloader and SvnDriver --- src/Composer/Util/Svn.php | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/src/Composer/Util/Svn.php b/src/Composer/Util/Svn.php index b7b45790b..f09fedd7c 100644 --- a/src/Composer/Util/Svn.php +++ b/src/Composer/Util/Svn.php @@ -39,6 +39,11 @@ class Svn */ protected $url; + /** + * @var bool $useCache Cache credentials. + */ + protected $useCache = false; + /** * __construct * @@ -53,14 +58,43 @@ class Svn $this->io = $io; } + /** + * doAuthDance + * + * Repositories requests credentials, let's put them in. + * + * @return \Composer\Util\Svn + * @uses self::$io + * @uses self::$hasAuth + * @uses self::$credentials + * @uses self::$useCache + */ + public function doAuthDance() + { + $this->io->write("The Subversion server ({$this->url}) requested credentials:"); + + $this->hasAuth = true; + $this->credentials = new \stdClass(); + + $this->credentials->username = $this->io->ask("Username: "); + $this->credentials->password = $this->io->askAndHideAnswer("Password: "); + + $pleaseCache = $this->io->askConfirmation("Should Subversion cache these credentials? (yes/no) ", false); + if ($pleaseCache === true) { + $this->useCache = true; + } + return $this; + } /** * Return the no-auth-cache switch. * * @return string + * @uses selfg::$useCache + * @see self::doAuthDance() */ public function getAuthCache() { - if (!$this->hasCache) { + if (!$this->useCache) { return '--no-auth-cache '; } return ''; From 059bde1adb2530fd65a09e3985e0f6dd311d631a Mon Sep 17 00:00:00 2001 From: till Date: Thu, 22 Mar 2012 18:40:49 +0100 Subject: [PATCH 42/47] * refactored with Composer\Util\Svn::doAuthDance() --- src/Composer/Repository/Vcs/SvnDriver.php | 40 ++++++++++------------- 1 file changed, 18 insertions(+), 22 deletions(-) diff --git a/src/Composer/Repository/Vcs/SvnDriver.php b/src/Composer/Repository/Vcs/SvnDriver.php index 5b49b7e77..61a9573fa 100644 --- a/src/Composer/Repository/Vcs/SvnDriver.php +++ b/src/Composer/Repository/Vcs/SvnDriver.php @@ -87,32 +87,28 @@ class SvnDriver extends VcsDriver $output ); - // this could be any failure - if ($status > 0 && $this->io->isInteractive()) { - - // the error is not auth-related - if (strpos($output, 'authorization failed:') === false) { - return $output; - } + if ($status == 0) { + return $output; + } - // no authorization has been detected so far - if (!$this->useAuth) { - $this->io->write("The Subversion server ({$this->baseUrl}) requested credentials:"); + // this could be any failure, since SVN exits with 1 always + if (!$this->io->isInteractive()) { + return $output; + } - $this->svnUsername = $this->io->ask("Username"); - $this->svnPassword = $this->io->askAndHideAnswer("Password"); - $this->useAuth = true; + // the error is not auth-related + if (strpos($output, 'authorization failed:') === false) { + return $output; + } - $pleaseCache = $this->io->askConfirmation("Should Subversion cache these credentials?", false); - if ($pleaseCache === true) { - $this->useCache = true; - } + // 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}"); - } + // restart the process + $output = $this->execute($command, $url); + } else { + $this->io->write("Authorization failed: {$svnCommand}"); } return $output; } From c7dc49fe10c48b71f288dd707ae781b0ae668ad2 Mon Sep 17 00:00:00 2001 From: till Date: Thu, 22 Mar 2012 18:41:10 +0100 Subject: [PATCH 43/47] * added execute() wrapper to generalize command execution in downloader * added Composer\Util\Svn::doAuthDance() to ask for credentials in interactive sessions --- src/Composer/Downloader/SvnDownloader.php | 52 ++++++++++++++++++++++- 1 file changed, 50 insertions(+), 2 deletions(-) diff --git a/src/Composer/Downloader/SvnDownloader.php b/src/Composer/Downloader/SvnDownloader.php index 5a47a755d..5d5929c7c 100644 --- a/src/Composer/Downloader/SvnDownloader.php +++ b/src/Composer/Downloader/SvnDownloader.php @@ -21,6 +21,11 @@ use Composer\Util\Svn as SvnUtil; */ class SvnDownloader extends VcsDownloader { + /** + * @var bool $useAuth + */ + protected $useAuth = false; + /** * @var \Composer\Util\Svn $util */ @@ -39,7 +44,7 @@ class SvnDownloader extends VcsDownloader $command = $util->getCommand("svn co", sprintf("%s/%s", $url, $ref), $path); $this->io->write(" Checking out ".$package->getSourceReference()); - $this->process->execute($command); + $this->execute($command, $util); } /** @@ -54,7 +59,7 @@ class SvnDownloader extends VcsDownloader $command = $util->getCommand("svn switch", sprintf("%s/%s", $url, $ref)); $this->io->write(" Checking out " . $ref); - $this->process->execute(sprintf('cd %s && %s', $path, $command)); + $this->execute(sprintf('cd %s && %s', $path, $command), $util); } /** @@ -68,6 +73,49 @@ class SvnDownloader extends VcsDownloader } } + /** + * Wrap {@link \Composer\Util\ProcessExecutor::execute(). + * + * @param string $cmd + * @param SvnUtil $util + * + * @return string + */ + protected function execute($command, SvnUtil $util) + { + $status = $this->process->execute($command, $output); + if ($status == 0) { + 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; + } + + // the error is not auth-related + if (strpos($output, 'authorization failed:') === false) { + 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; + } + /** * This is heavy - recreating Util often. * From a6366be5a68b56f5268ebbb4618fbe9a0270d6e8 Mon Sep 17 00:00:00 2001 From: Beau Simensen Date: Thu, 22 Mar 2012 14:58:52 -0700 Subject: [PATCH 44/47] Handle auth challenge in SvnDriver supports. --- src/Composer/Repository/Vcs/SvnDriver.php | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/Composer/Repository/Vcs/SvnDriver.php b/src/Composer/Repository/Vcs/SvnDriver.php index 61a9573fa..c11e417ab 100644 --- a/src/Composer/Repository/Vcs/SvnDriver.php +++ b/src/Composer/Repository/Vcs/SvnDriver.php @@ -262,7 +262,16 @@ class SvnDriver extends VcsDriver $ignoredOutput ); - return $exit === 0; + if ($exit === 0) { + // This is definitely a Subversion repository. + return true; + } + if (preg_match('/authorization failed/i', $processExecutor->getErrorOutput())) { + // This is likely a remote Subversion repository that requires + // authentication. We will handle actual authentication later. + return true; + } + return false; } /** From 80bb040468027114dbc41e0d4b8e7910ddf856a1 Mon Sep 17 00:00:00 2001 From: Flo Date: Fri, 23 Mar 2012 00:29:19 +0100 Subject: [PATCH 45/47] Improve 'getTags()' and 'getBranches()' --- src/Composer/Repository/Vcs/SvnDriver.php | 42 ++++++++++++++--------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/src/Composer/Repository/Vcs/SvnDriver.php b/src/Composer/Repository/Vcs/SvnDriver.php index 61a9573fa..307df6d0b 100644 --- a/src/Composer/Repository/Vcs/SvnDriver.php +++ b/src/Composer/Repository/Vcs/SvnDriver.php @@ -198,11 +198,14 @@ class SvnDriver extends VcsDriver public function getTags() { if (null === $this->tags) { - $output = $this->execute('svn ls', $this->baseUrl . '/tags'); $this->tags = array(); - foreach ($this->process->splitLines($output) as $tag) { - if ($tag) { - $this->tags[rtrim($tag, '/')] = '/tags/'.$tag; + + $output = $this->execute('svn ls', $this->baseUrl . '/tags'); + if ($output) { + foreach ($this->process->splitLines($output) as $tag) { + if ($tag) { + $this->tags[rtrim($tag, '/')] = '/tags/'.$tag; + } } } } @@ -216,25 +219,32 @@ class SvnDriver extends VcsDriver public function getBranches() { if (null === $this->branches) { - $output = $this->execute('svn ls --verbose', $this->baseUrl . '/'); $this->branches = array(); - foreach ($this->process->splitLines($output) as $line) { - preg_match('{^\s*(\S+).*?(\S+)\s*$}', $line, $match); - if ($match[2] === 'trunk/') { - $this->branches['trunk'] = '/trunk/@'.$match[1]; - break; + + $output = $this->execute('svn ls --verbose', $this->baseUrl . '/'); + if ($output) { + foreach ($this->process->splitLines($output) as $line) { + $line = trim($line); + if ($line && preg_match('{^\s*(\S+).*?(\S+)\s*$}', $line, $match)) { + if (isset($match[1]) && isset($match[2]) && $match[2] === 'trunk/') { + $this->branches['trunk'] = '/trunk/@'.$match[1]; + break; + } + } } } unset($output); $output = $this->execute('svn ls --verbose', $this->baseUrl . '/branches'); - - foreach ($this->process->splitLines(trim($output)) as $line) { - preg_match('{^\s*(\S+).*?(\S+)\s*$}', $line, $match); - if ($match[2] === './') { - continue; + if ($output) { + foreach ($this->process->splitLines(trim($output)) as $line) { + $line = trim($line); + if ($line && preg_match('{^\s*(\S+).*?(\S+)\s*$}', $line, $match)) { + if (isset($match[1]) && isset($match[2]) && $match[2] !== './') { + $this->branches[rtrim($match[2], '/')] = '/branches/'.$match[2].'@'.$match[1]; + } + } } - $this->branches[rtrim($match[2], '/')] = '/branches/'.$match[2].'@'.$match[1]; } } From c6653f07115027206cdcaf19f0a6e8075e15e55b Mon Sep 17 00:00:00 2001 From: till Date: Fri, 23 Mar 2012 16:03:52 +0100 Subject: [PATCH 46/47] * fix up CS suggestions from stof/seldaek --- src/Composer/Downloader/SvnDownloader.php | 9 +++++---- src/Composer/Repository/Vcs/SvnDriver.php | 21 ++++++++------------ src/Composer/Util/Svn.php | 24 +++++++---------------- 3 files changed, 20 insertions(+), 34 deletions(-) diff --git a/src/Composer/Downloader/SvnDownloader.php b/src/Composer/Downloader/SvnDownloader.php index 5d5929c7c..045bf6a81 100644 --- a/src/Composer/Downloader/SvnDownloader.php +++ b/src/Composer/Downloader/SvnDownloader.php @@ -18,16 +18,17 @@ use Composer\Util\Svn as SvnUtil; /** * @author Ben Bieker + * @author Till Klampaeckel */ class SvnDownloader extends VcsDownloader { /** - * @var bool $useAuth + * @var bool */ protected $useAuth = false; /** - * @var \Composer\Util\Svn $util + * @var \Composer\Util\Svn */ protected $util; @@ -99,7 +100,7 @@ class SvnDownloader extends VcsDownloader } // the error is not auth-related - if (strpos($output, 'authorization failed:') === false) { + if (false === strpos($output, 'authorization failed:')) { return $output; } @@ -117,7 +118,7 @@ class SvnDownloader extends VcsDownloader } /** - * This is heavy - recreating Util often. + * This is potentially heavy - recreating Util often. * * @param string $url * diff --git a/src/Composer/Repository/Vcs/SvnDriver.php b/src/Composer/Repository/Vcs/SvnDriver.php index 1bcafa454..b9ce93ccc 100644 --- a/src/Composer/Repository/Vcs/SvnDriver.php +++ b/src/Composer/Repository/Vcs/SvnDriver.php @@ -9,6 +9,7 @@ use Composer\IO\IOInterface; /** * @author Jordi Boggiano + * @author Till Klampaeckel */ class SvnDriver extends VcsDriver { @@ -18,41 +19,38 @@ class SvnDriver extends VcsDriver protected $infoCache = array(); /** - * @var boolean $useAuth Contains credentials, or not? + * Contains credentials, or not? + * @var boolean */ protected $useAuth = false; /** - * @var boolean $useCache To determine if we should cache the credentials - * supplied by the user. By default: no cache. - * @see self::getSvnAuthCache() + * To determine if we should cache the credentials supplied by the user. By default: no cache. + * @var boolean */ protected $useCache = false; /** - * @var string $svnUsername + * @var string */ protected $svnUsername = ''; /** - * @var string $svnPassword + * @var string */ protected $svnPassword = ''; /** - * @var Composer\Util\Svn $util + * @var \Composer\Util\Svn */ protected $util; /** - * __construct - * * @param string $url * @param IOInterface $io * @param ProcessExecutor $process * * @return $this - * @uses self::detectSvnAuth() */ public function __construct($url, IOInterface $io, ProcessExecutor $process = null) { @@ -74,9 +72,6 @@ class SvnDriver extends VcsDriver * @param string $url The SVN URL. * * @return string - * @uses Composer\Util\Svn::getCommand() - * @uses parent::$process - * @see ProcessExecutor::execute() */ public function execute($command, $url) { diff --git a/src/Composer/Util/Svn.php b/src/Composer/Util/Svn.php index f09fedd7c..ca1e7e1d5 100644 --- a/src/Composer/Util/Svn.php +++ b/src/Composer/Util/Svn.php @@ -19,34 +19,32 @@ use Composer\IO\IOInterface; class Svn { /** - * @var mixed $credentials - * @see self::hasAuth() + * @var mixed */ protected $credentials; /** - * @var boolean $hasAuth + * @var bool */ protected $hasAuth; /** - * @var \Composer\IO\IOInterface $io + * @var \Composer\IO\IOInterface */ protected $io; /** - * @var string $url + * @var string */ protected $url; /** - * @var bool $useCache Cache credentials. + * Cache credentials. + * @var bool */ protected $useCache = false; /** - * __construct - * * @param string $url * @param \Composer\IO\IOInterface $io * @@ -59,15 +57,9 @@ class Svn } /** - * doAuthDance - * * Repositories requests credentials, let's put them in. * * @return \Composer\Util\Svn - * @uses self::$io - * @uses self::$hasAuth - * @uses self::$credentials - * @uses self::$useCache */ public function doAuthDance() { @@ -80,7 +72,7 @@ class Svn $this->credentials->password = $this->io->askAndHideAnswer("Password: "); $pleaseCache = $this->io->askConfirmation("Should Subversion cache these credentials? (yes/no) ", false); - if ($pleaseCache === true) { + if ($pleaseCache) { $this->useCache = true; } return $this; @@ -89,8 +81,6 @@ class Svn * Return the no-auth-cache switch. * * @return string - * @uses selfg::$useCache - * @see self::doAuthDance() */ public function getAuthCache() { From 434f10f2d7b1cb77d394dce9d89bedf44e95b681 Mon Sep 17 00:00:00 2001 From: till Date: Fri, 23 Mar 2012 16:32:46 +0100 Subject: [PATCH 47/47] * more cs fixes --- src/Composer/Downloader/SvnDownloader.php | 2 +- src/Composer/Repository/Vcs/SvnDriver.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Composer/Downloader/SvnDownloader.php b/src/Composer/Downloader/SvnDownloader.php index 045bf6a81..6ded2e93f 100644 --- a/src/Composer/Downloader/SvnDownloader.php +++ b/src/Composer/Downloader/SvnDownloader.php @@ -85,7 +85,7 @@ class SvnDownloader extends VcsDownloader protected function execute($command, SvnUtil $util) { $status = $this->process->execute($command, $output); - if ($status == 0) { + if (0 === $status) { return $output; } diff --git a/src/Composer/Repository/Vcs/SvnDriver.php b/src/Composer/Repository/Vcs/SvnDriver.php index b9ce93ccc..74c188da7 100644 --- a/src/Composer/Repository/Vcs/SvnDriver.php +++ b/src/Composer/Repository/Vcs/SvnDriver.php @@ -82,7 +82,7 @@ class SvnDriver extends VcsDriver $output ); - if ($status == 0) { + if (0 === $status) { return $output; }