diff --git a/src/InlineRecipeCommand.php b/src/InlineRecipeCommand.php deleted file mode 100644 index 45d2618..0000000 --- a/src/InlineRecipeCommand.php +++ /dev/null @@ -1,30 +0,0 @@ -setName('inline'); - $this->setDescription('Invoke this command to inline a recipe into your root composer.json'); - $this->addArgument( - 'recipe', - InputArgument::REQUIRED, - 'Recipe name to inline. Must be of type ' - . RecipePlugin::RECIPE - . ' and be included in the root as a direct dependency' - ); - $this->addUsage('composer inline silverstripe/recipe-blogging'); - } - - public function execute(InputInterface $input, OutputInterface $output) - { - parent::execute($input, $output); // TODO: Change the autogenerated stub - } -} diff --git a/src/RecipeCommandProvider.php b/src/RecipeCommandProvider.php index 0b21354..167c567 100644 --- a/src/RecipeCommandProvider.php +++ b/src/RecipeCommandProvider.php @@ -15,7 +15,7 @@ class RecipeCommandProvider implements CommandProvider public function getCommands() { return [ - new InlineRecipeCommand() + new RequireRecipeCommand() ]; } } diff --git a/src/RecipeInstaller.php b/src/RecipeInstaller.php index 817899b..b679ce7 100644 --- a/src/RecipeInstaller.php +++ b/src/RecipeInstaller.php @@ -92,7 +92,7 @@ class RecipeInstaller extends LibraryInstaller { public function installLibrary(PackageInterface $package) { // Check if silverstripe-recipe type - if ($package->getType() !== RecipePlugin::RECIPE) { + if ($package->getType() !== 'silverstripe-recipe') { return; } diff --git a/src/RecipePlugin.php b/src/RecipePlugin.php index 6256876..addc2cd 100644 --- a/src/RecipePlugin.php +++ b/src/RecipePlugin.php @@ -21,12 +21,6 @@ use Composer\Plugin\Capability\CommandProvider; */ class RecipePlugin implements PluginInterface, EventSubscriberInterface, Capable { - - /** - * Recipe type - */ - const RECIPE = 'silverstripe-recipe'; - public function activate(Composer $composer, IOInterface $io) { } diff --git a/src/RequireRecipeCommand.php b/src/RequireRecipeCommand.php new file mode 100644 index 0000000..c188545 --- /dev/null +++ b/src/RequireRecipeCommand.php @@ -0,0 +1,173 @@ +setName('require-recipe'); + $this->setDescription('Invoke this command to inline a recipe into your root composer.json'); + $this->addArgument( + 'recipe', + InputArgument::REQUIRED, + 'Recipe name to require inline' + ); + $this->addArgument( + 'version', + InputArgument::REQUIRED, + 'Version to require' + ); + $this->addUsage('silverstripe/recipe-blogging 1.0.0'); + $this->setHelp( + <<composer require-recipe silverstripe/recipe-blogging 1.0.0 adds the following: + + + "require": { + "silverstripe/blog": "3.0.0", + "silverstripe/lumberjack": "3.0.1", + "silverstripe/comments": "2.1.0" + }, + "provide": { + "silverstripe/recipe-blogging": "1.0.0" + } + +HELP + ); + } + + public function execute(InputInterface $input, OutputInterface $output) + { + $recipe = $input->getArgument('recipe'); + $version = $input->getArgument('version'); + + // Get existing composer data + $composerData = $this->loadComposer(getcwd()); + if (isset($composerData['provide'][$recipe])) { + $output->writeln("This recipe is already added to provide"); + return -1; + } + + // Ensure composer require includes this recipe + $returnCode = $this->requireRecipe($output, $recipe, $version); + if ($returnCode) { + return $returnCode; + } + + // Get composer data for both root and newly installed recipe + $composerData = $this->loadComposer(getcwd()); + $recipeData = $this->loadComposer(getcwd().'/vendor/'.$recipe); + + // Promote all dependencies + if (!empty($recipeData['require'])) { + $output->writeln("Inlining all dependencies for recipe $recipe:"); + foreach ($recipeData['require'] as $dependencyName => $dependencyVersion) { + $output->writeln(" * Inline dependency $dependencyName as $dependencyVersion"); + $composerData['require'][$dependencyName] = $dependencyVersion; + } + } + + // Move recipe from 'require' to 'provide' + unset($composerData['require'][$recipe]); + if (!isset($composerData['provide'])) { + $composerData['provide'] = []; + } + $composerData['provide'][$recipe] = $version; + + // Update composer.json and synchronise composer.lock + $this->saveComposer(getcwd(), $composerData); + $this->updateProject($output); + + return $returnCode; + } + + /** + * Load composer data from the given directory + * + * @param string $directory + * @return array + */ + protected function loadComposer($directory) + { + $path = $directory.'/composer.json'; + if (!file_exists($path)) { + throw new BadMethodCallException("Could not find composer.json"); + } + $data = json_decode(file_get_contents($path), true); + if (json_last_error() !== JSON_ERROR_NONE) { + throw new \LogicException("Invalid composer.json with error: " . json_last_error_msg()); + } + return $data; + } + + /** + * Save the given data to the composer file in the given directory + * + * @param string $directory + * @param array $data + */ + protected function saveComposer($directory, $data) + { + $path = $directory.'/composer.json'; + if (!file_exists($path)) { + throw new BadMethodCallException("Could not find composer.json"); + } + $content = json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); + // Make sure errors are reported + if (json_last_error()) { + throw new InvalidArgumentException("Invalid composer.json data with error: " . json_last_error_msg()); + } + file_put_contents($path, $content); + } + + /** + * @param OutputInterface $output + * @param $recipe + * @param $version + * @return int + */ + protected function requireRecipe(OutputInterface $output, $recipe, $version) + { + /** @var RequireCommand $command */ + $command = $this->getApplication()->find('require'); + $package = $recipe . ':' . $version; + $arguments = [ + 'command' => 'require', + 'packages' => [$package], + ]; + $requireInput = new ArrayInput($arguments); + $returnCode = $command->run($requireInput, $output); + return $returnCode; + } + + /** + * Update the project + * + * @param OutputInterface $output + * @return int + */ + protected function updateProject(OutputInterface $output) + { + /** @var UpdateCommand $command */ + $command = $this->getApplication()->find('update'); + $arguments = [ 'command' => 'update' ]; + $requireInput = new ArrayInput($arguments); + $returnCode = $command->run($requireInput, $output); + return $returnCode; + } +}