Merge pull request #3325 from rdohms/implementing-abandoned-packages
Abandoned Package Warningspull/3442/head
commit
6f4be698a5
|
@ -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) {
|
||||||
|
|
|
@ -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()) {
|
||||||
|
|
|
@ -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().')';
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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)) {
|
||||||
|
|
|
@ -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)
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue