overtrue / phplint

:bug: A tool that can speed up linting of php files by running several lint processes at once.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Don't forget to change Console Application version before publishing a new release

llaville opened this issue · comments

As lot of others application that provide a Symfony Console Application, PHPLint suffer to a hard-coded concept about Application versionning.

Look at https://github.com/overtrue/phplint/blob/9.1/src/Console/Application.php#L39

As maintainer of application, if we forgot to change VERSION value before publish a new version, we will keep an outdated version number on all official versions : including PHAR and Docker distributions.

To avoid such issue, here is my proposal :

Add a new PHP CS Fixer to check and fix Application::VERSION on a git push hook via Composer.

  • composer.json patch
diff --git a/composer.json b/composer.json
index 1d6d4e4..0c27795 100644
--- a/composer.json
+++ b/composer.json
@@ -54,6 +54,9 @@
       "pre-commit": [
         "composer style:fix",
         "composer code:check"
+      ],
+      "pre-push": [
+        "composer qa:check"
       ]
     },
     "branch-alias": {
@@ -73,6 +76,8 @@
       "@composer bin all install --ansi"
     ],
     "cghooks": "vendor/bin/cghooks",
+    "qa:check": "vendor/bin/php-cs-fixer fix --config=.php-cs-fixer.release.php --using-cache=no --verbose --ansi --diff --dry-run",
+    "qa:fix": "vendor/bin/php-cs-fixer fix --config=.php-cs-fixer.release.php --using-cache=no --verbose --ansi",
     "style:check": "vendor/bin/php-cs-fixer fix --config=.php-cs-fixer.dist.php --using-cache=no --verbose --ansi --diff --dry-run",
     "style:fix": "vendor/bin/php-cs-fixer fix --config=.php-cs-fixer.dist.php --using-cache=no --verbose --ansi",
     "tests:unit": "vendor/bin/phpunit --testsuite=cache,configuration,finder",
@@ -84,6 +89,8 @@
   "minimum-stability": "dev",
   "prefer-stable": true,
   "scripts-descriptions": {
+    "qa:check": "Run QA style checks before pushing new tag and releasing a new version (only dry run - no fixing!).",
+    "qa:fix": "Run QA style checks and fix violations.",
     "style:check": "Run style checks (only dry run - no fixing!).",
     "style:fix": "Run style checks and fix violations.",
     "tests:unit": "Run unit tests on following components: cache, configuration, finder",

With new PHP CS Fixer config file .php-cs-fixer.release.php

<?php

require_once 'vendor-bin/php-cs-fixer/src/ApplicationVersionFixer.php';

use Overtrue\CodingStandard\Fixer\ApplicationVersionFixer;

return (new PhpCsFixer\Config())
    ->registerCustomFixers([
        new ApplicationVersionFixer(),
    ])
    ->setRules([
        ApplicationVersionFixer::name() => true,
    ])
    ->setFinder(
        PhpCsFixer\Finder::create()
            ->in([__DIR__.'/src/Console'])
    )
;

And new Fixer

<?php

namespace Overtrue\CodingStandard\Fixer;

use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
use SplFileInfo;

/**
 * @link https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/blob/master/doc/cookbook_fixers.rst
 * @link https://tomasvotruba.com/blog/2017/07/24/how-to-write-custom-fixer-for-php-cs-fixer-24
 *
 * @author Laurent Laville
 * @since Release 9.1.0
 */
final class ApplicationVersionFixer extends AbstractFixer
{
    /**
     * @inheritDoc
     */
    public function isCandidate(Tokens $tokens): bool
    {
        return $tokens->isAllTokenKindsFound([T_CLASS, T_CONSTANT_ENCAPSED_STRING]);
    }

    /**
     * @inheritDoc
     */
    public function isRisky(): bool
    {
        return false;
    }

    protected function applyFix(SplFileInfo $file, Tokens $tokens): void
    {
        foreach ($tokens as $index => $token) {
            if (!$token->isGivenKind(T_CONSTANT_ENCAPSED_STRING)) {
                continue;
            }
            if (!$this->isVersionConst($tokens, $index)) {
                continue;
            }

            $tag = @exec('git describe --tags --abbrev=0 2>&1');

            if ($token->getContent() !== $tag) {
                $tokens[$index] = new Token([$token->getId(), "'$tag'"]);
            }
        }
    }

    private function isVersionConst(Tokens $tokens, int $index): bool
    {
        $prevTokenIndex = $tokens->getPrevMeaningfulToken($index);
        if (!$tokens[$prevTokenIndex]->equals('=')) {
            return false;
        }

        $constantNamePosition = $tokens->getPrevMeaningfulToken($prevTokenIndex);
        return $tokens[$constantNamePosition]->equals([T_STRING, 'VERSION']);
    }

    /**
     * @inheritDoc
     */
    public function getDefinition(): FixerDefinitionInterface
    {
        return new FixerDefinition(
            'Application::VERSION constant value must match the current git tag.',
            []
        );
    }

    /**
     * @inheritDoc
     */
    public function getName(): string
    {
        return self::name();
    }

    public static function name(): string
    {
        return 'OvertrueCsFixer/application_version';
    }

    /**
     * @inheritDoc
     */
    public function supports(SplFileInfo $file): bool
    {
        return $file->getBasename() === 'Application.php';
    }

    /**
     * @inheritDoc
     */
    public function getPriority(): int
    {
        return 0;
    }
}

TIP When we still want to push additional code to repository before the final release, we can by pass the qa:check failures if any, with the --no-verify option of git push command.

This is the same solution, if we want to by pass style:fix on pre-commit git hook : git commit --no-verify

@overtrue Tell me if you're agree to add a such solution !

Here is a preview of the new Composer script in action

composer_qa_check

The results are obtained before to tagging the code with git tag -a 9.1.0 command (i.e)