diff --git a/lib/composer/bamarni/composer-bin-plugin/.github/workflows/phpstan.yml b/lib/composer/bamarni/composer-bin-plugin/.github/workflows/phpstan.yml new file mode 100644 index 0000000000000..ebb576728121e --- /dev/null +++ b/lib/composer/bamarni/composer-bin-plugin/.github/workflows/phpstan.yml @@ -0,0 +1,35 @@ +name: "Static analysis" + +on: + push: + branches: + - "main" + - "master" + pull_request: null + +jobs: + static-analysis: + runs-on: "ubuntu-latest" + name: "PHPStan on PHP ${{ matrix.php }}" + strategy: + fail-fast: false + matrix: + php: + - "8.1" + steps: + - name: "Check out repository code" + uses: "actions/checkout@v2" + + - name: "Setup PHP" + uses: "shivammathur/setup-php@v2" + with: + php-version: "${{ matrix.php }}" + tools: "composer" + + - name: "Install Composer dependencies" + uses: "ramsey/composer-install@v2" + with: + dependency-versions: "highest" + + - name: "Perform static analysis" + run: "make phpstan" diff --git a/lib/composer/bamarni/composer-bin-plugin/.github/workflows/tests.yaml b/lib/composer/bamarni/composer-bin-plugin/.github/workflows/tests.yaml new file mode 100644 index 0000000000000..5bec38a4ae966 --- /dev/null +++ b/lib/composer/bamarni/composer-bin-plugin/.github/workflows/tests.yaml @@ -0,0 +1,80 @@ +name: "Tests" + +on: + push: + branches: + - "main" + - "master" + pull_request: null + +jobs: + unit-tests: + runs-on: "ubuntu-latest" + name: "Unit Tests on PHP ${{ matrix.php }} and ${{ matrix.tools }}" + strategy: + fail-fast: false + matrix: + php: + - "7.2" + - "7.3" + - "7.4" + - "8.0" + - "8.1" + tools: [ "composer" ] + dependency-versions: [ "highest" ] + include: + - php: "7.2" + tools: "composer:v2.0" + dependency-versions: "lowest" + + steps: + - name: "Check out repository code" + uses: "actions/checkout@v2" + + - name: "Setup PHP" + uses: "shivammathur/setup-php@v2" + with: + php-version: "${{ matrix.php }}" + tools: "${{ matrix.tools }}" + + - name: "Install Composer dependencies" + uses: "ramsey/composer-install@v2" + with: + dependency-versions: "${{ matrix.dependency-versions }}" + + - name: "Validate composer.json" + run: "composer validate --strict --no-check-lock" + + - name: "Run tests" + run: "vendor/bin/phpunit --group default" + + e2e-tests: + runs-on: "ubuntu-latest" + name: "E2E Tests on PHP ${{ matrix.php }}" + strategy: + fail-fast: false + matrix: + php: + - "8.1" + + steps: + - name: "Check out repository code" + uses: "actions/checkout@v2" + + - name: "Setup PHP" + uses: "shivammathur/setup-php@v2" + with: + php-version: "${{ matrix.php }}" + tools: "composer" + + - name: "Correct bin plugin version for e2e scenarios (PR-only)" + if: github.event_name == 'pull_request' + run: find e2e -maxdepth 1 -mindepth 1 -type d -exec bash -c "cd {} && composer require --dev bamarni/composer-bin-plugin:dev-${GITHUB_SHA} --no-update" \; + + - name: "Install Composer dependencies" + uses: "ramsey/composer-install@v2" + with: + dependency-versions: "highest" + + - name: "Run tests" + run: "vendor/bin/phpunit --group e2e" diff --git a/lib/composer/bamarni/composer-bin-plugin/.makefile/touch.sh b/lib/composer/bamarni/composer-bin-plugin/.makefile/touch.sh new file mode 100755 index 0000000000000..5c0a7759a3e97 --- /dev/null +++ b/lib/composer/bamarni/composer-bin-plugin/.makefile/touch.sh @@ -0,0 +1,61 @@ +#!/usr/bin/env bash +# +# Takes a given string, e.g. 'bin/console' or 'docker-compose exec php bin/console' +# and split it by words. For each words, if the target is a file, it is touched. +# +# This allows to implement a similar rule to: +# +# ```Makefile +# bin/php-cs-fixer: vendor +# touch $@ +# ``` +# +# Indeed when the rule `bin/php-cs-fixer` is replaced with a docker-compose +# equivalent, it will not play out as nicely. +# +# Arguments: +# $1 - {string} Command potentially containing a file +# + +set -Eeuo pipefail; + + +readonly ERROR_COLOR="\e[41m"; +readonly NO_COLOR="\e[0m"; + + +if [ $# -ne 1 ]; then + printf "${ERROR_COLOR}Illegal number of parameters.${NO_COLOR}\n"; + + exit 1; +fi + + +readonly FILES="$1"; + + +####################################### +# Touch the given file path if the target is a file and do not create the file +# if does not exist. +# +# Globals: +# None +# +# Arguments: +# $1 - {string} File path +# +# Returns: +# None +####################################### +touch_file() { + local file="$1"; + + if [ -e ${file} ]; then + touch -c ${file}; + fi +} + +for file in ${FILES} +do + touch_file ${file}; +done diff --git a/lib/composer/bamarni/composer-bin-plugin/.phive/phars.xml b/lib/composer/bamarni/composer-bin-plugin/.phive/phars.xml new file mode 100644 index 0000000000000..335086e3ce3b7 --- /dev/null +++ b/lib/composer/bamarni/composer-bin-plugin/.phive/phars.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/lib/composer/bamarni/composer-bin-plugin/.php-cs-fixer.php b/lib/composer/bamarni/composer-bin-plugin/.php-cs-fixer.php new file mode 100644 index 0000000000000..507763adac6b1 --- /dev/null +++ b/lib/composer/bamarni/composer-bin-plugin/.php-cs-fixer.php @@ -0,0 +1,17 @@ +files() + ->in(['src', 'tests']); + +$config = new PhpCsFixer\Config(); + +return $config + ->setRules([ + '@PSR12' => true, + 'strict_param' => true, + 'array_syntax' => ['syntax' => 'short'], + 'no_unused_imports' => true, + ]) + ->setRiskyAllowed(true) + ->setFinder($finder); diff --git a/lib/composer/bamarni/composer-bin-plugin/LICENSE b/lib/composer/bamarni/composer-bin-plugin/LICENSE new file mode 100644 index 0000000000000..b5b6c2f85d417 --- /dev/null +++ b/lib/composer/bamarni/composer-bin-plugin/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2016 Bilal Amarni + +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: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +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. diff --git a/lib/composer/bamarni/composer-bin-plugin/Makefile b/lib/composer/bamarni/composer-bin-plugin/Makefile new file mode 100644 index 0000000000000..031dfde18e3e4 --- /dev/null +++ b/lib/composer/bamarni/composer-bin-plugin/Makefile @@ -0,0 +1,121 @@ +# See https://tech.davis-hansson.com/p/make/ +MAKEFLAGS += --warn-undefined-variables +MAKEFLAGS += --no-builtin-rules + +# General variables +TOUCH = bash .makefile/touch.sh + +# PHP variables +COMPOSER=composer +COVERAGE_DIR = dist/coverage +INFECTION_BIN = tools/infection +INFECTION = php -d zend.enable_gc=0 $(INFECTION_BIN) --skip-initial-tests --coverage=$(COVERAGE_DIR) --only-covered --threads=4 --min-msi=100 --min-covered-msi=100 --ansi +PHPUNIT_BIN = vendor/bin/phpunit +PHPUNIT = php -d zend.enable_gc=0 $(PHPUNIT_BIN) +PHPUNIT_COVERAGE = XDEBUG_MODE=coverage $(PHPUNIT) --group default --coverage-xml=$(COVERAGE_DIR)/coverage-xml --log-junit=$(COVERAGE_DIR)/phpunit.junit.xml +PHPSTAN_BIN = vendor/bin/phpstan +PHPSTAN = $(PHPSTAN_BIN) analyse --level=5 src tests +PHP_CS_FIXER_BIN = tools/php-cs-fixer +PHP_CS_FIXER = $(PHP_CS_FIXER_BIN) fix --ansi --verbose --config=.php-cs-fixer.php +COMPOSER_NORMALIZE_BIN=tools/composer-normalize +COMPOSER_NORMALIZE = ./$(COMPOSER_NORMALIZE_BIN) + + +.DEFAULT_GOAL := default + + +# +# Command +#--------------------------------------------------------------------------- + +.PHONY: help +help: ## Shows the help +help: + @printf "\033[33mUsage:\033[0m\n make TARGET\n\n\033[32m#\n# Commands\n#---------------------------------------------------------------------------\033[0m\n" + @fgrep -h "##" $(MAKEFILE_LIST) | fgrep -v fgrep | sed -e 's/\\$$//' | sed -e 's/##//' | awk 'BEGIN {FS = ":"}; {printf "\033[33m%s:\033[0m%s\n", $$1, $$2}' + + +.PHONY: default +default: ## Runs the default task: CS fix and all the tests +default: cs test + + +.PHONY: cs +cs: ## Runs PHP-CS-Fixer +cs: $(PHP_CS_FIXER_BIN) $(COMPOSER_NORMALIZE_BIN) + $(PHP_CS_FIXER) + $(COMPOSER_NORMALIZE) + + +.PHONY: phpstan +phpstan: ## Runs PHPStan +phpstan: + $(PHPSTAN) + + +.PHONY: infection +infection: ## Runs infection +infection: $(INFECTION_BIN) $(COVERAGE_DIR) vendor + if [ -d $(COVERAGE_DIR)/coverage-xml ]; then $(INFECTION); fi + + +.PHONY: test +test: ## Runs all the tests +test: validate-package phpstan $(COVERAGE_DIR) e2e #infection include infection later + + +.PHONY: validate-package +validate-package: ## Validates the Composer package +validate-package: vendor + $(COMPOSER) validate --strict + + +.PHONY: coverage +coverage: ## Runs PHPUnit with code coverage +coverage: $(PHPUNIT_BIN) vendor + $(PHPUNIT_COVERAGE) + + +.PHONY: unit-test +unit-test: ## Runs PHPUnit (default group) +unit-test: $(PHPUNIT_BIN) vendor + $(PHPUNIT) --group default + + +.PHONY: e2e +e2e: ## Runs PHPUnit end-to-end tests +e2e: $(PHPUNIT_BIN) vendor + $(PHPUNIT) --group e2e + + +# +# Rules +#--------------------------------------------------------------------------- + +# Vendor does not depend on the composer.lock since the later is not tracked +# or committed. +vendor: composer.json + $(COMPOSER) update + $(TOUCH) "$@" + +$(PHPUNIT_BIN): vendor + $(TOUCH) "$@" + +$(INFECTION_BIN): ./.phive/phars.xml + phive install infection + $(TOUCH) "$@" + +$(COMPOSER_NORMALIZE_BIN): ./.phive/phars.xml + phive install composer-normalize + $(TOUCH) "$@" + +$(COVERAGE_DIR): $(PHPUNIT_BIN) src tests phpunit.xml.dist + $(PHPUNIT_COVERAGE) + $(TOUCH) "$@" + +$(PHP_CS_FIXER_BIN): vendor + phive install php-cs-fixer + $(TOUCH) "$@" + +$(PHPSTAN_BIN): vendor + $(TOUCH) "$@" diff --git a/lib/composer/bamarni/composer-bin-plugin/README.md b/lib/composer/bamarni/composer-bin-plugin/README.md new file mode 100644 index 0000000000000..19509518c1708 --- /dev/null +++ b/lib/composer/bamarni/composer-bin-plugin/README.md @@ -0,0 +1,248 @@ +# Composer bin plugin — Isolate your bin dependencies + +[![Package version](http://img.shields.io/packagist/v/bamarni/composer-bin-plugin.svg?style=flat-square)](https://packagist.org/packages/bamarni/composer-bin-plugin) +[![License](https://img.shields.io/badge/license-MIT-red.svg?style=flat-square)](LICENSE) + + +## Table of Contents + +1. [Why? A hard problem with a simple solution.](#why-a-hard-problem-with-a-simple-solution) +1. [Usage; How does this plugin work?](#usage-how-does-this-plugin-work) +1. [Installation](#installation) +1. [Configuration](#configuration) + 1. [Bin links (`bin-links`)](#bin-links-bin-links) + 1. [Target directory (`target-directory`)](#target-directory-target-directory) + 1. [Forward command (`forward-command`)](#forward-command-forward-command) +1. [Tips & Tricks](#tips--tricks) + 1. [Auto-installation](#auto-installation) + 1. [Reduce clutter](#reduce-clutter) + 1. [GitHub Actions integration](#github-actions-integration) +1. [Related plugins](#related-plugins) +1. [Backward Compatibility Promise](#backward-compatibility-promise) +1. [Contributing](#contributing) + + +## Why? A hard problem with a simple solution. + +When managing your dependencies with [Composer][composer], your dependencies are +flattened with compatible versions, or when not possible, result in conflict +errors. + +There is cases however when adding a tool as a dependency, for example [PHPStan][phpstan]* +or [Rector][rector] could have undesired effects due to the dependencies they +are bringing. For example if phpstan depends on `nikic/php-parser` 4.x and rector +3.x, you cannot install both tools at the same time (despite the fact that from +a usage perspective, they do not need to be compatible). Another example, maybe +you can no longer add a non-dev dependency because a dependency brought by PHPStan +is not compatible with it. + +There is nothing special or exceptional about this problem: this is how dependencies +work in PHP with Composer. It is however annoying in the case highlighted above, +because the conflicts should not be: it is a limitation of Composer because it +cannot infer how you are using each dependency. + +One way to solve the problem above, is to install those dependencies in a +different `composer.json` file. It comes with its caveats, for example if you +were to do that with [PHPUnit][phpunit], you may find yourself in the scenario +where PHPUnit will not be able to execute your tests because your code is not +compatible with it and Composer is not able to tell since the PHPUnit dependency +sits alone in its own `composer.json`. It is the very problem Composer aim to +solve. As a rule of thumb, **you should limit this approach to tools which do not +autoload your code.** + +However, managing several `composer.json` kind be a bit annoying. This plugin +aims at helping you doing this. + + +*: You will in practice not have this problem with PHPStan as the Composer package +`phpstan/phpstan` is shipping a scoped PHAR (scoped via [PHP-Scoper][php-scoper]) +which provides not only a package with no dependencies but as well that has no +risk of conflicting/crash when autoloading your code. + + +## Usage; How does this plugin work? + +This plugin registers a `bin ` command that allows you to +interact with the `vendor-bin//composer.json`* file. + +For example: + +```bash +$ composer bin php-cs-fixer require --dev friendsofphp/php-cs-fixer + +# Equivalent to manually doing: +$ mkdir vendor-bin/php-cs-fixer +$ cd vendor-bin/php-cs-fixer && composer require --dev friendsofphp/php-cs-fixer +``` + +You also have a special `all` namespace to interact with all the bin namespaces: + +```bash +# Runs "composer update" for each bin namespace +$ composer bin all update +``` + + +## Installation + +```bash +$ composer require --dev bamarni/composer-bin-plugin +``` + + +## Configuration + +```json +{ + ... + "extra": { + "bamarni-bin": { + "bin-links": false, + "target-directory": "vendor-bin", + "forward-command": true + } + } +} +``` + + +### Bin links (`bin-links`) + +In 1.x: enabled by default. +In 2.x: disabled by default. + +When installing a Composer package, Composer may add "bin links" to a bin +directory. For example, by default when installing `phpunit/phpunit`, it will +add a symlink `vendor/bin/phpunit` pointing to the PHPUnit script somewhere in +`vendor/phpunit/phpunit`. + +In 1.x, BamarniBinPlugin behaves the same way for "bin dependencies", i.e. when +executing `composer bin php-cs-fixer require --dev friendsofphp/php-cs-fixer`, +it will add a bin link `vendor/bin/php-cs-fixer -> vendor-bin/php-cs-fixer/vendor/friendsofphp/php-cs-fixer`. + +This is however a bit tricky and cannot provide consistent behaviour. For example +when installing several packages with the same bin, (e.g. with the case above installing +another tool that uses PHP-CS-Fixer as a dependency in another bin namespace), +the symlink may or may not be overridden, or not created at all. Since it is not +possible to control this behaviour, neither provide an intuitive or deterministic +approach, it is recommended to set this setting to `false` which will be the +default in 2.x. + +It does mean that instead of using `vendor/bin/php-cs-fixer` you will have to +use `vendor-bin/php-cs-fixer/vendor/friendsofphp/php-cs-fixer/path/tophp-cs-fixer` +(in which case setting an alias via a Composer script or something is recommended). + + +### Target directory (`target-directory`) + +Defaults to `vendor-bin`, can be overridden to anything you wish. + + +### Forward command (`forward-command`) + +Disabled by default in 1.x, will be enabled by default in 2.x. If this mode is +enabled, all your `composer install` and `composer update` commands are forwarded +to _all_ bin directories. + +This is a replacement for the tasks shown in section [Auto-installation](#auto-installation). + + +## Tips & Tricks + +### Auto-installation + +You can easily forward a command upon a `composer install` to forward the install +to all (in which case having `extra.bamarni-bin.forward_command = true` is more +adapted) or a specific of bin namespace: + +```json +{ + "scripts": { + "bin": "echo 'bin not installed'", + "post-install-cmd": ["@composer bin php-cs-fixer install --ansi"] + } +} +``` + +You can customise this as you wish leveraging the [Composer script events][composer-script-events]). + + +### Reduce clutter + +You can add the following line to your `.gitignore` file in order to avoid +committing dependencies of your tools. + +```.gitignore +# .gitignore +/vendor-bin/**/vendor/ +``` + +Updating each tool can create many not legible changes in `composer.lock` files. +You can use a `.gitattributes` file in order to inform git that it shouldn't show +diffs of `composer.lock` files. + +```.gitattributes +# .gitattributes +/vendor-bin/**/composer.lock binary +``` + +### GitHub Actions integration + +There is currently no way to leverage `ramsey/composer-install` to install all +namespace bins. However it is unlikely you need this in the CI and not locally, +in which case [forwarding the command](#forward-command-forward-command) should +be good enough. + +If you still need to install specific bin namespaces, you can do it by setting +the `working-directory`: + +```yaml +#... + - name: "Install PHP-CS-Fixer Composer dependencies" + uses: "ramsey/composer-install@v2" + with: + working-directory: "vendor-bin/php-cs-fixer" +``` + + + +## Related plugins + +* [theofidry/composer-inheritance-plugin][theofidry-composer-inheritance-plugin]: Opinionated version + of [Wikimedia composer-merge-plugin][wikimedia-composer-merge-plugin] to work in pair with this plugin. + + +## Backward Compatibility Promise + +The backward compatibility promise only applies to the following API: + +- The commands registered by the plugin +- The behaviour of the commands (but not their logging/output) +- The Composer configuration + +The plugin implementation is considered to be strictly internal and its code may +change at any time in a non back-ward compatible way. + + +## Contributing + +A makefile is available to help out: + +```bash +$ make # Runs all checks +$ make help # List all available commands +``` + +**Note:** you do need to install [phive][phive] first. + + +[composer]: https://getcomposer.org +[composer-script-events]: https://getcomposer.org/doc/articles/scripts.md#command-events +[phive]: https://phar.io/ +[php-scoper]: https://github.com/humbug/php-scoper +[phpstan]: https://phpstan.org/ +[phpunit]: https://github.com/sebastianbergmann/phpunit +[rector]: https://github.com/rectorphp/rector +[symfony-bc-policy]: https://symfony.com/doc/current/contributing/code/bc.html +[theofidry-composer-inheritance-plugin]: https://github.com/theofidry/composer-inheritance-plugin +[wikimedia-composer-merge-plugin]: https://github.com/wikimedia/composer-merge-plugin diff --git a/lib/composer/bamarni/composer-bin-plugin/composer.json b/lib/composer/bamarni/composer-bin-plugin/composer.json new file mode 100644 index 0000000000000..5b3809ba2de63 --- /dev/null +++ b/lib/composer/bamarni/composer-bin-plugin/composer.json @@ -0,0 +1,50 @@ +{ + "name": "bamarni/composer-bin-plugin", + "description": "No conflicts for your bin dependencies", + "license": "MIT", + "type": "composer-plugin", + "keywords": [ + "composer", + "dependency", + "tool", + "isolation", + "conflict", + "executable" + ], + "require": { + "php": "^7.2.5 || ^8.0", + "composer-plugin-api": "^2.0" + }, + "require-dev": { + "ext-json": "*", + "composer/composer": "^2.0", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-phpunit": "^1.1", + "phpunit/phpunit": "^8.5 || ^9.5", + "symfony/console": "^2.8.52 || ^3.4.35 || ^4.4 || ^5.0 || ^6.0", + "symfony/finder": "^2.8.52 || ^3.4.35 || ^4.4 || ^5.0 || ^6.0", + "symfony/process": "^2.8.52 || ^3.4.35 || ^4.4 || ^5.0 || ^6.0" + }, + "autoload": { + "psr-4": { + "Bamarni\\Composer\\Bin\\": "src" + } + }, + "autoload-dev": { + "psr-4": { + "Bamarni\\Composer\\Bin\\Tests\\": "tests" + } + }, + "config": { + "allow-plugins": { + "phpstan/extension-installer": true, + "ergebnis/composer-normalize": true, + "infection/extension-installer": true + }, + "sort-packages": true + }, + "extra": { + "class": "Bamarni\\Composer\\Bin\\BamarniBinPlugin" + } +} diff --git a/lib/composer/bamarni/composer-bin-plugin/e2e/scenario0/README.md b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario0/README.md new file mode 100644 index 0000000000000..6b2a3c9035e8e --- /dev/null +++ b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario0/README.md @@ -0,0 +1 @@ +Regular installation on a project with multiple namespaces. diff --git a/lib/composer/bamarni/composer-bin-plugin/e2e/scenario0/composer.json b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario0/composer.json new file mode 100644 index 0000000000000..dff4cca9137e1 --- /dev/null +++ b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario0/composer.json @@ -0,0 +1,16 @@ +{ + "repositories": [ + { + "type": "path", + "url": "../../" + } + ], + "require-dev": { + "bamarni/composer-bin-plugin": "dev-master" + }, + "config": { + "allow-plugins": { + "bamarni/composer-bin-plugin": true + } + } +} diff --git a/lib/composer/bamarni/composer-bin-plugin/e2e/scenario0/expected.txt b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario0/expected.txt new file mode 100644 index 0000000000000..a192945fdb862 --- /dev/null +++ b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario0/expected.txt @@ -0,0 +1,16 @@ +[bamarni-bin] Checking namespace vendor-bin/ns1 +Loading composer repositories with package information +Updating dependencies +Nothing to modify in lock file +Writing lock file +Installing dependencies from lock file (including require-dev) +Nothing to install, update or remove +Generating autoload files +[bamarni-bin] Checking namespace vendor-bin/ns2 +Loading composer repositories with package information +Updating dependencies +Nothing to modify in lock file +Writing lock file +Installing dependencies from lock file (including require-dev) +Nothing to install, update or remove +Generating autoload files diff --git a/lib/composer/bamarni/composer-bin-plugin/e2e/scenario0/script.sh b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario0/script.sh new file mode 100755 index 0000000000000..75e81f0fc93e4 --- /dev/null +++ b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario0/script.sh @@ -0,0 +1,28 @@ +#!/usr/bin/env bash + +set -Eeuo pipefail + +# Set env variables in order to experience a behaviour closer to what happens +# in the CI locally. It should not hurt to set those in the CI as the CI should +# contain those values. +export CI=1 +export COMPOSER_NO_INTERACTION=1 + +readonly ORIGINAL_WORKING_DIR=$(pwd) + +trap "cd ${ORIGINAL_WORKING_DIR}" err exit + +# Change to script directory +cd "$(dirname "$0")" + +# Ensure we have a clean state +rm -rf actual.txt || true +rm -rf composer.lock || true +rm -rf vendor || true +rm -rf vendor-bin/*/composer.lock || true +rm -rf vendor-bin/*/vendor || true + +composer update + +# Actual command to execute the test itself +composer bin all update 2>&1 | tee > actual.txt diff --git a/lib/composer/bamarni/composer-bin-plugin/e2e/scenario0/vendor-bin/ns1/composer.json b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario0/vendor-bin/ns1/composer.json new file mode 100644 index 0000000000000..0967ef424bce6 --- /dev/null +++ b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario0/vendor-bin/ns1/composer.json @@ -0,0 +1 @@ +{} diff --git a/lib/composer/bamarni/composer-bin-plugin/e2e/scenario0/vendor-bin/ns2/composer.json b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario0/vendor-bin/ns2/composer.json new file mode 100644 index 0000000000000..0967ef424bce6 --- /dev/null +++ b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario0/vendor-bin/ns2/composer.json @@ -0,0 +1 @@ +{} diff --git a/lib/composer/bamarni/composer-bin-plugin/e2e/scenario1/README.md b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario1/README.md new file mode 100644 index 0000000000000..4552c2690f911 --- /dev/null +++ b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario1/README.md @@ -0,0 +1 @@ +Regular installation on a project with multiple namespaces in verbose mode. diff --git a/lib/composer/bamarni/composer-bin-plugin/e2e/scenario1/composer.json b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario1/composer.json new file mode 100644 index 0000000000000..dff4cca9137e1 --- /dev/null +++ b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario1/composer.json @@ -0,0 +1,16 @@ +{ + "repositories": [ + { + "type": "path", + "url": "../../" + } + ], + "require-dev": { + "bamarni/composer-bin-plugin": "dev-master" + }, + "config": { + "allow-plugins": { + "bamarni/composer-bin-plugin": true + } + } +} diff --git a/lib/composer/bamarni/composer-bin-plugin/e2e/scenario1/expected.txt b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario1/expected.txt new file mode 100644 index 0000000000000..d0dcc1cbdd6b7 --- /dev/null +++ b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario1/expected.txt @@ -0,0 +1,28 @@ +[bamarni-bin] Current working directory: /path/to/project/e2e/scenario1 +[bamarni-bin] Configuring bin directory to /path/to/project/e2e/scenario1/vendor/bin. +[bamarni-bin] Checking namespace vendor-bin/ns1 +[bamarni-bin] Changed current directory to vendor-bin/ns1. +[bamarni-bin] Running `@composer update --verbose --working-dir='.'`. +Loading composer repositories with package information +Updating dependencies +Analyzed 90 packages to resolve dependencies +Analyzed 90 rules to resolve dependencies +Nothing to modify in lock file +Writing lock file +Installing dependencies from lock file (including require-dev) +Nothing to install, update or remove +Generating autoload files +[bamarni-bin] Changed current directory to /path/to/project/e2e/scenario1. +[bamarni-bin] Checking namespace vendor-bin/ns2 +[bamarni-bin] Changed current directory to vendor-bin/ns2. +[bamarni-bin] Running `@composer update --verbose --working-dir='.'`. +Loading composer repositories with package information +Updating dependencies +Analyzed 90 packages to resolve dependencies +Analyzed 90 rules to resolve dependencies +Nothing to modify in lock file +Writing lock file +Installing dependencies from lock file (including require-dev) +Nothing to install, update or remove +Generating autoload files +[bamarni-bin] Changed current directory to /path/to/project/e2e/scenario1. diff --git a/lib/composer/bamarni/composer-bin-plugin/e2e/scenario1/script.sh b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario1/script.sh new file mode 100755 index 0000000000000..4edb432137d9d --- /dev/null +++ b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario1/script.sh @@ -0,0 +1,28 @@ +#!/usr/bin/env bash + +set -Eeuo pipefail + +# Set env variables in order to experience a behaviour closer to what happens +# in the CI locally. It should not hurt to set those in the CI as the CI should +# contain those values. +export CI=1 +export COMPOSER_NO_INTERACTION=1 + +readonly ORIGINAL_WORKING_DIR=$(pwd) + +trap "cd ${ORIGINAL_WORKING_DIR}" err exit + +# Change to script directory +cd "$(dirname "$0")" + +# Ensure we have a clean state +rm -rf actual.txt || true +rm -rf composer.lock || true +rm -rf vendor || true +rm -rf vendor-bin/*/composer.lock || true +rm -rf vendor-bin/*/vendor || true + +composer update + +# Actual command to execute the test itself +composer bin all update --verbose 2>&1 | tee > actual.txt diff --git a/lib/composer/bamarni/composer-bin-plugin/e2e/scenario1/vendor-bin/ns1/composer.json b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario1/vendor-bin/ns1/composer.json new file mode 100644 index 0000000000000..0967ef424bce6 --- /dev/null +++ b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario1/vendor-bin/ns1/composer.json @@ -0,0 +1 @@ +{} diff --git a/lib/composer/bamarni/composer-bin-plugin/e2e/scenario1/vendor-bin/ns2/composer.json b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario1/vendor-bin/ns2/composer.json new file mode 100644 index 0000000000000..0967ef424bce6 --- /dev/null +++ b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario1/vendor-bin/ns2/composer.json @@ -0,0 +1 @@ +{} diff --git a/lib/composer/bamarni/composer-bin-plugin/e2e/scenario10/README.md b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario10/README.md new file mode 100644 index 0000000000000..0ffbf033d53dd --- /dev/null +++ b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario10/README.md @@ -0,0 +1 @@ +Check that the composer environment variables are well respected when commands are forwarded to the namespaces. diff --git a/lib/composer/bamarni/composer-bin-plugin/e2e/scenario10/composer.json b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario10/composer.json new file mode 100644 index 0000000000000..3061d0c0f64cf --- /dev/null +++ b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario10/composer.json @@ -0,0 +1,21 @@ +{ + "repositories": [ + { + "type": "path", + "url": "../../" + } + ], + "require": { + "bamarni/composer-bin-plugin": "dev-master" + }, + "config": { + "allow-plugins": { + "bamarni/composer-bin-plugin": true + } + }, + "extra": { + "bamarni-bin": { + "forward-command": true + } + } +} diff --git a/lib/composer/bamarni/composer-bin-plugin/e2e/scenario10/expected.txt b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario10/expected.txt new file mode 100644 index 0000000000000..033cf0b13f59f --- /dev/null +++ b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario10/expected.txt @@ -0,0 +1,2 @@ +./.composer +.composer diff --git a/lib/composer/bamarni/composer-bin-plugin/e2e/scenario10/script.sh b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario10/script.sh new file mode 100755 index 0000000000000..20bc504249c31 --- /dev/null +++ b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario10/script.sh @@ -0,0 +1,31 @@ +#!/usr/bin/env bash + +set -Eeuo pipefail + +# Set env variables in order to experience a behaviour closer to what happens +# in the CI locally. It should not hurt to set those in the CI as the CI should +# contain those values. +export CI=1 +export COMPOSER_NO_INTERACTION=1 + +readonly ORIGINAL_WORKING_DIR=$(pwd) + +trap "cd ${ORIGINAL_WORKING_DIR}" err exit + +# Change to script directory +cd "$(dirname "$0")" + +# Ensure we have a clean state +rm -rf actual.txt || true +rm -rf .composer || true +rm -rf composer.lock || true +rm -rf vendor || true +rm -rf vendor-bin/*/composer.lock || true +rm -rf vendor-bin/*/vendor || true +rm -rf vendor-bin/*/.composer || true + +readonly CUSTOM_COMPOSER_DIR=$(pwd)/.composer +COMPOSER_CACHE_DIR=$CUSTOM_COMPOSER_DIR composer update + +# Actual command to execute the test itself +find . ".composer" -name ".composer" -type d 2>&1 | sort -n | tee > actual.txt || true diff --git a/lib/composer/bamarni/composer-bin-plugin/e2e/scenario10/vendor-bin/ns1/composer.json b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario10/vendor-bin/ns1/composer.json new file mode 100644 index 0000000000000..9871ea3c5844a --- /dev/null +++ b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario10/vendor-bin/ns1/composer.json @@ -0,0 +1,5 @@ +{ + "require": { + "nikic/iter": "v1.6.0" + } +} diff --git a/lib/composer/bamarni/composer-bin-plugin/e2e/scenario11/README.md b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario11/README.md new file mode 100644 index 0000000000000..03ab64e1ec69b --- /dev/null +++ b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario11/README.md @@ -0,0 +1 @@ +Check that the deprecation messages are well rendered. diff --git a/lib/composer/bamarni/composer-bin-plugin/e2e/scenario11/composer.json b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario11/composer.json new file mode 100644 index 0000000000000..3061d0c0f64cf --- /dev/null +++ b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario11/composer.json @@ -0,0 +1,21 @@ +{ + "repositories": [ + { + "type": "path", + "url": "../../" + } + ], + "require": { + "bamarni/composer-bin-plugin": "dev-master" + }, + "config": { + "allow-plugins": { + "bamarni/composer-bin-plugin": true + } + }, + "extra": { + "bamarni-bin": { + "forward-command": true + } + } +} diff --git a/lib/composer/bamarni/composer-bin-plugin/e2e/scenario11/expected.txt b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario11/expected.txt new file mode 100644 index 0000000000000..1d2c3f39b129b --- /dev/null +++ b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario11/expected.txt @@ -0,0 +1,19 @@ +Loading composer repositories with package information +Updating dependencies +Lock file operations: 1 install, 0 updates, 0 removals + - Locking bamarni/composer-bin-plugin (dev-hash) +Writing lock file +Installing dependencies from lock file (including require-dev) +Package operations: 1 install, 0 updates, 0 removals + - Installing bamarni/composer-bin-plugin (dev-hash): Symlinking from ../.. +Generating autoload files +[bamarni-bin] The setting "extra.bamarni-bin.bin-links" will be set to "false" from 2.x onwards. If you wish to keep it to "true", you need to set it explicitly. +[bamarni-bin] The command is being forwarded. +[bamarni-bin] Checking namespace vendor-bin/ns1 +Loading composer repositories with package information +Updating dependencies +Nothing to modify in lock file +Writing lock file +Installing dependencies from lock file (including require-dev) +Nothing to install, update or remove +Generating autoload files diff --git a/lib/composer/bamarni/composer-bin-plugin/e2e/scenario11/script.sh b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario11/script.sh new file mode 100755 index 0000000000000..ef69309292545 --- /dev/null +++ b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario11/script.sh @@ -0,0 +1,28 @@ +#!/usr/bin/env bash + +set -Eeuo pipefail + +# Set env variables in order to experience a behaviour closer to what happens +# in the CI locally. It should not hurt to set those in the CI as the CI should +# contain those values. +export CI=1 +export COMPOSER_NO_INTERACTION=1 + +readonly ORIGINAL_WORKING_DIR=$(pwd) + +trap "cd ${ORIGINAL_WORKING_DIR}" err exit + +# Change to script directory +cd "$(dirname "$0")" + +# Ensure we have a clean state +rm -rf actual.txt || true +rm -rf .composer || true +rm -rf composer.lock || true +rm -rf vendor || true +rm -rf vendor-bin/*/composer.lock || true +rm -rf vendor-bin/*/vendor || true +rm -rf vendor-bin/*/.composer || true + +# Actual command to execute the test itself +composer update 2>&1 | tee > actual.txt diff --git a/lib/composer/bamarni/composer-bin-plugin/e2e/scenario11/vendor-bin/ns1/composer.json b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario11/vendor-bin/ns1/composer.json new file mode 100644 index 0000000000000..0967ef424bce6 --- /dev/null +++ b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario11/vendor-bin/ns1/composer.json @@ -0,0 +1 @@ +{} diff --git a/lib/composer/bamarni/composer-bin-plugin/e2e/scenario12/README.md b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario12/README.md new file mode 100644 index 0000000000000..03ab64e1ec69b --- /dev/null +++ b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario12/README.md @@ -0,0 +1 @@ +Check that the deprecation messages are well rendered. diff --git a/lib/composer/bamarni/composer-bin-plugin/e2e/scenario12/composer.json b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario12/composer.json new file mode 100644 index 0000000000000..ae4fe648b6a3e --- /dev/null +++ b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario12/composer.json @@ -0,0 +1,22 @@ +{ + "repositories": [ + { + "type": "path", + "url": "../../" + } + ], + "require": { + "bamarni/composer-bin-plugin": "dev-master", + "psr/log": "=3.0.0" + }, + "config": { + "allow-plugins": { + "bamarni/composer-bin-plugin": true + } + }, + "extra": { + "bamarni-bin": { + "forward-command": true + } + } +} diff --git a/lib/composer/bamarni/composer-bin-plugin/e2e/scenario12/expected.txt b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario12/expected.txt new file mode 100644 index 0000000000000..2965cc86e515b --- /dev/null +++ b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario12/expected.txt @@ -0,0 +1,8 @@ +Get versions installed in root; executed from root. +3.0.0 +Get versions installed in vendor-bin/tool; executed from root. +2.0.0 +Get versions installed in root; executed from vendor-bin/tool. +3.0.0 +Get versions installed in vendor-bin/too; executed from vendor-bin/tool. +2.0.0 diff --git a/lib/composer/bamarni/composer-bin-plugin/e2e/scenario12/root_root.php b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario12/root_root.php new file mode 100644 index 0000000000000..d6ba00c176a31 --- /dev/null +++ b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario12/root_root.php @@ -0,0 +1,10 @@ +> actual.txt +php root_vendorbin.php | tee >> actual.txt +php vendor-bin/tool/root_root.php | tee >> actual.txt +php vendor-bin/tool/root_vendorbin.php | tee >> actual.txt diff --git a/lib/composer/bamarni/composer-bin-plugin/e2e/scenario12/vendor-bin/tool/composer.json b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario12/vendor-bin/tool/composer.json new file mode 100644 index 0000000000000..ff49ff83f4e10 --- /dev/null +++ b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario12/vendor-bin/tool/composer.json @@ -0,0 +1,5 @@ +{ + "require": { + "psr/log": "=2.0.0" + } +} diff --git a/lib/composer/bamarni/composer-bin-plugin/e2e/scenario12/vendor-bin/tool/root_root.php b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario12/vendor-bin/tool/root_root.php new file mode 100644 index 0000000000000..ac9c40fd5c90b --- /dev/null +++ b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario12/vendor-bin/tool/root_root.php @@ -0,0 +1,10 @@ + echo ns1 +ns1 +[bamarni-bin] Checking namespace vendor-bin/ns2 +> echo ns2 +ns2 diff --git a/lib/composer/bamarni/composer-bin-plugin/e2e/scenario2/script.sh b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario2/script.sh new file mode 100755 index 0000000000000..f01dc754c47c3 --- /dev/null +++ b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario2/script.sh @@ -0,0 +1,28 @@ +#!/usr/bin/env bash + +set -Eeuo pipefail + +# Set env variables in order to experience a behaviour closer to what happens +# in the CI locally. It should not hurt to set those in the CI as the CI should +# contain those values. +export CI=1 +export COMPOSER_NO_INTERACTION=1 + +readonly ORIGINAL_WORKING_DIR=$(pwd) + +trap "cd ${ORIGINAL_WORKING_DIR}" err exit + +# Change to script directory +cd "$(dirname "$0")" + +# Ensure we have a clean state +rm -rf actual.txt || true +rm -rf composer.lock || true +rm -rf vendor || true +rm -rf vendor-bin/*/composer.lock || true +rm -rf vendor-bin/*/vendor || true + +composer update + +# Actual command to execute the test itself +composer bin all run-script foo 2>&1 | tee > actual.txt diff --git a/lib/composer/bamarni/composer-bin-plugin/e2e/scenario2/vendor-bin/ns1/composer.json b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario2/vendor-bin/ns1/composer.json new file mode 100644 index 0000000000000..7f276e92124e8 --- /dev/null +++ b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario2/vendor-bin/ns1/composer.json @@ -0,0 +1,5 @@ +{ + "scripts": { + "foo": "echo ns1" + } +} diff --git a/lib/composer/bamarni/composer-bin-plugin/e2e/scenario2/vendor-bin/ns2/composer.json b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario2/vendor-bin/ns2/composer.json new file mode 100644 index 0000000000000..3b8933bde7a50 --- /dev/null +++ b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario2/vendor-bin/ns2/composer.json @@ -0,0 +1,5 @@ +{ + "scripts": { + "foo": "echo ns2" + } +} diff --git a/lib/composer/bamarni/composer-bin-plugin/e2e/scenario3/README.md b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario3/README.md new file mode 100644 index 0000000000000..ad1c3b8ecf34f --- /dev/null +++ b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario3/README.md @@ -0,0 +1 @@ +Regular installation on a project with forwarded mode enabled. diff --git a/lib/composer/bamarni/composer-bin-plugin/e2e/scenario3/composer.json b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario3/composer.json new file mode 100644 index 0000000000000..a1cc7371977bf --- /dev/null +++ b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario3/composer.json @@ -0,0 +1,22 @@ +{ + "repositories": [ + { + "type": "path", + "url": "../../" + } + ], + "require-dev": { + "bamarni/composer-bin-plugin": "dev-master" + }, + "config": { + "allow-plugins": { + "bamarni/composer-bin-plugin": true + } + }, + "extra": { + "bamarni-bin": { + "bin-links": false, + "forward-command": true + } + } +} diff --git a/lib/composer/bamarni/composer-bin-plugin/e2e/scenario3/expected.txt b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario3/expected.txt new file mode 100644 index 0000000000000..133ffe77ce95c --- /dev/null +++ b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario3/expected.txt @@ -0,0 +1,59 @@ +Loading composer repositories with package information +Updating dependencies +Analyzed 90 packages to resolve dependencies +Analyzed 90 rules to resolve dependencies +Lock file operations: 1 install, 0 updates, 0 removals +Installs: bamarni/composer-bin-plugin:dev-hash + - Locking bamarni/composer-bin-plugin (dev-hash) +Writing lock file +Installing dependencies from lock file (including require-dev) +Package operations: 1 install, 0 updates, 0 removals +Installs: bamarni/composer-bin-plugin:dev-hash + - Installing bamarni/composer-bin-plugin (dev-hash): Symlinking from ../.. +Generating autoload files +> post-autoload-dump: Bamarni\Composer\Bin\BamarniBinPlugin->onPostAutoloadDump +[bamarni-bin] Calling onPostAutoloadDump(). +[bamarni-bin] The command is being forwarded. +[bamarni-bin] Original input: update --verbose. +[bamarni-bin] Current working directory: /path/to/project/e2e/scenario3 +[bamarni-bin] Checking namespace vendor-bin/ns1 +[bamarni-bin] Changed current directory to vendor-bin/ns1. +[bamarni-bin] Running `@composer update --verbose --working-dir='.'`. +Loading composer repositories with package information +Updating dependencies +Analyzed 90 packages to resolve dependencies +Analyzed 90 rules to resolve dependencies +Nothing to modify in lock file +Writing lock file +Installing dependencies from lock file (including require-dev) +Nothing to install, update or remove +Generating autoload files +[bamarni-bin] Changed current directory to /path/to/project/e2e/scenario3. +––––––––––––––––––––– +[bamarni-bin] Calling onCommandEvent(). +[bamarni-bin] The command is being forwarded. +[bamarni-bin] Original input: update --verbose. +[bamarni-bin] Current working directory: /path/to/project/e2e/scenario3 +[bamarni-bin] Checking namespace vendor-bin/ns1 +[bamarni-bin] Changed current directory to vendor-bin/ns1. +[bamarni-bin] Running `@composer update --verbose --working-dir='.'`. +Loading composer repositories with package information +Updating dependencies +Analyzed 90 packages to resolve dependencies +Analyzed 90 rules to resolve dependencies +Nothing to modify in lock file +Installing dependencies from lock file (including require-dev) +Nothing to install, update or remove +Generating autoload files +[bamarni-bin] Changed current directory to /path/to/project/e2e/scenario3. +Loading composer repositories with package information +Updating dependencies +Analyzed 90 packages to resolve dependencies +Analyzed 90 rules to resolve dependencies +Nothing to modify in lock file +Installing dependencies from lock file (including require-dev) +Nothing to install, update or remove +Generating autoload files +> post-autoload-dump: Bamarni\Composer\Bin\BamarniBinPlugin->onPostAutoloadDump +[bamarni-bin] Calling onPostAutoloadDump(). +[bamarni-bin] Command already forwarded within the process: skipping. diff --git a/lib/composer/bamarni/composer-bin-plugin/e2e/scenario3/script.sh b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario3/script.sh new file mode 100755 index 0000000000000..b66fa41f33f37 --- /dev/null +++ b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario3/script.sh @@ -0,0 +1,28 @@ +#!/usr/bin/env bash + +set -Eeuo pipefail + +# Set env variables in order to experience a behaviour closer to what happens +# in the CI locally. It should not hurt to set those in the CI as the CI should +# contain those values. +export CI=1 +export COMPOSER_NO_INTERACTION=1 + +readonly ORIGINAL_WORKING_DIR=$(pwd) + +trap "cd ${ORIGINAL_WORKING_DIR}" err exit + +# Change to script directory +cd "$(dirname "$0")" + +# Ensure we have a clean state +rm -rf actual.txt || true +rm -rf composer.lock || true +rm -rf vendor || true +rm -rf vendor-bin/*/composer.lock || true +rm -rf vendor-bin/*/vendor || true + +# Actual command to execute the test itself +composer update --verbose 2>&1 | tee > actual.txt +echo "–––––––––––––––––––––" >> actual.txt +composer update --verbose 2>&1 | tee >> actual.txt diff --git a/lib/composer/bamarni/composer-bin-plugin/e2e/scenario3/vendor-bin/ns1/composer.json b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario3/vendor-bin/ns1/composer.json new file mode 100644 index 0000000000000..0967ef424bce6 --- /dev/null +++ b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario3/vendor-bin/ns1/composer.json @@ -0,0 +1 @@ +{} diff --git a/lib/composer/bamarni/composer-bin-plugin/e2e/scenario5/README.md b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario5/README.md new file mode 100644 index 0000000000000..71f75d81a8890 --- /dev/null +++ b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario5/README.md @@ -0,0 +1 @@ +Regular installation on a project with multiple namespaces and with links disabled. diff --git a/lib/composer/bamarni/composer-bin-plugin/e2e/scenario5/composer.json b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario5/composer.json new file mode 100644 index 0000000000000..3d06a59b991d0 --- /dev/null +++ b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario5/composer.json @@ -0,0 +1,21 @@ +{ + "repositories": [ + { + "type": "path", + "url": "../../" + } + ], + "require-dev": { + "bamarni/composer-bin-plugin": "dev-master" + }, + "config": { + "allow-plugins": { + "bamarni/composer-bin-plugin": true + } + }, + "extra": { + "bamarni-bin": { + "bin-links": false + } + } +} diff --git a/lib/composer/bamarni/composer-bin-plugin/e2e/scenario5/expected.txt b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario5/expected.txt new file mode 100644 index 0000000000000..3cdb7f2600e68 --- /dev/null +++ b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario5/expected.txt @@ -0,0 +1,5 @@ +find: vendor/bin: No such file or directory +vendor-bin/ns1/vendor/bin/phpstan +vendor-bin/ns1/vendor/bin/phpstan.phar +vendor-bin/ns2/vendor/bin/phpstan +vendor-bin/ns2/vendor/bin/phpstan.phar diff --git a/lib/composer/bamarni/composer-bin-plugin/e2e/scenario5/script.sh b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario5/script.sh new file mode 100755 index 0000000000000..7ada5a180b57f --- /dev/null +++ b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario5/script.sh @@ -0,0 +1,29 @@ +#!/usr/bin/env bash + +set -Eeuo pipefail + +# Set env variables in order to experience a behaviour closer to what happens +# in the CI locally. It should not hurt to set those in the CI as the CI should +# contain those values. +export CI=1 +export COMPOSER_NO_INTERACTION=1 + +readonly ORIGINAL_WORKING_DIR=$(pwd) + +trap "cd ${ORIGINAL_WORKING_DIR}" err exit + +# Change to script directory +cd "$(dirname "$0")" + +# Ensure we have a clean state +rm -rf actual.txt || true +rm -rf composer.lock || true +rm -rf vendor || true +rm -rf vendor-bin/*/composer.lock || true +rm -rf vendor-bin/*/vendor || true + +composer update +composer bin all update + +# Actual command to execute the test itself +find vendor/bin vendor-bin/*/vendor/bin -maxdepth 1 -type f 2>&1 | sort -n | tee > actual.txt || true diff --git a/lib/composer/bamarni/composer-bin-plugin/e2e/scenario5/vendor-bin/ns1/composer.json b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario5/vendor-bin/ns1/composer.json new file mode 100644 index 0000000000000..ba924f72ab710 --- /dev/null +++ b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario5/vendor-bin/ns1/composer.json @@ -0,0 +1,5 @@ +{ + "require": { + "phpstan/phpstan": "1.8.0" + } +} diff --git a/lib/composer/bamarni/composer-bin-plugin/e2e/scenario5/vendor-bin/ns2/composer.json b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario5/vendor-bin/ns2/composer.json new file mode 100644 index 0000000000000..ba924f72ab710 --- /dev/null +++ b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario5/vendor-bin/ns2/composer.json @@ -0,0 +1,5 @@ +{ + "require": { + "phpstan/phpstan": "1.8.0" + } +} diff --git a/lib/composer/bamarni/composer-bin-plugin/e2e/scenario6/README.md b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario6/README.md new file mode 100644 index 0000000000000..2174f999f0ff6 --- /dev/null +++ b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario6/README.md @@ -0,0 +1 @@ +Regular installation on a project with multiple namespaces and with links enabled and conflicting symlinks. diff --git a/lib/composer/bamarni/composer-bin-plugin/e2e/scenario6/composer.json b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario6/composer.json new file mode 100644 index 0000000000000..23f275bf4ef69 --- /dev/null +++ b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario6/composer.json @@ -0,0 +1,21 @@ +{ + "repositories": [ + { + "type": "path", + "url": "../../" + } + ], + "require-dev": { + "bamarni/composer-bin-plugin": "dev-master" + }, + "config": { + "allow-plugins": { + "bamarni/composer-bin-plugin": true + } + }, + "extra": { + "bamarni-bin": { + "bin-links": true + } + } +} diff --git a/lib/composer/bamarni/composer-bin-plugin/e2e/scenario6/expected.txt b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario6/expected.txt new file mode 100644 index 0000000000000..25f159fc2c648 --- /dev/null +++ b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario6/expected.txt @@ -0,0 +1,4 @@ +find: vendor-bin/*/vendor/bin: No such file or directory +vendor/bin/phpstan +vendor/bin/phpstan.phar +PHPStan - PHP Static Analysis Tool 1.8.0 diff --git a/lib/composer/bamarni/composer-bin-plugin/e2e/scenario6/script.sh b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario6/script.sh new file mode 100755 index 0000000000000..c79a745371015 --- /dev/null +++ b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario6/script.sh @@ -0,0 +1,30 @@ +#!/usr/bin/env bash + +set -Eeuo pipefail + +# Set env variables in order to experience a behaviour closer to what happens +# in the CI locally. It should not hurt to set those in the CI as the CI should +# contain those values. +export CI=1 +export COMPOSER_NO_INTERACTION=1 + +readonly ORIGINAL_WORKING_DIR=$(pwd) + +trap "cd ${ORIGINAL_WORKING_DIR}" err exit + +# Change to script directory +cd "$(dirname "$0")" + +# Ensure we have a clean state +rm -rf actual.txt || true +rm -rf composer.lock || true +rm -rf vendor || true +rm -rf vendor-bin/*/composer.lock || true +rm -rf vendor-bin/*/vendor || true + +composer update +composer bin all update + +# Actual command to execute the test itself +find vendor/bin vendor-bin/*/vendor/bin -maxdepth 1 -type f 2>&1 | sort -n | tee > actual.txt || true +vendor/bin/phpstan --version >> actual.txt diff --git a/lib/composer/bamarni/composer-bin-plugin/e2e/scenario6/vendor-bin/ns1/composer.json b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario6/vendor-bin/ns1/composer.json new file mode 100644 index 0000000000000..ba924f72ab710 --- /dev/null +++ b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario6/vendor-bin/ns1/composer.json @@ -0,0 +1,5 @@ +{ + "require": { + "phpstan/phpstan": "1.8.0" + } +} diff --git a/lib/composer/bamarni/composer-bin-plugin/e2e/scenario6/vendor-bin/ns2/composer.json b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario6/vendor-bin/ns2/composer.json new file mode 100644 index 0000000000000..7826cef2072f8 --- /dev/null +++ b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario6/vendor-bin/ns2/composer.json @@ -0,0 +1,5 @@ +{ + "require": { + "phpstan/phpstan": "1.6.0" + } +} diff --git a/lib/composer/bamarni/composer-bin-plugin/e2e/scenario7/README.md b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario7/README.md new file mode 100644 index 0000000000000..87140b4f1b3fb --- /dev/null +++ b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario7/README.md @@ -0,0 +1 @@ +Tests that dev dependencies are not installed if no-dev is passed. diff --git a/lib/composer/bamarni/composer-bin-plugin/e2e/scenario7/composer.json b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario7/composer.json new file mode 100644 index 0000000000000..bff880fd74709 --- /dev/null +++ b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario7/composer.json @@ -0,0 +1,16 @@ +{ + "repositories": [ + { + "type": "path", + "url": "../../" + } + ], + "require": { + "bamarni/composer-bin-plugin": "dev-master" + }, + "config": { + "allow-plugins": { + "bamarni/composer-bin-plugin": true + } + } +} diff --git a/lib/composer/bamarni/composer-bin-plugin/e2e/scenario7/expected.txt b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario7/expected.txt new file mode 100644 index 0000000000000..c00cece9cb983 --- /dev/null +++ b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario7/expected.txt @@ -0,0 +1,10 @@ +[bamarni-bin] Current working directory: /path/to/project/e2e/scenario7 +[bamarni-bin] Configuring bin directory to /path/to/project/e2e/scenario7/vendor/bin. +[bamarni-bin] Checking namespace vendor-bin/ns1 +[bamarni-bin] Changed current directory to vendor-bin/ns1. +[bamarni-bin] Running `@composer update --no-dev --verbose --working-dir='.' -- foo`. +Cannot update only a partial set of packages without a lock file present. Run `composer update` to generate a lock file. +[bamarni-bin] Changed current directory to /path/to/project/e2e/scenario7. +––––––––––––––––––––– +[bamarni-bin] Checking namespace vendor-bin/ns1 +No dependencies installed. Try running composer install or update. diff --git a/lib/composer/bamarni/composer-bin-plugin/e2e/scenario7/script.sh b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario7/script.sh new file mode 100755 index 0000000000000..326700216200e --- /dev/null +++ b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario7/script.sh @@ -0,0 +1,30 @@ +#!/usr/bin/env bash + +set -Eeuo pipefail + +# Set env variables in order to experience a behaviour closer to what happens +# in the CI locally. It should not hurt to set those in the CI as the CI should +# contain those values. +export CI=1 +export COMPOSER_NO_INTERACTION=1 + +readonly ORIGINAL_WORKING_DIR=$(pwd) + +trap "cd ${ORIGINAL_WORKING_DIR}" err exit + +# Change to script directory +cd "$(dirname "$0")" + +# Ensure we have a clean state +rm -rf actual.txt || true +rm -rf composer.lock || true +rm -rf vendor || true +rm -rf vendor-bin/*/composer.lock || true +rm -rf vendor-bin/*/vendor || true + +composer update + +# Actual command to execute the test itself +composer bin ns1 update --no-dev --verbose -- foo 2>&1 | tee > actual.txt || true +echo "–––––––––––––––––––––" >> actual.txt +composer bin ns1 show --direct --name-only 2>&1 | tee >> actual.txt || true diff --git a/lib/composer/bamarni/composer-bin-plugin/e2e/scenario7/vendor-bin/ns1/composer.json b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario7/vendor-bin/ns1/composer.json new file mode 100644 index 0000000000000..7b72340c4270c --- /dev/null +++ b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario7/vendor-bin/ns1/composer.json @@ -0,0 +1,8 @@ +{ + "require": { + "nikic/iter": "v1.6.0" + }, + "require-dev": { + "phpstan/phpstan": "1.8.0" + } +} diff --git a/lib/composer/bamarni/composer-bin-plugin/e2e/scenario8/README.md b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario8/README.md new file mode 100644 index 0000000000000..9af1b6db2ad70 --- /dev/null +++ b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario8/README.md @@ -0,0 +1 @@ +Tests that extra arguments and options are not lost when forwarding the command to a bin namespace. diff --git a/lib/composer/bamarni/composer-bin-plugin/e2e/scenario8/composer.json b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario8/composer.json new file mode 100644 index 0000000000000..7681c07cb6e17 --- /dev/null +++ b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario8/composer.json @@ -0,0 +1,22 @@ +{ + "repositories": [ + { + "type": "path", + "url": "../../" + } + ], + "require": { + "bamarni/composer-bin-plugin": "dev-master" + }, + "config": { + "allow-plugins": { + "bamarni/composer-bin-plugin": true + } + }, + "extra": { + "bamarni-bin": { + "bin-links": false, + "forward-command": true + } + } +} diff --git a/lib/composer/bamarni/composer-bin-plugin/e2e/scenario8/expected.txt b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario8/expected.txt new file mode 100644 index 0000000000000..625e41f0f2827 --- /dev/null +++ b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario8/expected.txt @@ -0,0 +1,31 @@ +Loading composer repositories with package information +Updating dependencies +Analyzed 90 packages to resolve dependencies +Analyzed 90 rules to resolve dependencies +Lock file operations: 1 install, 0 updates, 0 removals +Installs: bamarni/composer-bin-plugin:dev-hash + - Locking bamarni/composer-bin-plugin (dev-hash) +Writing lock file +Installing dependencies from lock file (including require-dev) +Package operations: 1 install, 0 updates, 0 removals +Installs: bamarni/composer-bin-plugin:dev-hash + - Installing bamarni/composer-bin-plugin (dev-hash): Symlinking from ../.. +Generating autoload files +> post-autoload-dump: Bamarni\Composer\Bin\BamarniBinPlugin->onPostAutoloadDump +[bamarni-bin] Calling onPostAutoloadDump(). +[bamarni-bin] The command is being forwarded. +[bamarni-bin] Original input: update --prefer-lowest --verbose. +[bamarni-bin] Current working directory: /path/to/project/e2e/scenario8 +[bamarni-bin] Checking namespace vendor-bin/ns1 +[bamarni-bin] Changed current directory to vendor-bin/ns1. +[bamarni-bin] Running `@composer update --prefer-lowest --verbose --working-dir='.'`. +Loading composer repositories with package information +Updating dependencies +Analyzed 90 packages to resolve dependencies +Analyzed 90 rules to resolve dependencies +Nothing to modify in lock file +Writing lock file +Installing dependencies from lock file (including require-dev) +Nothing to install, update or remove +Generating autoload files +[bamarni-bin] Changed current directory to /path/to/project/e2e/scenario8. diff --git a/lib/composer/bamarni/composer-bin-plugin/e2e/scenario8/script.sh b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario8/script.sh new file mode 100755 index 0000000000000..1e4d0e12b578c --- /dev/null +++ b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario8/script.sh @@ -0,0 +1,26 @@ +#!/usr/bin/env bash + +set -Eeuo pipefail + +# Set env variables in order to experience a behaviour closer to what happens +# in the CI locally. It should not hurt to set those in the CI as the CI should +# contain those values. +export CI=1 +export COMPOSER_NO_INTERACTION=1 + +readonly ORIGINAL_WORKING_DIR=$(pwd) + +trap "cd ${ORIGINAL_WORKING_DIR}" err exit + +# Change to script directory +cd "$(dirname "$0")" + +# Ensure we have a clean state +rm -rf actual.txt || true +rm -rf composer.lock || true +rm -rf vendor || true +rm -rf vendor-bin/*/composer.lock || true +rm -rf vendor-bin/*/vendor || true + +# Actual command to execute the test itself +composer update --prefer-lowest --verbose 2>&1 | tee >> actual.txt || true diff --git a/lib/composer/bamarni/composer-bin-plugin/e2e/scenario8/vendor-bin/ns1/composer.json b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario8/vendor-bin/ns1/composer.json new file mode 100644 index 0000000000000..0967ef424bce6 --- /dev/null +++ b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario8/vendor-bin/ns1/composer.json @@ -0,0 +1 @@ +{} diff --git a/lib/composer/bamarni/composer-bin-plugin/e2e/scenario9/README.md b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario9/README.md new file mode 100644 index 0000000000000..80f120b6d9f8b --- /dev/null +++ b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario9/README.md @@ -0,0 +1 @@ +Tests that plugins installed in a namespace are loaded when a command is executed in the namespace. diff --git a/lib/composer/bamarni/composer-bin-plugin/e2e/scenario9/composer.json b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario9/composer.json new file mode 100644 index 0000000000000..3061d0c0f64cf --- /dev/null +++ b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario9/composer.json @@ -0,0 +1,21 @@ +{ + "repositories": [ + { + "type": "path", + "url": "../../" + } + ], + "require": { + "bamarni/composer-bin-plugin": "dev-master" + }, + "config": { + "allow-plugins": { + "bamarni/composer-bin-plugin": true + } + }, + "extra": { + "bamarni-bin": { + "forward-command": true + } + } +} diff --git a/lib/composer/bamarni/composer-bin-plugin/e2e/scenario9/expected.txt b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario9/expected.txt new file mode 100644 index 0000000000000..0515c410e6969 --- /dev/null +++ b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario9/expected.txt @@ -0,0 +1,9 @@ +[bamarni-bin] Checking namespace vendor-bin/composer-unused + +Loading packages +---------------- + + ! [NOTE] Found 0 package(s) to be checked. + + [OK] Done. No required packages to scan. + diff --git a/lib/composer/bamarni/composer-bin-plugin/e2e/scenario9/script.sh b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario9/script.sh new file mode 100755 index 0000000000000..a56dfb9027d96 --- /dev/null +++ b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario9/script.sh @@ -0,0 +1,28 @@ +#!/usr/bin/env bash + +set -Eeuo pipefail + +# Set env variables in order to experience a behaviour closer to what happens +# in the CI locally. It should not hurt to set those in the CI as the CI should +# contain those values. +export CI=1 +export COMPOSER_NO_INTERACTION=1 + +readonly ORIGINAL_WORKING_DIR=$(pwd) + +trap "cd ${ORIGINAL_WORKING_DIR}" err exit + +# Change to script directory +cd "$(dirname "$0")" + +# Ensure we have a clean state +rm -rf actual.txt || true +rm -rf composer.lock || true +rm -rf vendor || true +rm -rf vendor-bin/*/composer.lock || true +rm -rf vendor-bin/*/vendor || true + +composer update + +# Actual command to execute the test itself +composer bin composer-unused unused --no-progress 2>&1 | tee > actual.txt || true diff --git a/lib/composer/bamarni/composer-bin-plugin/e2e/scenario9/vendor-bin/composer-unused/composer.json b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario9/vendor-bin/composer-unused/composer.json new file mode 100644 index 0000000000000..3c5a3123c36c8 --- /dev/null +++ b/lib/composer/bamarni/composer-bin-plugin/e2e/scenario9/vendor-bin/composer-unused/composer.json @@ -0,0 +1,11 @@ +{ + "require": { + "composer-runtime-api": "^2.2", + "icanhazstring/composer-unused": "~0.7.12" + }, + "config": { + "allow-plugins": { + "icanhazstring/composer-unused": true + } + } +} diff --git a/lib/composer/bamarni/composer-bin-plugin/infection.json b/lib/composer/bamarni/composer-bin-plugin/infection.json new file mode 100644 index 0000000000000..4db61213ea832 --- /dev/null +++ b/lib/composer/bamarni/composer-bin-plugin/infection.json @@ -0,0 +1,14 @@ +{ + "$schema": "vendor/infection/infection/resources/schema.json", + "source": { + "directories": [ + "src" + ] + }, + "logs": { + "text": "dist/infection.txt" + }, + "mutators": { + "@default": true + } +} diff --git a/lib/composer/bamarni/composer-bin-plugin/src/ApplicationFactory/FreshInstanceApplicationFactory.php b/lib/composer/bamarni/composer-bin-plugin/src/ApplicationFactory/FreshInstanceApplicationFactory.php new file mode 100644 index 0000000000000..a4dcd58324271 --- /dev/null +++ b/lib/composer/bamarni/composer-bin-plugin/src/ApplicationFactory/FreshInstanceApplicationFactory.php @@ -0,0 +1,15 @@ +composer = $composer; + $this->io = $io; + $this->logger = new Logger($io); + } + + public function getCapabilities(): array + { + return [ + ComposerPluginCommandProvider::class => BamarniCommandProvider::class, + ]; + } + + public function deactivate(Composer $composer, IOInterface $io): void + { + } + + public function uninstall(Composer $composer, IOInterface $io): void + { + } + + public static function getSubscribedEvents(): array + { + return [ + PluginEvents::COMMAND => 'onCommandEvent', + ScriptEvents::POST_AUTOLOAD_DUMP => 'onPostAutoloadDump', + ]; + } + + public function onPostAutoloadDump(Event $event): void + { + $this->logger->logDebug('Calling onPostAutoloadDump().'); + + $eventIO = $event->getIO(); + + if (!($eventIO instanceof ConsoleIO)) { + return; + } + + // This is a bit convoluted but Event does not expose the input unlike + // CommandEvent. + $publicIO = PublicIO::fromConsoleIO($eventIO); + $eventInput = $publicIO->getInput(); + + $this->onEvent( + $eventInput->getArgument('command'), + $eventInput, + $publicIO->getOutput() + ); + } + + public function onCommandEvent(CommandEvent $event): bool + { + $this->logger->logDebug('Calling onCommandEvent().'); + + return $this->onEvent( + $event->getCommandName(), + $event->getInput(), + $event->getOutput() + ); + } + + private function onEvent( + string $commandName, + InputInterface $input, + OutputInterface $output + ): bool { + $config = Config::fromComposer($this->composer); + + $deprecations = $config->getDeprecations(); + + if (count($deprecations) > 0) { + foreach ($deprecations as $deprecation) { + $this->logger->logStandard($deprecation); + } + } + + if ($config->isCommandForwarded() + && in_array($commandName, self::FORWARDED_COMMANDS, true) + ) { + return $this->onForwardedCommand($input, $output); + } + + return true; + } + + protected function onForwardedCommand( + InputInterface $input, + OutputInterface $output + ): bool { + if ($this->forwarded) { + $this->logger->logDebug('Command already forwarded within the process: skipping.'); + + return true; + } + + $this->forwarded = true; + + $this->logger->logStandard('The command is being forwarded.'); + $this->logger->logDebug( + sprintf( + 'Original input: %s.', + $input->__toString() + ) + ); + + // Note that the input & output of $io should be the same as the event + // input & output. + $io = $this->io; + + $application = new Application(); + + $command = new BinCommand(); + $command->setComposer($this->composer); + $command->setApplication($application); + $command->setIO($io); + + $forwardedCommandInput = BinInputFactory::createForwardedCommandInput($input); + + try { + return Command::SUCCESS === $command->run( + $forwardedCommandInput, + $output + ); + } catch (Throwable $throwable) { + return false; + } + } +} diff --git a/lib/composer/bamarni/composer-bin-plugin/src/Command/BinCommand.php b/lib/composer/bamarni/composer-bin-plugin/src/Command/BinCommand.php new file mode 100644 index 0000000000000..9edb4c3e641fd --- /dev/null +++ b/lib/composer/bamarni/composer-bin-plugin/src/Command/BinCommand.php @@ -0,0 +1,330 @@ +applicationFactory = $applicationFactory ?? new FreshInstanceApplicationFactory(); + $this->logger = $logger ?? new Logger(new NullIO()); + } + + protected function configure(): void + { + $this + ->setDescription('Run a command inside a bin namespace') + ->addArgument( + self::NAMESPACE_ARG, + InputArgument::REQUIRED + ) + ->ignoreValidationErrors(); + } + + public function setIO(IOInterface $io): void + { + parent::setIO($io); + + $this->logger = new Logger($io); + } + + public function getIO(): IOInterface + { + $io = parent::getIO(); + + $this->logger = new Logger($io); + + return $io; + } + + public function isProxyCommand(): bool + { + return true; + } + + public function execute(InputInterface $input, OutputInterface $output): int + { + // Switch to requireComposer() once Composer 2.3 is set as the minimum + $composer = method_exists($this, 'requireComposer') + ? $this->requireComposer() + : $this->getComposer(); + + $config = Config::fromComposer($composer); + $currentWorkingDir = getcwd(); + + $this->logger->logDebug( + sprintf( + 'Current working directory: %s', + $currentWorkingDir + ) + ); + + // Ensures Composer is reset – we are setting some environment variables + // & co. so a fresh Composer instance is required. + $this->resetComposers(); + + $this->configureBinLinksDir($config); + + $vendorRoot = $config->getTargetDirectory(); + $namespace = $input->getArgument(self::NAMESPACE_ARG); + + $binInput = BinInputFactory::createInput( + $namespace, + $input + ); + + return (self::ALL_NAMESPACES !== $namespace) + ? $this->executeInNamespace( + $currentWorkingDir, + $vendorRoot.'/'.$namespace, + $binInput, + $output + ) + : $this->executeAllNamespaces( + $currentWorkingDir, + $vendorRoot, + $binInput, + $output + ); + } + + /** + * @return list + */ + private static function getBinNamespaces(string $binVendorRoot): array + { + return glob($binVendorRoot.'/*', GLOB_ONLYDIR); + } + + private function executeAllNamespaces( + string $originalWorkingDir, + string $binVendorRoot, + InputInterface $input, + OutputInterface $output + ): int { + $namespaces = self::getBinNamespaces($binVendorRoot); + + if (count($namespaces) === 0) { + $this->logger->logStandard('Could not find any bin namespace.'); + + // Is a valid scenario: the user may not have set up any bin + // namespace yet + return 0; + } + + $exitCode = 0; + + foreach ($namespaces as $namespace) { + $exitCode += $this->executeInNamespace( + $originalWorkingDir, + $namespace, + $input, + $output + ); + } + + return min($exitCode, 255); + } + + private function executeInNamespace( + string $originalWorkingDir, + string $namespace, + InputInterface $input, + OutputInterface $output + ): int { + $this->logger->logStandard( + sprintf( + 'Checking namespace %s', + $namespace + ) + ); + + try { + self::createNamespaceDirIfDoesNotExist($namespace); + } catch (CouldNotCreateNamespaceDir $exception) { + $this->logger->logStandard( + sprintf( + '%s', + $exception->getMessage() + ) + ); + + return 1; + } + + // Use a new application: this avoids a variety of issues: + // - A command may be added in a namespace which may cause side effects + // when executed in another namespace afterwards (since it is the same + // process). + // - Different plugins may be registered in the namespace in which case + // an already executed application will not pick that up. + $namespaceApplication = $this->applicationFactory->create( + $this->getApplication() + ); + + // It is important to clean up the state either for follow-up plugins + // or for example the execution in the next namespace. + $cleanUp = function () use ($originalWorkingDir): void { + $this->chdir($originalWorkingDir); + $this->resetComposers(); + }; + + $this->chdir($namespace); + + $this->ensureComposerFileExists(); + + $namespaceInput = BinInputFactory::createNamespaceInput($input); + + $this->logger->logDebug( + sprintf( + 'Running `@composer %s`.', + $namespaceInput->__toString() + ) + ); + + try { + $exitCode = $namespaceApplication->doRun($namespaceInput, $output); + } catch (Throwable $executionFailed) { + // Ensure we do the cleanup even in case of failure + $cleanUp(); + + throw $executionFailed; + } + + $cleanUp(); + + return $exitCode; + } + + /** + * @throws CouldNotCreateNamespaceDir + */ + private static function createNamespaceDirIfDoesNotExist(string $namespace): void + { + if (file_exists($namespace)) { + return; + } + + $mkdirResult = mkdir($namespace, 0777, true); + + if (!$mkdirResult && !is_dir($namespace)) { + throw CouldNotCreateNamespaceDir::forNamespace($namespace); + } + } + + private function configureBinLinksDir(Config $config): void + { + if (!$config->binLinksAreEnabled()) { + return; + } + + $binDir = ConfigFactory::createConfig()->get('bin-dir'); + + putenv( + sprintf( + 'COMPOSER_BIN_DIR=%s', + $binDir + ) + ); + + $this->logger->logDebug( + sprintf( + 'Configuring bin directory to %s.', + $binDir + ) + ); + } + + private function ensureComposerFileExists(): void + { + // Some plugins require access to the Composer file e.g. Symfony Flex + $namespaceComposerFile = Factory::getComposerFile(); + + if (file_exists($namespaceComposerFile)) { + return; + } + + file_put_contents($namespaceComposerFile, '{}'); + + $this->logger->logDebug( + sprintf( + 'Created the file %s.', + $namespaceComposerFile + ) + ); + } + + private function resetComposers(): void + { + $this->getApplication()->resetComposer(); + + foreach ($this->getApplication()->all() as $command) { + if ($command instanceof BaseCommand) { + $command->resetComposer(); + } + } + } + + private function chdir(string $dir): void + { + chdir($dir); + + $this->logger->logDebug( + sprintf( + 'Changed current directory to %s.', + $dir + ) + ); + } +} diff --git a/lib/composer/bamarni/composer-bin-plugin/src/Command/CouldNotCreateNamespaceDir.php b/lib/composer/bamarni/composer-bin-plugin/src/Command/CouldNotCreateNamespaceDir.php new file mode 100644 index 0000000000000..33145078e9f96 --- /dev/null +++ b/lib/composer/bamarni/composer-bin-plugin/src/Command/CouldNotCreateNamespaceDir.php @@ -0,0 +1,21 @@ + true, + self::TARGET_DIRECTORY => 'vendor-bin', + self::FORWARD_COMMAND => false, + ]; + + /** + * @var bool + */ + private $binLinks; + + /** + * @var string + */ + private $targetDirectory; + + /** + * @var bool + */ + private $forwardCommand; + + /** + * @var list + */ + private $deprecations = []; + + /** + * @throws InvalidBamarniComposerExtraConfig + */ + public static function fromComposer(Composer $composer): self + { + return new self($composer->getPackage()->getExtra()); + } + + /** + * @param mixed[] $extra + * + * @throws InvalidBamarniComposerExtraConfig + */ + public function __construct(array $extra) + { + $userExtra = $extra[self::EXTRA_CONFIG_KEY] ?? []; + + $config = array_merge(self::DEFAULT_CONFIG, $userExtra); + + $getType = function_exists('get_debug_type') ? 'get_debug_type' : 'gettype'; + + $binLinks = $config[self::BIN_LINKS_ENABLED]; + + if (!is_bool($binLinks)) { + throw new InvalidBamarniComposerExtraConfig( + sprintf( + 'Expected setting "extra.%s.%s" to be a boolean value. Got "%s".', + self::EXTRA_CONFIG_KEY, + self::BIN_LINKS_ENABLED, + $getType($binLinks) + ) + ); + } + + $binLinksSetExplicitly = array_key_exists(self::BIN_LINKS_ENABLED, $userExtra); + + if ($binLinks && !$binLinksSetExplicitly) { + $this->deprecations[] = sprintf( + 'The setting "extra.%s.%s" will be set to "false" from 2.x onwards. If you wish to keep it to "true", you need to set it explicitly.', + self::EXTRA_CONFIG_KEY, + self::BIN_LINKS_ENABLED + ); + } + + $targetDirectory = $config[self::TARGET_DIRECTORY]; + + if (!is_string($targetDirectory)) { + throw new InvalidBamarniComposerExtraConfig( + sprintf( + 'Expected setting "extra.%s.%s" to be a string. Got "%s".', + self::EXTRA_CONFIG_KEY, + self::TARGET_DIRECTORY, + $getType($targetDirectory) + ) + ); + } + + $forwardCommand = $config[self::FORWARD_COMMAND]; + + if (!is_bool($forwardCommand)) { + throw new InvalidBamarniComposerExtraConfig( + sprintf( + 'Expected setting "extra.%s.%s" to be a boolean value. Got "%s".', + self::EXTRA_CONFIG_KEY, + self::FORWARD_COMMAND, + gettype($forwardCommand) + ) + ); + } + + $forwardCommandSetExplicitly = array_key_exists(self::FORWARD_COMMAND, $userExtra); + + if (!$forwardCommand && !$forwardCommandSetExplicitly) { + $this->deprecations[] = sprintf( + 'The setting "extra.%s.%s" will be set to "true" from 2.x onwards. If you wish to keep it to "false", you need to set it explicitly.', + self::EXTRA_CONFIG_KEY, + self::FORWARD_COMMAND + ); + } + + $this->binLinks = $binLinks; + $this->targetDirectory = $targetDirectory; + $this->forwardCommand = $forwardCommand; + } + + public function binLinksAreEnabled(): bool + { + return $this->binLinks; + } + + public function getTargetDirectory(): string + { + return $this->targetDirectory; + } + + public function isCommandForwarded(): bool + { + return $this->forwardCommand; + } + + /** + * @return list + */ + public function getDeprecations(): array + { + return $this->deprecations; + } +} diff --git a/lib/composer/bamarni/composer-bin-plugin/src/Config/ConfigFactory.php b/lib/composer/bamarni/composer-bin-plugin/src/Config/ConfigFactory.php new file mode 100644 index 0000000000000..b2fc1d4ed7570 --- /dev/null +++ b/lib/composer/bamarni/composer-bin-plugin/src/Config/ConfigFactory.php @@ -0,0 +1,39 @@ +exists()) { + return $config; + } + + $file->validateSchema(JsonFile::LAX_SCHEMA); + + $config->merge($file->read()); + + return $config; + } + + private function __construct() + { + } +} diff --git a/lib/composer/bamarni/composer-bin-plugin/src/Config/InvalidBamarniComposerExtraConfig.php b/lib/composer/bamarni/composer-bin-plugin/src/Config/InvalidBamarniComposerExtraConfig.php new file mode 100644 index 0000000000000..105f9ea61aeb3 --- /dev/null +++ b/lib/composer/bamarni/composer-bin-plugin/src/Config/InvalidBamarniComposerExtraConfig.php @@ -0,0 +1,11 @@ + `update --prefer-lowest` + * + * Note that no input definition is bound in the resulting input. + */ + public static function createInput( + string $namespace, + InputInterface $previousInput + ): InputInterface { + $matchResult = preg_match( + sprintf( + '/^(?.+)?bin (?:(?.+?) )?(?:%1$s|\'%1$s\') (?.+?)(? -- .*)?$/', + preg_quote($namespace, '/') + ), + $previousInput->__toString(), + $matches + ); + + if (1 !== $matchResult) { + throw InvalidBinInput::forBinInput($previousInput); + } + + $inputParts = array_filter( + array_map( + 'trim', + [ + $matches['binCommand'], + $matches['preBinOptions2'] ?? '', + $matches['preBinOptions'] ?? '', + $matches['extraInput'] ?? '', + ] + ) + ); + + // Move the options present _before_ bin namespaceName to after, but + // before the end of option marker (--) if present. + $reorderedInput = implode(' ', $inputParts); + + return new StringInput($reorderedInput); + } + + public static function createNamespaceInput(InputInterface $previousInput): InputInterface + { + $matchResult = preg_match( + '/^(.+?\s?)(--(?: .+)?)?$/', + $previousInput->__toString(), + $matches + ); + + if (1 !== $matchResult) { + throw InvalidBinInput::forNamespaceInput($previousInput); + } + + $inputParts = array_filter( + array_map( + 'trim', + [ + $matches[1], + '--working-dir=.', + $matches[2] ?? '', + ] + ) + ); + + $newInput = implode(' ', $inputParts); + + return new StringInput($newInput); + } + + public static function createForwardedCommandInput(InputInterface $input): InputInterface + { + return new StringInput( + sprintf( + 'bin all %s', + $input->__toString() + ) + ); + } + + private function __construct() + { + } +} diff --git a/lib/composer/bamarni/composer-bin-plugin/src/Input/InvalidBinInput.php b/lib/composer/bamarni/composer-bin-plugin/src/Input/InvalidBinInput.php new file mode 100644 index 0000000000000..2418d9c80844f --- /dev/null +++ b/lib/composer/bamarni/composer-bin-plugin/src/Input/InvalidBinInput.php @@ -0,0 +1,32 @@ + ", for example "bin all update --prefer-lowest".', + $input->__toString() + ) + ); + } + + public static function forNamespaceInput(InputInterface $input): self + { + return new self( + sprintf( + 'Could not parse the input (executed within the namespace) "%s".', + $input->__toString() + ) + ); + } +} diff --git a/lib/composer/bamarni/composer-bin-plugin/src/Logger.php b/lib/composer/bamarni/composer-bin-plugin/src/Logger.php new file mode 100644 index 0000000000000..190571926de2a --- /dev/null +++ b/lib/composer/bamarni/composer-bin-plugin/src/Logger.php @@ -0,0 +1,39 @@ +io = $io; + } + + public function logStandard(string $message): void + { + $this->log($message, false); + } + + public function logDebug(string $message): void + { + $this->log($message, true); + } + + private function log(string $message, bool $debug): void + { + $verbosity = $debug + ? IOInterface::VERBOSE + : IOInterface::NORMAL; + + $this->io->writeError('[bamarni-bin] '.$message, true, $verbosity); + } +} diff --git a/lib/composer/bamarni/composer-bin-plugin/src/PublicIO.php b/lib/composer/bamarni/composer-bin-plugin/src/PublicIO.php new file mode 100644 index 0000000000000..6c90d84c4c8b7 --- /dev/null +++ b/lib/composer/bamarni/composer-bin-plugin/src/PublicIO.php @@ -0,0 +1,31 @@ +input, + $io->output, + $io->helperSet + ); + } + + public function getInput(): InputInterface + { + return $this->input; + } + + public function getOutput(): OutputInterface + { + return $this->output; + } +} diff --git a/lib/private/Share20/DefaultShareProvider.php b/lib/private/Share20/DefaultShareProvider.php index 9dd862abb3175..12cca5c1dece5 100644 --- a/lib/private/Share20/DefaultShareProvider.php +++ b/lib/private/Share20/DefaultShareProvider.php @@ -38,6 +38,7 @@ use OC\Share20\Exception\BackendError; use OC\Share20\Exception\InvalidShare; use OC\Share20\Exception\ProviderException; +use OCP\AppFramework\Utility\ITimeFactory; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\Defaults; use OCP\Files\Folder; @@ -90,19 +91,19 @@ class DefaultShareProvider implements IShareProvider { /** @var IURLGenerator */ private $urlGenerator; - /** @var IConfig */ - private $config; + private ITimeFactory $timeFactory; public function __construct( - IDBConnection $connection, - IUserManager $userManager, - IGroupManager $groupManager, - IRootFolder $rootFolder, - IMailer $mailer, - Defaults $defaults, - IFactory $l10nFactory, - IURLGenerator $urlGenerator, - IConfig $config) { + IDBConnection $connection, + IUserManager $userManager, + IGroupManager $groupManager, + IRootFolder $rootFolder, + IMailer $mailer, + Defaults $defaults, + IFactory $l10nFactory, + IURLGenerator $urlGenerator, + ITimeFactory $timeFactory, + ) { $this->dbConn = $connection; $this->userManager = $userManager; $this->groupManager = $groupManager; @@ -111,7 +112,7 @@ public function __construct( $this->defaults = $defaults; $this->l10nFactory = $l10nFactory; $this->urlGenerator = $urlGenerator; - $this->config = $config; + $this->timeFactory = $timeFactory; } /** @@ -216,32 +217,22 @@ public function create(\OCP\Share\IShare $share) { } // Set the time this share was created - $qb->setValue('stime', $qb->createNamedParameter(time())); + $shareTime = $this->timeFactory->now(); + $qb->setValue('stime', $qb->createNamedParameter($shareTime->getTimestamp())); // insert the data and fetch the id of the share - $this->dbConn->beginTransaction(); - $qb->execute(); - $id = $this->dbConn->lastInsertId('*PREFIX*share'); - - // Now fetch the inserted share and create a complete share object - $qb = $this->dbConn->getQueryBuilder(); - $qb->select('*') - ->from('share') - ->where($qb->expr()->eq('id', $qb->createNamedParameter($id))); + $qb->executeStatement(); - $cursor = $qb->execute(); - $data = $cursor->fetch(); - $this->dbConn->commit(); - $cursor->closeCursor(); + // Update mandatory data + $id = $qb->getLastInsertId(); + $share->setId((string)$id); + $share->setProviderId($this->identifier()); - if ($data === false) { - throw new ShareNotFound('Newly created share could not be found'); - } + $share->setShareTime(\DateTime::createFromImmutable($shareTime)); $mailSendValue = $share->getMailSend(); - $data['mail_send'] = ($mailSendValue === null) ? true : $mailSendValue; + $share->setMailSend(($mailSendValue === null) ? true : $mailSendValue); - $share = $this->createShare($data); return $share; }