├── .gitignore ├── LICENSE ├── README.md ├── compify.phar ├── composer.json └── src └── Compify ├── Command └── CrushCommand.php ├── CommandProvider.php ├── Compify.php ├── CompifyPlugin.php └── Console └── Application.php /.gitignore: -------------------------------------------------------------------------------- 1 | vendor 2 | composer.phar 3 | composer.lock 4 | .idea 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010-2016 Carlos Buenosvinos 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is furnished 8 | to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Compify, save Composer disk and bandwith 2 | ======================================== 3 | 4 | Compify is a tool to save disk usage and bandwith in your composer vendor folder. 5 | 6 | Maybe you haven't noticed, but if you take a look to your vendor folder after doing 7 | a ```php composer.phar install``` there are so much useless information like tests, 8 | ```.travis.yml``` like files that you just don't need in production. 9 | 10 | Consider also packages installed from source (using ```git clone``` or ```svn checkout```, not 11 | downloading a zip or methods base), they include folders like ```.git``` or ```.svn``` and, believe me, 12 | you don't want those folders in production. 13 | 14 | In most of the cases, you will be deploying to production and storing different versions 15 | of your application, installing dependencies directly from your servers or rsyncing 16 | from a deployer machine. So, using compify you can save bandwith or time when deploying. 17 | 18 | Installation 19 | ------------ 20 | 21 | ### Locally 22 | 23 | Download the 24 | [`compify.phar`](https://raw.github.com/carlosbuenosvinos/compify/master/compify.phar) file and 25 | store it somewhere on your computer. 26 | 27 | ### Globally 28 | 29 | You can run these commands to easily acces `compify` from anywhere on your system: 30 | 31 | $ sudo https://github.com/carlosbuenosvinos/compify/raw/master/compify.phar -O /usr/local/bin/compify 32 | 33 | then: 34 | 35 | $ sudo chmod a+x /usr/local/bin/compify 36 | 37 | and: 38 | 39 | $ sudo vi /etc/php/php.ini 40 | # search for "basedir" 41 | # add ":/usr/local/bin" at the end and save 42 | 43 | Then, just run `compify` in order to run compify 44 | 45 | Update 46 | ------ 47 | 48 | ### Locally 49 | 50 | The `self-update` command tries to update compify itself: 51 | 52 | $ php compify.phar self-update 53 | 54 | ### Globally 55 | 56 | You can update compify through this command: 57 | 58 | $ sudo compify self-update 59 | 60 | 61 | ## Usage 62 | 63 | php compify.phar crush --help 64 | Usage: 65 | crush [vendor-path] 66 | 67 | Arguments: 68 | vendor-path Composer vendor path (default: "./vendor") 69 | 70 | Help: 71 | The crush command removes all the 72 | unnecessary files for each composer 73 | package in order to save disk usage 74 | and bandwidth. 75 | 76 | ## How does it work 77 | 78 | Compify goes through every local composer dependency installed in your machine 79 | and removes typical unnecessary files and folders (what we call generic rules) 80 | for that package. Also, we have identified specific rules for specific packages 81 | that will also merge with the generic rules. 82 | 83 | public static $rules = array( 84 | 'generic-rules' => array( 85 | '.git', 86 | '.svn', 87 | 'test', 88 | 'tests', 89 | 'docs', 90 | 'doc', 91 | '.gitattributes', 92 | '.gitmodules', 93 | '.gitignore', 94 | '.travis.yml', 95 | 'CHANGELOG*', 96 | 'README*', 97 | 'phpunit.xml.*', 98 | 'LICENSE*' 99 | ), 100 | 'packages-rules' => array( 101 | 'twig/twig' => array( 102 | '.editorconfig', 103 | 'AUTHORS', 104 | 'ext' 105 | ) 106 | ) 107 | ); 108 | 109 | You can contribute adding new package specific rules or any code update obsviously. 110 | 111 | ## Example 112 | 113 | Let's assummed a small project with following composer.json 114 | 115 | ... 116 | "require": { 117 | "twig/twig": ">=1.8,<2.0-dev", 118 | "symfony/twig-bridge": "2.1.*", 119 | "swiftmailer/swiftmailer": "4.3.x-dev", 120 | "symfony/console": "2.1.*", 121 | "doctrine/dbal": "2.3.*", 122 | "silex/silex": "1.0.*", 123 | "guzzle/guzzle": "3.0.*" 124 | }, 125 | ... 126 | 127 | $ php compify.phar crush 128 | Crushing vendors (by Carlos Buenosvinos) 129 | Vendor size before crushing: 73M 130 | Vendor size after crushing: 18M 131 | 132 | Do you need more arguments? 133 | 134 | ## More Information 135 | 136 | For any issue use PR github system, for other info, send me an email to carlos.buenosvinos@gmail.com 137 | 138 | ## License 139 | 140 | Compify is licensed under the MIT license. 141 | -------------------------------------------------------------------------------- /compify.phar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carlosbuenosvinos/compify/37c0960f20da452e87096535b13f894602dcdb6b/compify.phar -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "carlosbuenosvinos/compify", 3 | "type": "composer-plugin", 4 | "description": "Including the tests and other useless information like .travis.yml in distributed packages is not a good idea. \"Compify\" tries to remove everything, in order to save bandwidth and disk usage, but the necessary code for the package to work in your app or library.", 5 | "keywords": [ 6 | "composer", "bandwidth" 7 | ], 8 | "homepage": "https://github.com/carlosbuenosvinos/compify", 9 | "license": "MIT", 10 | "require": { 11 | "composer-plugin-api": "^1.0", 12 | "symfony/process": ">=2.1,^3.0", 13 | "symfony/finder": ">=2.1,^3.0", 14 | "symfony/console": ">=2.1,^3.0" 15 | }, 16 | "authors": [ 17 | { 18 | "name": "Carlos Buenosvinos", 19 | "email": "carlos.buenosvinos@gmail.com" 20 | } 21 | ], 22 | "autoload": { 23 | "psr-4": { 24 | "CarlosBuenosvinos\\": "src/" 25 | } 26 | }, 27 | "extra": { 28 | "class": "CarlosBuenosvinos\\Compify\\CompifyPlugin" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Compify/Command/CrushCommand.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace CarlosBuenosvinos\Compify\Command; 13 | 14 | use Composer\Command\BaseCommand; 15 | use Symfony\Component\Console\Input\InputInterface; 16 | use Symfony\Component\Console\Output\OutputInterface; 17 | use Symfony\Component\Console\Input\InputArgument; 18 | use Symfony\Component\Process\Process; 19 | use Composer\Composer; 20 | use Composer\Package\AliasPackage; 21 | 22 | /** 23 | * @author Carlos Buenosvinos 24 | */ 25 | class CrushCommand extends BaseCommand 26 | { 27 | protected function configure() 28 | { 29 | $this 30 | ->setName('crush') 31 | ->setDescription('Removes unnecesary files for each composer dependency') 32 | ->setDefinition(array( 33 | new InputArgument('vendor-path', InputArgument::OPTIONAL, 'Composer vendor path', './vendor') 34 | )) 35 | ->setHelp(<<crush command removes all the 37 | unnecessary files for each composer 38 | package in order to save disk usage 39 | and bandwidth. 40 | EOT 41 | ) 42 | ; 43 | } 44 | 45 | protected function execute(InputInterface $input, OutputInterface $output) 46 | { 47 | set_time_limit(0); 48 | 49 | $verbose = $input->getOption('verbose'); 50 | $path = $input->getArgument('vendor-path'); 51 | 52 | $composer = $this->getApplication()->getComposer(); 53 | $output->writeln('Crushing vendors (by Carlos Buenosvinos)'); 54 | $originalSize = $this->calculateVendorSize($path); 55 | $this->crushPackages($composer, $path, $output, $verbose); 56 | $finalSize = $this->calculateVendorSize($path); 57 | $output->writeln('Vendor size before crushing: ' . $originalSize . ''); 58 | $output->writeln('Vendor size after crushing: ' . $finalSize . ''); 59 | } 60 | 61 | private function crushPackages($composer, $path, OutputInterface $output, $verbose = false) 62 | { 63 | $config = \CarlosBuenosvinos\Compify\Compify::$rules; 64 | $packageRules = $config['packages-rules']; 65 | $genericRules = $config['generic-rules']; 66 | 67 | $packages = $this->selectPackages($composer, $output, $verbose); 68 | foreach ($packages as $package) { 69 | $prettyName = $package->getPrettyName(); 70 | if ($verbose) { 71 | $output->writeln('Processing package ' . $prettyName . ''); 72 | } 73 | 74 | $rules = $genericRules; 75 | if (isset($packageRules[$prettyName])) { 76 | $rules = array_merge($rules, $packageRules[$prettyName]); 77 | } 78 | 79 | foreach ($rules as $rule) { 80 | $cmd = 'rm -rf ' . $path . '/' . $prettyName . '/' . $rule; 81 | if ($verbose) { 82 | $output->writeln($cmd); 83 | } 84 | $process = new Process($cmd); 85 | $process->setTimeout(600); 86 | $process->run(); 87 | if (!$process->isSuccessful()) { 88 | throw new \RuntimeException($process->getErrorOutput()); 89 | } 90 | } 91 | } 92 | 93 | // Cleaning .git and .svn 94 | $cleanCommands = array(); 95 | $cleanCommands[] = 'rm `find "' . $path . '" -name \'.git\'` -rf'; 96 | $cleanCommands[] = 'rm `find "' . $path . '" -name \'.svn\'` -rf'; 97 | foreach ($cleanCommands as $cmd) { 98 | if ($verbose) { 99 | $output->writeln('' . $cmd . ''); 100 | } 101 | $process = new Process($cmd); 102 | $process->setTimeout(600); 103 | $process->run(); 104 | if (!$process->isSuccessful()) { 105 | throw new \RuntimeException($process->getErrorOutput()); 106 | } 107 | } 108 | } 109 | 110 | private function calculateVendorSize($path) 111 | { 112 | $cmd = 'du -sh ' . $path; 113 | $process = new Process($cmd); 114 | $process->run(); 115 | if (!$process->isSuccessful()) { 116 | throw new \RuntimeException($process->getErrorOutput()); 117 | } 118 | 119 | $output = explode("\t", $process->getOutput()); 120 | return trim($output[0]); 121 | } 122 | 123 | 124 | private function selectPackages(Composer $composer) 125 | { 126 | $selected = array(); 127 | foreach ($composer->getRepositoryManager()->getLocalRepositories() as $repository) { 128 | foreach ($repository->getPackages() as $package) { 129 | // skip aliases 130 | if ($package instanceof AliasPackage) { 131 | continue; 132 | } 133 | 134 | $selected[] = $package; 135 | } 136 | } 137 | return $selected; 138 | } 139 | } -------------------------------------------------------------------------------- /src/Compify/CommandProvider.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace CarlosBuenosvinos\Compify; 13 | 14 | /** 15 | * @author Carlos Buenosvinos 16 | */ 17 | class Compify 18 | { 19 | const VERSION = '1.0.0-dev'; 20 | 21 | public static $rules = array( 22 | 'generic-rules' => array( 23 | '.git', 24 | '.svn', 25 | 'test', 26 | 'tests', 27 | 'docs', 28 | 'doc', 29 | '.editorconfig', 30 | '.gitattributes', 31 | '.gitmodules', 32 | '.gitignore', 33 | '.travis.yml', 34 | 'CHANGELOG*', 35 | 'CREDITS*', 36 | 'README*', 37 | 'readme.md', 38 | 'phpunit.xml.*', 39 | 'LICENSE*', 40 | 'CONTRIBUT*', 41 | 'UPGRADE*', 42 | 'TODO', 43 | 'VERSION' 44 | ), 45 | 'packages-rules' => array( 46 | 'symfony/assetic-bundle' => array( 47 | 'src/Symfony/Bundle/AsseticBundle/Tests', 48 | ), 49 | 'symfony/monolog-bundle' => array( 50 | 'src/Symfony/Bundle/MonologBundle/Tests', 51 | ), 52 | 'symfony/swiftmailer-bundle' => array( 53 | 'src/Symfony/Bundle/SwiftmailerBundle/Tests', 54 | ), 55 | 'symfony/symfony' => array( 56 | 'src/Symfony/Bridge/Doctrine/Tests', 57 | 'src/Symfony/Bridge/Monolog/Tests', 58 | 'src/Symfony/Bridge/Propel1/Tests', 59 | 'src/Symfony/Bridge/Swiftmailer/Tests', 60 | 'src/Symfony/Bridge/Twig/Tests', 61 | 62 | 'src/Symfony/Bundle/FrameworkBundle/Tests', 63 | 'src/Symfony/Bundle/SecurityBundle/Tests', 64 | 'src/Symfony/Bundle/TwigBundle/Tests', 65 | 'src/Symfony/Bundle/WebProfilerBundle/Tests', 66 | 67 | 'src/Symfony/Component/BrowserKit/Tests', 68 | 'src/Symfony/Component/ClassLoader/Tests', 69 | 'src/Symfony/Component/Config/Tests', 70 | 'src/Symfony/Component/Console/Tests', 71 | 'src/Symfony/Component/CssSelector/Tests', 72 | 'src/Symfony/Component/DependencyInjection/Tests', 73 | 'src/Symfony/Component/DomCrawler/Tests', 74 | 'src/Symfony/Component/EventDispatcher/Tests', 75 | 'src/Symfony/Component/Filesystem/Tests', 76 | 'src/Symfony/Component/Finder/Tests', 77 | 'src/Symfony/Component/Form/Tests', 78 | 'src/Symfony/Component/HttpFoundation/Tests', 79 | 'src/Symfony/Component/HttpKernel/Tests', 80 | 'src/Symfony/Component/Locale/Tests', 81 | 'src/Symfony/Component/OptionsResolver/Tests', 82 | 'src/Symfony/Component/Process/Tests', 83 | 'src/Symfony/Component/Routing/Tests', 84 | 'src/Symfony/Component/Security/Tests', 85 | 'src/Symfony/Component/Serializer/Tests', 86 | 'src/Symfony/Component/Templating/Tests', 87 | 'src/Symfony/Component/Translation/Tests', 88 | 'src/Symfony/Component/Validator/Tests', 89 | 'src/Symfony/Component/Yaml/Tests' 90 | ), 91 | 'mockery/mockery' => array( 92 | 'examples', 93 | 'package.xml' 94 | ), 95 | 'predis/predis' => array( 96 | 'bin', 97 | 'examples', 98 | 'package.ini', 99 | 'FAQ*' 100 | ), 101 | 'silex/silex' => array( 102 | 'bin' 103 | ), 104 | 'swiftmailer/swiftmailer' => array( 105 | 'notes', 106 | 'test-suite', 107 | 'build.xml', 108 | 'CHANGES', 109 | 'create_pear_package.php', 110 | 'package.xml.tpl' 111 | ), 112 | 'twig/twig' => array( 113 | 'bin', 114 | 'ext', 115 | '.editorconfig', 116 | 'AUTHORS', 117 | 'package.xml.tpl' 118 | ), 119 | 'videlalvaro/php-amqplib' => array( 120 | 'benchmark', 121 | 'bin', 122 | 'demo' 123 | ) 124 | ) 125 | ); 126 | } 127 | -------------------------------------------------------------------------------- /src/Compify/CompifyPlugin.php: -------------------------------------------------------------------------------- 1 | composer = $composer; 18 | $this->io = $io; 19 | // $installer = new TemplateInstaller($io, $composer); 20 | // $composer->getInstallationManager()->addInstaller($installer); 21 | } 22 | 23 | /** 24 | * Method by which a Plugin announces its API implementations, through an array 25 | * with a special structure. 26 | * 27 | * The key must be a string, representing a fully qualified class/interface name 28 | * which Composer Plugin API exposes. 29 | * The value must be a string as well, representing the fully qualified class name 30 | * of the implementing class. 31 | * 32 | * @tutorial 33 | * 34 | * return array( 35 | * 'Composer\Plugin\Capability\CommandProvider' => 'My\CommandProvider', 36 | * 'Composer\Plugin\Capability\Validator' => 'My\Validator', 37 | * ); 38 | * 39 | * @return string[] 40 | */ 41 | public function getCapabilities() 42 | { 43 | return array( 44 | 'Composer\Plugin\Capability\CommandProvider' => 'CarlosBuenosvinos\Compify\CommandProvider', 45 | // 'Composer\Plugin\Capability\Validator' => 'My\Validator', 46 | ); 47 | } 48 | } -------------------------------------------------------------------------------- /src/Compify/Console/Application.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace CarlosBuenosvinos\Compify\Console; 13 | 14 | use Symfony\Component\Console\Application as BaseApplication; 15 | use Symfony\Component\Console\Input\InputInterface; 16 | use Symfony\Component\Console\Output\OutputInterface; 17 | use Composer\IO\ConsoleIO; 18 | use Composer\Factory; 19 | use Composer\Util\ErrorHandler; 20 | 21 | use CarlosBuenosvinos\Compify\Command; 22 | use CarlosBuenosvinos\Compify\Compify; 23 | 24 | /** 25 | * @author Carlos Buenosvinos 26 | */ 27 | class Application extends BaseApplication 28 | { 29 | protected $io; 30 | protected $composer; 31 | 32 | public function __construct() 33 | { 34 | parent::__construct('Compify', Compify::VERSION); 35 | ErrorHandler::register(); 36 | } 37 | 38 | /** 39 | * {@inheritDoc} 40 | */ 41 | public function doRun(InputInterface $input, OutputInterface $output) 42 | { 43 | $this->registerCommands(); 44 | $this->io = new ConsoleIO($input, $output, $this->getHelperSet()); 45 | return parent::doRun($input, $output); 46 | } 47 | 48 | /** 49 | * @return Composer 50 | */ 51 | public function getComposer($required = true, $config = null) 52 | { 53 | if (null === $this->composer) { 54 | try { 55 | $this->composer = Factory::create($this->io, $config); 56 | } catch (\InvalidArgumentException $e) { 57 | $this->io->write($e->getMessage()); 58 | exit(1); 59 | } 60 | } 61 | 62 | return $this->composer; 63 | } 64 | 65 | /** 66 | * Initializes all the composer commands 67 | */ 68 | protected function registerCommands() 69 | { 70 | $this->add(new Command\CrushCommand()); 71 | } 72 | } --------------------------------------------------------------------------------