├── .github └── PULL_REQUEST_TEMPLATE.md ├── .gitignore ├── LICENSE ├── README.md ├── composer.json ├── composer.lock └── src ├── DiffPlugin.php └── IO.php /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Please read the CLA carefully before submitting your contribution to Mercari. 2 | Under any circumstances, by submitting your contribution, you are deemed to accept and agree to be bound by the terms and conditions of the CLA. 3 | 4 | https://www.mercari.com/cla/ 5 | 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor/ 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Mercari, Inc 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | composer-diff-plugin 2 | ================== 3 | 4 | composer plugin to show library version diff at "composer update". 5 | 6 | ![composer-diff-plugin](https://cloud.githubusercontent.com/assets/835251/11893915/46c9bb40-a5b7-11e5-8340-db3917d04221.png) 7 | 8 | feature 9 | ----------------- 10 | 11 | * print upgrade/downgrade/add/remove of libraries. 12 | * output `composer.list` file 13 | 14 | 15 | Install 16 | ----------------- 17 | 18 | ```bash 19 | $ composer global require mercari/composer-diff-plugin 20 | ``` 21 | 22 | Uninstall 23 | ---------------- 24 | 25 | ```bash 26 | $ composer global remove mercari/composer-diff-plugin 27 | ``` 28 | 29 | License 30 | --------------- 31 | 32 | Copyright 2016 Mercari, Inc. 33 | 34 | Licensed under the MIT License 35 | 36 | Contribution 37 | -------------- 38 | 39 | Fork the project, create a feature branch, and send us a pull request. 40 | 41 | Please read the CLA carefully before submitting your contribution to Mercari. 42 | 43 | https://www.mercari.com/cla/ 44 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mercari/composer-diff-plugin", 3 | "description": "composer plugin to show library version diff at \"composer update\"", 4 | "type": "composer-plugin", 5 | "license": "MIT", 6 | "authors": [ 7 | { 8 | "name": "Hiraku NAKANO", 9 | "email": "hiraku@mercari.com" 10 | } 11 | ], 12 | "require": { 13 | "composer-plugin-api": ">=1.0.0-alpha10" 14 | }, 15 | "require-dev": { 16 | "composer/composer": "1.0.0-alpha10" 17 | }, 18 | "autoload": { 19 | "psr-4": { 20 | "Mercari\\ComposerDiffPlugin\\": "src/" 21 | } 22 | }, 23 | "extra": { 24 | "class": "Mercari\\ComposerDiffPlugin\\DiffPlugin" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /composer.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_readme": [ 3 | "This file locks the dependencies of your project to a known state", 4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", 5 | "This file is @generated automatically" 6 | ], 7 | "hash": "dc34ca1049e690532f851699385bf351", 8 | "content-hash": "6255cbcd06460c5cfa83ef5776a612b1", 9 | "packages": [], 10 | "packages-dev": [ 11 | { 12 | "name": "composer/composer", 13 | "version": "1.0.0-alpha10", 14 | "source": { 15 | "type": "git", 16 | "url": "https://github.com/composer/composer.git", 17 | "reference": "775f6cd5c633facf2e7b99611fdcaa900b58ddb7" 18 | }, 19 | "dist": { 20 | "type": "zip", 21 | "url": "https://api.github.com/repos/composer/composer/zipball/775f6cd5c633facf2e7b99611fdcaa900b58ddb7", 22 | "reference": "775f6cd5c633facf2e7b99611fdcaa900b58ddb7", 23 | "shasum": "" 24 | }, 25 | "require": { 26 | "justinrainbow/json-schema": "~1.4", 27 | "php": ">=5.3.2", 28 | "seld/jsonlint": "~1.0", 29 | "symfony/console": "~2.5", 30 | "symfony/finder": "~2.2", 31 | "symfony/process": "~2.1" 32 | }, 33 | "require-dev": { 34 | "phpunit/phpunit": "~4.5" 35 | }, 36 | "suggest": { 37 | "ext-openssl": "Enabling the openssl extension allows you to access https URLs for repositories and packages", 38 | "ext-zip": "Enabling the zip extension allows you to unzip archives, and allows gzip compression of all internet traffic" 39 | }, 40 | "bin": [ 41 | "bin/composer" 42 | ], 43 | "type": "library", 44 | "extra": { 45 | "branch-alias": { 46 | "dev-master": "1.0-dev" 47 | } 48 | }, 49 | "autoload": { 50 | "psr-0": { 51 | "Composer": "src/" 52 | } 53 | }, 54 | "notification-url": "https://packagist.org/downloads/", 55 | "license": [ 56 | "MIT" 57 | ], 58 | "authors": [ 59 | { 60 | "name": "Nils Adermann", 61 | "email": "naderman@naderman.de", 62 | "homepage": "http://www.naderman.de" 63 | }, 64 | { 65 | "name": "Jordi Boggiano", 66 | "email": "j.boggiano@seld.be", 67 | "homepage": "http://seld.be" 68 | } 69 | ], 70 | "description": "Composer helps you declare, manage and install dependencies of PHP projects, ensuring you have the right stack everywhere.", 71 | "homepage": "http://getcomposer.org/", 72 | "keywords": [ 73 | "autoload", 74 | "dependency", 75 | "package" 76 | ], 77 | "time": "2015-04-14 21:18:51" 78 | }, 79 | { 80 | "name": "justinrainbow/json-schema", 81 | "version": "1.6.1", 82 | "source": { 83 | "type": "git", 84 | "url": "https://github.com/justinrainbow/json-schema.git", 85 | "reference": "cc84765fb7317f6b07bd8ac78364747f95b86341" 86 | }, 87 | "dist": { 88 | "type": "zip", 89 | "url": "https://api.github.com/repos/justinrainbow/json-schema/zipball/cc84765fb7317f6b07bd8ac78364747f95b86341", 90 | "reference": "cc84765fb7317f6b07bd8ac78364747f95b86341", 91 | "shasum": "" 92 | }, 93 | "require": { 94 | "php": ">=5.3.29" 95 | }, 96 | "require-dev": { 97 | "json-schema/json-schema-test-suite": "1.1.0", 98 | "phpdocumentor/phpdocumentor": "~2", 99 | "phpunit/phpunit": "~3.7" 100 | }, 101 | "bin": [ 102 | "bin/validate-json" 103 | ], 104 | "type": "library", 105 | "extra": { 106 | "branch-alias": { 107 | "dev-master": "1.6.x-dev" 108 | } 109 | }, 110 | "autoload": { 111 | "psr-4": { 112 | "JsonSchema\\": "src/JsonSchema/" 113 | } 114 | }, 115 | "notification-url": "https://packagist.org/downloads/", 116 | "license": [ 117 | "BSD-3-Clause" 118 | ], 119 | "authors": [ 120 | { 121 | "name": "Bruno Prieto Reis", 122 | "email": "bruno.p.reis@gmail.com" 123 | }, 124 | { 125 | "name": "Justin Rainbow", 126 | "email": "justin.rainbow@gmail.com" 127 | }, 128 | { 129 | "name": "Igor Wiedler", 130 | "email": "igor@wiedler.ch" 131 | }, 132 | { 133 | "name": "Robert Schönthal", 134 | "email": "seroscho@googlemail.com" 135 | } 136 | ], 137 | "description": "A library to validate a json schema.", 138 | "homepage": "https://github.com/justinrainbow/json-schema", 139 | "keywords": [ 140 | "json", 141 | "schema" 142 | ], 143 | "time": "2016-01-25 15:43:01" 144 | }, 145 | { 146 | "name": "seld/jsonlint", 147 | "version": "1.4.0", 148 | "source": { 149 | "type": "git", 150 | "url": "https://github.com/Seldaek/jsonlint.git", 151 | "reference": "66834d3e3566bb5798db7294619388786ae99394" 152 | }, 153 | "dist": { 154 | "type": "zip", 155 | "url": "https://api.github.com/repos/Seldaek/jsonlint/zipball/66834d3e3566bb5798db7294619388786ae99394", 156 | "reference": "66834d3e3566bb5798db7294619388786ae99394", 157 | "shasum": "" 158 | }, 159 | "require": { 160 | "php": "^5.3 || ^7.0" 161 | }, 162 | "bin": [ 163 | "bin/jsonlint" 164 | ], 165 | "type": "library", 166 | "autoload": { 167 | "psr-4": { 168 | "Seld\\JsonLint\\": "src/Seld/JsonLint/" 169 | } 170 | }, 171 | "notification-url": "https://packagist.org/downloads/", 172 | "license": [ 173 | "MIT" 174 | ], 175 | "authors": [ 176 | { 177 | "name": "Jordi Boggiano", 178 | "email": "j.boggiano@seld.be", 179 | "homepage": "http://seld.be" 180 | } 181 | ], 182 | "description": "JSON Linter", 183 | "keywords": [ 184 | "json", 185 | "linter", 186 | "parser", 187 | "validator" 188 | ], 189 | "time": "2015-11-21 02:21:41" 190 | }, 191 | { 192 | "name": "symfony/console", 193 | "version": "v2.8.2", 194 | "source": { 195 | "type": "git", 196 | "url": "https://github.com/symfony/console.git", 197 | "reference": "d0239fb42f98dd02e7d342f793c5d2cdee0c478d" 198 | }, 199 | "dist": { 200 | "type": "zip", 201 | "url": "https://api.github.com/repos/symfony/console/zipball/d0239fb42f98dd02e7d342f793c5d2cdee0c478d", 202 | "reference": "d0239fb42f98dd02e7d342f793c5d2cdee0c478d", 203 | "shasum": "" 204 | }, 205 | "require": { 206 | "php": ">=5.3.9", 207 | "symfony/polyfill-mbstring": "~1.0" 208 | }, 209 | "require-dev": { 210 | "psr/log": "~1.0", 211 | "symfony/event-dispatcher": "~2.1|~3.0.0", 212 | "symfony/process": "~2.1|~3.0.0" 213 | }, 214 | "suggest": { 215 | "psr/log": "For using the console logger", 216 | "symfony/event-dispatcher": "", 217 | "symfony/process": "" 218 | }, 219 | "type": "library", 220 | "extra": { 221 | "branch-alias": { 222 | "dev-master": "2.8-dev" 223 | } 224 | }, 225 | "autoload": { 226 | "psr-4": { 227 | "Symfony\\Component\\Console\\": "" 228 | }, 229 | "exclude-from-classmap": [ 230 | "/Tests/" 231 | ] 232 | }, 233 | "notification-url": "https://packagist.org/downloads/", 234 | "license": [ 235 | "MIT" 236 | ], 237 | "authors": [ 238 | { 239 | "name": "Fabien Potencier", 240 | "email": "fabien@symfony.com" 241 | }, 242 | { 243 | "name": "Symfony Community", 244 | "homepage": "https://symfony.com/contributors" 245 | } 246 | ], 247 | "description": "Symfony Console Component", 248 | "homepage": "https://symfony.com", 249 | "time": "2016-01-14 08:33:16" 250 | }, 251 | { 252 | "name": "symfony/finder", 253 | "version": "v2.8.2", 254 | "source": { 255 | "type": "git", 256 | "url": "https://github.com/symfony/finder.git", 257 | "reference": "c90fabdd97e431ee19b6383999cf35334dff27da" 258 | }, 259 | "dist": { 260 | "type": "zip", 261 | "url": "https://api.github.com/repos/symfony/finder/zipball/c90fabdd97e431ee19b6383999cf35334dff27da", 262 | "reference": "c90fabdd97e431ee19b6383999cf35334dff27da", 263 | "shasum": "" 264 | }, 265 | "require": { 266 | "php": ">=5.3.9" 267 | }, 268 | "type": "library", 269 | "extra": { 270 | "branch-alias": { 271 | "dev-master": "2.8-dev" 272 | } 273 | }, 274 | "autoload": { 275 | "psr-4": { 276 | "Symfony\\Component\\Finder\\": "" 277 | }, 278 | "exclude-from-classmap": [ 279 | "/Tests/" 280 | ] 281 | }, 282 | "notification-url": "https://packagist.org/downloads/", 283 | "license": [ 284 | "MIT" 285 | ], 286 | "authors": [ 287 | { 288 | "name": "Fabien Potencier", 289 | "email": "fabien@symfony.com" 290 | }, 291 | { 292 | "name": "Symfony Community", 293 | "homepage": "https://symfony.com/contributors" 294 | } 295 | ], 296 | "description": "Symfony Finder Component", 297 | "homepage": "https://symfony.com", 298 | "time": "2016-01-14 08:26:52" 299 | }, 300 | { 301 | "name": "symfony/polyfill-mbstring", 302 | "version": "v1.1.0", 303 | "source": { 304 | "type": "git", 305 | "url": "https://github.com/symfony/polyfill-mbstring.git", 306 | "reference": "1289d16209491b584839022f29257ad859b8532d" 307 | }, 308 | "dist": { 309 | "type": "zip", 310 | "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/1289d16209491b584839022f29257ad859b8532d", 311 | "reference": "1289d16209491b584839022f29257ad859b8532d", 312 | "shasum": "" 313 | }, 314 | "require": { 315 | "php": ">=5.3.3" 316 | }, 317 | "suggest": { 318 | "ext-mbstring": "For best performance" 319 | }, 320 | "type": "library", 321 | "extra": { 322 | "branch-alias": { 323 | "dev-master": "1.1-dev" 324 | } 325 | }, 326 | "autoload": { 327 | "psr-4": { 328 | "Symfony\\Polyfill\\Mbstring\\": "" 329 | }, 330 | "files": [ 331 | "bootstrap.php" 332 | ] 333 | }, 334 | "notification-url": "https://packagist.org/downloads/", 335 | "license": [ 336 | "MIT" 337 | ], 338 | "authors": [ 339 | { 340 | "name": "Nicolas Grekas", 341 | "email": "p@tchwork.com" 342 | }, 343 | { 344 | "name": "Symfony Community", 345 | "homepage": "https://symfony.com/contributors" 346 | } 347 | ], 348 | "description": "Symfony polyfill for the Mbstring extension", 349 | "homepage": "https://symfony.com", 350 | "keywords": [ 351 | "compatibility", 352 | "mbstring", 353 | "polyfill", 354 | "portable", 355 | "shim" 356 | ], 357 | "time": "2016-01-20 09:13:37" 358 | }, 359 | { 360 | "name": "symfony/process", 361 | "version": "v2.8.2", 362 | "source": { 363 | "type": "git", 364 | "url": "https://github.com/symfony/process.git", 365 | "reference": "6f1979c3b0f4c22c77a8a8971afaa7dd07f082ac" 366 | }, 367 | "dist": { 368 | "type": "zip", 369 | "url": "https://api.github.com/repos/symfony/process/zipball/6f1979c3b0f4c22c77a8a8971afaa7dd07f082ac", 370 | "reference": "6f1979c3b0f4c22c77a8a8971afaa7dd07f082ac", 371 | "shasum": "" 372 | }, 373 | "require": { 374 | "php": ">=5.3.9" 375 | }, 376 | "type": "library", 377 | "extra": { 378 | "branch-alias": { 379 | "dev-master": "2.8-dev" 380 | } 381 | }, 382 | "autoload": { 383 | "psr-4": { 384 | "Symfony\\Component\\Process\\": "" 385 | }, 386 | "exclude-from-classmap": [ 387 | "/Tests/" 388 | ] 389 | }, 390 | "notification-url": "https://packagist.org/downloads/", 391 | "license": [ 392 | "MIT" 393 | ], 394 | "authors": [ 395 | { 396 | "name": "Fabien Potencier", 397 | "email": "fabien@symfony.com" 398 | }, 399 | { 400 | "name": "Symfony Community", 401 | "homepage": "https://symfony.com/contributors" 402 | } 403 | ], 404 | "description": "Symfony Process Component", 405 | "homepage": "https://symfony.com", 406 | "time": "2016-01-06 09:59:23" 407 | } 408 | ], 409 | "aliases": [], 410 | "minimum-stability": "stable", 411 | "stability-flags": { 412 | "composer-plugin-api": 15, 413 | "composer/composer": 15 414 | }, 415 | "prefer-stable": false, 416 | "prefer-lowest": false, 417 | "platform": [], 418 | "platform-dev": [] 419 | } 420 | -------------------------------------------------------------------------------- /src/DiffPlugin.php: -------------------------------------------------------------------------------- 1 | composer = $composer; 33 | $this->io = $io; 34 | } 35 | 36 | public static function getSubscribedEvents() 37 | { 38 | return array( 39 | ScriptEvents::PRE_UPDATE_CMD => array( 40 | 'onPreUpdate', 41 | ), 42 | ScriptEvents::POST_UPDATE_CMD => array( 43 | 'onPostUpdate', 44 | ), 45 | ); 46 | } 47 | 48 | public function onPreUpdate(Event $arg) 49 | { 50 | $this->before = $this->licenses(); 51 | } 52 | 53 | public function onPostUpdate(Event $arg) 54 | { 55 | $this->after = $this->licenses(); 56 | $io = $this->io; 57 | 58 | $before = $this->before; 59 | $after = $this->after; 60 | 61 | $io->write("\n" . '[[[ library version information ]]]'); 62 | if ($before == $after) { 63 | $io->write('no change!'); 64 | return; 65 | } 66 | $output = IO::getSymfonyOutput($io); 67 | 68 | $table = new Table($output); 69 | $table->setStyle('compact'); 70 | $table->getStyle()->setVerticalBorderChar(''); 71 | $table->getStyle()->setCellRowContentFormat('%s '); 72 | 73 | // deleted packages 74 | $minus = array_diff_key($before, $after); 75 | if ($minus) { 76 | foreach ($minus as $name => $val) { 77 | $table->addRow(array( 78 | '[-]', 79 | $name, 80 | $val['version'], 81 | implode(', ', $val['license']) ?: 'none', 82 | )); 83 | 84 | unset($before[$name]); 85 | } 86 | } 87 | 88 | // added packages 89 | $plus = array_diff_key($after, $before); 90 | if ($plus) { 91 | foreach ($plus as $name => $val) { 92 | $table->addRow(array( 93 | '[+]', 94 | $name, 95 | $val['version'], 96 | implode(', ', $val['license']) ?: 'none', 97 | )); 98 | 99 | unset($after[$name]); 100 | } 101 | } 102 | 103 | // changed packages 104 | $diff = array(); 105 | foreach ($before as $name => $val) { 106 | if ($val == $after[$name]) { 107 | continue; //no change 108 | } 109 | $diff[$name] = array($val, $after[$name]); 110 | } 111 | if ($diff) { 112 | foreach ($diff as $name => $val) { 113 | $a = $val[0]['version']; 114 | $b = $val[1]['version']; 115 | $vd = version_compare($a, $b); 116 | switch ($vd) { 117 | case 1: 118 | $change = '[d]'; 119 | break; 120 | case -1: 121 | $change = '[u]'; 122 | break; 123 | case 0: 124 | $change = '[?]'; 125 | break; 126 | } 127 | $table->addRow(array($change, $name, "$a=>$b", '')); 128 | } 129 | } 130 | $table->render(); 131 | 132 | $this->writeLibraryList($this->after); 133 | } 134 | 135 | /** 136 | * to array composer licenses 137 | */ 138 | protected function licenses() 139 | { 140 | $composer = $this->composer; 141 | 142 | $root = $composer->getPackage(); 143 | $repo = $composer->getRepositoryManager()->getLocalRepository(); 144 | 145 | $packages = array(); 146 | foreach ($repo->getPackages() as $p) { 147 | $packages[$p->getName()] = $p; 148 | } 149 | 150 | ksort($packages); 151 | 152 | $libs = array(); 153 | foreach ($packages as $p) { 154 | $libs[$p->getPrettyName()] = array( 155 | 'version' => $p->getFullPrettyVersion(), 156 | 'license' => $p->getLicense(), 157 | ); 158 | } 159 | 160 | return $libs; 161 | } 162 | 163 | /** 164 | * write composer.list 165 | */ 166 | protected function writeLibraryList($packages) 167 | { 168 | $fp = fopen('composer.list', 'wb'); 169 | 170 | $output = new \Symfony\Component\Console\Output\StreamOutput($fp); 171 | $table = new Table($output); 172 | $table->setStyle('compact'); 173 | $table->getStyle()->setVerticalBorderChar(''); 174 | $table->getStyle()->setCellRowContentFormat('%s '); 175 | $table->setHeaders(array('Name', 'Version', 'License')); 176 | foreach ($packages as $name => $p) { 177 | $table->addRow(array( 178 | $name, 179 | $p['version'], 180 | implode(', ', $p['license']) ?: 'none', 181 | )); 182 | } 183 | $table->render(); 184 | 185 | unset($table); 186 | fclose($fp); 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /src/IO.php: -------------------------------------------------------------------------------- 1 | input; 13 | } 14 | 15 | public static function getSymfonyOutput(\Composer\IO\ConsoleIO $io) 16 | { 17 | return $io->output; 18 | } 19 | } 20 | --------------------------------------------------------------------------------