Skip to content

Commit

Permalink
[DX] Add "setup-ci" command to ease integration to CI (#3425)
Browse files Browse the repository at this point in the history
  • Loading branch information
TomasVotruba committed Feb 28, 2023
1 parent 8ef705e commit 8a0b838
Show file tree
Hide file tree
Showing 4 changed files with 147 additions and 25 deletions.
26 changes: 1 addition & 25 deletions .github/workflows/rector.yaml
@@ -1,24 +1,8 @@
####
# Due to some Github Actions limitations, we are running realtime fixes (commits) only for self-owned-pr
#
# Current limitations:
# - Secrets (ACCESS_TOKEN) are not available in PRs from forks
# - Github Token has Read-only access (can not commit), Personal Access Token must be used instead
# - Github Token does not trigger workflows after push
#
# So we basically have chicken-egg problem here
#
# https://help.github.com/en/actions/configuring-and-managing-workflows/authenticating-with-the-github_token#permissions-for-the-github_token
####
name: Rector

on:
pull_request: null

env:
# see https://github.com/composer/composer/issues/9368#issuecomment-718112361
COMPOSER_ROOT_VERSION: "dev-main"

jobs:
rector:
# Don't run on forks.
Expand All @@ -33,23 +17,15 @@ jobs:
- config utils

runs-on: ubuntu-latest
timeout-minutes: 10
timeout-minutes: 8

steps:
# workaround for missing secret in fork PRs - see https://github.com/actions/checkout/issues/298
# see https://github.com/rectorphp/rector/commit/d395e1c28b8e6a56711dcc2e10490a82965850e4
-
if: github.event.pull_request.head.repo.full_name == github.repository
uses: actions/checkout@v3
with:
# Must be used to trigger workflow after push
token: ${{ secrets.ACCESS_TOKEN }}

# in forks, the token is not available - so we cannot us eit
-
if: github.event.pull_request.head.repo.full_name != github.repository
uses: actions/checkout@v3

-
uses: shivammathur/setup-php@v2
with:
Expand Down
3 changes: 3 additions & 0 deletions phpstan.neon
Expand Up @@ -820,3 +820,6 @@ parameters:
-
message: '#Anonymous function has an unused use \$container#'
path: src/Autoloading/BootstrapFilesIncluder.php

# false positive on enums
- '#Method Rector\\Core\\Console\\Command\\SetupCICommand\:\:resolveCurrentCI\(\) never returns (.*?) so it can be removed from the return type#'
95 changes: 95 additions & 0 deletions src/Console/Command/SetupCICommand.php
@@ -0,0 +1,95 @@
<?php

declare(strict_types=1);

namespace Rector\Core\Console\Command;

use Nette\Utils\FileSystem;
use Nette\Utils\Strings;
use OndraM\CiDetector\CiDetector;
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;

final class SetupCICommand extends Command
{
/**
* @var string
* @see https://regex101.com/r/etcmog/1
*/
private const GITHUB_REPOSITORY_REGEX = '#github\.com:(?<repository_name>.*?)\.git#';

public function __construct(
private readonly SymfonyStyle $symfonyStyle
) {
parent::__construct();
}

protected function configure(): void
{
$this->setName('setup-ci');
$this->setDescription('Add CI workflow to let Rector work for you');
}

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) {
$rectorWorkflowFilePath = getcwd() . '/.github/workflows/rector.yaml';
if (file_exists($rectorWorkflowFilePath)) {
$this->symfonyStyle->warning('The "rector.yaml" workflow already exists');
return self::SUCCESS;
}

$currentRepository = $this->resolveCurrentRepositoryName(getcwd());
if ($currentRepository === null) {
$this->symfonyStyle->error('Current repository name could not be resolved');

return self::FAILURE;
}

$workflowTemplate = FileSystem::read(__DIR__ . '/../../../templates/rector-github-action-check.yaml');

$workflowContents = strtr($workflowTemplate, [
'__CURRENT_REPOSITORY__' => $currentRepository,
]);

FileSystem::write($rectorWorkflowFilePath, $workflowContents);
$this->symfonyStyle->success('The "rector.yaml" workflow was added');
}

return Command::SUCCESS;
}

/**
* @return CiDetector::CI_*|null
*/
private function resolveCurrentCI(): ?string
{
if (file_exists(getcwd() . '/.github')) {
return CiDetector::CI_GITHUB_ACTIONS;
}

return null;
}

private function resolveCurrentRepositoryName(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;
}
}
48 changes: 48 additions & 0 deletions templates/rector-github-action-check.yaml
@@ -0,0 +1,48 @@
# github action that checks code with Rector
name: Rector

on:
pull_request: null


jobs:
rector:
# Don't run on forks.
if: github.repository == '__CURRENT_REPOSITORY__'

runs-on: ubuntu-latest

steps:
-
uses: actions/checkout@v3
with:
# needed to trigger workflow after push
token: ${{ secrets.ACCESS_TOKEN }}

-
uses: shivammathur/setup-php@v2
with:
php-version: 8.1
coverage: none

- uses: "ramsey/composer-install@v2"

## First run Rector - here can't be --dry-run !!! it would stop the job with it and not commit anything in the future
- run: vendor/bin/rector --ansi

- run: vendor/bin/ecs check --fix --ansi

# see https://github.com/EndBug/add-and-commit
-
# commit only to core contributors who have repository access
if: github.event.pull_request.head.repo.full_name == github.repository
uses: EndBug/add-and-commit@v7.5.0
with:
# The arguments for the `git add` command (see the paragraph below for more info)
add: .
message: "[rector] Rector fixes"
author_name: "GitHub Action"
author_email: "action@github.com"
env:
# to get push access
GITHUB_TOKEN: ${{ secrets.ACCESS_TOKEN }}

0 comments on commit 8a0b838

Please sign in to comment.