Browse Source

Merge branch 'master' into 2.0

Jordi Boggiano 5 years ago
parent
commit
a5b178084c

+ 3 - 2
doc/04-schema.md

@@ -276,10 +276,11 @@ Example:
 
 All links are optional fields.
 
-`require` and `require-dev` additionally support stability flags ([root-only](04-schema.md#root-package)).
+`require` and `require-dev` additionally support _stability flags_ ([root-only](04-schema.md#root-package)).
+They take the form "_constraint_@_stability flag_". 
 These allow you to further restrict or expand the stability of a package beyond
 the scope of the [minimum-stability](#minimum-stability) setting. You can apply
-them to a constraint, or apply them to an empty constraint if you want to
+them to a constraint, or apply them to an empty _constraint_ if you want to
 allow unstable packages of a dependency for example.
 
 Example:

+ 47 - 0
src/Composer/Command/ConfigCommand.php

@@ -111,6 +111,14 @@ To disable packagist:
 You can alter repositories in the global config.json file by passing in the
 <info>--global</info> option.
 
+To add or edit suggested packages you can use:
+
+    <comment>%command.full_name% suggest.package reason for the suggestion</comment>
+
+To add or edit extra properties you can use:
+
+    <comment>%command.full_name% extra.property value</comment>
+
 To edit the file in an external editor:
 
     <comment>%command.full_name% --editor</comment>
@@ -478,6 +486,23 @@ EOT
 
             return 0;
         }
+        // handle preferred-install per-package config
+        if (preg_match('/^preferred-install\.(.+)/', $settingKey, $matches)) {
+            if ($input->getOption('unset')) {
+                $this->configSource->removeConfigSetting($settingKey);
+
+                return 0;
+            }
+
+            list($validator) = $uniqueConfigValues['preferred-install'];
+            if (!$validator($values[0])) {
+                throw new \RuntimeException('Invalid value for '.$settingKey.'. Should be one of: auto, source, or dist');
+            }
+
+            $this->configSource->addConfigSetting($settingKey, $values[0]);
+
+            return 0;
+        }
 
         // handle properties
         $uniqueProps = array(
@@ -601,6 +626,26 @@ EOT
             return 0;
         }
 
+        // handle suggest
+        if (preg_match('/^suggest\.(.+)/', $settingKey, $matches)) {
+            if ($input->getOption('unset')) {
+                $this->configSource->removeProperty($settingKey);
+
+                return 0;
+            }
+
+            $this->configSource->addProperty($settingKey, implode(' ', $values));
+
+            return 0;
+        }
+
+        // handle unsetting extra/suggest
+        if (in_array($settingKey, array('suggest', 'extra'), true) && $input->getOption('unset')) {
+            $this->configSource->removeProperty($settingKey);
+
+            return 0;
+        }
+
         // handle platform
         if (preg_match('/^platform\.(.+)/', $settingKey, $matches)) {
             if ($input->getOption('unset')) {
@@ -613,6 +658,8 @@ EOT
 
             return 0;
         }
+
+        // handle unsetting platform
         if ($settingKey === 'platform' && $input->getOption('unset')) {
             $this->configSource->removeConfigSetting($settingKey);
 

+ 2 - 0
src/Composer/Command/DiagnoseCommand.php

@@ -172,6 +172,8 @@ EOT
             $io->write(sprintf('PHP binary path: <comment>%s</comment>', PHP_BINARY));
         }
 
+        $io->write(sprintf('OpenSSL version: <comment>%s</comment>', OPENSSL_VERSION_TEXT));
+
         return $this->exitCode;
     }
 

+ 12 - 1
src/Composer/Command/ExecCommand.php

@@ -39,7 +39,7 @@ class ExecCommand extends BaseCommand
             ->setHelp(
                 <<<EOT
 Executes a vendored binary/script.
-                
+
 Read more at https://getcomposer.org/doc/03-cli.md#exec
 EOT
             )
@@ -92,6 +92,17 @@ EOT
             $output->setVerbosity(OutputInterface::VERBOSITY_QUIET);
         }
 
+        // If the CWD was modified, we restore it to what it was initially, as it was
+        // most likely modified by the global command, and we want exec to run in the local working directory
+        // not the global one
+        if (getcwd() !== $this->getApplication()->getInitialWorkingDirectory()) {
+            try {
+                chdir($this->getApplication()->getInitialWorkingDirectory());
+            } catch (\Exception $e) {
+                throw new \RuntimeException('Could not switch back to working directory "'.$this->getApplication()->getWorkingDirectory().'"', 0, $e);
+            }
+        }
+
         return $dispatcher->dispatchScript('__exec_command', true, $input->getArgument('args'));
     }
 }

+ 30 - 0
src/Composer/Command/InitCommand.php

@@ -438,11 +438,34 @@ EOT
         }
 
         $versionParser = new VersionParser();
+
+        // Collect existing packages
+        $composer = $this->getComposer(false);
+        $installedRepo = $composer ? $composer->getRepositoryManager()->getLocalRepository() : null;
+        $existingPackages = [];
+        if ($installedRepo) {
+            foreach ($installedRepo->getPackages() as $package) {
+                $existingPackages[] = $package->getName();
+            }
+        }
+        foreach ($requires as $requiredPackage) {
+            $existingPackages[] = substr($requiredPackage, 0, strpos($requiredPackage, ' '));
+        }
+        unset($composer, $installedRepo, $requiredPackage);
+
         $io = $this->getIO();
         while (null !== $package = $io->ask('Search for a package: ')) {
             $matches = $this->findPackages($package);
 
             if (count($matches)) {
+                // Remove existing packages from search results.
+                foreach ($matches as $position => $foundPackage) {
+                    if (in_array($foundPackage['name'], $existingPackages, true)) {
+                        unset($matches[$position]);
+                    }
+                }
+                $matches = array_values($matches);
+
                 $exactMatch = null;
                 $choices = array();
                 foreach ($matches as $position => $foundPackage) {
@@ -540,6 +563,7 @@ EOT
 
                 if (false !== $package) {
                     $requires[] = $package;
+                    $existingPackages[] = substr($package, 0, strpos($package, ' '));
                 }
             }
         }
@@ -794,7 +818,13 @@ EOT
         }
         $similarPackages = array();
 
+        $installedRepo = $this->getComposer()->getRepositoryManager()->getLocalRepository();
+
         foreach ($results as $result) {
+            if ($installedRepo->findPackage($result['name'], '*')) {
+                // Ignore installed package
+                continue;
+            }
             $similarPackages[$result['name']] = levenshtein($package, $result['name']);
         }
         asort($similarPackages);

+ 1 - 1
src/Composer/Command/ValidateCommand.php

@@ -93,7 +93,7 @@ EOT
         $composer = Factory::create($io, $file, $input->hasParameterOption('--no-plugins'));
         $locker = $composer->getLocker();
         if ($locker->isLocked() && !$locker->isFresh()) {
-            $lockErrors[] = 'The lock file is not up to date with the latest changes in composer.json, it is recommended that you run `composer update`.';
+            $lockErrors[] = 'The lock file is not up to date with the latest changes in composer.json, it is recommended that you run `composer update` or `composer update <package name>`.';
         }
 
         $this->outputResult($io, $file, $errors, $warnings, $checkPublish, $publishErrors, $checkLock, $lockErrors, true, $isStrict);

+ 18 - 0
src/Composer/Console/Application.php

@@ -62,6 +62,11 @@ class Application extends BaseApplication
     private $hasPluginCommands = false;
     private $disablePluginsByDefault = false;
 
+    /**
+     * @var string Store the initial working directory at startup time
+     */
+    private $initialWorkingDirectory = '';
+
     public function __construct()
     {
         static $shutdownRegistered = false;
@@ -91,6 +96,8 @@ class Application extends BaseApplication
 
         $this->io = new NullIO();
 
+        $this->initialWorkingDirectory = getcwd();
+
         parent::__construct('Composer', Composer::getVersion());
     }
 
@@ -131,6 +138,7 @@ class Application extends BaseApplication
         if ($newWorkDir = $this->getNewWorkingDir($input)) {
             $oldWorkingDir = getcwd();
             chdir($newWorkDir);
+            $this->initialWorkingDirectory = $newWorkDir;
             $io->writeError('Changed CWD to ' . getcwd(), true, IOInterface::DEBUG);
         }
 
@@ -497,4 +505,14 @@ class Application extends BaseApplication
 
         return $commands;
     }
+
+    /**
+     * Get the working directoy at startup time
+     *
+     * @return string
+     */
+    public function getInitialWorkingDirectory()
+    {
+        return $this->initialWorkingDirectory;
+    }
 }

+ 1 - 1
src/Composer/Installer.php

@@ -586,7 +586,7 @@ class Installer
             $request = $this->createRequest($this->fixedRootPackage, $platformRepo, $lockedRepository);
 
             if (!$this->locker->isFresh()) {
-                $this->io->writeError('<warning>Warning: The lock file is not up to date with the latest changes in composer.json. You may be getting outdated dependencies. Run update to update them.</warning>', true, IOInterface::QUIET);
+                $this->io->writeError('<warning>Warning: The lock file is not up to date with the latest changes in composer.json. You may be getting outdated dependencies. It is recommended that you run `composer update` or `composer update <package name>`.</warning>', true, IOInterface::QUIET);
             }
 
             foreach ($lockedRepository->getPackages() as $package) {

+ 8 - 0
src/Composer/Json/JsonManipulator.php

@@ -167,6 +167,10 @@ class JsonManipulator
 
     public function addProperty($name, $value)
     {
+        if (substr($name, 0, 8) === 'suggest.') {
+            return $this->addSubNode('suggest', substr($name, 8), $value);
+        }
+
         if (substr($name, 0, 6) === 'extra.') {
             return $this->addSubNode('extra', substr($name, 6), $value);
         }
@@ -180,6 +184,10 @@ class JsonManipulator
 
     public function removeProperty($name)
     {
+        if (substr($name, 0, 8) === 'suggest.') {
+            return $this->removeSubNode('suggest', substr($name, 8));
+        }
+
         if (substr($name, 0, 6) === 'extra.') {
             return $this->removeSubNode('extra', substr($name, 6));
         }

+ 12 - 0
src/Composer/Package/Archiver/ZipArchiver.php

@@ -45,6 +45,18 @@ class ZipArchiver implements ArchiverInterface
                 } else {
                     $zip->addFile($filepath, $localname);
                 }
+
+                /**
+                 * ZipArchive::setExternalAttributesName is available from >= PHP 5.6
+                 */
+                if (PHP_VERSION_ID >= 50600) {
+                    $perms = fileperms($filepath);
+
+                    /**
+                     * Ensure to preserve the permission umasks for the filepath in the archive.
+                     */
+                    $zip->setExternalAttributesName($localname, ZipArchive::OPSYS_UNIX, $perms << 16);
+                }
             }
             if ($zip->close()) {
                 return $target;

+ 21 - 1
src/Composer/Package/Version/VersionGuesser.php

@@ -60,7 +60,7 @@ class VersionGuesser
      * @param array  $packageConfig
      * @param string $path          Path to guess into
      *
-     * @return null|array versionData, 'version', 'pretty_version' and 'commit' keys
+     * @return null|array versionData, 'version', 'pretty_version' and 'commit' keys, if the version is a feature branch, 'feature_version' and 'feature_pretty_version' keys may also be returned
      */
     public function guessVersion(array $packageConfig, $path)
     {
@@ -89,10 +89,18 @@ class VersionGuesser
 
     private function postprocess(array $versionData)
     {
+        if (!empty($versionData['feature_version']) && $versionData['feature_version'] === $versionData['version'] && $versionData['feature_pretty_version'] === $versionData['feature_pretty_version']) {
+            unset($versionData['feature_version'], $versionData['feature_pretty_version']);
+        }
+
         if ('-dev' === substr($versionData['version'], -4) && preg_match('{\.9{7}}', $versionData['version'])) {
             $versionData['pretty_version'] = preg_replace('{(\.9{7})+}', '.x', $versionData['version']);
         }
 
+        if (!empty($versionData['feature_version']) && '-dev' === substr($versionData['feature_version'], -4) && preg_match('{\.9{7}}', $versionData['feature_version'])) {
+            $versionData['feature_pretty_version'] = preg_replace('{(\.9{7})+}', '.x', $versionData['feature_version']);
+        }
+
         return $versionData;
     }
 
@@ -102,6 +110,8 @@ class VersionGuesser
         $commit = null;
         $version = null;
         $prettyVersion = null;
+        $featureVersion = null;
+        $featurePrettyVersion = null;
         $isDetached = false;
 
         // try to fetch current version from git branch
@@ -136,6 +146,8 @@ class VersionGuesser
             }
 
             if ($isFeatureBranch) {
+                $featureVersion = $version;
+                $featurePrettyVersion = $prettyVersion;
                 // try to find the best (nearest) version branch to assume this feature's version
                 $result = $this->guessFeatureVersion($packageConfig, $version, $branches, 'git rev-list %candidate%..%branch%', $path);
                 $version = $result['version'];
@@ -148,6 +160,8 @@ class VersionGuesser
             if ($result) {
                 $version = $result['version'];
                 $prettyVersion = $result['pretty_version'];
+                $featureVersion = null;
+                $featurePrettyVersion = null;
             }
         }
 
@@ -158,6 +172,10 @@ class VersionGuesser
             }
         }
 
+        if ($featureVersion) {
+            return array('version' => $version, 'commit' => $commit, 'pretty_version' => $prettyVersion, 'feature_version' => $featureVersion, 'feature_pretty_version' => $featurePrettyVersion);
+        }
+
         return array('version' => $version, 'commit' => $commit, 'pretty_version' => $prettyVersion);
     }
 
@@ -200,6 +218,8 @@ class VersionGuesser
             // try to find the best (nearest) version branch to assume this feature's version
             $result = $this->guessFeatureVersion($packageConfig, $version, $branches, 'hg log -r "not ancestors(\'%candidate%\') and ancestors(\'%branch%\')" --template "{node}\\n"', $path);
             $result['commit'] = '';
+            $result['feature_version'] = $version;
+            $result['feature_pretty_version'] = $version;
 
             return $result;
         }

+ 8 - 0
src/Composer/Repository/ComposerRepository.php

@@ -391,6 +391,14 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito
             $this->loadProviderListings($this->loadRootServerFile());
         }
 
+        if ($this->hasPartialPackages) {
+            if (null === $this->partialPackagesByName) {
+                $this->initializePartialPackages();
+            }
+
+            return array_keys($this->partialPackagesByName);
+        }
+
         if ($this->lazyProvidersUrl) {
             // Can not determine list of provided packages for lazy repositories
             return array();

+ 11 - 4
src/Composer/Repository/PathRepository.php

@@ -159,19 +159,26 @@ class PathRepository extends ArrayRepository implements ConfigurableRepositoryIn
                 }
             }
 
+            $output = '';
+            if (is_dir($path . DIRECTORY_SEPARATOR . '.git') && 0 === $this->process->execute('git log -n1 --pretty=%H', $output, $path)) {
+                $package['dist']['reference'] = trim($output);
+            }
+
             if (!isset($package['version'])) {
                 $versionData = $this->versionGuesser->guessVersion($package, $path);
                 if (is_array($versionData) && $versionData['pretty_version']) {
+                    // if there is a feature branch detected, we add a second packages with the feature branch version
+                    if (!empty($versionData['feature_pretty_version'])) {
+                        $package['version'] = $versionData['feature_pretty_version'];
+                        $this->addPackage($this->loader->load($package));
+                    }
+
                     $package['version'] = $versionData['pretty_version'];
                 } else {
                     $package['version'] = 'dev-master';
                 }
             }
 
-            $output = '';
-            if (is_dir($path . DIRECTORY_SEPARATOR . '.git') && 0 === $this->process->execute('git log -n1 --pretty=%H', $output, $path)) {
-                $package['dist']['reference'] = trim($output);
-            }
             $package = $this->loader->load($package);
             $this->addPackage($package);
         }

+ 5 - 0
src/Composer/Repository/Vcs/GitHubDriver.php

@@ -35,6 +35,7 @@ class GitHubDriver extends VcsDriver
     protected $hasIssues;
     protected $infoCache = array();
     protected $isPrivate = false;
+    private $isArchived = false;
 
     /**
      * Git Driver
@@ -163,6 +164,9 @@ class GitHubDriver extends VcsDriver
                 if (!isset($composer['support']['issues']) && $this->hasIssues) {
                     $composer['support']['issues'] = sprintf('https://%s/%s/%s/issues', $this->originUrl, $this->owner, $this->repository);
                 }
+                if (!isset($composer['abandoned']) && $this->isArchived) {
+                    $composer['abandoned'] = true;
+                }
             }
 
             if ($this->shouldCache($identifier)) {
@@ -432,6 +436,7 @@ class GitHubDriver extends VcsDriver
             $this->rootIdentifier = 'master';
         }
         $this->hasIssues = !empty($this->repoData['has_issues']);
+        $this->isArchived = !empty($this->repoData['archived']);
     }
 
     protected function attemptCloneFallback()

+ 36 - 0
src/Composer/Repository/Vcs/GitLabDriver.php

@@ -120,6 +120,42 @@ class GitLabDriver extends VcsDriver
         $this->httpDownloader = $httpDownloader;
     }
 
+    /**
+     * {@inheritDoc}
+     */
+    public function getComposerInformation($identifier)
+    {
+        if ($this->gitDriver) {
+            return $this->gitDriver->getComposerInformation($identifier);
+        }
+
+        if (!isset($this->infoCache[$identifier])) {
+            if ($this->shouldCache($identifier) && $res = $this->cache->read($identifier)) {
+                return $this->infoCache[$identifier] = JsonFile::parseJson($res);
+            }
+
+            $composer = $this->getBaseComposerInformation($identifier);
+
+            if ($composer) {
+                // specials for gitlab (this data is only available if authentication is provided)
+                if (!isset($composer['support']['issues']) && isset($this->project['_links']['issues'])) {
+                    $composer['support']['issues'] = $this->project['_links']['issues'];
+                }
+                if (!isset($composer['abandoned']) && !empty($this->project['archived'])) {
+                    $composer['abandoned'] = true;
+                }
+            }
+
+            if ($this->shouldCache($identifier)) {
+                $this->cache->write($identifier, json_encode($composer));
+            }
+
+            $this->infoCache[$identifier] = $composer;
+        }
+
+        return $this->infoCache[$identifier];
+    }
+
     /**
      * {@inheritdoc}
      */

+ 85 - 0
tests/Composer/Test/Json/JsonManipulatorTest.php

@@ -1814,6 +1814,91 @@ class JsonManipulatorTest extends TestCase
 ', $manipulator->getContents());
     }
 
+    public function testAddConfigWithPackage() {
+        $manipulator = new JsonManipulator('{
+    "repositories": [
+        {
+            "type": "package",
+            "package": {
+                "authors": [],
+                "extra": {
+                    "package-xml": "package.xml"
+                }
+            }
+        }
+    ],
+    "config": {
+        "platform": {
+            "php": "5.3.9"
+        }
+    }
+}');
+
+        $this->assertTrue($manipulator->addConfigSetting('preferred-install.my-organization/stable-package', 'dist'));
+        $this->assertEquals('{
+    "repositories": [
+        {
+            "type": "package",
+            "package": {
+                "authors": [],
+                "extra": {
+                    "package-xml": "package.xml"
+                }
+            }
+        }
+    ],
+    "config": {
+        "platform": {
+            "php": "5.3.9"
+        },
+        "preferred-install": {
+            "my-organization/stable-package": "dist"
+        }
+    }
+}
+', $manipulator->getContents());
+    }
+
+    public function testAddSuggestWithPackage()
+    {
+        $manipulator = new JsonManipulator('{
+    "repositories": [
+        {
+            "type": "package",
+            "package": {
+                "authors": [],
+                "extra": {
+                    "package-xml": "package.xml"
+                }
+            }
+        }
+    ],
+    "suggest": {
+        "package": "Description"
+    }
+}');
+
+        $this->assertTrue($manipulator->addProperty('suggest.new-package', 'new-description'));
+        $this->assertEquals('{
+    "repositories": [
+        {
+            "type": "package",
+            "package": {
+                "authors": [],
+                "extra": {
+                    "package-xml": "package.xml"
+                }
+            }
+        }
+    ],
+    "suggest": {
+        "package": "Description",
+        "new-package": "new-description"
+    }
+}
+', $manipulator->getContents());
+    }
+
     public function testAddRepositoryCanInitializeEmptyRepositories()
     {
         $manipulator = new JsonManipulator('{

+ 11 - 3
tests/Composer/Test/Package/Version/VersionGuesserTest.php

@@ -126,13 +126,15 @@ class VersionGuesserTest extends TestCase
 
         $this->assertEquals("9999999-dev", $versionArray['version']);
         $this->assertEquals("dev-master", $versionArray['pretty_version']);
+        $this->assertArrayNotHasKey('feature_version', $versionArray);
+        $this->assertArrayNotHasKey('feature_pretty_version', $versionArray);
         $this->assertEquals($commitHash, $versionArray['commit']);
     }
 
     public function testGuessVersionReadsAndRespectsNonFeatureBranchesConfigurationForArbitraryNaming()
     {
         $commitHash = '03a15d220da53c52eddd5f32ffca64a7b3801bea';
-        $anotherCommitHash = '03a15d220da53c52eddd5f32ffca64a7b3801bea';
+        $anotherCommitHash = '13a15d220da53c52eddd5f32ffca64a7b3801bea';
 
         $executor = $this->getMockBuilder('\\Composer\\Util\\ProcessExecutor')
             ->setMethods(array('execute'))
@@ -172,12 +174,14 @@ class VersionGuesserTest extends TestCase
 
         $this->assertEquals("dev-arbitrary", $versionArray['version']);
         $this->assertEquals($anotherCommitHash, $versionArray['commit']);
+        $this->assertEquals("dev-current", $versionArray['feature_version']);
+        $this->assertEquals("dev-current", $versionArray['feature_pretty_version']);
     }
 
     public function testGuessVersionReadsAndRespectsNonFeatureBranchesConfigurationForArbitraryNamingRegex()
     {
         $commitHash = '03a15d220da53c52eddd5f32ffca64a7b3801bea';
-        $anotherCommitHash = '03a15d220da53c52eddd5f32ffca64a7b3801bea';
+        $anotherCommitHash = '13a15d220da53c52eddd5f32ffca64a7b3801bea';
 
         $executor = $this->getMockBuilder('\\Composer\\Util\\ProcessExecutor')
             ->setMethods(array('execute'))
@@ -217,12 +221,14 @@ class VersionGuesserTest extends TestCase
 
         $this->assertEquals("dev-latest-testing", $versionArray['version']);
         $this->assertEquals($anotherCommitHash, $versionArray['commit']);
+        $this->assertEquals("dev-current", $versionArray['feature_version']);
+        $this->assertEquals("dev-current", $versionArray['feature_pretty_version']);
     }
 
     public function testGuessVersionReadsAndRespectsNonFeatureBranchesConfigurationForArbitraryNamingWhenOnNonFeatureBranch()
     {
         $commitHash = '03a15d220da53c52eddd5f32ffca64a7b3801bea';
-        $anotherCommitHash = '03a15d220da53c52eddd5f32ffca64a7b3801bea';
+        $anotherCommitHash = '13a15d220da53c52eddd5f32ffca64a7b3801bea';
 
         $executor = $this->getMockBuilder('\\Composer\\Util\\ProcessExecutor')
             ->setMethods(array('execute'))
@@ -251,6 +257,8 @@ class VersionGuesserTest extends TestCase
 
         $this->assertEquals("dev-latest-testing", $versionArray['version']);
         $this->assertEquals($commitHash, $versionArray['commit']);
+        $this->assertArrayNotHasKey('feature_version', $versionArray);
+        $this->assertArrayNotHasKey('feature_pretty_version', $versionArray);
     }
 
     public function testDetachedHeadBecomesDevHash()

+ 29 - 0
tests/Composer/Test/Repository/ComposerRepositoryTest.php

@@ -283,4 +283,33 @@ class ComposerRepositoryTest extends TestCase
             ),
         );
     }
+
+    public function testGetProviderNamesWillReturnPartialPackageNames()
+    {
+        $rfs = $this->getMockBuilder('Composer\Util\RemoteFilesystem')
+            ->disableOriginalConstructor()
+            ->getMock();
+
+        $rfs->expects($this->at(0))
+            ->method('getContents')
+            ->with('example.org', 'http://example.org/packages.json', false)
+            ->willReturn(json_encode(array(
+                'providers-lazy-url' => '/foo/p/%package%.json',
+                'packages' => array('foo/bar' => array(
+                    'dev-branch' => array(),
+                    'v1.0.0' => array(),
+                ))
+            )));
+
+        $repository = new ComposerRepository(
+            array('url' => 'http://example.org/packages.json'),
+            new NullIO(),
+            FactoryMock::createConfig(),
+            null,
+            $rfs
+        );
+
+        $this->assertTrue($repository->hasProviders());
+        $this->assertEquals(array('foo/bar'), $repository->getProviderNames());
+    }
 }

+ 43 - 1
tests/Composer/Test/Repository/Vcs/GitHubDriverTest.php

@@ -216,7 +216,49 @@ class GitHubDriverTest extends TestCase
         $this->assertEquals($repoUrl, $source['url']);
         $this->assertEquals($sha, $source['reference']);
 
-        $gitHubDriver->getComposerInformation($identifier);
+        $data = $gitHubDriver->getComposerInformation($identifier);
+
+        $this->assertArrayNotHasKey('abandoned', $data);
+    }
+
+    public function testPublicRepositoryArchived()
+    {
+        $repoUrl = 'http://github.com/composer/packagist';
+        $repoApiUrl = 'https://api.github.com/repos/composer/packagist';
+        $identifier = 'v0.0.0';
+        $sha = 'SOMESHA';
+        $composerJsonUrl = 'https://api.github.com/repos/composer/packagist/contents/composer.json?ref=' . $sha;
+
+        $io = $this->getMockBuilder('Composer\IO\IOInterface')->getMock();
+        $io->expects($this->any())
+            ->method('isInteractive')
+            ->will($this->returnValue(true));
+
+        $remoteFilesystem = $this->getMockBuilder('Composer\Util\RemoteFilesystem')
+            ->setConstructorArgs(array($io))
+            ->getMock();
+
+        $remoteFilesystem->expects($this->at(0))
+            ->method('getContents')
+            ->with($this->equalTo('github.com'), $this->equalTo($repoApiUrl), $this->equalTo(false))
+            ->will($this->returnValue('{"master_branch": "test_master", "owner": {"login": "composer"}, "name": "packagist", "archived": true}'));
+
+        $remoteFilesystem->expects($this->at(1))
+            ->method('getContents')
+            ->with($this->equalTo('github.com'), $this->equalTo($composerJsonUrl), $this->equalTo(false))
+            ->will($this->returnValue('{"encoding": "base64", "content": "' . base64_encode('{"name": "composer/packagist"}') . '"}'));
+
+        $repoConfig = array(
+            'url' => $repoUrl,
+        );
+
+        $gitHubDriver = new GitHubDriver($repoConfig, $io, $this->config, null, $remoteFilesystem);
+        $gitHubDriver->initialize();
+        $this->setAttribute($gitHubDriver, 'tags', array($identifier => $sha));
+
+        $data = $gitHubDriver->getComposerInformation($sha);
+
+        $this->assertTrue($data['abandoned']);
     }
 
     public function testPrivateRepositoryNoInteraction()

+ 1 - 1
tests/Composer/Test/Util/ProcessExecutorTest.php

@@ -108,7 +108,7 @@ class ProcessExecutorTest extends TestCase
     public function testConsoleIODoesNotFormatSymfonyConsoleStyle()
     {
         $output = new BufferedOutput(OutputInterface::VERBOSITY_NORMAL, true);
-        $process = new ProcessExecutor(new ConsoleIO(new ArrayInput([]), $output, new HelperSet([])));
+        $process = new ProcessExecutor(new ConsoleIO(new ArrayInput(array()), $output, new HelperSet(array())));
 
         $process->execute('echo \'<error>foo</error>\'');
         $this->assertSame('<error>foo</error>'.PHP_EOL, $output->fetch());