Prechádzať zdrojové kódy

Fail over from source to dist and vice versa when downloads fail

Any RuntimeException descendent will be caught and cause another
download attempt using either source or dist depending on what was
attempted first.
Nils Adermann 11 rokov pred
rodič
commit
5ed18d9aa2

+ 38 - 9
src/Composer/Downloader/DownloadManager.php

@@ -14,6 +14,7 @@ namespace Composer\Downloader;
 
 
 use Composer\Package\PackageInterface;
 use Composer\Package\PackageInterface;
 use Composer\Downloader\DownloaderInterface;
 use Composer\Downloader\DownloaderInterface;
+use Composer\IO\IOInterface;
 use Composer\Util\Filesystem;
 use Composer\Util\Filesystem;
 
 
 /**
 /**
@@ -23,6 +24,7 @@ use Composer\Util\Filesystem;
  */
  */
 class DownloadManager
 class DownloadManager
 {
 {
+    private $io;
     private $preferDist = false;
     private $preferDist = false;
     private $preferSource = false;
     private $preferSource = false;
     private $filesystem;
     private $filesystem;
@@ -31,11 +33,13 @@ class DownloadManager
     /**
     /**
      * Initializes download manager.
      * Initializes download manager.
      *
      *
+     * @param IOInterface     $io           The Input Output Interface
      * @param bool            $preferSource prefer downloading from source
      * @param bool            $preferSource prefer downloading from source
      * @param Filesystem|null $filesystem   custom Filesystem object
      * @param Filesystem|null $filesystem   custom Filesystem object
      */
      */
-    public function __construct($preferSource = false, Filesystem $filesystem = null)
+    public function __construct(IOInterface $io, $preferSource = false, Filesystem $filesystem = null)
     {
     {
+        $this->io = $io;
         $this->preferSource = $preferSource;
         $this->preferSource = $preferSource;
         $this->filesystem = $filesystem ?: new Filesystem();
         $this->filesystem = $filesystem ?: new Filesystem();
     }
     }
@@ -168,19 +172,44 @@ class DownloadManager
         $sourceType   = $package->getSourceType();
         $sourceType   = $package->getSourceType();
         $distType     = $package->getDistType();
         $distType     = $package->getDistType();
 
 
-        if ((!$package->isDev() || $this->preferDist || !$sourceType) && !($preferSource && $sourceType) && $distType) {
-            $package->setInstallationSource('dist');
-        } elseif ($sourceType) {
-            $package->setInstallationSource('source');
-        } else {
+        $wantDist = !$package->isDev() || $this->preferDist || !$sourceType;
+        $wantSource = $preferSource && $sourceType;
+
+        $types = array();
+        if ($sourceType) {
+            $types[] = 'source';
+        }
+        if ($distType) {
+            $types[] = 'dist';
+        }
+
+        if (empty($types)) {
             throw new \InvalidArgumentException('Package '.$package.' must have a source or dist specified');
             throw new \InvalidArgumentException('Package '.$package.' must have a source or dist specified');
         }
         }
 
 
+        if ($wantDist && !$wantSource) {
+            $types = array_reverse($types);
+        }
+
         $this->filesystem->ensureDirectoryExists($targetDir);
         $this->filesystem->ensureDirectoryExists($targetDir);
 
 
-        $downloader = $this->getDownloaderForInstalledPackage($package);
-        if ($downloader) {
-            $downloader->download($package, $targetDir);
+        foreach ($types as $source) {
+            $package->setInstallationSource($source);
+            try {
+                $downloader = $this->getDownloaderForInstalledPackage($package);
+                if ($downloader) {
+                    $downloader->download($package, $targetDir);
+                }
+                break;
+            } catch (\RuntimeException $e) {
+                $this->io->write(
+                    '<warning>Caught an exception while trying to download '.
+                    $package->getPrettyString().
+                    ': '.
+                    $e->getMessage().'</warning>'
+                );
+                continue;
+            }
         }
         }
     }
     }
 
 

+ 1 - 1
src/Composer/Factory.php

@@ -344,7 +344,7 @@ class Factory
             $cache = new Cache($io, $config->get('cache-files-dir'), 'a-z0-9_./');
             $cache = new Cache($io, $config->get('cache-files-dir'), 'a-z0-9_./');
         }
         }
 
 
-        $dm = new Downloader\DownloadManager();
+        $dm = new Downloader\DownloadManager($io);
         switch ($config->get('preferred-install')) {
         switch ($config->get('preferred-install')) {
             case 'dist':
             case 'dist':
                 $dm->setPreferDist(true);
                 $dm->setPreferDist(true);

+ 2 - 1
tests/Composer/Test/ComposerTest.php

@@ -47,7 +47,8 @@ class ComposerTest extends TestCase
     public function testSetGetDownloadManager()
     public function testSetGetDownloadManager()
     {
     {
         $composer = new Composer();
         $composer = new Composer();
-        $manager = $this->getMock('Composer\Downloader\DownloadManager');
+        $io = $this->getMock('Composer\IO\IOInterface');
+        $manager = $this->getMock('Composer\Downloader\DownloadManager', array(), array($io));
         $composer->setDownloadManager($manager);
         $composer->setDownloadManager($manager);
 
 
         $this->assertSame($manager, $composer->getDownloadManager());
         $this->assertSame($manager, $composer->getDownloadManager());

+ 81 - 23
tests/Composer/Test/Downloader/DownloadManagerTest.php

@@ -17,16 +17,18 @@ use Composer\Downloader\DownloadManager;
 class DownloadManagerTest extends \PHPUnit_Framework_TestCase
 class DownloadManagerTest extends \PHPUnit_Framework_TestCase
 {
 {
     protected $filesystem;
     protected $filesystem;
+    protected $io;
 
 
     public function setUp()
     public function setUp()
     {
     {
         $this->filesystem = $this->getMock('Composer\Util\Filesystem');
         $this->filesystem = $this->getMock('Composer\Util\Filesystem');
+        $this->io = $this->getMock('Composer\IO\IOInterface');
     }
     }
 
 
     public function testSetGetDownloader()
     public function testSetGetDownloader()
     {
     {
         $downloader = $this->createDownloaderMock();
         $downloader = $this->createDownloaderMock();
-        $manager    = new DownloadManager(false, $this->filesystem);
+        $manager    = new DownloadManager($this->io, false, $this->filesystem);
 
 
         $manager->setDownloader('test', $downloader);
         $manager->setDownloader('test', $downloader);
         $this->assertSame($downloader, $manager->getDownloader('test'));
         $this->assertSame($downloader, $manager->getDownloader('test'));
@@ -43,7 +45,7 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase
             ->method('getInstallationSource')
             ->method('getInstallationSource')
             ->will($this->returnValue(null));
             ->will($this->returnValue(null));
 
 
-        $manager = new DownloadManager(false, $this->filesystem);
+        $manager = new DownloadManager($this->io, false, $this->filesystem);
 
 
         $this->setExpectedException('InvalidArgumentException');
         $this->setExpectedException('InvalidArgumentException');
 
 
@@ -69,7 +71,7 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase
             ->will($this->returnValue('dist'));
             ->will($this->returnValue('dist'));
 
 
         $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager')
         $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager')
-            ->setConstructorArgs(array(false, $this->filesystem))
+            ->setConstructorArgs(array($this->io, false, $this->filesystem))
             ->setMethods(array('getDownloader'))
             ->setMethods(array('getDownloader'))
             ->getMock();
             ->getMock();
 
 
@@ -101,7 +103,7 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase
             ->will($this->returnValue('source'));
             ->will($this->returnValue('source'));
 
 
         $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager')
         $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager')
-            ->setConstructorArgs(array(false, $this->filesystem))
+            ->setConstructorArgs(array($this->io, false, $this->filesystem))
             ->setMethods(array('getDownloader'))
             ->setMethods(array('getDownloader'))
             ->getMock();
             ->getMock();
 
 
@@ -135,7 +137,7 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase
             ->will($this->returnValue('source'));
             ->will($this->returnValue('source'));
 
 
         $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager')
         $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager')
-            ->setConstructorArgs(array(false, $this->filesystem))
+            ->setConstructorArgs(array($this->io, false, $this->filesystem))
             ->setMethods(array('getDownloader'))
             ->setMethods(array('getDownloader'))
             ->getMock();
             ->getMock();
 
 
@@ -167,7 +169,7 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase
             ->will($this->returnValue('dist'));
             ->will($this->returnValue('dist'));
 
 
         $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager')
         $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager')
-            ->setConstructorArgs(array(false, $this->filesystem))
+            ->setConstructorArgs(array($this->io, false, $this->filesystem))
             ->setMethods(array('getDownloader'))
             ->setMethods(array('getDownloader'))
             ->getMock();
             ->getMock();
 
 
@@ -190,7 +192,7 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase
           ->method('getType')
           ->method('getType')
           ->will($this->returnValue('metapackage'));
           ->will($this->returnValue('metapackage'));
 
 
-        $manager = new DownloadManager(false, $this->filesystem);
+        $manager = new DownloadManager($this->io, false, $this->filesystem);
 
 
         $this->assertNull($manager->getDownloaderForInstalledPackage($package));
         $this->assertNull($manager->getDownloaderForInstalledPackage($package));
     }
     }
@@ -219,7 +221,7 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase
             ->with($package, 'target_dir');
             ->with($package, 'target_dir');
 
 
         $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager')
         $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager')
-            ->setConstructorArgs(array(false, $this->filesystem))
+            ->setConstructorArgs(array($this->io, false, $this->filesystem))
             ->setMethods(array('getDownloaderForInstalledPackage'))
             ->setMethods(array('getDownloaderForInstalledPackage'))
             ->getMock();
             ->getMock();
         $manager
         $manager
@@ -231,6 +233,62 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase
         $manager->download($package, 'target_dir');
         $manager->download($package, 'target_dir');
     }
     }
 
 
+    public function testFullPackageDownloadFailover()
+    {
+        $package = $this->createPackageMock();
+        $package
+            ->expects($this->once())
+            ->method('getSourceType')
+            ->will($this->returnValue('git'));
+        $package
+            ->expects($this->once())
+            ->method('getDistType')
+            ->will($this->returnValue('pear'));
+        $package
+            ->expects($this->any())
+            ->method('getPrettyString')
+            ->will($this->returnValue('prettyPackage'));
+
+        $package
+            ->expects($this->at(3))
+            ->method('setInstallationSource')
+            ->with('dist');
+        $package
+            ->expects($this->at(5))
+            ->method('setInstallationSource')
+            ->with('source');
+
+        $downloaderFail = $this->createDownloaderMock();
+        $downloaderFail
+            ->expects($this->once())
+            ->method('download')
+            ->with($package, 'target_dir')
+            ->will($this->throwException(new \RuntimeException("Foo")));
+
+        $downloaderSuccess = $this->createDownloaderMock();
+        $downloaderSuccess
+            ->expects($this->once())
+            ->method('download')
+            ->with($package, 'target_dir');
+
+        $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager')
+            ->setConstructorArgs(array($this->io, false, $this->filesystem))
+            ->setMethods(array('getDownloaderForInstalledPackage'))
+            ->getMock();
+        $manager
+            ->expects($this->at(0))
+            ->method('getDownloaderForInstalledPackage')
+            ->with($package)
+            ->will($this->returnValue($downloaderFail));
+        $manager
+            ->expects($this->at(1))
+            ->method('getDownloaderForInstalledPackage')
+            ->with($package)
+            ->will($this->returnValue($downloaderSuccess));
+
+        $manager->download($package, 'target_dir');
+    }
+
     public function testBadPackageDownload()
     public function testBadPackageDownload()
     {
     {
         $package = $this->createPackageMock();
         $package = $this->createPackageMock();
@@ -243,7 +301,7 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase
             ->method('getDistType')
             ->method('getDistType')
             ->will($this->returnValue(null));
             ->will($this->returnValue(null));
 
 
-        $manager = new DownloadManager(false, $this->filesystem);
+        $manager = new DownloadManager($this->io, false, $this->filesystem);
 
 
         $this->setExpectedException('InvalidArgumentException');
         $this->setExpectedException('InvalidArgumentException');
         $manager->download($package, 'target_dir');
         $manager->download($package, 'target_dir');
@@ -273,7 +331,7 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase
             ->with($package, 'target_dir');
             ->with($package, 'target_dir');
 
 
         $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager')
         $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager')
-            ->setConstructorArgs(array(false, $this->filesystem))
+            ->setConstructorArgs(array($this->io, false, $this->filesystem))
             ->setMethods(array('getDownloaderForInstalledPackage'))
             ->setMethods(array('getDownloaderForInstalledPackage'))
             ->getMock();
             ->getMock();
         $manager
         $manager
@@ -309,7 +367,7 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase
             ->with($package, 'target_dir');
             ->with($package, 'target_dir');
 
 
         $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager')
         $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager')
-            ->setConstructorArgs(array(false, $this->filesystem))
+            ->setConstructorArgs(array($this->io, false, $this->filesystem))
             ->setMethods(array('getDownloaderForInstalledPackage'))
             ->setMethods(array('getDownloaderForInstalledPackage'))
             ->getMock();
             ->getMock();
         $manager
         $manager
@@ -339,7 +397,7 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase
           ->with('source');
           ->with('source');
 
 
         $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager')
         $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager')
-          ->setConstructorArgs(array(false, $this->filesystem))
+          ->setConstructorArgs(array($this->io, false, $this->filesystem))
           ->setMethods(array('getDownloaderForInstalledPackage'))
           ->setMethods(array('getDownloaderForInstalledPackage'))
           ->getMock();
           ->getMock();
         $manager
         $manager
@@ -375,7 +433,7 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase
             ->with($package, 'target_dir');
             ->with($package, 'target_dir');
 
 
         $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager')
         $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager')
-            ->setConstructorArgs(array(false, $this->filesystem))
+            ->setConstructorArgs(array($this->io, false, $this->filesystem))
             ->setMethods(array('getDownloaderForInstalledPackage'))
             ->setMethods(array('getDownloaderForInstalledPackage'))
             ->getMock();
             ->getMock();
         $manager
         $manager
@@ -412,7 +470,7 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase
             ->with($package, 'target_dir');
             ->with($package, 'target_dir');
 
 
         $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager')
         $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager')
-            ->setConstructorArgs(array(false, $this->filesystem))
+            ->setConstructorArgs(array($this->io, false, $this->filesystem))
             ->setMethods(array('getDownloaderForInstalledPackage'))
             ->setMethods(array('getDownloaderForInstalledPackage'))
             ->getMock();
             ->getMock();
         $manager
         $manager
@@ -449,7 +507,7 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase
             ->with($package, 'target_dir');
             ->with($package, 'target_dir');
 
 
         $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager')
         $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager')
-            ->setConstructorArgs(array(false, $this->filesystem))
+            ->setConstructorArgs(array($this->io, false, $this->filesystem))
             ->setMethods(array('getDownloaderForInstalledPackage'))
             ->setMethods(array('getDownloaderForInstalledPackage'))
             ->getMock();
             ->getMock();
         $manager
         $manager
@@ -474,7 +532,7 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase
             ->method('getDistType')
             ->method('getDistType')
             ->will($this->returnValue(null));
             ->will($this->returnValue(null));
 
 
-        $manager = new DownloadManager(false, $this->filesystem);
+        $manager = new DownloadManager($this->io, false, $this->filesystem);
         $manager->setPreferSource(true);
         $manager->setPreferSource(true);
 
 
         $this->setExpectedException('InvalidArgumentException');
         $this->setExpectedException('InvalidArgumentException');
@@ -510,7 +568,7 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase
             ->with($initial, $target, 'vendor/bundles/FOS/UserBundle');
             ->with($initial, $target, 'vendor/bundles/FOS/UserBundle');
 
 
         $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager')
         $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager')
-            ->setConstructorArgs(array(false, $this->filesystem))
+            ->setConstructorArgs(array($this->io, false, $this->filesystem))
             ->setMethods(array('getDownloaderForInstalledPackage'))
             ->setMethods(array('getDownloaderForInstalledPackage'))
             ->getMock();
             ->getMock();
         $manager
         $manager
@@ -547,7 +605,7 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase
             ->with($initial, 'vendor/bundles/FOS/UserBundle');
             ->with($initial, 'vendor/bundles/FOS/UserBundle');
 
 
         $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager')
         $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager')
-            ->setConstructorArgs(array(false, $this->filesystem))
+            ->setConstructorArgs(array($this->io, false, $this->filesystem))
             ->setMethods(array('getDownloaderForInstalledPackage', 'download'))
             ->setMethods(array('getDownloaderForInstalledPackage', 'download'))
             ->getMock();
             ->getMock();
         $manager
         $manager
@@ -588,7 +646,7 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase
             ->with($initial, $target, 'vendor/pkg');
             ->with($initial, $target, 'vendor/pkg');
 
 
         $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager')
         $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager')
-            ->setConstructorArgs(array(false, $this->filesystem))
+            ->setConstructorArgs(array($this->io, false, $this->filesystem))
             ->setMethods(array('getDownloaderForInstalledPackage', 'download'))
             ->setMethods(array('getDownloaderForInstalledPackage', 'download'))
             ->getMock();
             ->getMock();
         $manager
         $manager
@@ -625,7 +683,7 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase
             ->with($initial, 'vendor/pkg');
             ->with($initial, 'vendor/pkg');
 
 
         $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager')
         $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager')
-            ->setConstructorArgs(array(false, $this->filesystem))
+            ->setConstructorArgs(array($this->io, false, $this->filesystem))
             ->setMethods(array('getDownloaderForInstalledPackage', 'download'))
             ->setMethods(array('getDownloaderForInstalledPackage', 'download'))
             ->getMock();
             ->getMock();
         $manager
         $manager
@@ -647,7 +705,7 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase
         $target = $this->createPackageMock();
         $target = $this->createPackageMock();
 
 
         $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager')
         $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager')
-          ->setConstructorArgs(array(false, $this->filesystem))
+          ->setConstructorArgs(array($this->io, false, $this->filesystem))
           ->setMethods(array('getDownloaderForInstalledPackage'))
           ->setMethods(array('getDownloaderForInstalledPackage'))
           ->getMock();
           ->getMock();
         $manager
         $manager
@@ -670,7 +728,7 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase
             ->with($package, 'vendor/bundles/FOS/UserBundle');
             ->with($package, 'vendor/bundles/FOS/UserBundle');
 
 
         $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager')
         $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager')
-            ->setConstructorArgs(array(false, $this->filesystem))
+            ->setConstructorArgs(array($this->io, false, $this->filesystem))
             ->setMethods(array('getDownloaderForInstalledPackage'))
             ->setMethods(array('getDownloaderForInstalledPackage'))
             ->getMock();
             ->getMock();
         $manager
         $manager
@@ -687,7 +745,7 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase
         $package = $this->createPackageMock();
         $package = $this->createPackageMock();
 
 
         $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager')
         $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager')
-          ->setConstructorArgs(array(false, $this->filesystem))
+          ->setConstructorArgs(array($this->io, false, $this->filesystem))
           ->setMethods(array('getDownloaderForInstalledPackage'))
           ->setMethods(array('getDownloaderForInstalledPackage'))
           ->getMock();
           ->getMock();
         $manager
         $manager

+ 1 - 1
tests/Composer/Test/InstallerTest.php

@@ -51,7 +51,7 @@ class InstallerTest extends TestCase
     {
     {
         $io = $this->getMock('Composer\IO\IOInterface');
         $io = $this->getMock('Composer\IO\IOInterface');
 
 
-        $downloadManager = $this->getMock('Composer\Downloader\DownloadManager');
+        $downloadManager = $this->getMock('Composer\Downloader\DownloadManager', array(), array($io));
         $config = $this->getMock('Composer\Config');
         $config = $this->getMock('Composer\Config');
 
 
         $repositoryManager = new RepositoryManager($io, $config);
         $repositoryManager = new RepositoryManager($io, $config);