Jelajahi Sumber

Merge pull request #659 from Seldaek/github_https_fallback

Github https fallback
Nils Adermann 13 tahun lalu
induk
melakukan
655588a5e0

+ 30 - 0
src/Composer/Downloader/GitDownloader.php

@@ -46,6 +46,12 @@ class GitDownloader extends VcsDownloader
         $this->io->write("    Checking out ".$target->getSourceReference());
         $command = 'cd %s && git remote set-url composer %s && git fetch composer && git fetch --tags composer && git checkout %3$s && git reset --hard %3$s';
 
+        // capture username/password from github URL if there is one
+        $this->process->execute(sprintf('cd %s && git remote -v', escapeshellarg($path)), $output);
+        if (preg_match('{^composer\s+https://(.+):(.+)@github.com/}im', $output, $match)) {
+            $this->io->setAuthorization('github.com', $match[1], $match[2]);
+        }
+
         // TODO: BC for the composer remote that didn't exist, to be remove after May 18th.
         $this->process->execute(sprintf('cd %s && git remote add composer %s', escapeshellarg($path), escapeshellarg($initial->getSourceUrl())), $ignoredOutput);
 
@@ -102,6 +108,30 @@ class GitDownloader extends VcsDownloader
 
         $command = call_user_func($commandCallable, $url);
         if (0 !== $this->process->execute($command, $handler)) {
+            if (preg_match('{^git@github.com:(.+?)\.git$}i', $url, $match) && $this->io->isInteractive()) {
+                // private repository without git access, try https with auth
+                $retries = 3;
+                $retrying = false;
+                do {
+                    if ($retrying) {
+                        $this->io->write('Invalid credentials');
+                    }
+                    if (!$this->io->hasAuthorization('github.com') || $retrying) {
+                        $username = $this->io->ask('Username: ');
+                        $password = $this->io->askAndHideAnswer('Password: ');
+                        $this->io->setAuthorization('github.com', $username, $password);
+                    }
+
+                    $auth = $this->io->getAuthorization('github.com');
+                    $url = 'https://'.$auth['username'] . ':' . $auth['password'] . '@github.com/'.$match[1].'.git';
+
+                    $command = call_user_func($commandCallable, $url);
+                    if (0 === $this->process->execute($command, $handler)) {
+                        return;
+                    }
+                    $retrying = true;
+                } while (--$retries);
+            }
             $this->throwException('Failed to execute ' . $command . "\n\n" . $this->process->getErrorOutput(), $url);
         }
     }

+ 4 - 24
src/Composer/IO/ConsoleIO.php

@@ -22,6 +22,7 @@ use Symfony\Component\Console\Helper\HelperSet;
  * The Input/Output helper.
  *
  * @author François Pluchino <francois.pluchino@opendisplay.com>
+ * @author Jordi Boggiano <j.boggiano@seld.be>
  */
 class ConsoleIO implements IOInterface
 {
@@ -29,8 +30,6 @@ class ConsoleIO implements IOInterface
     protected $output;
     protected $helperSet;
     protected $authorizations = array();
-    protected $lastUsername;
-    protected $lastPassword;
     protected $lastMessage;
 
     /**
@@ -179,22 +178,6 @@ class ConsoleIO implements IOInterface
         return $this->ask($question);
     }
 
-    /**
-     * {@inheritDoc}
-     */
-    public function getLastUsername()
-    {
-        return $this->lastUsername;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public function getLastPassword()
-    {
-        return $this->lastPassword;
-    }
-
     /**
      * {@inheritDoc}
      */
@@ -209,6 +192,7 @@ class ConsoleIO implements IOInterface
     public function hasAuthorization($repositoryName)
     {
         $auths = $this->getAuthorizations();
+
         return isset($auths[$repositoryName]);
     }
 
@@ -218,6 +202,7 @@ class ConsoleIO implements IOInterface
     public function getAuthorization($repositoryName)
     {
         $auths = $this->getAuthorizations();
+
         return isset($auths[$repositoryName]) ? $auths[$repositoryName] : array('username' => null, 'password' => null);
     }
 
@@ -226,11 +211,6 @@ class ConsoleIO implements IOInterface
      */
     public function setAuthorization($repositoryName, $username, $password = null)
     {
-        $auths = $this->getAuthorizations();
-        $auths[$repositoryName] = array('username' => $username, 'password' => $password);
-
-        $this->authorizations = $auths;
-        $this->lastUsername = $username;
-        $this->lastPassword = $password;
+        $this->authorizations[$repositoryName] = array('username' => $username, 'password' => $password);
     }
 }

+ 0 - 14
src/Composer/IO/IOInterface.php

@@ -108,20 +108,6 @@ interface IOInterface
      */
     function askAndHideAnswer($question);
 
-    /**
-     * Get the last username entered.
-     *
-     * @return string The username
-     */
-    function getLastUsername();
-
-    /**
-     * Get the last password entered.
-     *
-     * @return string The password
-     */
-    function getLastPassword();
-
     /**
      * Get all authorization informations entered.
      *

+ 0 - 16
src/Composer/IO/NullIO.php

@@ -89,22 +89,6 @@ class NullIO implements IOInterface
         return null;
     }
 
-    /**
-     * {@inheritDoc}
-     */
-    public function getLastUsername()
-    {
-        return null;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public function getLastPassword()
-    {
-        return null;
-    }
-
     /**
      * {@inheritDoc}
      */

+ 1 - 0
src/Composer/Repository/Vcs/GitBitbucketDriver.php

@@ -35,6 +35,7 @@ class GitBitbucketDriver extends VcsDriver implements VcsDriverInterface
         preg_match('#^https://bitbucket\.org/([^/]+)/(.+?)\.git$#', $this->url, $match);
         $this->owner = $match[1];
         $this->repository = $match[2];
+        $this->originUrl = 'bitbucket.org';
     }
 
     /**

+ 2 - 1
src/Composer/Repository/Vcs/GitHubDriver.php

@@ -46,6 +46,7 @@ class GitHubDriver extends VcsDriver
         preg_match('#^(?:https?|git)://github\.com/([^/]+)/(.+?)(?:\.git)?$#', $this->url, $match);
         $this->owner = $match[1];
         $this->repository = $match[2];
+        $this->originUrl = 'github.com';
 
         $this->fetchRootIdentifier();
     }
@@ -245,7 +246,7 @@ class GitHubDriver extends VcsDriver
                         $this->io->write('Authentication required (<info>'.$this->url.'</info>):');
                         $username = $this->io->ask('Username: ');
                         $password = $this->io->askAndHideAnswer('Password: ');
-                        $this->io->setAuthorization($this->url, $username, $password);
+                        $this->io->setAuthorization($this->originUrl, $username, $password);
                         break;
 
                     default:

+ 1 - 0
src/Composer/Repository/Vcs/HgBitbucketDriver.php

@@ -35,6 +35,7 @@ class HgBitbucketDriver extends VcsDriver
         preg_match('#^https://bitbucket\.org/([^/]+)/([^/]+)/?$#', $this->url, $match);
         $this->owner = $match[1];
         $this->repository = $match[2];
+        $this->originUrl = 'bitbucket.org';
     }
 
     /**

+ 3 - 1
src/Composer/Repository/Vcs/VcsDriver.php

@@ -26,6 +26,7 @@ use Composer\Util\RemoteFilesystem;
 abstract class VcsDriver implements VcsDriverInterface
 {
     protected $url;
+    protected $originUrl;
     protected $io;
     protected $config;
     protected $process;
@@ -43,6 +44,7 @@ abstract class VcsDriver implements VcsDriverInterface
     final public function __construct($url, IOInterface $io, Config $config, ProcessExecutor $process = null, $remoteFilesystem = null)
     {
         $this->url = $url;
+        $this->originUrl = $url;
         $this->io = $io;
         $this->config = $config;
         $this->process = $process ?: new ProcessExecutor;
@@ -86,7 +88,7 @@ abstract class VcsDriver implements VcsDriverInterface
      */
     protected function getContents($url)
     {
-        return $this->remoteFilesystem->getContents($this->url, $url, false);
+        return $this->remoteFilesystem->getContents($this->originUrl, $url, false);
     }
 
     protected static function isLocalUrl($url)

+ 0 - 4
src/Composer/Util/RemoteFilesystem.php

@@ -223,10 +223,6 @@ class RemoteFilesystem
             $auth = $this->io->getAuthorization($originUrl);
             $authStr = base64_encode($auth['username'] . ':' . $auth['password']);
             $options['http']['header'] .= "Authorization: Basic $authStr\r\n";
-        } elseif (null !== $this->io->getLastUsername()) {
-            $authStr = base64_encode($this->io->getLastUsername() . ':' . $this->io->getLastPassword());
-            $options['http']['header'] .= "Authorization: Basic $authStr\r\n";
-            $this->io->setAuthorization($originUrl, $this->io->getLastUsername(), $this->io->getLastPassword());
         }
 
         return $options;

+ 10 - 2
tests/Composer/Test/Downloader/GitDownloaderTest.php

@@ -156,9 +156,13 @@ class GitDownloaderTest extends \PHPUnit_Framework_TestCase
             ->will($this->returnValue(0));
         $processExecutor->expects($this->at(1))
             ->method('execute')
-            ->with($this->equalTo($this->getCmd("cd 'composerPath' && git remote add composer 'https://github.com/composer/composer'")))
+            ->with($this->equalTo($this->getCmd("cd 'composerPath' && git remote -v")))
             ->will($this->returnValue(0));
         $processExecutor->expects($this->at(2))
+            ->method('execute')
+            ->with($this->equalTo($this->getCmd("cd 'composerPath' && git remote add composer 'https://github.com/composer/composer'")))
+            ->will($this->returnValue(0));
+        $processExecutor->expects($this->at(3))
             ->method('execute')
             ->with($this->equalTo($expectedGitUpdateCommand))
             ->will($this->returnValue(0));
@@ -189,9 +193,13 @@ class GitDownloaderTest extends \PHPUnit_Framework_TestCase
             ->will($this->returnValue(0));
         $processExecutor->expects($this->at(1))
             ->method('execute')
-            ->with($this->equalTo($this->getCmd("cd 'composerPath' && git remote add composer 'https://github.com/composer/composer'")))
+            ->with($this->equalTo($this->getCmd("cd 'composerPath' && git remote -v")))
             ->will($this->returnValue(0));
         $processExecutor->expects($this->at(2))
+            ->method('execute')
+            ->with($this->equalTo($this->getCmd("cd 'composerPath' && git remote add composer 'https://github.com/composer/composer'")))
+            ->will($this->returnValue(0));
+        $processExecutor->expects($this->at(3))
             ->method('execute')
             ->with($this->equalTo($expectedGitUpdateCommand))
             ->will($this->returnValue(1));

+ 0 - 26
tests/Composer/Test/IO/ConsoleIOTest.php

@@ -190,30 +190,4 @@ class ConsoleIOTest extends TestCase
         $this->assertTrue($consoleIO->hasAuthorization('repoName'));
         $this->assertFalse($consoleIO->hasAuthorization('repoName2'));
     }
-
-    public function testGetLastUsername()
-    {
-        $inputMock = $this->getMock('Symfony\Component\Console\Input\InputInterface');
-        $outputMock = $this->getMock('Symfony\Component\Console\Output\OutputInterface');
-        $helperMock = $this->getMock('Symfony\Component\Console\Helper\HelperSet');
-
-        $consoleIO = new ConsoleIO($inputMock, $outputMock, $helperMock);
-        $consoleIO->setAuthorization('repoName', 'l3l0', 'passwd');
-        $consoleIO->setAuthorization('repoName2', 'l3l02', 'passwd2');
-
-        $this->assertEquals('l3l02', $consoleIO->getLastUsername());
-    }
-
-    public function testGetLastPassword()
-    {
-        $inputMock = $this->getMock('Symfony\Component\Console\Input\InputInterface');
-        $outputMock = $this->getMock('Symfony\Component\Console\Output\OutputInterface');
-        $helperMock = $this->getMock('Symfony\Component\Console\Helper\HelperSet');
-
-        $consoleIO = new ConsoleIO($inputMock, $outputMock, $helperMock);
-        $consoleIO->setAuthorization('repoName', 'l3l0', 'passwd');
-        $consoleIO->setAuthorization('repoName2', 'l3l02', 'passwd2');
-
-        $this->assertEquals('passwd2', $consoleIO->getLastPassword());
-    }
 }

+ 0 - 14
tests/Composer/Test/IO/NullIOTest.php

@@ -31,20 +31,6 @@ class NullIOTest extends TestCase
         $this->assertFalse($io->hasAuthorization('foo'));
     }
 
-    public function testGetLastPassword()
-    {
-        $io = new NullIO();
-
-        $this->assertNull($io->getLastPassword());
-    }
-
-    public function testGetLastUsername()
-    {
-        $io = new NullIO();
-
-        $this->assertNull($io->getLastUsername());
-    }
-
     public function testAskAndHideAnswer()
     {
         $io = new NullIO();

+ 5 - 5
tests/Composer/Test/Repository/Vcs/GitHubDriverTest.php

@@ -40,7 +40,7 @@ class GitHubDriverTest extends \PHPUnit_Framework_TestCase
 
         $remoteFilesystem->expects($this->at(0))
             ->method('getContents')
-            ->with($this->equalTo($repoUrl), $this->equalTo($repoApiUrl), $this->equalTo(false))
+            ->with($this->equalTo('github.com'), $this->equalTo($repoApiUrl), $this->equalTo(false))
             ->will($this->throwException(new TransportException('HTTP/1.1 404 Not Found', 404)));
 
         $io->expects($this->once())
@@ -55,11 +55,11 @@ class GitHubDriverTest extends \PHPUnit_Framework_TestCase
 
         $io->expects($this->once())
             ->method('setAuthorization')
-            ->with($this->equalTo($repoUrl), 'someuser', 'somepassword');
+            ->with($this->equalTo('github.com'), 'someuser', 'somepassword');
 
         $remoteFilesystem->expects($this->at(1))
             ->method('getContents')
-            ->with($this->equalTo($repoUrl), $this->equalTo($repoApiUrl), $this->equalTo(false))
+            ->with($this->equalTo('github.com'), $this->equalTo($repoApiUrl), $this->equalTo(false))
             ->will($this->returnValue('{"master_branch": "test_master"}'));
 
         $gitHubDriver = new GitHubDriver($repoUrl, $io, new Config(), null, $remoteFilesystem);
@@ -109,7 +109,7 @@ class GitHubDriverTest extends \PHPUnit_Framework_TestCase
 
         $remoteFilesystem->expects($this->at(0))
             ->method('getContents')
-            ->with($this->equalTo($repoUrl), $this->equalTo($repoApiUrl), $this->equalTo(false))
+            ->with($this->equalTo('github.com'), $this->equalTo($repoApiUrl), $this->equalTo(false))
             ->will($this->returnValue('{"master_branch": "test_master"}'));
 
         $gitHubDriver = new GitHubDriver($repoUrl, $io, new Config(), null, $remoteFilesystem);
@@ -164,7 +164,7 @@ class GitHubDriverTest extends \PHPUnit_Framework_TestCase
 
         $remoteFilesystem->expects($this->at(0))
             ->method('getContents')
-            ->with($this->equalTo($repoUrl), $this->equalTo($repoApiUrl), $this->equalTo(false))
+            ->with($this->equalTo('github.com'), $this->equalTo($repoApiUrl), $this->equalTo(false))
             ->will($this->throwException(new TransportException('HTTP/1.1 404 Not Found', 404)));
 
         // clean local clone if present

+ 0 - 32
tests/Composer/Test/Util/RemoteFilesystemTest.php

@@ -25,11 +25,6 @@ class RemoteFilesystemTest extends \PHPUnit_Framework_TestCase
             ->method('hasAuthorization')
             ->will($this->returnValue(false))
         ;
-        $io
-            ->expects($this->once())
-            ->method('getLastUsername')
-            ->will($this->returnValue(null))
-        ;
 
         $res = $this->callGetOptionsForUrl($io, array('http://example.org'));
         $this->assertTrue(isset($res['http']['header']) && false !== strpos($res['http']['header'], 'User-Agent'), 'getOptions must return an array with a header containing a User-Agent');
@@ -53,33 +48,6 @@ class RemoteFilesystemTest extends \PHPUnit_Framework_TestCase
         $this->assertContains('Authorization: Basic', $options['http']['header']);
     }
 
-    public function testGetOptionsForUrlWithLastUsername()
-    {
-        $io = $this->getMock('Composer\IO\IOInterface');
-        $io
-            ->expects($this->once())
-            ->method('hasAuthorization')
-            ->will($this->returnValue(false))
-        ;
-        $io
-            ->expects($this->any())
-            ->method('getLastUsername')
-            ->will($this->returnValue('login'))
-        ;
-        $io
-            ->expects($this->any())
-            ->method('getLastPassword')
-            ->will($this->returnValue('password'))
-        ;
-        $io
-            ->expects($this->once())
-            ->method('setAuthorization')
-        ;
-
-        $options = $this->callGetOptionsForUrl($io, array('http://example.org'));
-        $this->assertContains('Authorization: Basic', $options['http']['header']);
-    }
-
     public function testCallbackGetFileSize()
     {
         $fs = new RemoteFilesystem($this->getMock('Composer\IO\IOInterface'));