From 955a5de414501f77b80b321a90ee35d4fe49e9e2 Mon Sep 17 00:00:00 2001 From: Tomas Votruba Date: Wed, 24 Jan 2024 22:24:18 +0100 Subject: [PATCH] Add gitlab to "setup-ci" command (#5497) * [dx] Add gitlab command support to setup-ci * cs * add gitlab check --- ...amTypeBasedOnPHPUnitDataProviderRector.php | 7 +- src/Console/Command/SetupCICommand.php | 128 +++++++++++------- src/Git/RepositoryHelper.php | 29 ++++ templates/rector-github-action-check.yaml | 1 + templates/rector-gitlab-check.yaml | 27 ++++ 5 files changed, 142 insertions(+), 50 deletions(-) create mode 100644 src/Git/RepositoryHelper.php create mode 100644 templates/rector-gitlab-check.yaml diff --git a/rules/TypeDeclaration/Rector/ClassMethod/AddParamTypeBasedOnPHPUnitDataProviderRector.php b/rules/TypeDeclaration/Rector/ClassMethod/AddParamTypeBasedOnPHPUnitDataProviderRector.php index 47e72a9d407..7c41b7e14da 100644 --- a/rules/TypeDeclaration/Rector/ClassMethod/AddParamTypeBasedOnPHPUnitDataProviderRector.php +++ b/rules/TypeDeclaration/Rector/ClassMethod/AddParamTypeBasedOnPHPUnitDataProviderRector.php @@ -150,8 +150,11 @@ public function refactor(Node $node): ?Node return null; } - private function inferParam(Class_ $class, int $parameterPosition, PhpDocTagNode | Attribute $dataProviderNode): Type - { + private function inferParam( + Class_ $class, + int $parameterPosition, + PhpDocTagNode | Attribute $dataProviderNode + ): Type { $dataProviderClassMethod = $this->resolveDataProviderClassMethod($class, $dataProviderNode); if (! $dataProviderClassMethod instanceof ClassMethod) { return new MixedType(); diff --git a/src/Console/Command/SetupCICommand.php b/src/Console/Command/SetupCICommand.php index b0e848b43f1..29ed815d541 100644 --- a/src/Console/Command/SetupCICommand.php +++ b/src/Console/Command/SetupCICommand.php @@ -5,23 +5,16 @@ namespace Rector\Console\Command; use Nette\Utils\FileSystem; -use Nette\Utils\Strings; use OndraM\CiDetector\CiDetector; +use Rector\Git\RepositoryHelper; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; -use Symfony\Component\Process\Process; use function sprintf; final class SetupCICommand extends Command { - /** - * @var string - * @see https://regex101.com/r/etcmog/2 - */ - private const GITHUB_REPOSITORY_REGEX = '#github\.com[:\/](?.*?)\.git#'; - public function __construct( private readonly SymfonyStyle $symfonyStyle ) { @@ -38,48 +31,51 @@ protected function execute(InputInterface $input, OutputInterface $output): int { // detect current CI $ci = $this->resolveCurrentCI(); - if ($ci === null) { - $this->symfonyStyle->error('No CI detected'); - return self::FAILURE; - } - if ($ci !== CiDetector::CI_GITHUB_ACTIONS) { - $noteMessage = sprintf( - 'Only Github Action is supported for now.%sCreate an issue to add your CI %s', - PHP_EOL, - 'https://github.com/rectorphp/rector/issues/' - ); - - $this->symfonyStyle->note($noteMessage); - return self::SUCCESS; + if ($ci === CiDetector::CI_GITLAB) { + return $this->handleGitlabCi(); } - $rectorWorkflowFilePath = getcwd() . '/.github/workflows/rector.yaml'; - if (file_exists($rectorWorkflowFilePath)) { - $response = $this->symfonyStyle->ask('The "rector.yaml" workflow already exists. Overwrite it?', 'Yes'); - if (! in_array($response, ['y', 'yes', 'Yes'], true)) { - $this->symfonyStyle->note('Nothing changed'); - return self::SUCCESS; - } + if ($ci === CiDetector::CI_GITHUB_ACTIONS) { + return $this->handleGithubActions(); } - $currentRepository = $this->resolveCurrentRepositoryName(getcwd()); - if ($currentRepository === null) { - $this->symfonyStyle->error('Current repository name could not be resolved'); + $noteMessage = sprintf( + 'Only Github and GitLab are currently supported.%s Contribute your CI template to Rector to make this work: %s', + PHP_EOL, + 'https://github.com/rectorphp/rector-src/' + ); - return self::FAILURE; + $this->symfonyStyle->note($noteMessage); + return self::SUCCESS; + } + + /** + * @return CiDetector::CI_*|null + */ + private function resolveCurrentCI(): ?string + { + if (file_exists(getcwd() . '/.github')) { + return CiDetector::CI_GITHUB_ACTIONS; } - $workflowTemplate = FileSystem::read(__DIR__ . '/../../../templates/rector-github-action-check.yaml'); + if (file_exists(getcwd() . '/.gitlab-ci.yml')) { + return CiDetector::CI_GITLAB; + } + + return null; + } + private function addGithubActionsWorkflow(string $currentRepository, string $targetWorkflowFilePath): void + { + $workflowTemplate = FileSystem::read(__DIR__ . '/../../../templates/rector-github-action-check.yaml'); $workflowContents = strtr($workflowTemplate, [ '__CURRENT_REPOSITORY__' => $currentRepository, ]); - FileSystem::write($rectorWorkflowFilePath, $workflowContents); + FileSystem::write($targetWorkflowFilePath, $workflowContents); $this->symfonyStyle->newLine(); - $this->symfonyStyle->success('The ".github/workflows/rector.yaml" file was added'); $this->symfonyStyle->writeln('2 more steps to run Rector in CI:'); @@ -95,31 +91,67 @@ protected function execute(InputInterface $input, OutputInterface $output): int $this->symfonyStyle->writeln( '2) Add the token to Action secrets as "ACCESS_TOKEN":' . \PHP_EOL . $repositoryNewSecretsLink ); + } + + private function addGitlabFile(string $targetGitlabFilePath): void + { + $gitlabTemplate = FileSystem::read(__DIR__ . '/../../../templates/rector-gitlab-check.yaml'); + FileSystem::write($targetGitlabFilePath, $gitlabTemplate); + + $this->symfonyStyle->newLine(); + $this->symfonyStyle->success('The "gitlab/rector.yaml" file was added'); - return Command::SUCCESS; + $this->symfonyStyle->newLine(); + $this->symfonyStyle->writeln( + '1) Register it in your ".gitlab-ci.yml" file:' . \PHP_EOL . 'include:' . \PHP_EOL . ' - local: gitlab/rector.yaml' + ); } /** - * @return CiDetector::CI_*|null + * @return self::SUCCESS */ - private function resolveCurrentCI(): ?string + private function handleGitlabCi(): int { - if (file_exists(getcwd() . '/.github')) { - return CiDetector::CI_GITHUB_ACTIONS; + // add snippet in the end of file or include it? + $ciRectorFilePath = getcwd() . '/gitlab/rector.yaml'; + + if (file_exists($ciRectorFilePath)) { + $response = $this->symfonyStyle->ask( + 'The "gitlab/rector.yaml" workflow already exists. Overwrite it?', + 'Yes' + ); + if (! in_array($response, ['y', 'yes', 'Yes'], true)) { + $this->symfonyStyle->note('Nothing changed'); + return self::SUCCESS; + } } - return null; + $this->addGitlabFile($ciRectorFilePath); + return self::SUCCESS; } - private function resolveCurrentRepositoryName(string $currentDirectory): ?string + /** + * @return self::SUCCESS|self::FAILURE + */ + private function handleGithubActions(): int { - // resolve current repository name - $process = new Process(['git', 'remote', 'get-url', 'origin'], $currentDirectory, null, null, null); - $process->run(); + $rectorWorkflowFilePath = getcwd() . '/.github/workflows/rector.yaml'; + if (file_exists($rectorWorkflowFilePath)) { + $response = $this->symfonyStyle->ask('The "rector.yaml" workflow already exists. Overwrite it?', 'Yes'); + if (! in_array($response, ['y', 'yes', 'Yes'], true)) { + $this->symfonyStyle->note('Nothing changed'); + return self::SUCCESS; + } + } + + $currentRepository = RepositoryHelper::resolveGithubRepositoryName(getcwd()); + if ($currentRepository === null) { + $this->symfonyStyle->error('Current repository name could not be resolved'); - $output = $process->getOutput(); + return self::FAILURE; + } - $match = Strings::match($output, self::GITHUB_REPOSITORY_REGEX); - return $match['repository_name'] ?? null; + $this->addGithubActionsWorkflow($currentRepository, $rectorWorkflowFilePath); + return self::SUCCESS; } } diff --git a/src/Git/RepositoryHelper.php b/src/Git/RepositoryHelper.php new file mode 100644 index 00000000000..1a9bac72c6c --- /dev/null +++ b/src/Git/RepositoryHelper.php @@ -0,0 +1,29 @@ +.*?)\.git#'; + + public static function resolveGithubRepositoryName(string $currentDirectory): ?string + { + // resolve current repository name + $process = new Process(['git', 'remote', 'get-url', 'origin'], $currentDirectory, null, null, null); + $process->run(); + + $output = $process->getOutput(); + + $match = Strings::match($output, self::GITHUB_REPOSITORY_REGEX); + return $match['repository_name'] ?? null; + } +} diff --git a/templates/rector-github-action-check.yaml b/templates/rector-github-action-check.yaml index 08fa6f7ea3a..4f2260f3be6 100644 --- a/templates/rector-github-action-check.yaml +++ b/templates/rector-github-action-check.yaml @@ -25,6 +25,7 @@ jobs: - uses: "ramsey/composer-install@v2" - run: vendor/bin/rector --ansi + # @todo apply coding standard if used - # commit only to core contributors who have repository access diff --git a/templates/rector-gitlab-check.yaml b/templates/rector-gitlab-check.yaml new file mode 100644 index 00000000000..36581f2a303 --- /dev/null +++ b/templates/rector-gitlab-check.yaml @@ -0,0 +1,27 @@ +stages: + - setup + - rector + - commit_changes + +setup: + stage: setup + # see https://github.com/thecodingmachine/docker-images-php + image: thecodingmachine/php:8.2-v4-slim-cli + +rector: + stage: rector + script: + - vendor/bin/rector --ansi + # @todo apply coding standard if used + +commit_changes: + stage: commit_changes + script: + - git config --global user.email "ci@gitlab.com" + - git config --global user.name "GitLab CI + # - git checkout $CI_COMMIT_REF_NAME + - git add . + - git commit -m "[rector] Rector fixes" + - git push origin $CI_COMMIT_REF_NAME + only: + - merge_requests