1
0
Fork 0

Merge pull request #3325 from rdohms/implementing-abandoned-packages

Abandoned Package Warnings
pull/3442/head
Nils Adermann 2014-11-20 13:09:18 +01:00
commit 6f4be698a5
12 changed files with 172 additions and 3 deletions

View File

@ -294,6 +294,16 @@ EOT
$output->writeln('<info>dist</info> : ' . sprintf('[%s] <comment>%s</comment> %s', $package->getDistType(), $package->getDistUrl(), $package->getDistReference())); $output->writeln('<info>dist</info> : ' . sprintf('[%s] <comment>%s</comment> %s', $package->getDistType(), $package->getDistUrl(), $package->getDistReference()));
$output->writeln('<info>names</info> : ' . implode(', ', $package->getNames())); $output->writeln('<info>names</info> : ' . implode(', ', $package->getNames()));
if ($package->isAbandoned()) {
$replacement = ($package->getReplacementPackage() !== null)
? ' The author suggests using the ' . $package->getReplacementPackage(). ' package instead.'
: null;
$output->writeln(
sprintf('<error>Attention: This package is abandoned and no longer maintained.%s</error>', $replacement)
);
}
if ($package->getSupport()) { if ($package->getSupport()) {
$output->writeln("\n<info>support</info>"); $output->writeln("\n<info>support</info>");
foreach ($package->getSupport() as $type => $value) { foreach ($package->getSupport() as $type => $value) {

View File

@ -31,6 +31,7 @@ use Composer\Installer\NoopInstaller;
use Composer\IO\IOInterface; use Composer\IO\IOInterface;
use Composer\Json\JsonFile; use Composer\Json\JsonFile;
use Composer\Package\AliasPackage; use Composer\Package\AliasPackage;
use Composer\Package\CompletePackage;
use Composer\Package\Link; use Composer\Package\Link;
use Composer\Package\LinkConstraint\VersionConstraint; use Composer\Package\LinkConstraint\VersionConstraint;
use Composer\Package\Locker; use Composer\Package\Locker;
@ -240,6 +241,25 @@ class Installer
} }
} }
# Find abandoned packages and warn user
foreach ($localRepo->getPackages() as $package) {
if (!$package instanceof CompletePackage || !$package->isAbandoned()) {
continue;
}
$replacement = (is_string($package->getReplacementPackage()))
? 'Use ' . $package->getReplacementPackage() . ' instead'
: 'No replacement was suggested';
$this->io->write(
sprintf(
"<error>Package %s is abandoned, you should avoid using it. %s.</error>",
$package->getPrettyName(),
$replacement
)
);
}
if (!$this->dryRun) { if (!$this->dryRun) {
// write lock // write lock
if ($this->update || !$this->locker->isLocked()) { if ($this->update || !$this->locker->isLocked()) {

View File

@ -333,6 +333,14 @@ class AliasPackage extends BasePackage implements CompletePackageInterface
{ {
return $this->aliasOf->getArchiveExcludes(); return $this->aliasOf->getArchiveExcludes();
} }
public function isAbandoned()
{
return $this->aliasOf->isAbandoned();
}
public function getReplacementPackage()
{
return $this->aliasOf->getReplacementPackage();
}
public function __toString() public function __toString()
{ {
return parent::__toString().' (alias of '.$this->aliasOf->getVersion().')'; return parent::__toString().' (alias of '.$this->aliasOf->getVersion().')';

View File

@ -27,6 +27,7 @@ class CompletePackage extends Package implements CompletePackageInterface
protected $homepage; protected $homepage;
protected $scripts = array(); protected $scripts = array();
protected $support = array(); protected $support = array();
protected $abandoned = false;
/** /**
* @param array $scripts * @param array $scripts
@ -169,4 +170,30 @@ class CompletePackage extends Package implements CompletePackageInterface
{ {
return $this->support; return $this->support;
} }
/**
* @return boolean
*/
public function isAbandoned()
{
return (boolean) $this->abandoned;
}
/**
* @param boolean|string $abandoned
*/
public function setAbandoned($abandoned)
{
$this->abandoned = $abandoned;
}
/**
* If the package is abandoned and has a suggested replacement, this method returns it
*
* @return string|null
*/
public function getReplacementPackage()
{
return is_string($this->abandoned)? $this->abandoned : null;
}
} }

View File

@ -78,4 +78,18 @@ interface CompletePackageInterface extends PackageInterface
* @return array * @return array
*/ */
public function getSupport(); public function getSupport();
/**
* Returns if the package is abandoned or not
*
* @return boolean
*/
public function isAbandoned();
/**
* If the package is abandoned and has a suggested replacement, this method returns it
*
* @return string
*/
public function getReplacementPackage();
} }

View File

@ -105,6 +105,10 @@ class ArrayDumper
if (isset($data['keywords']) && is_array($data['keywords'])) { if (isset($data['keywords']) && is_array($data['keywords'])) {
sort($data['keywords']); sort($data['keywords']);
} }
if ($package->isAbandoned()) {
$data['abandoned'] = $package->getReplacementPackage() ?: true;
}
} }
if ($package instanceof RootPackageInterface) { if ($package instanceof RootPackageInterface) {

View File

@ -195,6 +195,10 @@ class ArrayLoader implements LoaderInterface
if (isset($config['support'])) { if (isset($config['support'])) {
$package->setSupport($config['support']); $package->setSupport($config['support']);
} }
if (isset($config['abandoned'])) {
$package->setAbandoned($config['abandoned']);
}
} }
if ($aliasNormalized = $this->getBranchAlias($config)) { if ($aliasNormalized = $this->getBranchAlias($config)) {

View File

@ -0,0 +1,36 @@
--TEST--
Abandoned packages are flagged
--COMPOSER--
{
"repositories": [
{
"type": "package",
"package": [
{ "name": "a/a", "version": "1.0.0", "abandoned": true }
]
},
{
"type": "package",
"package": [
{ "name": "c/c", "version": "1.0.0", "abandoned": "b/b" }
]
}
],
"require": {
"a/a": "1.0.0",
"c/c": "1.0.0"
}
}
--RUN--
install
--EXPECT-OUTPUT--
<info>Loading composer repositories with package information</info>
<info>Installing dependencies (including require-dev)</info>
<error>Package a/a is abandoned, you should avoid using it. No replacement was suggested.</error>
<error>Package c/c is abandoned, you should avoid using it. Use b/b instead.</error>
<info>Writing lock file</info>
<info>Generating autoload files</info>
--EXPECT--
Installing a/a (1.0.0)
Installing c/c (1.0.0)

View File

@ -306,7 +306,7 @@ class InstallerTest extends TestCase
die(sprintf('Test "%s" is not valid, did not match the expected format.', str_replace($fixturesDir.'/', '', $file))); die(sprintf('Test "%s" is not valid, did not match the expected format.', str_replace($fixturesDir.'/', '', $file)));
} }
$tests[] = array(str_replace($fixturesDir.'/', '', $file), $message, $condition, $composer, $lock, $installed, $run, $expectLock, $expectOutput, $expect, $expectExitCode); $tests[basename($file)] = array(str_replace($fixturesDir.'/', '', $file), $message, $condition, $composer, $lock, $installed, $run, $expectLock, $expectOutput, $expect, $expectExitCode);
} }
return $tests; return $tests;

View File

@ -62,12 +62,33 @@ class ArrayDumperTest extends \PHPUnit_Framework_TestCase
$this->assertSame('dev', $config['minimum-stability']); $this->assertSame('dev', $config['minimum-stability']);
} }
public function testDumpAbandoned()
{
$this->packageExpects('isAbandoned', true);
$this->packageExpects('getReplacementPackage', true);
$config = $this->dumper->dump($this->package);
$this->assertSame(true, $config['abandoned']);
}
public function testDumpAbandonedReplacement()
{
$this->packageExpects('isAbandoned', true);
$this->packageExpects('getReplacementPackage', 'foo/bar');
$config = $this->dumper->dump($this->package);
$this->assertSame('foo/bar', $config['abandoned']);
}
/** /**
* @dataProvider getKeys * @dataProvider getKeys
*/ */
public function testKeys($key, $value, $method = null, $expectedValue = null) public function testKeys($key, $value, $method = null, $expectedValue = null)
{ {
$this->packageExpects('get'.ucfirst($method ?: $key), $value); $this->packageExpects('get'.ucfirst($method ?: $key), $value);
$this->packageExpects('isAbandoned', $value);
$config = $this->dumper->dump($this->package); $config = $this->dumper->dump($this->package);

View File

@ -117,7 +117,8 @@ class ArrayLoaderTest extends \PHPUnit_Framework_TestCase
'archive' => array( 'archive' => array(
'exclude' => array('/foo/bar', 'baz', '!/foo/bar/baz'), 'exclude' => array('/foo/bar', 'baz', '!/foo/bar/baz'),
), ),
'transport-options' => array('ssl' => array('local_cert' => '/opt/certs/test.pem')) 'transport-options' => array('ssl' => array('local_cert' => '/opt/certs/test.pem')),
'abandoned' => 'foo/bar'
); );
$package = $this->loader->load($config); $package = $this->loader->load($config);
@ -138,4 +139,28 @@ class ArrayLoaderTest extends \PHPUnit_Framework_TestCase
$this->assertInstanceOf('Composer\Package\AliasPackage', $package); $this->assertInstanceOf('Composer\Package\AliasPackage', $package);
$this->assertEquals('1.0.x-dev', $package->getPrettyVersion()); $this->assertEquals('1.0.x-dev', $package->getPrettyVersion());
} }
public function testAbandoned()
{
$config = array(
'name' => 'A',
'version' => '1.2.3.4',
'abandoned' => 'foo/bar'
);
$package = $this->loader->load($config);
$this->assertTrue($package->isAbandoned());
$this->assertEquals('foo/bar', $package->getReplacementPackage());
}
public function testNotAbandoned()
{
$config = array(
'name' => 'A',
'version' => '1.2.3.4'
);
$package = $this->loader->load($config);
$this->assertFalse($package->isAbandoned());
}
} }