Skip to content

Commit 4fcbe58

Browse files
committed
add strict mode
1 parent dcb8bd3 commit 4fcbe58

File tree

9 files changed

+231
-64
lines changed

9 files changed

+231
-64
lines changed

.gitattributes

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,6 @@
22
/.github export-ignore
33
.gitattributes export-ignore
44
.gitignore export-ignore
5-
.php_cs.dist export-ignore
5+
.php-cs-fixer.dist.php export-ignore
66
phpunit.xml.dist export-ignore
77
infection.json.dist export-ignore

README.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,21 @@ composer diff -p # include platform dependencies
8282
composer diff -f json # Output as JSON instead of table
8383
```
8484

85+
### Strict mode
86+
87+
To help you control your dependencies, you may pass `--strict` option when running in CI. If there are any changes detected, a non-zero exit code will be returned.
88+
89+
Exit code of the command is built using following bit flags:
90+
91+
* `0` - OK.
92+
* `1` - General error.
93+
* `2` - There were changes in prod packages.
94+
* `4` - There were changes is dev packages.
95+
* `8` - There were downgrades in prod packages.
96+
* `16` - There were downgrades in dev packages.
97+
98+
You may check for individual flags or simply check if the status is greater or equal 8 if you don't want to downgrade any package.
99+
85100
# Similar packages
86101

87102
While there are several existing packages offering similar functionality:

src/Command/DiffCommand.php

Lines changed: 83 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
namespace IonBazan\ComposerDiff\Command;
44

55
use Composer\Command\BaseCommand;
6+
use Composer\DependencyResolver\Operation\OperationInterface;
7+
use Composer\DependencyResolver\Operation\UpdateOperation;
68
use IonBazan\ComposerDiff\Formatter\Formatter;
79
use IonBazan\ComposerDiff\Formatter\JsonFormatter;
810
use IonBazan\ComposerDiff\Formatter\MarkdownListFormatter;
@@ -16,6 +18,10 @@
1618

1719
class DiffCommand extends BaseCommand
1820
{
21+
const CHANGES_PROD = 2;
22+
const CHANGES_DEV = 4;
23+
const DOWNGRADES_PROD = 8;
24+
const DOWNGRADES_DEV = 16;
1925
/**
2026
* @var PackageDiff
2127
*/
@@ -54,43 +60,61 @@ protected function configure()
5460
->addOption('with-links', 'l', InputOption::VALUE_NONE, 'Include compare/release URLs')
5561
->addOption('format', 'f', InputOption::VALUE_REQUIRED, 'Output format (mdtable, mdlist, json)', 'mdtable')
5662
->addOption('gitlab-domains', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'Extra Gitlab domains (inherited from Composer config by default)', array())
63+
->addOption('strict', 's', InputOption::VALUE_NONE, 'Return non-zero exit code if there are any changes')
5764
->setHelp(<<<'EOF'
58-
The <comment>%command.name%</comment> command displays all dependency changes between two <info>composer.lock</info> files.
59-
By default, it will compare current filesystem changes with git <info>HEAD</info>:
65+
The <info>%command.name%</info> command displays all dependency changes between two <comment>composer.lock</comment> files.
6066
61-
<comment>%command.full_name%</comment>
67+
By default, it will compare current filesystem changes with git <comment>HEAD</comment>:
68+
69+
<info>%command.full_name%</info>
6270
6371
To compare with specific branch, pass its name as argument:
6472
65-
<comment>%command.full_name% master</comment>
73+
<info>%command.full_name% master</info>
6674
6775
You can specify any valid git refs to compare with:
6876
69-
<comment>%command.full_name% HEAD~3 be4aabc</comment>
77+
<info>%command.full_name% HEAD~3 be4aabc</info>
7078
7179
You can also use more verbose syntax for <info>base</info> and <info>target</info> options:
7280
73-
<comment>%command.full_name% --base master --target composer.lock</comment>
81+
<info>%command.full_name% --base master --target composer.lock</info>
7482
7583
To compare files in specific path, use following syntax:
7684
77-
<comment>%command.full_name% master:subdirectory/composer.lock /path/to/another/composer.lock</comment>
85+
<info>%command.full_name% master:subdirectory/composer.lock /path/to/another/composer.lock</info>
7886
79-
By default, <info>platform</info> dependencies are hidden. Add <comment>--with-platform</comment> option to include them in the report:
87+
By default, <info>platform</info> dependencies are hidden. Add <info>--with-platform</info> option to include them in the report:
8088
81-
<comment>%command.full_name% --with-platform</comment>
89+
<info>%command.full_name% --with-platform</info>
8290
83-
Use <comment>--with-links</comment> to include release and compare URLs in the report:
91+
Use <info>--with-links</info> to include release and compare URLs in the report:
8492
85-
<comment>%command.full_name% --with-links</comment>
93+
<info>%command.full_name% --with-links</info>
8694
87-
You can customize output format by specifying it with <comment>--format</comment> option. Choose between <info>mdtable</info>, <info>mdlist</info> and <info>json</info>:
95+
You can customize output format by specifying it with <info>--format</info> option. Choose between <comment>mdtable</comment>, <comment>mdlist</comment> and <comment>json</comment>:
96+
97+
<info>%command.full_name% --format=json</info>
98+
99+
Hide <info>dev</info> dependencies using <info>--no-dev</info> option:
100+
101+
<info>%command.full_name% --no-dev</info>
102+
103+
Passing <info>--strict</info> option may help you to disallow changes or downgrades by returning non-zero exit code:
104+
105+
<info>%command.full_name% --strict</info>
88106
89-
<comment>%command.full_name% --format=json</comment>
107+
Exit code
108+
---------
90109
91-
Hide <info>dev</info> dependencies using <comment>--no-dev</comment> option:
110+
Exit code of the command is built using following bit flags:
92111
93-
<comment>%command.full_name% --no-dev</comment>
112+
* 0 - OK.
113+
* 1 - General error.
114+
* 2 - There were changes in prod packages.
115+
* 4 - There were changes is dev packages.
116+
* 8 - There were downgrades in prod packages.
117+
* 16 - There were downgrades in dev packages.
94118
EOF
95119
)
96120
;
@@ -122,7 +146,50 @@ protected function execute(InputInterface $input, OutputInterface $output)
122146

123147
$formatter->render($prodOperations, $devOperations, $withUrls);
124148

125-
return 0;
149+
return $input->getOption('strict') ? $this->getExitCode($prodOperations, $devOperations) : 0;
150+
}
151+
152+
/**
153+
* @param OperationInterface[] $prodOperations
154+
* @param OperationInterface[] $devOperations
155+
*
156+
* @return int Exit code
157+
*/
158+
private function getExitCode(array $prodOperations, array $devOperations)
159+
{
160+
$exitCode = 0;
161+
162+
if (!empty($prodOperations)) {
163+
$exitCode = self::CHANGES_PROD;
164+
165+
if ($this->hasDowngrades($prodOperations)) {
166+
$exitCode |= self::DOWNGRADES_PROD;
167+
}
168+
}
169+
170+
if (!empty($devOperations)) {
171+
$exitCode |= self::CHANGES_DEV;
172+
173+
if ($this->hasDowngrades($devOperations)) {
174+
$exitCode |= self::DOWNGRADES_DEV;
175+
}
176+
}
177+
178+
return $exitCode;
179+
}
180+
181+
/**
182+
* @param OperationInterface[] $operations
183+
*
184+
* @return bool
185+
*/
186+
private function hasDowngrades(array $operations)
187+
{
188+
$downgrades = array_filter($operations, function (OperationInterface $operation) {
189+
return $operation instanceof UpdateOperation && !PackageDiff::isUpgrade($operation);
190+
});
191+
192+
return !empty($downgrades);
126193
}
127194

128195
/**

src/Formatter/AbstractFormatter.php

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,8 @@
77
use Composer\DependencyResolver\Operation\UninstallOperation;
88
use Composer\DependencyResolver\Operation\UpdateOperation;
99
use Composer\Package\PackageInterface;
10-
use Composer\Semver\Semver;
11-
use Composer\Semver\VersionParser;
1210
use IonBazan\ComposerDiff\Url\GeneratorContainer;
1311
use Symfony\Component\Console\Output\OutputInterface;
14-
use UnexpectedValueException;
1512

1613
abstract class AbstractFormatter implements Formatter
1714
{
@@ -74,21 +71,4 @@ private function getReleaseUrl(PackageInterface $package)
7471

7572
return $generator->getReleaseUrl($package);
7673
}
77-
78-
/**
79-
* @return bool
80-
*/
81-
protected static function isUpgrade(UpdateOperation $operation)
82-
{
83-
$versionParser = new VersionParser();
84-
try {
85-
$normalizedFrom = $versionParser->normalize($operation->getInitialPackage()->getVersion());
86-
$normalizedTo = $versionParser->normalize($operation->getTargetPackage()->getVersion());
87-
} catch (UnexpectedValueException $e) {
88-
return true; // Consider as upgrade if versions are not parsable
89-
}
90-
$sorted = Semver::sort(array($normalizedTo, $normalizedFrom));
91-
92-
return $sorted[0] === $normalizedFrom;
93-
}
9474
}

src/Formatter/JsonFormatter.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
use Composer\DependencyResolver\Operation\OperationInterface;
77
use Composer\DependencyResolver\Operation\UninstallOperation;
88
use Composer\DependencyResolver\Operation\UpdateOperation;
9+
use IonBazan\ComposerDiff\PackageDiff;
910

1011
class JsonFormatter extends AbstractFormatter
1112
{
@@ -78,7 +79,7 @@ private function transformOperation(OperationInterface $operation)
7879
if ($operation instanceof UpdateOperation) {
7980
return array(
8081
'name' => $operation->getInitialPackage()->getName(),
81-
'operation' => self::isUpgrade($operation) ? 'upgrade' : 'downgrade',
82+
'operation' => PackageDiff::isUpgrade($operation) ? 'upgrade' : 'downgrade',
8283
'version_base' => $operation->getInitialPackage()->getFullPrettyVersion(),
8384
'version_target' => $operation->getTargetPackage()->getFullPrettyVersion(),
8485
);

src/Formatter/MarkdownListFormatter.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
use Composer\DependencyResolver\Operation\OperationInterface;
77
use Composer\DependencyResolver\Operation\UninstallOperation;
88
use Composer\DependencyResolver\Operation\UpdateOperation;
9+
use IonBazan\ComposerDiff\PackageDiff;
910

1011
class MarkdownListFormatter extends MarkdownFormatter
1112
{
@@ -58,7 +59,7 @@ private function getRow(OperationInterface $operation, $withUrls)
5859
}
5960

6061
if ($operation instanceof UpdateOperation) {
61-
$isUpgrade = self::isUpgrade($operation);
62+
$isUpgrade = PackageDiff::isUpgrade($operation);
6263

6364
return sprintf(
6465
' - %s <fg=green>%s</> (<fg=yellow>%s</> => <fg=yellow>%s</>)%s',

src/Formatter/MarkdownTableFormatter.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use Composer\DependencyResolver\Operation\UninstallOperation;
88
use Composer\DependencyResolver\Operation\UpdateOperation;
99
use IonBazan\ComposerDiff\Formatter\Helper\Table;
10+
use IonBazan\ComposerDiff\PackageDiff;
1011

1112
class MarkdownTableFormatter extends MarkdownFormatter
1213
{
@@ -68,7 +69,7 @@ private function getTableRow(OperationInterface $operation)
6869
if ($operation instanceof UpdateOperation) {
6970
return array(
7071
$operation->getInitialPackage()->getName(),
71-
self::isUpgrade($operation) ? '<fg=cyan>Upgraded</>' : '<fg=yellow>Downgraded</>',
72+
PackageDiff::isUpgrade($operation) ? '<fg=cyan>Upgraded</>' : '<fg=yellow>Downgraded</>',
7273
$operation->getInitialPackage()->getFullPrettyVersion(),
7374
$operation->getTargetPackage()->getFullPrettyVersion(),
7475
);

src/PackageDiff.php

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99
use Composer\Package\CompletePackage;
1010
use Composer\Package\Loader\ArrayLoader;
1111
use Composer\Repository\ArrayRepository;
12+
use Composer\Semver\Semver;
13+
use Composer\Semver\VersionParser;
14+
use UnexpectedValueException;
1215

1316
class PackageDiff
1417
{
@@ -50,6 +53,23 @@ public function getPackageDiff($from, $to, $dev, $withPlatform)
5053
return $operations;
5154
}
5255

56+
/**
57+
* @return bool
58+
*/
59+
public static function isUpgrade(UpdateOperation $operation)
60+
{
61+
$versionParser = new VersionParser();
62+
try {
63+
$normalizedFrom = $versionParser->normalize($operation->getInitialPackage()->getVersion());
64+
$normalizedTo = $versionParser->normalize($operation->getTargetPackage()->getVersion());
65+
} catch (UnexpectedValueException $e) {
66+
return true; // Consider as upgrade if versions are not parsable
67+
}
68+
$sorted = Semver::sort(array($normalizedTo, $normalizedFrom));
69+
70+
return $sorted[0] === $normalizedFrom;
71+
}
72+
5373
/**
5474
* @param string $path
5575
* @param bool $dev

0 commit comments

Comments
 (0)