Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,11 @@ jobs:
- '7.3'
- '7.4'
- '8.0'
- '8.1'
include:
- php-versions: '7.0'
composer-flags: '--prefer-lowest'
operating-system: ubuntu-latest
- php-versions: '5.3'
composer-flags: '--prefer-lowest'
operating-system: ubuntu-latest
Expand Down Expand Up @@ -50,15 +54,14 @@ jobs:
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v1
- name: Run mutation tests
if: ${{ matrix.php-versions == 7.4 && matrix.operating-system == 'ubuntu-latest' }}
if: ${{ matrix.php-versions == 8.0 && matrix.operating-system == 'ubuntu-latest' }}
env:
STRYKER_DASHBOARD_API_KEY: ${{ secrets.STRYKER_DASHBOARD_API_KEY }}
run: |
composer req infection/infection
vendor/bin/infection --ignore-msi-with-no-mutations --min-covered-msi=100 --min-msi=100 -s -j4
- name: Run phpstan
if: ${{ matrix.php-versions >= 7.1 && matrix.php-versions < 8.0 }}
if: ${{ matrix.php-versions == 8.0 && matrix.operating-system == 'ubuntu-latest' }}
run: |
composer req phpstan/phpstan
vendor/bin/phpstan analyse src -l 6

vendor/bin/phpstan
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
# Composer Diff Plugin

[![PHP 5.3+ | 7.x | 8.x](https://img.shields.io/badge/PHP-^5.3_|_^7_|_^8-blue.svg)](https://packagist.org/packages/ion-bazan/composer-diff)
[![Composer v1 | v2](https://img.shields.io/badge/Composer-^1.1_|_^2-success.svg)](https://packagist.org/packages/ion-bazan/composer-diff)
[![Dependencies: 0](https://img.shields.io/badge/dependencies-0-success.svg)](https://packagist.org/packages/ion-bazan/composer-diff)
[![Latest version](https://img.shields.io/packagist/v/ion-bazan/composer-diff.svg)](https://packagist.org/packages/ion-bazan/composer-diff)
[![GitHub Workflow Status](https://img.shields.io/github/workflow/status/IonBazan/composer-diff/Tests)](https://github.com/IonBazan/composer-diff/actions)
[![PHP version](https://img.shields.io/packagist/php-v/ion-bazan/composer-diff.svg)](https://packagist.org/packages/ion-bazan/composer-diff)
[![Codecov](https://img.shields.io/codecov/c/gh/IonBazan/composer-diff)](https://codecov.io/gh/IonBazan/composer-diff)
[![Mutation testing badge](https://img.shields.io/endpoint?style=flat&url=https%3A%2F%2Fbadge-api.stryker-mutator.io%2Fgithub.com%2FIonBazan%2Fcomposer-diff%2Fmaster)](https://dashboard.stryker-mutator.io/reports/github.com/IonBazan/composer-diff/master)
[![Downloads](https://img.shields.io/packagist/dt/ion-bazan/composer-diff.svg)](https://packagist.org/packages/ion-bazan/composer-diff)
Expand Down
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
"platform-check": false
},
"extra": {
"class": "IonBazan\\ComposerDiff\\Plugin"
"class": "IonBazan\\ComposerDiff\\Composer\\Plugin"
},
"autoload": {
"psr-4": {
Expand Down
6 changes: 6 additions & 0 deletions phpstan.neon
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
parameters:
level: 6
paths:
- src
checkGenericClassInNonGenericObjectType: true
checkMissingIterableValueType: true
1 change: 1 addition & 0 deletions phpunit.xml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
>
<php>
<env name="SYMFONY_DEPRECATIONS_HELPER" value="weak"/>
<env name="COMPOSER_DISABLE_XDEBUG_WARN" value="1"/>
</php>
<testsuites>
<testsuite name="Composer Diff Test Suite">
Expand Down
36 changes: 17 additions & 19 deletions src/Command/DiffCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
namespace IonBazan\ComposerDiff\Command;

use Composer\Command\BaseCommand;
use Composer\DependencyResolver\Operation\OperationInterface;
use Composer\DependencyResolver\Operation\UpdateOperation;
use IonBazan\ComposerDiff\Diff\DiffEntries;
use IonBazan\ComposerDiff\Diff\DiffEntry;
use IonBazan\ComposerDiff\Formatter\Formatter;
use IonBazan\ComposerDiff\Formatter\JsonFormatter;
use IonBazan\ComposerDiff\Formatter\MarkdownListFormatter;
Expand Down Expand Up @@ -133,8 +133,8 @@ protected function execute(InputInterface $input, OutputInterface $output)

$formatter = $this->getFormatter($input, $output);

$prodOperations = array();
$devOperations = array();
$prodOperations = new DiffEntries(array());
$devOperations = new DiffEntries(array());

if (!$input->getOption('no-prod')) {
$prodOperations = $this->packageDiff->getPackageDiff($base, $target, false, $withPlatform);
Expand All @@ -150,27 +150,24 @@ protected function execute(InputInterface $input, OutputInterface $output)
}

/**
* @param OperationInterface[] $prodOperations
* @param OperationInterface[] $devOperations
*
* @return int Exit code
*/
private function getExitCode(array $prodOperations, array $devOperations)
private function getExitCode(DiffEntries $prodEntries, DiffEntries $devEntries)
{
$exitCode = 0;

if (!empty($prodOperations)) {
if (count($prodEntries)) {
$exitCode = self::CHANGES_PROD;

if ($this->hasDowngrades($prodOperations)) {
if ($this->hasDowngrades($prodEntries)) {
$exitCode |= self::DOWNGRADES_PROD;
}
}

if (!empty($devOperations)) {
if (count($devEntries)) {
$exitCode |= self::CHANGES_DEV;

if ($this->hasDowngrades($devOperations)) {
if ($this->hasDowngrades($devEntries)) {
$exitCode |= self::DOWNGRADES_DEV;
}
}
Expand All @@ -179,17 +176,18 @@ private function getExitCode(array $prodOperations, array $devOperations)
}

/**
* @param OperationInterface[] $operations
*
* @return bool
*/
private function hasDowngrades(array $operations)
private function hasDowngrades(DiffEntries $entries)
{
$downgrades = array_filter($operations, function (OperationInterface $operation) {
return $operation instanceof UpdateOperation && !PackageDiff::isUpgrade($operation);
});
/** @var DiffEntry $entry */
foreach ($entries as $entry) {
if ($entry->isDowngrade()) {
return true;
}
}

return !empty($downgrades);
return false;
}

/**
Expand Down
2 changes: 1 addition & 1 deletion src/Plugin.php → src/Composer/Plugin.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?php

namespace IonBazan\ComposerDiff;
namespace IonBazan\ComposerDiff\Composer;

use Composer\Composer;
use Composer\IO\IOInterface;
Expand Down
37 changes: 37 additions & 0 deletions src/Diff/DiffEntries.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php

namespace IonBazan\ComposerDiff\Diff;

use ArrayIterator;
use Countable;
use IteratorAggregate;

/**
* @implements IteratorAggregate<int, DiffEntry>
*/
class DiffEntries implements IteratorAggregate, Countable
{
/** @var DiffEntry[] */
private $entries;

/**
* @param DiffEntry[] $entries
*/
public function __construct(array $entries)
{
$this->entries = $entries;
}

/**
* @return ArrayIterator<int, DiffEntry>
*/
public function getIterator()
{
return new ArrayIterator($this->entries);
}

public function count()
{
return \count($this->entries);
}
}
111 changes: 111 additions & 0 deletions src/Diff/DiffEntry.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
<?php

namespace IonBazan\ComposerDiff\Diff;

use Composer\DependencyResolver\Operation\InstallOperation;
use Composer\DependencyResolver\Operation\OperationInterface;
use Composer\DependencyResolver\Operation\UninstallOperation;
use Composer\DependencyResolver\Operation\UpdateOperation;

class DiffEntry
{
const TYPE_INSTALL = 'install';
const TYPE_UPGRADE = 'upgrade';
const TYPE_DOWNGRADE = 'downgrade';
const TYPE_REMOVE = 'remove';
const TYPE_CHANGE = 'change';

/** @var OperationInterface */
private $operation;

/** @var string */
private $type;

public function __construct(OperationInterface $operation)
{
$this->operation = $operation;
$this->type = $this->determineType();
}

/**
* @return OperationInterface
*/
public function getOperation()
{
return $this->operation;
}

/**
* @return string
*/
public function getType()
{
return $this->type;
}

/**
* @return bool
*/
public function isInstall()
{
return self::TYPE_INSTALL === $this->type;
}

/**
* @return bool
*/
public function isUpgrade()
{
return self::TYPE_UPGRADE === $this->type;
}

/**
* @return bool
*/
public function isDowngrade()
{
return self::TYPE_DOWNGRADE === $this->type;
}

/**
* @return bool
*/
public function isRemove()
{
return self::TYPE_REMOVE === $this->type;
}

/**
* @return bool
*/
public function isChange()
{
return self::TYPE_CHANGE === $this->type;
}

/**
* @return string
*/
private function determineType()
{
if ($this->operation instanceof InstallOperation) {
return self::TYPE_INSTALL;
}

if ($this->operation instanceof UninstallOperation) {
return self::TYPE_REMOVE;
}

if ($this->operation instanceof UpdateOperation) {
$upgrade = VersionComparator::isUpgrade($this->operation);

if (null === $upgrade) {
return self::TYPE_CHANGE;
}

return $upgrade ? self::TYPE_UPGRADE : self::TYPE_DOWNGRADE;
}

return self::TYPE_CHANGE;
}
}
39 changes: 39 additions & 0 deletions src/Diff/VersionComparator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php

namespace IonBazan\ComposerDiff\Diff;

use Composer\DependencyResolver\Operation\UpdateOperation;
use Composer\Semver\Semver;
use Composer\Semver\VersionParser;
use UnexpectedValueException;

class VersionComparator
{
/**
* @return bool|null true if it's upgrade, false if it's downgrade, null if it is a change
*/
public static function isUpgrade(UpdateOperation $operation)
{
$versionParser = new VersionParser();
try {
$normalizedFrom = $versionParser->normalize($operation->getInitialPackage()->getVersion());
$normalizedTo = $versionParser->normalize($operation->getTargetPackage()->getVersion());
} catch (UnexpectedValueException $e) {
return null; // Consider as change if versions are not parseable
}

/* @infection-ignore-all False-positive, handled by build matrix with Composer 1 installed */
if (
'9999999-dev' === $normalizedFrom
|| '9999999-dev' === $normalizedTo // BC for Composer 1.x
|| 0 === strpos($normalizedFrom, 'dev-')
|| 0 === strpos($normalizedTo, 'dev-')
) {
return null;
}

$sorted = Semver::sort(array($normalizedTo, $normalizedFrom));

return $sorted[0] === $normalizedFrom;
}
}
6 changes: 4 additions & 2 deletions src/Formatter/AbstractFormatter.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
namespace IonBazan\ComposerDiff\Formatter;

use Composer\DependencyResolver\Operation\InstallOperation;
use Composer\DependencyResolver\Operation\OperationInterface;
use Composer\DependencyResolver\Operation\UninstallOperation;
use Composer\DependencyResolver\Operation\UpdateOperation;
use Composer\Package\PackageInterface;
use IonBazan\ComposerDiff\Diff\DiffEntry;
use IonBazan\ComposerDiff\Url\GeneratorContainer;
use Symfony\Component\Console\Output\OutputInterface;

Expand All @@ -31,8 +31,10 @@ public function __construct(OutputInterface $output, GeneratorContainer $generat
/**
* @return string|null
*/
public function getUrl(OperationInterface $operation)
public function getUrl(DiffEntry $entry)
{
$operation = $entry->getOperation();

if ($operation instanceof UpdateOperation) {
return $this->getCompareUrl($operation->getInitialPackage(), $operation->getTargetPackage());
}
Expand Down
Loading