diff --git a/devtools/.editorconfig b/devtools/.editorconfig new file mode 120000 index 0000000000000000000000000000000000000000..6f27d6090c578d428ea0947b9cf2356bd77d736c --- /dev/null +++ b/devtools/.editorconfig @@ -0,0 +1 @@ +editorconfig \ No newline at end of file diff --git a/devtools/.gitrepo b/devtools/.gitrepo new file mode 100644 index 0000000000000000000000000000000000000000..280a3d4ef9c8840cebaa898145a50669748df47e --- /dev/null +++ b/devtools/.gitrepo @@ -0,0 +1,11 @@ +; DO NOT EDIT (unless you know what you are doing) +; +; This subdirectory is a git "subrepo", and this file is maintained by the +; git-subrepo command. See https://github.com/git-commands/git-subrepo#readme +; +[subrepo] + remote = git@gitlab-ssh.irstea.fr:pole-is/devtools-config.git + branch = master + commit = 0c931beb09149c83b9defadd3f96284e7c91ba44 + parent = 06988e9db1b139f8a6baf91aa1007c717451455a + cmdver = 0.3.1 diff --git a/devtools/README.md b/devtools/README.md new file mode 100644 index 0000000000000000000000000000000000000000..431d33a46f403674be09900c8234bb34a43a34e4 --- /dev/null +++ b/devtools/README.md @@ -0,0 +1,48 @@ +Ce dépôt contient la configuration par défaut de quelques outils utiles pendant le développement. + +Installation +============ + +Cloner le dossier dans $HOME/.config/devtools. + +```bash +git clone git@gitlab-ssh.irstea.fr:pole-is/devtools-config.git $HOME/.config/devtools +``` + +Utilisation + +Globale +------- + +Créer des liens symboliques dans $HOME pour .editorconf et phpcs.xml.dist: + +```bash +ln -snf $HOME/.config/devtools/php/phpcs.xml.dist $HOME/phpcs.xml.dist +ln -snf $HOME/.config/devtools/.editorconfig $HOME/.editorconfig +``` + +Enfin, configurer PHPStorm pour utiliser les fichiers $HOME/.config/devtools/php/phpcs.xml.dist et $HOME/.config/devtools/php/phpmd-ruleset.xml pour phpcs et phpmd respectivement. + +Par projet +---------- + +php-cs-fixer nécessite de copier le fichier directement dans le projet : + +```bash +cp $HOME/.config/devtools/php/.php_cs.dist chemin/vers/mon/projet +``` + +Pour phpcs et phpmd, c'est également nécessaire pour l'intégration continue. + +Outils +====== + +* [editorconfig](): permet de configurer globalement l'encodage, l'indentation et la gestion des espaces. + +* [phpcs](https://github.com/squizlabs/PHP_CodeSniffer): vérification du style de codage. + +* [php-cs-fixer](https://github.com/FriendsOfPhp/PHP-CS-Fixer): cousin de phpcs, mais qui permet de corriger les fichiers et inclut également des aide pour migrer d'une version de PHP à une autre. + +* [phpmd](https://phpmd.org/): détection de bugs potentielles, de fonctions/classes trop complexes, ... + + diff --git a/devtools/editorconfig b/devtools/editorconfig new file mode 100644 index 0000000000000000000000000000000000000000..e12e5a76d0dcff60e60ac131ab91286c3245027c --- /dev/null +++ b/devtools/editorconfig @@ -0,0 +1,22 @@ +# editorconfig.org +root=true + +[*] +charset=utf-8 +end_of_line=lf +indent_size=4 +indent_style=space +insert_final_newline=true +tab_width=4 +trim_trailing_whitespace=true + +[*.{js,yml,json,ts}] +indent_size=2 +tab_width=2 + +[Makefile] +indent_style=tab + +[*.md] +max_line_length = off +trim_trailing_whitespace = false diff --git a/devtools/gitlab/gitlab-ci.bundle.yml b/devtools/gitlab/gitlab-ci.bundle.yml new file mode 100644 index 0000000000000000000000000000000000000000..0c9a6684c80b7ad7974170086e0bfbc07cbc7bbc --- /dev/null +++ b/devtools/gitlab/gitlab-ci.bundle.yml @@ -0,0 +1,40 @@ + +.defaults: &defaults + tags: [ docker ] + image: isdevtools.irstea.fr/poleis/php-analysis:7.1 + +phploc: + <<: *defaults + script: phploc src + +lint: + <<: *defaults + script: + - parallel-lint -j $(nproc) src + - twig-lint lint src + - yaml-lint src + +php-cs-fixer: + <<: *defaults + script: php-cs-fixer fix --dry-run --verbose + +phpcpd: + <<: *defaults + script: phpcpd --fuzzy src + +.with-vendors: &with-vendors + <<: *defaults + before_script: + - composer install --prefer-dist --no-progress --no-suggest + cache: + key: "$CI_COMMIT_REF_NAME" + paths: + - /composer/cache + +composer-require-checker: + <<: *with-vendors + script: composer-require-checker + +phpmd: + <<: *with-vendors + script: phpmd src text ./phpmd-ruleset.xml --suffixes=php diff --git a/devtools/php/.php_cs.dist b/devtools/php/.php_cs.dist new file mode 120000 index 0000000000000000000000000000000000000000..13ed54280da19d777d42f04eb2deca2dbdf52a4a --- /dev/null +++ b/devtools/php/.php_cs.dist @@ -0,0 +1 @@ +php_cs.dist \ No newline at end of file diff --git a/devtools/php/php_cs.dist b/devtools/php/php_cs.dist new file mode 100644 index 0000000000000000000000000000000000000000..745a58bf8e96cbf61e37737160e00df7a1240083 --- /dev/null +++ b/devtools/php/php_cs.dist @@ -0,0 +1,136 @@ +<?php +define('COMMIT_CACHE', '.php_cs.commit-cache'); + +$finder = PhpCsFixer\Finder::create() + ->exclude('vendor') + ->exclude('node_modules') + ->exclude('cache') + ->files() + ->name('*.php') + ->in('.'); + +$yearRange = getGitCommitYears(); + +$ruleSets = ['@PSR2' => true, '@Symfony' => true]; + +$rules = [ + // Configuration && overrides + 'binary_operator_spaces' => ['align_double_arrow' => true], + 'blank_line_after_opening_tag' => false, + 'concat_space' => ['spacing' => 'one'], + 'method_argument_space' => ['ensure_fully_multiline' => true], + + // Risky + 'is_null' => ['use_yoda_style' => false], + 'non_printable_character' => ['use_escape_sequences_in_strings' => true], + + // Safe + 'align_multiline_comment' => true, + 'array_syntax' => ['syntax' => 'short'], + 'general_phpdoc_annotation_remove' => ['annotations' => ['author', 'package']], + 'no_multiline_whitespace_before_semicolons' => true, + 'no_useless_else' => true, + 'no_useless_return' => true, + 'ordered_imports' => true, + 'phpdoc_add_missing_param_annotation' => true, + 'phpdoc_annotation_without_dot' => true, + 'phpdoc_order' => true, + 'semicolon_after_instruction' => true, + 'yoda_style' => false, + + 'header_comment' => [ + 'commentType' => 'comment', + 'location' => 'after_declare_strict', + 'separate' => 'bottom', + 'header' => <<<HEADER +Copyright (C) $yearRange IRSTEA +All rights reserved. +HEADER + , + ], +]; + +$phpVersion = findComposerPhpReq(); + +if ($phpVersion >= 5.6) { + $ruleSets['@PHP56Migration'] = true; +} +if ($phpVersion >= 7.0) { + $ruleSets['@PHP70Migration'] = true; + $rules['declare_strict_types'] = true; +} +if ($phpVersion >= 7.1) { + $ruleSets['@PHP71Migration'] = true; +} + +echo "Rulesets: " . implode(', ', array_keys($ruleSets)) . ".\n"; + +return PhpCsFixer\Config::create() + ->setRiskyAllowed(true) + ->setUsingCache(false) + ->setIndent(' ') + ->setLineEnding("\n") + ->setRules(array_merge($ruleSets, $rules)) + ->setFinder($finder); + +/** + * @return string + */ +function getGitCommitYears(): string +{ + return getCachedValue('years', function () { + echo "Examining git history...\n"; + $last = date('Y'); + $first = exec('git log --format=%cd --date=format:%Y --date-order | tail -n1') ?? $last; + return (null !== $last && $last !== $first) ? "$first-$last" : $first; + }); +} + +/** + * @return float + */ +function findComposerPhpReq() +{ + return getCachedValue('php-req', function () { + if (file_exists('composer.json')) { + $data = json_decode(file_get_contents('composer.json'), true); + if (is_array($data) && isset($data['require']['php'])) { + if (preg_match('/(?:>=?|\^|~)\s*([57]\.[0-9])/', $data['require']['php'], $groups)) { + return (float) $groups[1]; + } + } + } + + return 5.6; + }); +} + +/** + * @param string $key + * @param callable $produce + * + * @return string|int|bool|array + */ +function getCachedValue($key, $produce) +{ + static $commit = null; + if (null === $commit) { + $commit = trim(`git rev-parse HEAD`); + } + if (file_exists(COMMIT_CACHE) && filemtime(COMMIT_CACHE) >= filemtime(__FILE__)) { + $cache = json_decode(file_get_contents(COMMIT_CACHE), true); + } else { + $cache = []; + } + if (!isset($cache[$commit][$key])) { + if (!isset($cache[$commit])) { + $cache[$commit] = []; + } + $cache[$commit][$key] = $produce(); + file_put_contents(COMMIT_CACHE, json_encode($cache)); + } + + return $cache[$commit][$key]; +} + +// vim:filetype=php diff --git a/devtools/php/phpcs.xml.dist b/devtools/php/phpcs.xml.dist new file mode 100644 index 0000000000000000000000000000000000000000..f6de6b66839ca1496d0dc3ff25dcbc3133d8f467 --- /dev/null +++ b/devtools/php/phpcs.xml.dist @@ -0,0 +1,21 @@ +<?xml version="1.0"?> +<ruleset name="Irstea" namespace="Irstea\Standard"> + + <description>Coding standard of Irstea, pôle informatique scientifique</description> + + <file>src</file> + + <arg name="encoding" value="utf-8"/> + <arg name="extensions" value="php"/> + <arg name="ignore" value="vendor/*,*/cache/*"/> + <arg value="sp"/> + + <rule ref="PSR2"/> + + <rule ref="Generic.Files.LineLength"> + <properties> + <property name="lineLimit" value="140"/> + </properties> + </rule> + +</ruleset> diff --git a/devtools/php/phpmd-ruleset.xml b/devtools/php/phpmd-ruleset.xml new file mode 100644 index 0000000000000000000000000000000000000000..7e28455615076a99a6b6c1ba6f471d1f839b1ac6 --- /dev/null +++ b/devtools/php/phpmd-ruleset.xml @@ -0,0 +1,31 @@ +<?xml version="1.0"?> +<ruleset name="Irstea PHPMD ruleset" + xmlns="http://pmd.sf.net/ruleset/1.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://pmd.sf.net/ruleset/1.0.0 http://pmd.sf.net/ruleset_xml_schema.xsd"> + + <description>Irstea ruleset</description> + + <rule ref="rulesets/controversial.xml/Superglobals" /> + <rule ref="rulesets/controversial.xml/CamelCaseClassName"/> + <rule ref="rulesets/controversial.xml/CamelCasePropertyName"/> + + <rule ref="rulesets/design.xml/ExitExpression" /> + <rule ref="rulesets/design.xml/EvalExpression" /> + <rule ref="rulesets/design.xml/GotoStatement" /> + <rule ref="rulesets/design.xml/DevelopmentCodeFragment"> + <property name="unwanted-functions" value="var_dump,print_r,debug_zval_dump,debug_print_backtrace,dump" /> + </rule> + + <rule ref="rulesets/naming.xml/ShortMethodName" /> + <rule ref="rulesets/naming.xml/ConstructorWithNameAsEnclosingClass" /> + <rule ref="rulesets/naming.xml/ConstantNamingConventions" /> + + <rule ref="rulesets/unusedcode.xml/UnusedPrivateMethod" /> + <rule ref="rulesets/unusedcode.xml/UnusedPrivateField" /> + + <exclude-pattern>vendor/</exclude-pattern> + <exclude-pattern>node_modules/</exclude-pattern> + <exclude-pattern>.*/cache/</exclude-pattern> + +</ruleset>