Commit b7e0faee authored by Kosta Harlan's avatar Kosta Harlan
Browse files

Merge branch 'generate-fix-suggestions' into 'main'

Add generate-fix-suggestions command

See merge request KostaHarlan/fix-suggestions!1
parents 505dd8d8 16234ef8
# This file is a template, and might need editing before it works on your project.
include:
template: Jobs/Code-Quality.gitlab-ci.yml
#!/usr/bin/env php
<?php
require __DIR__.'/vendor/autoload.php';
use FixSuggestions\Command\GenerateFixSuggestions;
use Symfony\Component\Console\Application;
$application = new Application();
$application->add( new GenerateFixSuggestions() );
$application->run();
{
"name": "wikimedia/fix-suggestions",
"description": "Generate and publish fix suggestions for MediaWiki core and extensions/skins. Currently for Gerrit, later for GitLab.",
"type": "library",
"require": {
"symfony/console": "^5.3",
"symfony/http-foundation": "^5.3",
"mediawiki/mediawiki-codesniffer": "^37.0",
"symfony/process": "^5.3"
},
"autoload": {
"psr-4": {"FixSuggestions\\": "src/"}
},
"license": "GNU GPL v 3.0",
"authors": [
{
"name": "Kosta Harlan",
"email": "kharlan@wikimedia.org"
}
]
}
This diff is collapsed.
<?xml version="1.0"?>
<ruleset name="MediaWiki">
<rule ref="./vendor/mediawiki/mediawiki-codesniffer/MediaWiki">
</rule>
<file>.</file>
<arg name="encoding" value="UTF-8"/>
</ruleset>
<?php
namespace FixSuggestions\Command;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Process\Process;
class GenerateFixSuggestions extends Command {
public function configure(): void {
$this->setName( 'generate-fix-suggestions' );
$this->setDescription( 'Generate fix suggestions for a patch.' );
$this->addOption(
'suggestion-tools',
null,
InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY,
'Tools to use for generating fix suggestions.',
$this->defaultTools()
);
$this->addOption( 'working-directory',
null,
InputOption::VALUE_OPTIONAL,
'Working directory where fix suggestion tools should be run.',
getcwd()
);
$this->addOption( 'files',
null,
InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY,
'Files/directories to pass as arguments to the fix suggestion tools',
[ getcwd() ]
);
$this->addOption(
'output-directory',
null,
InputOption::VALUE_OPTIONAL,
'Path to where the fix suggestion output should be saved.',
getcwd() . '/reports'
);
$this->addOption(
'project',
null,
InputOption::VALUE_OPTIONAL,
'Project name. Used to organizing cache and report directories.',
'unknown'
);
$this->addOption(
'cache-directory',
null,
InputOption::VALUE_OPTIONAL,
'Path to where phpcs runs are cached.',
getcwd() . '/cache'
);
$this->addOption(
'code-review',
null,
InputOption::VALUE_REQUIRED,
'Code review system that suggestions will be generated for',
$this->defaultCodeReview()
);
$this->addOption(
'gerrit-robot-id',
null,
InputOption::VALUE_OPTIONAL,
'[gerrit only] The robot ID to use in fix suggestions.',
getenv( 'GERRIT_ROBOT_ID' )
);
$this->addOption(
'gerrit-robot-run-id',
null,
InputOption::VALUE_OPTIONAL,
'[gerrit only] The robot run ID to use in fix suggestions.',
getenv( 'GERRIT_ROBOT_RUN_ID' )
);
$this->addOption(
'gerrit-robot-url',
null,
InputOption::VALUE_OPTIONAL,
'[gerrit only] The robot URL to use in fix suggestions.',
// Ideally would be GERRIT_ROBOT_URL but the report format uses GERRIT_URL so keep
// that for consistency.
getenv( 'GERRIT_URL' )
);
}
/** @inheritDoc */
public function execute( InputInterface $input, OutputInterface $output ): int {
$validateTools = array_diff( $input->getOption( 'suggestion-tools' ), $this->supportedTools() );
if ( count( $validateTools ) ) {
$output->writeln(
sprintf( '<error>Unsupported tools specified:</error> %s', implode( ', ', $validateTools ) )
);
return Command::FAILURE;
}
if ( !in_array( $input->getOption( 'code-review' ), $this->supportedCodeReview() ) ) {
$output->writeln(
sprintf( '<error>Unsupported code review system specified:</error> %s',
implode( ', ', $input->getOption( 'code-review' ) ) )
);
return Command::FAILURE;
}
// TODO: Maybe extract this block into a class once we have GitLab and/or eslint added.
if ( $input->getOption( 'code-review' ) === 'gerrit' ) {
if ( $this->shouldRunPhpcs( $input ) ) {
$cacheFile = sprintf( '%s/%s.json',
$input->getOption( 'cache-directory' ),
$input->getOption( 'project' ) );
$this->makeDirectoriesIfNeeded( $cacheFile );
$process = new Process( [
'vendor/bin/phpcs',
'--cache=' . $cacheFile,
'-q',
'--report=vendor/mediawiki/mediawiki-codesniffer/MediaWiki/Reports/GerritRobotComments.php',
...$input->getOption( 'files' )
],
$input->getOption( 'working-directory' ),
[
'GERRIT_ROBOT_ID' => $input->getOption( 'gerrit-robot-id' ),
'GERRIT_ROBOT_RUN_ID' => $input->getOption( 'gerrit-robot-run-id' ),
'GERRIT_URL' => $input->getOption( 'gerrit-robot-url' ),
'PHPCS_ROOT_DIR' => $input->getOption( 'working-directory' )
]
);
$process->run();
if ( $process->getExitCode() !== 2 ) {
// phpcs returns exit code 2 even when generating report properly.
$output->writeln( '<error>An error occurred:</error>' );
$output->writeln( $process->getErrorOutput() );
$output->write( $process->getOutput() );
return Command::FAILURE;
}
$outputFile = sprintf( '%s/%s/%s.json',
$input->getOption( 'output-directory' ),
$input->getOption( 'project' ),
$input->getOption( 'gerrit-robot-run-id' )
);
$this->makeDirectoriesIfNeeded( $outputFile );
file_put_contents(
$outputFile,
$process->getOutput()
);
return Command::SUCCESS;
}
}
return Command::SUCCESS;
}
private function supportedTools(): array {
return [ 'phpcs' ];
}
private function defaultTools(): array {
return [ 'phpcs' ];
}
private function supportedCodeReview(): array {
return [ 'gerrit' ];
}
private function defaultCodeReview(): array {
return [ 'gerrit' ];
}
/**
* @param string $filename
*/
private function makeDirectoriesIfNeeded( string $filename ): void {
if ( !file_exists( dirname( $filename ) ) ) {
mkdir( dirname( $filename ), 0777, true );
}
}
/**
* @param InputInterface $input
* @return bool
*/
private function shouldRunPhpcs( InputInterface $input ): bool {
return in_array( 'phpcs', $input->getOption( 'suggestion-tools' ) );
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment