Merge pull request #3 from open-sausages/pulls/1.0/removable-resources
ENHANCEMENT Register installed modules and files in composer.extra
This commit is contained in:
commit
0592e33189
86
README.md
86
README.md
|
@ -14,7 +14,7 @@ These recipes allow for the following features:
|
||||||
- Recipes also can be used as a base composer project.
|
- Recipes also can be used as a base composer project.
|
||||||
- A `require-recipe` command to inline a recipe into the root composer.json, allowing the developer to customise the
|
- A `require-recipe` command to inline a recipe into the root composer.json, allowing the developer to customise the
|
||||||
recipe dependencies without mandating the inclusion of all requirements directly.
|
recipe dependencies without mandating the inclusion of all requirements directly.
|
||||||
- An `upgrade-recipe` command to upgrade to a newer version of a recipe.
|
- An `update-recipe` command to upgrade to a newer version of a recipe.
|
||||||
|
|
||||||
## Example output
|
## Example output
|
||||||
|
|
||||||
|
@ -26,38 +26,80 @@ Recipes can be introduced to any existing project (even if not created on a silv
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
$ composer init
|
$ composer init
|
||||||
$ composer require silverstripe/recipe-plugin ^0.1
|
$ composer require silverstripe/recipe-cms ^1.0@dev
|
||||||
$ composer require-recipe silverstripe/recipe-cms ^4.0@dev
|
|
||||||
````
|
````
|
||||||
|
|
||||||
Alternatively, instead of having to install the recipe-plugin manually, you can require the recipe
|
|
||||||
directly and inline this as a subsequent command. This is necessary to make the new commands available
|
|
||||||
to the command line.
|
|
||||||
|
|
||||||
```shell
|
|
||||||
$ composer init
|
|
||||||
$ composer require silverstripe/recipe-cms ^4.0@dev
|
|
||||||
$ composer upgrade-recipe silverstripe/recipe-cms
|
|
||||||
```
|
|
||||||
|
|
||||||
Alternatively you can create a new project based on an existing recipe
|
Alternatively you can create a new project based on an existing recipe
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
$ composer create-project silverstripe/recipe-cms ./myssproject ^4.0@dev
|
$ composer create-project silverstripe/recipe-cms ./myssproject ^1.0@dev
|
||||||
```
|
```
|
||||||
|
|
||||||
## Upgrading recipes
|
## Inlining recipes
|
||||||
|
|
||||||
Any existing recipe, whether installed via `composer require` or `composer require-recipe` can be safely upgraded
|
You can "inline" either a previously installed recipe, or a new one that you would like to include
|
||||||
via `composer upgrade-recipe`.
|
dependencies for in your main project. By inlining a recipe, you promote its requirements, as well as
|
||||||
|
its project files, up into your main project, and remove the recipe itself from your dependencies.
|
||||||
|
|
||||||
When upgrading a version constraint is recommended, but not necessary. If omitted, then the existing installed
|
This can be done with either `update-recipe`, which will update a recipe, or `require-recipe` which will
|
||||||
version will be detected, and a safe default chosen.
|
install a new recipe.
|
||||||
|
|
||||||
|
Note that if you with to run this command you must first install either a recipe via normal composer
|
||||||
|
commands, or install the recipe plugin:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
$ composer upgrade-recipe silverstripe/recipe-cms ^1.0@dev
|
$ composer init
|
||||||
|
$ composer require silverstripe/recipe-plugin ^0.1
|
||||||
|
$ composer require-recipe silverstripe/recipe-cms ^1.0@dev
|
||||||
```
|
```
|
||||||
|
|
||||||
|
or
|
||||||
|
|
||||||
|
```shell
|
||||||
|
$ composer init
|
||||||
|
$ composer require silverstripe/recipe-cms ^1.0@dev
|
||||||
|
$ composer update-recipe silverstripe/recipe-cms
|
||||||
|
```
|
||||||
|
|
||||||
|
## Removing recipe dependencies or files
|
||||||
|
|
||||||
|
Any project file installed via a recipe, or any module installed by inlining a recipe, can be easily removed.
|
||||||
|
Subsequent updates to this recipe will not re-install any of those files or dependencies.
|
||||||
|
|
||||||
|
In order to ensure this, a record of all inlined modules, and all installed files are stored in composer.json
|
||||||
|
as below.
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"extra": {
|
||||||
|
"project-files-installed": [
|
||||||
|
"mysite/code/Page.php",
|
||||||
|
"mysite/code/PageController.php"
|
||||||
|
],
|
||||||
|
"project-dependencies-installed": {
|
||||||
|
"silverstripe/admin": "1.0.x-dev",
|
||||||
|
"silverstripe/asset-admin": "1.0.x-dev",
|
||||||
|
"silverstripe/campaign-admin": "1.0.x-dev"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
To remove a file, simply delete it from the folder your project is installed in, but don't modify
|
||||||
|
`project-files-installed` (as this is how composer knows what not to re-install).
|
||||||
|
|
||||||
|
Likewise to remove a module, use `composer remove <module>` and it will be removed. As above, don't
|
||||||
|
modify `project-dependencies-instaleld`, otherwise that module will be re-installed on subsequent
|
||||||
|
`composer update-recipe`.
|
||||||
|
|
||||||
|
## Un-doing a deleted project file / dependency
|
||||||
|
|
||||||
|
If you have deleted a module or file and want to re-install it you should remove the appropriate
|
||||||
|
entry from either 'project-files-installed' or 'project-dependencies-installed' and then run
|
||||||
|
`composer update-recipe <recipe>` again.
|
||||||
|
|
||||||
|
The file or module will be re-installed.
|
||||||
|
|
||||||
## Removing recipes
|
## Removing recipes
|
||||||
|
|
||||||
As installation of a recipe inlines all dependencies and passes ownership to the root project,
|
As installation of a recipe inlines all dependencies and passes ownership to the root project,
|
||||||
|
@ -65,7 +107,7 @@ there is no automatic removal process. To remove a recipe, you should manually r
|
||||||
required module that is no longer desired via `composer remove <module>`.
|
required module that is no longer desired via `composer remove <module>`.
|
||||||
|
|
||||||
The `provide` reference to the recipe can also be safely removed, although it has no practical result
|
The `provide` reference to the recipe can also be safely removed, although it has no practical result
|
||||||
other than to disable future calls to `upgrade-recipe` on this recipe.
|
other than to disable future calls to `update-recipe` on this recipe.
|
||||||
|
|
||||||
## Installing or upgrading recipes without inlining them
|
## Installing or upgrading recipes without inlining them
|
||||||
|
|
||||||
|
@ -101,7 +143,7 @@ An example recipe:
|
||||||
"type": "silverstripe-recipe",
|
"type": "silverstripe-recipe",
|
||||||
"require": {
|
"require": {
|
||||||
"silverstripe/recipe-plugin": "^0.1",
|
"silverstripe/recipe-plugin": "^0.1",
|
||||||
"silverstripe/recipe-cms": "^4.0",
|
"silverstripe/recipe-cms": "^1.0",
|
||||||
"silverstripe/blog": "^3.0@dev",
|
"silverstripe/blog": "^3.0@dev",
|
||||||
"silverstripe/lumberjack": "^2.1@dev",
|
"silverstripe/lumberjack": "^2.1@dev",
|
||||||
},
|
},
|
||||||
|
|
|
@ -2,11 +2,12 @@
|
||||||
|
|
||||||
namespace SilverStripe\RecipePlugin;
|
namespace SilverStripe\RecipePlugin;
|
||||||
|
|
||||||
use BadMethodCallException;
|
|
||||||
use Composer\Command\RequireCommand;
|
use Composer\Command\RequireCommand;
|
||||||
use Composer\Command\UpdateCommand;
|
use Composer\Command\UpdateCommand;
|
||||||
use Composer\Composer;
|
use Composer\Composer;
|
||||||
use MongoDB\Driver\Exception\InvalidArgumentException;
|
use Composer\Factory;
|
||||||
|
use Composer\IO\IOInterface;
|
||||||
|
use Composer\Json\JsonFile;
|
||||||
use Symfony\Component\Console\Application;
|
use Symfony\Component\Console\Application;
|
||||||
use Symfony\Component\Console\Input\ArrayInput;
|
use Symfony\Component\Console\Input\ArrayInput;
|
||||||
use Symfony\Component\Console\Output\OutputInterface;
|
use Symfony\Component\Console\Output\OutputInterface;
|
||||||
|
@ -34,50 +35,9 @@ trait RecipeCommandBehaviour
|
||||||
public abstract function resetComposer();
|
public abstract function resetComposer();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load composer data from the given directory
|
* @return IOInterface
|
||||||
*
|
|
||||||
* @param string $path
|
|
||||||
* @param array|null $default If file doesn't exist use this default. If null, file is mandatory and there is
|
|
||||||
* no default
|
|
||||||
* @return array
|
|
||||||
*/
|
*/
|
||||||
protected function loadComposer($path, $default = null)
|
abstract public function getIO();
|
||||||
{
|
|
||||||
if (!file_exists($path)) {
|
|
||||||
if (isset($default)) {
|
|
||||||
return $default;
|
|
||||||
}
|
|
||||||
throw new BadMethodCallException("Could not find " . basename($path));
|
|
||||||
}
|
|
||||||
$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);
|
|
||||||
|
|
||||||
// Reset composer object
|
|
||||||
$this->resetComposer();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param OutputInterface $output
|
* @param OutputInterface $output
|
||||||
|
@ -147,7 +107,7 @@ trait RecipeCommandBehaviour
|
||||||
// Check requires
|
// Check requires
|
||||||
$requires = $this->getComposer()->getPackage()->getRequires();
|
$requires = $this->getComposer()->getPackage()->getRequires();
|
||||||
if (isset($requires[$recipe])) {
|
if (isset($requires[$recipe])) {
|
||||||
return $provides[$recipe]->getPrettyConstraint();
|
return $requires[$recipe]->getPrettyConstraint();
|
||||||
}
|
}
|
||||||
|
|
||||||
// No existing version
|
// No existing version
|
||||||
|
@ -224,36 +184,109 @@ trait RecipeCommandBehaviour
|
||||||
return $returnCode;
|
return $returnCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Begin modification of composer.json
|
// inline all dependencies inline into composer.json
|
||||||
$composerData = $this->loadComposer(getcwd() . '/composer.json');
|
$this->modifyComposer(function ($composerData) use ($output, $recipe, $installedVersion) {
|
||||||
|
// Check previously installed, and currently installed modules
|
||||||
|
$require = isset($composerData['require']) ? $composerData['require'] : [];
|
||||||
|
$previouslyInstalled = isset($composerData['extra'][RecipePlugin::PROJECT_DEPENDENCIES_INSTALLED])
|
||||||
|
? $composerData['extra'][RecipePlugin::PROJECT_DEPENDENCIES_INSTALLED]
|
||||||
|
: [];
|
||||||
|
|
||||||
// Get composer data for both root and newly installed recipe
|
// Get composer data for both root and newly installed recipe
|
||||||
$installedRecipe = $this
|
$installedRecipe = $this
|
||||||
->getComposer()
|
->getComposer()
|
||||||
->getRepositoryManager()
|
->getRepositoryManager()
|
||||||
->getLocalRepository()
|
->getLocalRepository()
|
||||||
->findPackage($recipe, '*');
|
->findPackage($recipe, '*');
|
||||||
if ($installedRecipe) {
|
if ($installedRecipe) {
|
||||||
$output->writeln("Inlining all dependencies for recipe <info>{$recipe}</info>:");
|
$output->writeln("Inlining all dependencies for recipe <info>{$recipe}</info>:");
|
||||||
foreach ($installedRecipe->getRequires() as $requireName => $require) {
|
foreach ($installedRecipe->getRequires() as $requireName => $requireConstraint) {
|
||||||
$requireVersion = $require->getPrettyConstraint();
|
$requireVersion = $requireConstraint->getPrettyConstraint();
|
||||||
$output->writeln(
|
|
||||||
" - Inlining <info>{$requireName}</info> (<comment>{$requireVersion}</comment>)"
|
// If already installed, upgrade
|
||||||
);
|
if (isset($require[$requireName])) {
|
||||||
$composerData['require'][$requireName] = $requireVersion;
|
// Check if upgrade or not
|
||||||
|
$requireInstalledVersion = $require[$requireName];
|
||||||
|
if ($requireInstalledVersion === $requireVersion) {
|
||||||
|
// No need to upgrade
|
||||||
|
$output->writeln(
|
||||||
|
" - Skipping <info>{$requireName}</info> "
|
||||||
|
. "(Already installed as <comment>{$requireVersion}</comment>)"
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// Upgrade obsolete version
|
||||||
|
$output->writeln(
|
||||||
|
" - Inlining <info>{$requireName}</info> "
|
||||||
|
. "(Updated to <comment>{$requireVersion}</comment> from "
|
||||||
|
. "<comment>{$requireInstalledVersion}</comment>)"
|
||||||
|
);
|
||||||
|
$require[$requireName] = $requireVersion;
|
||||||
|
}
|
||||||
|
} elseif (isset($previouslyInstalled[$requireName])) {
|
||||||
|
// Old module, manually removed
|
||||||
|
$output->writeln(
|
||||||
|
" - Skipping <info>{$requireName}</info> (Manually removed from recipe)"
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// New module
|
||||||
|
$output->writeln(
|
||||||
|
" - Inlining <info>{$requireName}</info> (<comment>{$requireVersion}</comment>)"
|
||||||
|
);
|
||||||
|
$require[$requireName] = $requireVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
// note dependency as previously installed
|
||||||
|
$previouslyInstalled[$requireName] = $requireVersion;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Move recipe from 'require' to 'provide'
|
// Add new require / extra-installed
|
||||||
$installedVersion = $this->findInstalledVersion($recipe) ?: $installedVersion;
|
$composerData['require'] = $require;
|
||||||
unset($composerData['require'][$recipe]);
|
if ($previouslyInstalled){
|
||||||
if (!isset($composerData['provide'])) {
|
if (!isset($composerData['extra'])) {
|
||||||
$composerData['provide'] = [];
|
$composerData['extra'] = [];
|
||||||
}
|
}
|
||||||
$composerData['provide'][$recipe] = $installedVersion;
|
ksort($previouslyInstalled);
|
||||||
|
$composerData['extra'][RecipePlugin::PROJECT_DEPENDENCIES_INSTALLED] = $previouslyInstalled;
|
||||||
|
}
|
||||||
|
|
||||||
// Update composer.json and synchronise composer.lock
|
// Move recipe from 'require' to 'provide'
|
||||||
$this->saveComposer(getcwd(), $composerData);
|
$installedVersion = $this->findInstalledVersion($recipe) ?: $installedVersion;
|
||||||
|
unset($composerData['require'][$recipe]);
|
||||||
|
if (!isset($composerData['provide'])) {
|
||||||
|
$composerData['provide'] = [];
|
||||||
|
}
|
||||||
|
$composerData['provide'][$recipe] = $installedVersion;
|
||||||
|
return $composerData;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Update synchronise composer.lock
|
||||||
return $this->updateProject($output);
|
return $this->updateProject($output);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* callback to safely modify composer.json data
|
||||||
|
*
|
||||||
|
* @param callable $callable Callable which will safely take and return the composer data.
|
||||||
|
* This should return false if no content changed, or the updated data
|
||||||
|
*/
|
||||||
|
protected function modifyComposer($callable)
|
||||||
|
{
|
||||||
|
// Begin modification of composer.json
|
||||||
|
$composerFile = new JsonFile(Factory::getComposerFile(), null, $this->getIO());
|
||||||
|
$composerData = $composerFile->read();
|
||||||
|
|
||||||
|
// Note: Respect call by ref $composerData
|
||||||
|
$result = $callable($composerData);
|
||||||
|
if ($result === false) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ($result) {
|
||||||
|
$composerData = $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update composer.json and refresh local composer instance
|
||||||
|
$composerFile->write($composerData);
|
||||||
|
$this->resetComposer();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,9 +2,11 @@
|
||||||
|
|
||||||
namespace SilverStripe\RecipePlugin;
|
namespace SilverStripe\RecipePlugin;
|
||||||
|
|
||||||
|
use Composer\Factory;
|
||||||
use Composer\Installer\LibraryInstaller;
|
use Composer\Installer\LibraryInstaller;
|
||||||
use Composer\Composer;
|
use Composer\Composer;
|
||||||
use Composer\IO\IOInterface;
|
use Composer\IO\IOInterface;
|
||||||
|
use Composer\Json\JsonFile;
|
||||||
use Composer\Package\PackageInterface;
|
use Composer\Package\PackageInterface;
|
||||||
use FilesystemIterator;
|
use FilesystemIterator;
|
||||||
use Iterator;
|
use Iterator;
|
||||||
|
@ -28,11 +30,19 @@ class RecipeInstaller extends LibraryInstaller {
|
||||||
*/
|
*/
|
||||||
protected function installProjectFiles($recipe, $sourceRoot, $destinationRoot, $filePatterns)
|
protected function installProjectFiles($recipe, $sourceRoot, $destinationRoot, $filePatterns)
|
||||||
{
|
{
|
||||||
|
// load composer json data
|
||||||
|
$composerFile = new JsonFile(Factory::getComposerFile(), null, $this->io);
|
||||||
|
$composerData = $composerFile->read();
|
||||||
|
$installedFiles = isset($composerData['extra'][RecipePlugin::PROJECT_FILES_INSTALLED])
|
||||||
|
? $composerData['extra'][RecipePlugin::PROJECT_FILES_INSTALLED]
|
||||||
|
: [];
|
||||||
|
|
||||||
|
// Load all project files
|
||||||
$fileIterator = $this->getFileIterator($sourceRoot, $filePatterns);
|
$fileIterator = $this->getFileIterator($sourceRoot, $filePatterns);
|
||||||
$any = false;
|
$any = false;
|
||||||
foreach($fileIterator as $path => $info) {
|
foreach($fileIterator as $path => $info) {
|
||||||
$relativePath = substr($path, strlen($sourceRoot));
|
$destination = $destinationRoot . substr($path, strlen($sourceRoot));
|
||||||
$destination = $destinationRoot . $relativePath;
|
$relativePath = substr($path, strlen($sourceRoot) + 1); // Name path without leading '/'
|
||||||
|
|
||||||
// Write header
|
// Write header
|
||||||
if (!$any) {
|
if (!$any) {
|
||||||
|
@ -51,11 +61,32 @@ class RecipeInstaller extends LibraryInstaller {
|
||||||
" - Skipping <info>$relativePath</info> (<comment>existing and modified in project</comment>)"
|
" - Skipping <info>$relativePath</info> (<comment>existing and modified in project</comment>)"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
} elseif (in_array($relativePath, $installedFiles)) {
|
||||||
|
// Don't re-install previously installed files that have been deleted
|
||||||
|
$this->io->write(
|
||||||
|
" - Skipping <info>$relativePath</info> (<comment>previously installed</comment>)"
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
|
$any++;
|
||||||
$this->io->write(" - Copying <info>$relativePath</info>");
|
$this->io->write(" - Copying <info>$relativePath</info>");
|
||||||
$this->filesystem->ensureDirectoryExists(dirname($destination));
|
$this->filesystem->ensureDirectoryExists(dirname($destination));
|
||||||
copy($path, $destination);
|
copy($path, $destination);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add file to installed (even if already exists)
|
||||||
|
if (!in_array($relativePath, $installedFiles)) {
|
||||||
|
$installedFiles[] = $relativePath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If any files are written, modify composer.json with newly installed files
|
||||||
|
if ($installedFiles) {
|
||||||
|
sort($installedFiles);
|
||||||
|
if (!isset($composerData['extra'])) {
|
||||||
|
$composerData['extra'] = [];
|
||||||
|
}
|
||||||
|
$composerData['extra'][RecipePlugin::PROJECT_FILES_INSTALLED] = $installedFiles;
|
||||||
|
$composerFile->write($composerData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,20 +139,22 @@ class RecipeInstaller extends LibraryInstaller {
|
||||||
public function installLibrary(PackageInterface $package)
|
public function installLibrary(PackageInterface $package)
|
||||||
{
|
{
|
||||||
// Check if silverstripe-recipe type
|
// Check if silverstripe-recipe type
|
||||||
if ($package->getType() !== 'silverstripe-recipe') {
|
if ($package->getType() !== RecipePlugin::RECIPE_TYPE) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Find project path
|
||||||
|
$destinationPath = dirname(realpath(Factory::getComposerFile()));
|
||||||
|
|
||||||
// Copy project files to root
|
// Copy project files to root
|
||||||
$destinationPath = getcwd();
|
|
||||||
$name = $package->getName();
|
$name = $package->getName();
|
||||||
$extra = $package->getExtra();
|
$extra = $package->getExtra();
|
||||||
if (isset($extra['project-files'])) {
|
if (isset($extra[RecipePlugin::PROJECT_FILES])) {
|
||||||
$this->installProjectFiles(
|
$this->installProjectFiles(
|
||||||
$name,
|
$name,
|
||||||
$this->getInstallPath($package),
|
$this->getInstallPath($package),
|
||||||
$destinationPath,
|
$destinationPath,
|
||||||
$extra['project-files']
|
$extra[RecipePlugin::PROJECT_FILES]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,14 +7,15 @@ use Composer\Composer;
|
||||||
use Composer\DependencyResolver\Operation\InstallOperation;
|
use Composer\DependencyResolver\Operation\InstallOperation;
|
||||||
use Composer\DependencyResolver\Operation\UpdateOperation;
|
use Composer\DependencyResolver\Operation\UpdateOperation;
|
||||||
use Composer\EventDispatcher\EventSubscriberInterface;
|
use Composer\EventDispatcher\EventSubscriberInterface;
|
||||||
|
use Composer\Factory;
|
||||||
use Composer\Installer\PackageEvent;
|
use Composer\Installer\PackageEvent;
|
||||||
use Composer\IO\IOInterface;
|
use Composer\IO\IOInterface;
|
||||||
|
use Composer\Json\JsonFile;
|
||||||
use Composer\Package\PackageInterface;
|
use Composer\Package\PackageInterface;
|
||||||
use Composer\Plugin\Capability\CommandProvider;
|
use Composer\Plugin\Capability\CommandProvider;
|
||||||
use Composer\Plugin\Capable;
|
use Composer\Plugin\Capable;
|
||||||
use Composer\Plugin\PluginInterface;
|
use Composer\Plugin\PluginInterface;
|
||||||
use Composer\Script\Event;
|
use Composer\Script\Event;
|
||||||
use LogicException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register the RecipeInstaller
|
* Register the RecipeInstaller
|
||||||
|
@ -23,6 +24,26 @@ use LogicException;
|
||||||
*/
|
*/
|
||||||
class RecipePlugin implements PluginInterface, EventSubscriberInterface, Capable
|
class RecipePlugin implements PluginInterface, EventSubscriberInterface, Capable
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* Type of recipe to check for
|
||||||
|
*/
|
||||||
|
const RECIPE_TYPE = 'silverstripe-recipe';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 'extra' key for project files
|
||||||
|
*/
|
||||||
|
const PROJECT_FILES = 'project-files';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 'extra' key for list of project files installed
|
||||||
|
*/
|
||||||
|
const PROJECT_FILES_INSTALLED = 'project-files-installed';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 'extra' key for project dependencies installed
|
||||||
|
*/
|
||||||
|
const PROJECT_DEPENDENCIES_INSTALLED = 'project-dependencies-installed';
|
||||||
|
|
||||||
public function activate(Composer $composer, IOInterface $io)
|
public function activate(Composer $composer, IOInterface $io)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -57,30 +78,17 @@ class RecipePlugin implements PluginInterface, EventSubscriberInterface, Capable
|
||||||
*/
|
*/
|
||||||
public function cleanupProject(Event $event)
|
public function cleanupProject(Event $event)
|
||||||
{
|
{
|
||||||
$path = getcwd() . '/composer.json';
|
$file = new JsonFile(Factory::getComposerFile());
|
||||||
|
$data = $file->read();
|
||||||
|
|
||||||
// Load composer data
|
// Remove project-files from project, and any empty extra
|
||||||
$data = json_decode(file_get_contents($path), true);
|
unset($data['extra']['project-files']);
|
||||||
if (json_last_error() !== JSON_ERROR_NONE) {
|
|
||||||
throw new LogicException("Invalid composer.json with error: " . json_last_error_msg());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove project-files from project
|
|
||||||
if (isset($data['extra']['project-files'])) {
|
|
||||||
unset($data['extra']['project-files']);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clean empty extra key
|
|
||||||
if (empty($data['extra'])) {
|
if (empty($data['extra'])) {
|
||||||
unset($data['extra']);
|
unset($data['extra']);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save back to composer.json
|
// Save back to composer.json
|
||||||
$content = json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
|
$file->write($data);
|
||||||
if (json_last_error()) {
|
|
||||||
throw new LogicException("Invalid composer.json data with error: " . json_last_error_msg());
|
|
||||||
}
|
|
||||||
file_put_contents($path, $content);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Reference in New Issue