Generates multiple variations of the original composer.json
composer -vv suite:generateexport COMPOSER='composer.my-suite-01.json'composer update --lockOther benefit is that, if there is any relative path in the composer.json –
for example under the #/repositories/FOO/url or anywhere under the #/extra –
then those paths will work with the alternative composer.*.json files as well.
{
"require": {
"symfony/console": "^4.0 || ^5.0",
"symfony/process": "^4.0 || ^5.0"
},
"require-dev": {
"phpunit/phpunit": "^8.0"
},
"scripts": {
"test": "phpunit"
},
"extra": {
"composer-suite": {
"symfony4": {
"description": "Make sure Symfony 4.x will be used.",
"actions": [
{
"type": "replaceRecursive",
"config": {
"items": {
"require": {
"symfony/console": "^4.0",
"symfony/process": "^4.0"
}
}
}
}
]
},
"symfony5": {
"description": "Make sure Symfony 5.x will be used.",
"actions": [
{
"type": "replaceRecursive",
"config": {
"items": {
"require": {
"symfony/console": "^5.0",
"symfony/process": "^5.0"
}
}
}
}
]
}
}
}
}
Run: composer suite:generate
The generated files:
composer.symfony4.json
{
"require": {
"symfony/console": "^4.0",
"symfony/process": "^4.0"
},
"require-dev": {
"phpunit/phpunit": "^8.0"
},
"scripts": {
"test": "phpunit"
},
"extra": {}
}
composer.symfony5.json
{
"require": {
"symfony/console": "^5.0",
"symfony/process": "^5.0"
},
"require-dev": {
"phpunit/phpunit": "^8.0"
},
"scripts": {
"test": "phpunit"
},
"extra": {}
}
Then
unset COMPOSER
composer update --lock
composer suite:generate
export COMPOSER='composer.symfony4.json'
composer update
composer run test
export COMPOSER='composer.symfony5.json'
composer update
composer run test
# Back to normal.
unset COMPOSER
composer update --lock
Use case
A certain third-party service has to be integrated into a framework.
Composer packages:
{
"type": "project",
"repositories": {},
"require": {
"my/module_01": "^1.0"
},
"require-dev": {
"sweetchuck/composer-suite": "^1.0"
},
"extra": {
"composer-suite": {
"local": {
"description": "Local development",
"actions": [
{
"type": "prepend",
"config": {
"parents": ["repositories"],
"items": {
"my/module_01": {
"type": "path",
"url": "../../my/module_01-1.x",
"options": {
"repo-path": {
"url": "https://github.com/my/module_01.git",
"branch": "1.x"
}
}
},
"my/restapi_client_01": {
"type": "path",
"url": "../../my/restapi_client_01-1.x",
"options": {
"repo-path": {
"url": "https://github.com/my/restapi_client_01.git",
"branch": "1.x"
}
}
}
}
}
},
{
"type": "replaceRecursive",
"config": {
"parents": ["require"],
"items": {
"my/module_01": "1.x-dev",
"my/restapi_client_01": "1.x-dev"
}
}
}
]
}
}
}
}
Run: composer suite:generate
The generated file:
composer.local.json
{
"type": "project",
"repositories": {
"my/module_01": {
"type": "path",
"url": "../../my/module_01-1.x",
"options": {
"repo-path": {
"url": "https://github.com/my/module_01.git",
"branch": "1.x"
}
}
},
"my/restapi_client_01": {
"type": "path",
"url": "../../my/restapi_client_01-1.x",
"options": {
"repo-path": {
"url": "https://github.com/my/restapi_client_01.git",
"branch": "1.x"
}
}
}
},
"require": {
"my/module_01": "1.x-dev",
"my/restapi_client_01": "1.x-dev"
},
"require-dev": {
"sweetchuck/composer-suite": "^1.0"
},
"extra": {}
}
Then
unset COMPOSER
composer update --lock
composer suite:generate
cp ./composer.lock ./composer.local.lock
export COMPOSER='composer.local.json'
composer update --lock
# You can work on both libraries (restapi_client_01 and module_01) on-the-fly.
# Back to normal.
unset COMPOSER
composer update --lock
You can have as many suites as many you want.
As the examples above show that, suites can be defined in the
composer.json#/extra/composer-suite, but suite definitions can be stored in
external files as well.
./.composer-suite/composer-suite.*.json.
Then it is up to you if these files will be tracked by the VCS or not.
Example ./.composer-suite/composer-suite.foo.json:
{
"name": "foo",
"description": "My foo suite.",
"actions": []
}
The name key is optional. If it is omitted then, the suite name will be parsed
from the file name.
If two suites have the same name, then the one which comes from external file, has the highest priority.
You can define different array manipulation actions under the
extra/composer-suite/<suite-id> keys.
An action has two main properties:
type.Official PHP documentation: array_replace_recursive()
{
"require": {
"a/b": "^1.0",
"symfony/console": "^4.0",
"symfony/process": "^4.0"
},
"extra": {
"composer-suite": {
"my-suite-01": {
"actions": {
"type": " replaceRecursive",
"config": {
"parents": [],
"items": {
"require": {
"symfony/console": "^5.0",
"symfony/process": "^5.0"
}
}
}
}
}
}
}
}
Result:
{
"require": {
"a/b": "^1.0",
"symfony/console": "^5.0",
"symfony/process": "^5.0"
},
"extra": {}
}
Removes the specified elements.
{
"name": "a/b",
"foo": {
"bar": "delete me"
},
"extra": {
"composer-suite": {
"my-suite-01": {
"actions": {
"type": "unset",
"config": {
"parents": [
"foo",
"bar"
]
}
}
}
}
}
}
Result:
{
"name": "a/b",
"foo": {},
"extra": {}
}
In the "config.parents" array the last item can be an array:
{
"name": "a/b",
"foo": {
"a": "delete me 1",
"b": "keep me",
"c": "delete me 2"
},
"extra": {
"composer-suite": {
"my-suite-01": {
"actions": {
"type": "unset",
"config": {
"parents": [
"foo",
[
"a",
"c"
]
]
}
}
}
}
}
}
Result:
{
"name": "a/b",
"foo": {
"b": "keep me"
},
"extra": {}
}
Adds new elements at the beginning of an array.
{
"repositories": {
"old/p1": {}
},
"extra": {
"composer-suite": {
"local": {
"actions": [
{
"type": "prepend",
"config": {
"parents": [
"repositories"
],
"items": {
"new/p1": {},
"new/p2": {}
}
}
}
]
}
}
}
}
Result:
{
"repositories": {
"new/p1": {},
"new/p2": {},
"old/p1": {}
},
"extra": {}
}
Adds new elements at the end of an array.
Insert one or more "items". The last item in the "parents" is the reference point.
Insert one or more "items". The last item in the "parents" is the reference point.
This Composer plugin defines the following commands:
composer suite:listcomposer suite:generateYou can check the status of the autogenerated files by running the official
COMPOSER='composer.json' composer validate command.
If one of the autogenerated file is exists AND out-of-date, then the exit
code will be other than 0.
This command does not report any problem about the missing composer.*.json files.
"$(composer config bin-dir)/codecept" _completion --generate-hook --program codecept | source /dev/stdinHow can I help you explore Laravel packages today?