Skip to content

Commit c6fe18c

Browse files
committed
initial
0 parents  commit c6fe18c

File tree

10 files changed

+482
-0
lines changed

10 files changed

+482
-0
lines changed

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
/vendor
2+
/composer.lock
3+
/.php_cs.cache
4+
/.php_cs

.php_cs.dist

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?php
2+
3+
$finder = PhpCsFixer\Finder::create()
4+
->files()
5+
->in(__DIR__.'/src')
6+
->in(__DIR__.'/tests')
7+
->name('*.php')
8+
;
9+
10+
return PhpCsFixer\Config::create()
11+
->setUsingCache(true)
12+
->setRiskyAllowed(true)
13+
->setRules(array(
14+
'@PSR2' => true,
15+
'@Symfony' => true,
16+
'array_syntax' => array('syntax' => 'long'),
17+
))
18+
->setFinder($finder);

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2020 Ion Bazan
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# Composer Diff Plugin
2+
3+
Generates packages changes report in Markdown format by comparing `composer.lock` files.
4+
5+
# Installation
6+
7+
```shell script
8+
composer global require ion-bazan/composer-diff
9+
```
10+
11+
# Usage
12+
13+
```shell script
14+
composer diff # Displays packages changed in current git tree compared with HEAD
15+
```
16+
17+
## Options
18+
19+
- `--base` (`-b`) - path, URL or git ref to original `composer.lock` file
20+
- `--target` (`-t`) - path, URL or git ref to modified `composer.lock` file
21+
- `--no-dev` - ignore dev dependencies (`require-dev`)
22+
- `--no-prod` - ignore prod dependencies (`require`)
23+
- `--with-platform` (`-p`) - include platform dependencies (PHP, extensions, etc.)
24+
25+
## Advanced usage
26+
27+
```shell script
28+
composer diff -b master:composer.lock -t develop:composer.lock -p # Compare master and develop branches, including platform dependencies
29+
composer diff --no-dev # ignore dev dependencies
30+
composer diff -p # include platform dependencies
31+
```
32+
33+

composer-diff

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#!/usr/bin/env php
2+
<?php
3+
4+
foreach ([__DIR__.'/../../autoload.php', __DIR__.'/../autoload.php', __DIR__.'/vendor/autoload.php'] as $file) {
5+
if (file_exists($file)) {
6+
require_once $file;
7+
break;
8+
}
9+
}
10+
11+
unset($file);
12+
13+
use IonBazan\ComposerDiff\Command\DiffCommand;
14+
use Symfony\Component\Console\Application;
15+
16+
$application = new Application();
17+
$application->add(new DiffCommand());
18+
$application->setDefaultCommand('diff', true);
19+
$application->run();

composer.json

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
{
2+
"name": "ion-bazan/composer-diff",
3+
"type": "composer-plugin",
4+
"license": "MIT",
5+
"authors": [
6+
{
7+
"name": "Ion Bazan",
8+
"email": "ion.bazan@gmail.com"
9+
}
10+
],
11+
"require": {
12+
"php": "^5.3.2 || ^7.0 || ^8.0",
13+
"ext-json": "*",
14+
"composer-plugin-api": "^1.1 || ^2.0",
15+
"composer/composer": "^1.1 || ^2.0",
16+
"symfony/console": "dev-master"
17+
},
18+
"require-dev": {},
19+
"config": {
20+
"platform-check": false
21+
},
22+
"extra": {
23+
"class": "IonBazan\\ComposerDiff\\Plugin"
24+
},
25+
"autoload": {
26+
"psr-4": {
27+
"IonBazan\\ComposerDiff\\": "src"
28+
}
29+
},
30+
"bin": [
31+
"composer-diff"
32+
]
33+
}

src/Command/DiffCommand.php

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
<?php
2+
3+
namespace IonBazan\ComposerDiff\Command;
4+
5+
use Composer\Command\BaseCommand;
6+
use Composer\DependencyResolver\Operation\InstallOperation;
7+
use Composer\DependencyResolver\Operation\OperationInterface;
8+
use Composer\DependencyResolver\Operation\UninstallOperation;
9+
use Composer\DependencyResolver\Operation\UpdateOperation;
10+
use IonBazan\ComposerDiff\MarkdownTable;
11+
use IonBazan\ComposerDiff\PackageDiff;
12+
use Symfony\Component\Console\Input\InputInterface;
13+
use Symfony\Component\Console\Input\InputOption;
14+
use Symfony\Component\Console\Output\OutputInterface;
15+
16+
class DiffCommand extends BaseCommand
17+
{
18+
protected function configure()
19+
{
20+
$this->setName('diff')
21+
->setDescription('Displays package diff')
22+
->addOption('base', 'b', InputOption::VALUE_REQUIRED, 'Base composer.lock file path or git ref', 'HEAD:composer.lock')
23+
->addOption('target', 't', InputOption::VALUE_REQUIRED, 'Target composer.lock file path or git ref', 'composer.lock')
24+
->addOption('no-dev', null, InputOption::VALUE_NONE, 'Ignore dev dependencies')
25+
->addOption('no-prod', null, InputOption::VALUE_NONE, 'Ignore prod dependencies')
26+
->addOption('with-platform', 'p', InputOption::VALUE_NONE, 'Include platform dependencies (PHP version, extensions, etc.)')
27+
;
28+
}
29+
30+
protected function execute(InputInterface $input, OutputInterface $output)
31+
{
32+
$base = $input->getOption('base');
33+
$target = $input->getOption('target');
34+
$withPlatform = $input->getOption('with-platform');
35+
$diff = new PackageDiff();
36+
37+
if (!$input->getOption('no-prod')) {
38+
$operations = $diff->getPackageDiff($base, $target, false, $withPlatform);
39+
$this->displayTable($operations, 'Prod Packages', $output);
40+
}
41+
42+
if (!$input->getOption('no-dev')) {
43+
$operations = $diff->getPackageDiff($base, $target, true, $withPlatform);
44+
$this->displayTable($operations, 'Dev Packages', $output);
45+
}
46+
47+
return 0;
48+
}
49+
50+
protected function displayTable(array $operations, $header, OutputInterface $output)
51+
{
52+
$table = new MarkdownTable($output);
53+
54+
foreach ($operations as $operation) {
55+
$table->addRow($this->getTableRow($operation));
56+
}
57+
58+
$table->setHeaders(array($header, 'Base', 'Target'))->render();
59+
$output->writeln('');
60+
}
61+
62+
protected function getTableRow(OperationInterface $operation)
63+
{
64+
if ($operation instanceof InstallOperation) {
65+
return array(
66+
$operation->getPackage()->getName(),
67+
'New',
68+
$operation->getPackage()->getFullPrettyVersion(),
69+
);
70+
}
71+
72+
if ($operation instanceof UpdateOperation) {
73+
return array(
74+
$operation->getInitialPackage()->getName(),
75+
$operation->getInitialPackage()->getFullPrettyVersion(),
76+
$operation->getTargetPackage()->getFullPrettyVersion(),
77+
);
78+
}
79+
80+
if ($operation instanceof UninstallOperation) {
81+
return array(
82+
$operation->getPackage()->getName(),
83+
$operation->getPackage()->getFullPrettyVersion(),
84+
'Removed',
85+
);
86+
}
87+
88+
throw new \InvalidArgumentException('Invalid operation');
89+
}
90+
}

src/MarkdownTable.php

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
<?php
2+
3+
namespace IonBazan\ComposerDiff;
4+
5+
use Symfony\Component\Console\Helper\TableSeparator;
6+
use Symfony\Component\Console\Output\OutputInterface;
7+
8+
class MarkdownTable
9+
{
10+
/**
11+
* @var OutputInterface
12+
*/
13+
protected $output;
14+
15+
protected $headers = array();
16+
17+
protected $rows = array();
18+
19+
/**
20+
* Column widths cache.
21+
*
22+
* @var array
23+
*/
24+
private $columnWidths = array();
25+
26+
public function __construct(OutputInterface $output)
27+
{
28+
$this->output = $output;
29+
}
30+
31+
public function setHeaders(array $headers)
32+
{
33+
$this->headers = array_values($headers);
34+
35+
return $this;
36+
}
37+
38+
public function setRows(array $rows)
39+
{
40+
$this->rows = array();
41+
42+
return $this->addRows($rows);
43+
}
44+
45+
public function addRows(array $rows)
46+
{
47+
foreach ($rows as $row) {
48+
$this->addRow($row);
49+
}
50+
51+
return $this;
52+
}
53+
54+
public function addRow(array $row)
55+
{
56+
$this->rows[] = array_values($row);
57+
58+
return $this;
59+
}
60+
61+
public function render()
62+
{
63+
$this->renderRow($this->headers);
64+
$this->renderHorizontalLine();
65+
66+
foreach ($this->rows as $row) {
67+
$this->renderRow($row);
68+
}
69+
}
70+
71+
private function renderRow(array $row)
72+
{
73+
$this->output->writeln(sprintf('| %s |', implode(' | ', $this->prepareRow($row))));
74+
}
75+
76+
private function prepareRow(array $row)
77+
{
78+
$line = array();
79+
80+
foreach ($row as $column => $cell) {
81+
$line[] = $this->prepareCell($row, $column);
82+
}
83+
84+
return $line;
85+
}
86+
87+
private function renderHorizontalLine()
88+
{
89+
$line = array();
90+
91+
foreach ($this->headers as $column => $cell) {
92+
$line[] = str_repeat('-', $this->getColumnWidth($column) + 2);
93+
}
94+
95+
$this->output->writeln(sprintf('|%s|', implode('|', $line)));
96+
}
97+
98+
private function prepareCell(array $row, $column)
99+
{
100+
return str_pad($row[$column], $this->getColumnWidth($column));
101+
}
102+
103+
private function getColumnWidth($column)
104+
{
105+
if (isset($this->columnWidths[$column])) {
106+
return $this->columnWidths[$column];
107+
}
108+
109+
foreach (array_merge($this->headers, $this->rows) as $row) {
110+
if ($row instanceof TableSeparator) {
111+
continue;
112+
}
113+
114+
$lengths[] = strlen($row[$column]);
115+
}
116+
117+
return $this->columnWidths[$column] = max($lengths);
118+
}
119+
}

0 commit comments

Comments
 (0)