Configurer Rector pour une vérification automatique du code à chaque commit via une CI

Ong Timothée
Reparcar
Published in
4 min readFeb 28, 2022
Photo by mohamed_hassan

L’objectif de cette article est de mettre en place un outil qui propose des changements de codes (syntaxiques et fonctionnels) à chaque commit d’une Pull Request, via Github Action, afin d’en assurer la pérennité.

Le développement d’application en équipe nécessite une certaine normalisation syntaxique afin d’améliorer la lisibilité durant la ‘review’ de code par les autres membres de son équipe.

Si certains outils existent (code style, clean code …) ils ne permettent pas d’analyser le fonctionnement des nouvelles méthodes implémentés.

Rector php

Concernant le language de programmation php avec un framework tel que symfony, rector est la solution pour avoir un code propre et fonctionnel.

Pouvant prendre en charge l’ensemble des versions 5.3 à 8.1 de php, cet outil agit sur les fichiers fournis, par exemple, ceux présents dans le dossier source d’une application, pour les mettre à jour et les refactoriser instantanément.

Comment ca marche ?

Configuré localement, en se lançant, rector va trouver l’ensemble des fichiers pour leurs appliquer les « recteurs » pertinents. Dans ce context, un recteur est une seul classe qui modifie une seul chose, par exemple, le nom d’un classe.

<?phpdeclare(strict_types=1);use Rector\Contract\Rector\PhpRectorInterface;
use PhpParser\Parser;
/** @var SplFileInfo[] $fileInfos */
foreach ($fileInfos as $fileInfo) {
// 1 file => nodes
/** @var Parser $phpParser */
$nodes = $phpParser->parse(file_get_contents($fileInfo->getRealPath()));
// nodes => 1 node
foreach ($nodes as $node) { // rather traverse all of them
/** @var PhpRectorInterface[] $rectors */
foreach ($rectors as $rector) {
foreach ($rector->getNodeTypes() as $nodeType) {
if (is_a($node, $nodeType, true)) {
$rector->refactor($node);
}
}
}
}
}

Chaque fichiers va être analyser par l’ensemble des recteurs, puis ajouter à la méthode setAttribute() de la classe StandaloneTraverseNodeTraverser. Quand tous les recteurs ont été parcourus, ceux nécessaire au fichier y seront appliqués.

Le lancement de rector s’effectue avec cette commande:

vendor/bin/rector process src - dry-run

Automatisation: mise en place de CI

Afin de pouvoir effectuer le lancement manuel de rector à chaque commit, chaque services d’hébergement tel que GitHub ou gitlab, proposent des pipelines qui automatisent des actions. Sur GitHub, les GitHub Actions vont réaliser ces vérifications. Seulement, elles indiqueront simplement les fichiers où des recteurs sont applicables dans l’onglet ‘Checks’ de GitHub .

Pour optimiser se fonctionnement, il est possible d’utiliser un outil qui ajoute un commentaire au commit: commit-comment

Commit comment

Commit-comment est un workflow capable de créer des messages informant de l’avancée des Github Action.

Utilisation avec Rector

Présent dans un repertoire de peter-evans, le commit comment peut publier, à l’aide des ‘bots’ de GitHub, un message de post-commit ayant un visuel similaire à celui d’une commande ‘git diff’. Pour obtenir cela avec rector il est nécessaire de styliser l’affichage à l’aide de scripts bash:

run: |
body=$(cat rector.log)
body="${body//'%'/'%25'}"
body="${body//$'\n'/'%0A'}"
body="${body//$'\r'/'%0D'}"
body="${body//$'\e[31m'/''}"
body="${body//$'\e[32m'/''}"
body="${body//$'\e[33m'/''}"
body="${body//$'\e[36m'/''}"
body="${body//$'\e[39m'/''}"
body="${body//$'\e[24m'/''}"
body="${body//$'\e[22m'/''}"
body="${body//$'\e[49m'/''}"
body="${body//$'\e[30m'/''}"
body="${body//$'\e[4m'/''}"
body="${body//$'\e[1m'/''}"
body="${body//$'\e[42m'/''}"
body="${body//$'\e[30;42m'/''}"
body="${body//$'\e[39;49m'/''}"
body="${body//'---------- begin diff ----------'/''}"
body="${body//' ----------- end diff -----------'/'``` '}"
body="${body//'@@ @@'/'```diff'}"
echo "::set-output name=body::$body"

Ainsi, le workflow final à mettre en place ressemblera à:

name: 'Rector commit Comment'
description: 'Create a comment for a commit on GitHub when rector request changes'
inputs:
token:
description: 'The GitHub authentication token'
default: ${{ github.token }}
auth_json:
description: 'The auth.json (for other bundles)'
default: ''
runs:
using: 'composite'
steps:
- uses: actions/checkout@v2
with:
# Solves the not "You are not currently on a branch" problem, see https://github.com/actions/checkout/issues/124#issuecomment-586664611
ref: ${{ github.event.pull_request.head.ref }}
# Must be used to trigger workflow after push
token: ${{ inputs.token }}
- uses: shivammathur/setup-php@v2
with:
php-version: 8.1
coverage: none
- name: Add AUTH JSON
shell: bash
run: echo '${{ inputs.auth_json }}' > auth.json
- run: composer install --no-progress --ansi
shell: bash
## First run Rector without --dry-run, it would stop the process with exit 1 here
- run: vendor/bin/rector process --ansi --no-progress-bar > rector.log
shell: bash
id: rector-check
- name: Exit for Rector modified files
shell: bash
id: rector-exit-check
run: echo ::set-output name=error::$(if [ `wc -l rector.log | awk '{print $1}'` -ge "6" ]; then echo "true"; else echo "false"; fi)
- name: Message Length
shell: bash
id: rector-length-check
run: echo ::set-output name=length::$(if [ `wc -m rector.log | awk '{print $1}'` -ge "64000" ]; then echo "false"; else echo "true"; fi)
- id: get-comment-body
shell: bash
if: steps.rector-exit-check.outputs.error == 'true'
run: |
body=$(cat rector.log)
body="${body//'%'/'%25'}"
body="${body//$'\n'/'%0A'}"
body="${body//$'\r'/'%0D'}"
body="${body//$'\e[31m'/''}"
body="${body//$'\e[32m'/''}"
body="${body//$'\e[33m'/''}"
body="${body//$'\e[36m'/''}"
body="${body//$'\e[39m'/''}"
body="${body//$'\e[24m'/''}"
body="${body//$'\e[22m'/''}"
body="${body//$'\e[49m'/''}"
body="${body//$'\e[30m'/''}"
body="${body//$'\e[4m'/''}"
body="${body//$'\e[1m'/''}"
body="${body//$'\e[42m'/''}"
body="${body//$'\e[30;42m'/''}"
body="${body//$'\e[39;49m'/''}"
body="${body//'---------- begin diff ----------'/''}"
body="${body//' ----------- end diff -----------'/'``` '}"
body="${body//'@@ @@'/'```diff'}"
echo "::set-output name=body::$body"
- name: Create commit comment
if: steps.rector-exit-check.outputs.error == 'true' && steps.rector-length-check.outputs.length == 'true'
uses: peter-evans/commit-comment@v1
with:
body: |
<details><summary>Some changes are requested by Rector:</summary><p>
${{ steps.get-comment-body.outputs.body }}
</p></details>

Le répertoire public de reparcar est à votre disposition : https://github.com/restarteco/rector-commit-comment

Sources:

https://github.com/rectorphp/rector

https://grafikart.fr/tutoriels/rector-php-refactor-1977

https://github.com/peter-evans/commit-comment

https://github.com/Roave/BetterReflection/blob/5.2.x/docs/how-it-works.md

--

--