From 14608d22432fec950db526963f442ab51cf82140 Mon Sep 17 00:00:00 2001 From: jrfnl Date: Mon, 27 Nov 2023 03:43:03 +0100 Subject: [PATCH] Composer: implement CS menu and extra commands This adds a number of extra CS-related Composer scripts and handler functions. This: * Uses the same menu as is already in use in the Free, Premium and several other plugins for predictability. * Where necessary, adjusts existing Composer scripts to, again, be in line with what is already in use in Free and Premium. This also adds the _ability_ to run CS with a Threshold. As the codebase is currently clean, this ability is not activated for CI for the time being, though it is likely that it will be activated once YoastCS 3.0 will be used. Note: this change does mean that, in contrast to before, running `check-cs` will now return only errors. While this is new for this package, it is in line with the expected behaviour for the script as used in other packages. --- .github/workflows/cs.yml | 2 +- .github/workflows/lint.yml | 2 + composer.json | 27 +++++- config/composer/actions.php | 189 ++++++++++++++++++++++++++++++++++++ 4 files changed, 218 insertions(+), 2 deletions(-) create mode 100644 config/composer/actions.php diff --git a/.github/workflows/cs.yml b/.github/workflows/cs.yml index dab09fd93..4c15ac7a5 100644 --- a/.github/workflows/cs.yml +++ b/.github/workflows/cs.yml @@ -95,7 +95,7 @@ jobs: # @link https://github.com/staabm/annotate-pull-request-from-checkstyle/ - name: Check PHP code style id: phpcs - run: composer check-cs -- --no-cache --report-full --report-checkstyle=./phpcs-report.xml + run: composer check-cs-warnings -- --no-cache --report-full --report-checkstyle=./phpcs-report.xml - name: Show PHPCS results in PR if: ${{ always() && steps.phpcs.outcome == 'failure' }} diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 377d90003..fdb45caf3 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -32,6 +32,7 @@ on: - '.github/workflows/deploy.yml' - '.github/workflows/test.yml' - 'config/**' + - '!config/composer/actions.php' - 'css/**' - 'js/**' pull_request: @@ -58,6 +59,7 @@ on: - '.github/workflows/deploy.yml' - '.github/workflows/test.yml' - 'config/**' + - '!config/composer/actions.php' - 'css/**' - 'js/**' # Allow manually triggering the workflow. diff --git a/composer.json b/composer.json index 7bd801f79..de14fb9c2 100644 --- a/composer.json +++ b/composer.json @@ -38,6 +38,9 @@ ] }, "autoload-dev": { + "classmap": [ + "config/" + ], "psr-4": { "Yoast\\WP\\Duplicate_Post\\Tests\\": "tests/" } @@ -53,9 +56,26 @@ "lint": [ "@php ./vendor/php-parallel-lint/php-parallel-lint/parallel-lint . -e php --show-deprecated --exclude vendor --exclude node_modules --exclude .git" ], + "cs": [ + "Yoast\\WP\\Duplicate_Post\\Config\\Composer\\Actions::check_coding_standards" + ], + "check-cs-thresholds": [ + "@putenv YOASTCS_THRESHOLD_ERRORS=0", + "@putenv YOASTCS_THRESHOLD_WARNINGS=0", + "Yoast\\WP\\Duplicate_Post\\Config\\Composer\\Actions::check_cs_thresholds" + ], "check-cs": [ + "@check-cs-warnings -n" + ], + "check-cs-warnings": [ "@php ./vendor/squizlabs/php_codesniffer/bin/phpcs" ], + "check-staged-cs": [ + "@check-cs-warnings --filter=GitStaged" + ], + "check-branch-cs": [ + "Yoast\\WP\\Duplicate_Post\\Config\\Composer\\Actions::check_branch_cs" + ], "fix-cs": [ "@php ./vendor/squizlabs/php_codesniffer/bin/phpcbf" ], @@ -80,7 +100,12 @@ }, "scripts-descriptions": { "lint": "Check the PHP files for parse errors.", - "check-cs": "Check the PHP files for code style violations and best practices.", + "cs": "See a menu with the code style checking script options.", + "check-cs-thresholds": "Check the PHP files for code style violations and best practices and verify the number of issues does not exceed predefined thresholds.", + "check-cs": "Check the PHP files for code style violations and best practices, ignoring warnings.", + "check-cs-warnings": "Check the PHP files for code style violations and best practices, including warnings.", + "check-staged-cs": "Check the staged PHP files for code style violations and best practices.", + "check-branch-cs": "Check the PHP files changed in the current branch for code style violations and best practices.", "fix-cs": "Auto-fix code style violations in the PHP files.", "test": "Run the unit tests without code coverage.", "coverage": "Run the unit tests with code coverage.", diff --git a/config/composer/actions.php b/config/composer/actions.php new file mode 100644 index 000000000..e70f5e507 --- /dev/null +++ b/config/composer/actions.php @@ -0,0 +1,189 @@ +getIO(); + + $choices = [ + '1' => [ + 'label' => 'Check staged files for coding standard warnings & errors.', + 'command' => 'check-staged-cs', + ], + '2' => [ + 'label' => 'Check current branch\'s changed files for coding standard warnings & errors.', + 'command' => 'check-branch-cs', + ], + '3' => [ + 'label' => 'Check for all coding standard errors.', + 'command' => 'check-cs', + ], + '4' => [ + 'label' => 'Check for all coding standard warnings & errors.', + 'command' => 'check-cs-warnings', + ], + '5' => [ + 'label' => 'Fix auto-fixable coding standards.', + 'command' => 'fix-cs', + ], + '6' => [ + 'label' => 'Verify coding standard violations are below thresholds.', + 'command' => 'check-cs-thresholds', + ], + ]; + + $args = $event->getArguments(); + if ( empty( $args ) ) { + foreach ( $choices as $choice => $data ) { + $io->write( \sprintf( '%d. %s', $choice, $data['label'] ) ); + } + + $choice = $io->ask( 'What do you want to do? ' ); + } + else { + $choice = $args[0]; + } + + if ( isset( $choices[ $choice ] ) ) { + $event_dispatcher = $event->getComposer()->getEventDispatcher(); + $event_dispatcher->dispatchScript( $choices[ $choice ]['command'] ); + } + else { + $io->write( 'Unknown choice.' ); + } + } + + /** + * Runs PHPCS on the files changed in the current branch. + * + * Used by the composer check-branch-cs command. + * + * @codeCoverageIgnore + * + * @param Event $event Composer event that triggered this script. + * + * @return void + */ + public static function check_branch_cs( Event $event ) { + $branch = 'trunk'; + + $args = $event->getArguments(); + if ( ! empty( $args ) ) { + $branch = $args[0]; + } + + exit( self::check_cs_for_changed_files( $branch ) ); + } + + /** + * Runs PHPCS on changed files compared to some git reference. + * + * @codeCoverageIgnore + * + * @param string $compare The git reference. + * + * @return int Exit code passed from the coding standards check. + */ + private static function check_cs_for_changed_files( $compare ) { + \exec( 'git diff --name-only --diff-filter=d ' . \escapeshellarg( $compare ), $files ); + + $php_files = self::filter_files( $files, '.php' ); + if ( empty( $php_files ) ) { + echo 'No files to compare! Exiting.' . \PHP_EOL; + + return 0; + } + + /* + * In CI, generate both the normal report as well as the checkstyle report. + * The normal report will be shown in the actions output and ensures human readable (and colorized!) results there. + * The checkstyle report is used to show the results inline in the GitHub code view. + */ + $extra_args = ( \getenv( 'CI' ) === false ) ? '' : ' --colors --no-cache --report-full --report-checkstyle=./phpcs-report.xml'; + $command = \sprintf( + 'composer check-cs-warnings -- %s %s', + \implode( ' ', \array_map( 'escapeshellarg', $php_files ) ), + $extra_args + ); + \system( $command, $exit_code ); + + return $exit_code; + } + + /** + * Checks if the CS errors and warnings are below or at thresholds. + * + * @return void + */ + public static function check_cs_thresholds() { + $in_ci = \getenv( 'CI' ); + + echo 'Running coding standards checks, this may take some time.', \PHP_EOL; + + $command = 'composer check-cs-warnings -- -mq --report="YoastCS\\Yoast\\Reports\\Threshold"'; + if ( $in_ci !== false ) { + // Always show the results in CI in color. + $command .= ' --colors'; + } + // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged -- Non-WP context, this is fine. + @\exec( $command, $phpcs_output, $return ); + + $phpcs_output = \implode( \PHP_EOL, $phpcs_output ); + echo $phpcs_output; + + $above_threshold = true; + if ( \strpos( $phpcs_output, 'Coding standards checks have passed!' ) !== false ) { + $above_threshold = false; + } + + /* + * Don't run the branch check in CI/GH Actions as it prevents the errors from being shown inline. + * The GH Actions script will run this via a separate script step. + */ + if ( $above_threshold === true && $in_ci === false ) { + echo \PHP_EOL; + echo 'Running check-branch-cs.', \PHP_EOL; + echo 'This might show problems on untouched lines. Focus on the lines you\'ve changed first.', \PHP_EOL; + echo \PHP_EOL; + + // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged -- Non-WP context, this is fine. + @\passthru( 'composer check-branch-cs' ); + } + + exit( ( $above_threshold === true || $return > 2 ) ? $return : 0 ); + } + + /** + * Filter files on extension. + * + * @param array $files List of files. + * @param string $extension Extension to filter on. + * + * @return array Filtered list of files. + */ + private static function filter_files( array $files, string $extension ): array { + return \array_filter( + $files, + static function ( $file ) use ( $extension ) { + return \substr( $file, ( 0 - \strlen( $extension ) ) ) === $extension; + } + ); + } +}