diff --git a/.ddev/commands/web/install-magento-ce b/.ddev/commands/web/install-magento-ce index ed01feca..f2ed551b 100755 --- a/.ddev/commands/web/install-magento-ce +++ b/.ddev/commands/web/install-magento-ce @@ -15,6 +15,10 @@ if [ -x PHP_BIN ]; then PHP_BIN="php" fi +if [ ! -x COMPOSER_BIN ]; then + COMPOSER_BIN="/usr/local/bin/composer" +fi + if [ -x $TZ ]; then TZ="UTC" fi @@ -26,7 +30,7 @@ MAGENTO_SUBDOMAIN="${MAGENTO_VERSION//[-_.]/-}" USE_ELASTICSEARCH=$2 check_if_db_exists() { - echo "SHOW DATABASES" | mysql -hdb -udb -pdb | grep -q "$MAGENTO_DB_NAME" + echo "SHOW DATABASES" | mysql -hdb -udb -pdb | grep -qE "^$MAGENTO_DB_NAME\$" if [ $? -eq 1 ]; then echo "CREATE DATABASE ${MAGENTO_DB_NAME}" | mysql -hdb -uroot -proot @@ -57,7 +61,7 @@ install_magento() { check_if_db_exists if [ ! -d $MAGENTO_ROOT_DIR ]; then - "$PHP_BIN" /usr/local/bin/composer --no-interaction create-project --repository-url=https://repo.magento.com/ magento/project-community-edition="$MAGENTO_VERSION" "$MAGENTO_ROOT_DIR" + "$PHP_BIN" $COMPOSER_BIN --no-interaction create-project --repository-url=https://repo.magento.com/ magento/project-community-edition="$MAGENTO_VERSION" "$MAGENTO_ROOT_DIR" fi cd $MAGENTO_ROOT_DIR diff --git a/.ddev/commands/web/install-magento-ce-git b/.ddev/commands/web/install-magento-ce-git index e8ab4b69..e649c737 100755 --- a/.ddev/commands/web/install-magento-ce-git +++ b/.ddev/commands/web/install-magento-ce-git @@ -26,7 +26,7 @@ MAGENTO_ROOT_DIR="/opt/magento-test-environments/magento_${NORMALIZED_GIT_BRANCH MAGENTO_SUBDOMAIN="${NORMALIZED_GIT_BRANCH//[-_.]/-}" check_if_db_exists() { - echo "SHOW DATABASES" | mysql -hdb -udb -pdb | grep -q "$MAGENTO_DB_NAME" + echo "SHOW DATABASES" | mysql -hdb -udb -pdb | grep -qE "^$MAGENTO_DB_NAME\$" if [ $? -eq 1 ]; then echo "CREATE DATABASE ${MAGENTO_DB_NAME}" | mysql -hdb -uroot -proot diff --git a/CHANGELOG.md b/CHANGELOG.md index 8d68d5d5..1c4ff541 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ RECENT CHANGES - Add: #1177: New commands to manage sales sequences (by Jeroen Boersma) - Add: #1176: New command to redeploy base packages (by Christian Münch) - Add: #1226: New sys:cron:kill command (by Christian Münch) +- Imp: #1179: New github:pr command (by Christian Münch) - Imp: #1182: Add debug output if Magento Core Commands cannot be used (by Christian Münch) - Imp: #1185: Do less compatibility checks (by Christian Münch) - Imp: Update 3rd party dependencies (php-cs-fixer, psysh, phpstan, phpunit, requests, symfony) diff --git a/README.md b/README.md index c006485b..718f27e3 100644 --- a/README.md +++ b/README.md @@ -1079,6 +1079,43 @@ n98-magerun2.phar integration:delete --- +### Github + +(experimental) Commands + +### Pull Requests + +Gets infos about Github Pull Requests. +If no Github Repository is defined by `---repository` (-r) option the default +Magento 2 Github Repository `magento/magento2` is used. +For the [Mage-OS](https://github.com/mage-os/mageos-magento2) repository we provide a shortcut option `--mage-os`. + +If the command is executed without any options it will show infos about the PR. + +```sh +# Magento 2 Open Source +n98-magerun2.phar github:pr:patch + +# Mage-OS +n98-magerun2.phar github:pr:patch --mage-os +``` + +*Create a patch file from PR:* + +```sh +n98-magerun2.phar github:pr:patch --patch +``` + +Files of the magento2-base and magento2-ee-base and b2b base packages are currently not handled by the command. + +**List only the raw diff:** + +```sh +n98-magerun2.phar github:pr:patch --diff +``` + +--- + ### Interactive Development Console Opens PHP interactive shell with initialized Magento Admin-Store. diff --git a/box.json.dist b/box.json.dist index 75d2bc1d..caafc692 100644 --- a/box.json.dist +++ b/box.json.dist @@ -11,6 +11,7 @@ "directories": [ "src", "res", + "vendor/rmccue/requests/certificates", "vendor/twig/twig/src" ], "replacements": { diff --git a/config.yaml b/config.yaml index 8a1e9dc8..a43c49d9 100644 --- a/config.yaml +++ b/config.yaml @@ -137,6 +137,7 @@ commands: - N98\Magento\Command\GiftCard\CreateCommand - N98\Magento\Command\GiftCard\InfoCommand - N98\Magento\Command\GiftCard\RemoveCommand + - N98\Magento\Command\Github\PullRequestCommand - N98\Magento\Command\Media\DumpCommand - N98\Magento\Command\OpenBrowserCommand - N98\Magento\Command\SearchEngine\ListCommand diff --git a/src/N98/Magento/Command/Github/PatchFileContent/Creator.php b/src/N98/Magento/Command/Github/PatchFileContent/Creator.php new file mode 100644 index 00000000..40d3e843 --- /dev/null +++ b/src/N98/Magento/Command/Github/PatchFileContent/Creator.php @@ -0,0 +1,39 @@ +process($diffContent); + + $appCodeProcessor = new AppCodeProcessor(); + $diffContent = $appCodeProcessor->process($diffContent); + + $i18nProcessor = new I18nProcessor(); + $diffContent = $i18nProcessor->process($diffContent); + + $libProcessor = new LibProcessor(); + $diffContent = $libProcessor->process($diffContent); + + return $diffContent; + } +} diff --git a/src/N98/Magento/Command/Github/PatchFileContent/Processor/AppCodeProcessor.php b/src/N98/Magento/Command/Github/PatchFileContent/Processor/AppCodeProcessor.php new file mode 100644 index 00000000..ee8ebd6e --- /dev/null +++ b/src/N98/Magento/Command/Github/PatchFileContent/Processor/AppCodeProcessor.php @@ -0,0 +1,25 @@ +/ with vendor/magento/theme-frontend-/ + $diffContent = preg_replace( + '/app\/design\/frontend\/Magento\/([a-zA-Z0-9_]+)\//', + 'vendor/magento/theme-frontend-$1/', + $diffContent + ); + + // preg_replace app/design/adminhtml/Magento// with vendor/magento/theme-adminhtml-/ + $diffContent = preg_replace( + '/app\/design\/adminhtml\/Magento\/([a-zA-Z0-9_]+)\//', + 'vendor/magento/theme-adminhtml-$1/', + $diffContent + ); + + return $diffContent; + } +} diff --git a/src/N98/Magento/Command/Github/PatchFileContent/Processor/I18nProcessor.php b/src/N98/Magento/Command/Github/PatchFileContent/Processor/I18nProcessor.php new file mode 100644 index 00000000..18fba68b --- /dev/null +++ b/src/N98/Magento/Command/Github/PatchFileContent/Processor/I18nProcessor.php @@ -0,0 +1,26 @@ + Message Queue is part of the framework directory but later on in an own package + $diffContent = preg_replace( + '/lib\/internal\/Magento\/Framework\/MessageQueue\/([a-zA-Z0-9_]+)\//', + 'vendor/magento/framework-message-queue/$1/', + $diffContent + ); + + // Handle the rest of the lib/internal/Magento directory + $callback = function ($matches) { + // camelcase to dash + $matches[1] = preg_replace('/([a-z])([A-Z])/', '$1-$2', $matches[1]); + + return 'vendor/magento/' . strtolower($matches[1]) . '/'; + }; + + $diffContent = (string) preg_replace_callback( + '/lib\/internal\/Magento\/([a-zA-Z0-9_-]+)\//', + $callback, + $diffContent + ); + + return $diffContent; + } +} diff --git a/src/N98/Magento/Command/Github/PatchFileContent/Processor/ProcessorInterface.php b/src/N98/Magento/Command/Github/PatchFileContent/Processor/ProcessorInterface.php new file mode 100644 index 00000000..330f505c --- /dev/null +++ b/src/N98/Magento/Command/Github/PatchFileContent/Processor/ProcessorInterface.php @@ -0,0 +1,15 @@ +setName('github:pr') + ->addArgument( + 'number', + InputArgument::REQUIRED, + 'Pull Request Number' + ) + ->addOption('repository', 'r', InputOption::VALUE_OPTIONAL, 'Repository to fetch from', 'magento/magento2') + ->addOption('mage-os', null, InputOption::VALUE_NONE, 'Shortcut option to use the mage-os/mageos-magento2 repository.') + ->addOption('patch', 'd', InputOption::VALUE_NONE, 'Download patch and prepare it for applying') + ->addOption('diff', null, InputOption::VALUE_NONE, 'Raw diff download') + ->addOption('json', null, InputOption::VALUE_NONE, 'Show pull request data as json') + ->setDescription('Download patch from github merge request (experimental)'); + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $this->diffContent = ''; + $this->repository = $input->getOption('repository'); + if ($input->getOption('mage-os')) { + $this->repository = 'mage-os/mageos-magento2'; + } + + $pullRequestDataResponse = $this->getPullRequestInfoByApi($input); + + if ($input->getOption('json')) { + $output->writeln($pullRequestDataResponse->body); + + return Command::SUCCESS; + } + + $prData = $pullRequestDataResponse->decode_body(true); + + if (!isset($prData['id'])) { + $output->writeln('Could not fetch pull request data'); + + return Command::FAILURE; + } + + $table = PullRequestInfoTable::create($output, $prData); + + /** + * Show only diff + */ + if ($input->getOption('diff')) { + $output->write($this->fetchDiffContent($prData['diff_url'])); + + return Command::SUCCESS; + } + + // Show infos as fallback + $table->render(); + + if ($input->getOption('patch')) { + $this->patchFile($prData, $output); + } + + if (!$input->getOption('patch') && !$input->getOption('diff')) { + $output->writeln('Use --patch to download the patch as ready to apply patch file'); + $output->writeln('Use --diff to see the raw diff'); + } + + return Command::SUCCESS; + } + + /** + * @param string $diffUrl + * @return string + */ + protected function fetchDiffContent($diffUrl): string + { + if ($this->diffContent === '') { + $response = Requests::get($diffUrl, [], ['verify' => true]); + $this->diffContent = $response->body; + } + + return $this->diffContent; + } + + /** + * @param array $prData + * @param OutputInterface $output + * @return void + */ + protected function patchFile(array $prData, OutputInterface $output): void + { + $patchFileContent = PatchFileContentCreator::create( + $this->fetchDiffContent($prData['diff_url']) + ); + + $filename = sprintf( + 'PR-%d-%s.patch', + $prData['number'], + str_replace('/', '-', $prData['base']['repo']['full_name']) + ); + + chdir(OperatingSystem::getCwd()); + if (file_put_contents($filename, $patchFileContent) === false) { + throw new \RuntimeException('Could not write patch file'); + } + + $output->writeln(sprintf('Patch file created: %s', $filename)); + } + + /** + * @param InputInterface $input + * @return \WpOrg\Requests\Response + */ + protected function getPullRequestInfoByApi(InputInterface $input): \WpOrg\Requests\Response + { + $pullRequestDataResponse = Requests::get( + sprintf( + 'https://api.github.com/repos/%s/pulls/%d.patch', + $this->repository, + $input->getArgument('number') + ), + [], + ['verify' => true] + ); + return $pullRequestDataResponse; + } +} diff --git a/src/N98/Magento/Command/Github/PullRequestInfoTable.php b/src/N98/Magento/Command/Github/PullRequestInfoTable.php new file mode 100644 index 00000000..09d7c0d8 --- /dev/null +++ b/src/N98/Magento/Command/Github/PullRequestInfoTable.php @@ -0,0 +1,35 @@ +addRow(['Number', $prData['number']]); + $table->addRow(['Title', $prData['title']]); + $table->addRow(['Created at', $prData['created_at']]); + $table->addRow(['User', $prData['user']['login']]); + $table->addRow(['State', $prData['state']]); + $table->addRow(['URL', $prData['url']]); + $table->addRow(['Diff-URL', $prData['diff_url']]); + + return $table; + } +} diff --git a/src/N98/Magento/Command/OpenBrowserCommand.php b/src/N98/Magento/Command/OpenBrowserCommand.php index 5b07fbec..81d8a8d0 100644 --- a/src/N98/Magento/Command/OpenBrowserCommand.php +++ b/src/N98/Magento/Command/OpenBrowserCommand.php @@ -26,7 +26,7 @@ protected function configure() $this ->setName('open-browser') ->addArgument('store', InputArgument::OPTIONAL, 'Store code or ID') - ->setDescription('Open current project in browser (experimental)'); + ->setDescription('Open current project in browser'); } /** diff --git a/tests/N98/Magento/Command/Github/PatchFileContent/CreatorTest.php b/tests/N98/Magento/Command/Github/PatchFileContent/CreatorTest.php new file mode 100644 index 00000000..bb6e5fc3 --- /dev/null +++ b/tests/N98/Magento/Command/Github/PatchFileContent/CreatorTest.php @@ -0,0 +1,25 @@ +assertEquals($expectedResult, $result); + } +} diff --git a/tests/N98/Magento/Command/Github/PatchFileContent/Processor/AppCodeProcessorTest.php b/tests/N98/Magento/Command/Github/PatchFileContent/Processor/AppCodeProcessorTest.php new file mode 100644 index 00000000..69c6f1a6 --- /dev/null +++ b/tests/N98/Magento/Command/Github/PatchFileContent/Processor/AppCodeProcessorTest.php @@ -0,0 +1,25 @@ +assertEquals($expectedResult, $processor->process($diffContent)); + } +} diff --git a/tests/N98/Magento/Command/Github/PatchFileContent/Processor/AppDesignProcessorTest.php b/tests/N98/Magento/Command/Github/PatchFileContent/Processor/AppDesignProcessorTest.php new file mode 100644 index 00000000..6917c7b5 --- /dev/null +++ b/tests/N98/Magento/Command/Github/PatchFileContent/Processor/AppDesignProcessorTest.php @@ -0,0 +1,28 @@ +assertSame( + $expectedResult, + $processor->process($diffContent) + ); + } +} diff --git a/tests/N98/Magento/Command/Github/PatchFileContent/Processor/I18nProcessorTest.php b/tests/N98/Magento/Command/Github/PatchFileContent/Processor/I18nProcessorTest.php new file mode 100644 index 00000000..4b680942 --- /dev/null +++ b/tests/N98/Magento/Command/Github/PatchFileContent/Processor/I18nProcessorTest.php @@ -0,0 +1,32 @@ +assertEquals($expectedResult, $processor->process($diffContent)); + } +} diff --git a/tests/N98/Magento/Command/Github/PatchFileContent/Processor/LibProcessorTest.php b/tests/N98/Magento/Command/Github/PatchFileContent/Processor/LibProcessorTest.php new file mode 100644 index 00000000..fcad5454 --- /dev/null +++ b/tests/N98/Magento/Command/Github/PatchFileContent/Processor/LibProcessorTest.php @@ -0,0 +1,32 @@ +assertEquals($expectedResult, $processor->process($diffContent)); + } +} diff --git a/tests/phar-test.sh b/tests/phar-test.sh index 3d3797ba..2dcff5d2 100755 --- a/tests/phar-test.sh +++ b/tests/phar-test.sh @@ -250,6 +250,13 @@ function test_magerun_commands() { # eav:attribute:remove # eav:attribute:view assert_command_contains "eav:attribute:view catalog_product sku" "catalog_product_entity" + # github:pr + assert_command_contains "github:pr 21787" "x_forwarded_for" + assert_command_contains "github:pr --patch 21787" "PR-21787-magento-magento2.patch" + assert_command_contains "github:pr --diff 21787" "setXForwardedFor" + assert_command_contains "github:pr --mage-os 1" "automatically" + assert_command_contains "github:pr --mage-os --patch 1" "PR-1-mage-os-mageos-magento2.patch" + assert_command_contains "github:pr --mage-os --diff 1" "server_url" # generation:flush assert_command_contains "generation:flush Symfony" "Removed" # index:list