This is a follow-up to #1266444: DrupalCI should return failed for poor code style - php

Problem/Motivation

We want drupalci to be able to provide a coding standards report for PHP, for core or contrib under test.

#1266444: DrupalCI should return failed for poor code style - php gave us a plugin to work with.

Proposed resolution

Modify the simpletest test definition to use phpcs, for core and contrib.

Add more tests for the core use-case.

Expand capability and tests for contrib use-case.

Remaining tasks

User interface changes

API changes

Data model changes

Comments

Mile23 created an issue. See original summary.

Mile23’s picture

Mile23’s picture

Title: DrupalCI should return failed for poor code style in contrib - php » DrupalCI should return failed for poor code style Part II - php
Issue summary: View changes
Berdir’s picture

Would be awesome to have this, but I think it would be important for contrib to be able to opt-in to this, at least for an initial phase and maybe opt-out later.

There are pretty large contrib projects out there, quite a few (co-)maintained by me and I fear that forcing this would bring development of them to a halt until all coding standards issues are fixed, which could be quite some work.

Mile23’s picture

That's part of the point here: Figure out the use-case for contrib.

So far the idea is that if the contrib project doesn't have a phpcs.xml.dist file, then there's no sniffing.

We're also just making a report at this point, and not doing the failing until there's a way to configure it through d.o.

hass’s picture

I fear more that it complains about non-poor code just because the software is too stupid. We have seen this in many cases in past where coder* was used. There have been tons of fail positives and a test runner was used that checked for non-core code style.

  • Mile23 committed cf4a30a on 2841472-phpcs-contrib
    Issue #2841472: Removed composer_force_coder, improved docs for fixture/...

  • Mile23 committed 0437f53 on 2841472-phpcs-contrib
    Issue #2841472: Framing out the contrib behavior.
    
  • Mile23 committed 6acb62c on 2841472-phpcs-contrib
    Issue #2841472: Better behavior for contrib, test using examples.
    
Mile23’s picture

Status: Active » Needs review

Added better behavior for phpcs plugin.

Don't sniff if:

  • There's no phpcs executable
  • There's no phpcs.xml(.dist) file in start_directory.

There's no longer a config for config_directory because start_directory should be where your phpcs.xml.dist file resides.

Added a test which uses Examples as a fixture. Added this issue to the Examples project to provide a place to park patches: #2842832: Dummy issue for DrupalCI

Mixologic’s picture

Should we add phpcs to the php containers, like we do with drush/composer? Does it even need to run in the version under test, or is phpcs something that can run on the host machine?

Mixologic’s picture

Status: Needs review » Needs work

Er rather I see now they run inside the container. I'll assume that means it cant be on the host, but phpcs *could* be installed globally if we wanted.

I took the liberty of tweaking our jenkins testing job to add some additional things, like, actually looking for checkstyle.xml, and being able to set the drupalci_testbot branch:

Couple of things: sniff_only_changed FALSE didnt work.

The checkstyle report produced inside the container has the container paths in it. which makes jenkins full of java exception hate:
https://dispatcher.drupalci.org/job/drupalci_test_containers/736/checkst...

I wonder if we can change the '<file name="/var/www/html/' in the xml to the build source directory? I wonder if that even matters after the fact -> i.e. I dont know if the checkstyle plugin for jenkins is doing the copy from the workspace when you click on the file or after it parses the results and therefore stashes it.

https://dispatcher.drupalci.org/job/drupalci_test_containers/735/

Other than that, hella sweets.

Mixologic’s picture

Also, for posterity and the eventual drupal.org displayin:

https://dispatcher.drupalci.org/job/drupalci_test_containers/736/checkst...

Mixologic’s picture

One more thing, we also might want to consider how to handle only changing phpcs.xml.dist. If a patch changes the rules, it ought to sniff the whole codebase? Not sure.

also, do we care about d7? does anything act differently if we do?

Mile23’s picture

Er rather I see now they run inside the container. I'll assume that means it cant be on the host, but phpcs *could* be installed globally if we wanted.

Coder/phpcs should be a dependency of the project, and we only check if it's there. This is the same problem solved by composering under the container: If the project specifies a version of coder/phpcs that can't be resolved under the platform constraints, then it's a bad idea to run it, and the maintainer should find out about it.

So once the compser-within-the-container thing happens, this part will solve itself.

The checkstyle report produced inside the container has the container paths in it. which makes jenkins full of java exception hate:

I'll look at other output formats because it would be much better to not have to modify anything in the report file. Otherwise we end up with the same container vs. host path problem, which is a bunch of string functions traversing an XML file.

One more thing, we also might want to consider how to handle only changing phpcs.xml.dist. If a patch changes the rules, it ought to sniff the whole codebase? Not sure.

This is a good point. If either phpcs.xml or the dist is changed, then sniff everything.

Mixologic’s picture

Otherwise we end up with the same container vs. host path problem, which is a bunch of string functions traversing an XML file.

I was thinking something much more brute forceish than caring if its XML:

Something along the lines of:

$checkstyle_report_filename = $build->getArtifactDirectory() . '/phpcs/phpcs_checkstyle.xml';
$checkstyle_xml = file_get_contents($checkstyle_report_filename);
$checkstyle_xml = preg_replace("!<file name=\"". $this->environment->getExecContainerSourceDir() . "!","<file name=\"" . $this->codebase->getSourceDirectory(), $checkstyle_xml);
 file_put_contents($checkstyle_report_filename, $checkstyle_xml);

The checkstyle plugin is definitely the format we want from jenkins, and I dont think we can really get away from the inside vs outside container path without translating it back, unless we run it from the outside to begin with.

As another totally random aside to this, should we be using phpcs to check our javascript? (https://github.com/squizlabs/PHP_CodeSniffer/wiki/Configuration-Options#...) Or are we settled on ESLint as whats needed there?

Mile23’s picture

It turns out core has a package.json file, which does all the specifying for eslint. #2809477: Add ES6 to ES5 build process So we'd do an npm plugin and then a run-jslint plugin, both in the container. I think our implementation issues for that are #2600626: [PP-1] Ensure availability of node/npm in the testrunners and #2575501: [PP-2] DrupalCI should return failed for poor code style - JS

  • Mile23 committed ada601e on 2841472-phpcs-contrib
    Issue #2841472: Test whole project if phpcs config is modified (with...
Mile23’s picture

ada601ee adds a behavior where if phpcs.xml or phpcs.xml.dist is changed, then the sniff is run against the whole project, with a test. Also we strongarm the checkstyle file paths to be host paths, as per #15.

  • Mile23 committed 28852df on 2841472-phpcs-contrib
    Issue #2841472: Updating test strings.
    
Mile23’s picture

Status: Needs work » Needs review
Mile23’s picture

  • Mile23 committed 56fc931 on 2841472-phpcs-contrib
    Issue #2841472: Minor docs, CS improvement.
    

  • Mixologic committed f1f3731 on 2841472-phpcs-contrib
    Issue #2841472: updates php version
    
Mixologic’s picture

Okay, been reviewing this one, and I think there's a few things Im trying to sort out. Lets start with something minor:

The value '/phpcs/phpcs_checkstyle.xml' shows up a few times. maybe we set that on a property on this plugin and use that instead (if it ever has to move, or if we ever decide that we're going to do something like automagically namespace plugin artifacts in the artifact dir, it'll be nice not to forget to change it everywhere)

so..
getPhpcsExecutable() -> Im kinda not sure we should be so rigid with our check. I think we ought to maybe leave room for a phpcs that just happens to live inside the executable containers, much like composer does, because we might want to have codesniffing for d7 modules (especially somewhere like project applications kind of work), and Im not sure its going to come in the same way. Or maybe it does.. just trying to imagine the d7 use case here. I like the idea of not codesniffing a project that does not have a phpcs.xml file, but I dont think we should extend that to whether or not the project also requires phpcs as a dependency... i.e. force coder/force phpcs I think is the way to go with the testing infra.

finally, lets ensure that whatever needs d7 overriding can easilly be done so in a phpcsD7 plugin.. I didnt fully look it over, maybe its already that way.

Be sure to pull.. I merged dev back into this. manytimes.

Mixologic’s picture

Status: Needs review » Needs work
Mile23’s picture

The value '/phpcs/phpcs_checkstyle.xml' shows up a few times. maybe we set that on a property on this plugin and use that instead

We really should be doing this generically anyway, so that it's always /{pluginname}/some_report.xml. But I'll make that a follow-up and allow configuring the report filepath.

getPhpcsExecutable() -> Im kinda not sure we should be so rigid with our check. I think we ought to maybe leave room for a phpcs that just happens to live inside the executable containers, much like composer does

D8 core is doing coding standards by adding individual rules in manageable patches, against a specific version of coder. That means we will always be requiring coder at a specific version, until maintainers decide to move on to the next one. Coder will always require the phpcs executable.

This means we can let core do all the work of determining whether phcps should be there, and what version it is.

This also means we can let Composer do the work of telling us when there are version and platform constraint problems wrt phpcs.

Also: In order for phpcs to work with coder, it has to be configured with installed_paths to use the composer-specified version of coder. That has to be the container path to /vendor/drupal/coder/coder_sniffer/.

Basically it's all the same reasons we should be re-running composer install inside the container.

I also think checking-for-the-tool is going to be a common pattern, too, because for JS linting we'll be doing an npm install and then checking whether eslint is present where we expect it.

finally, lets ensure that whatever needs d7 overriding can easilly be done so in a phpcsD7 plugin.. I didnt fully look it over, maybe its already that way.

We can't really test the behavior for D7 core because no config file. Find a D7 contrib with a phpcs.xml.dist and we can work on it. I think D7 core/contrib should be a follow-up when someone asks for it. It's really clear what D8's needs are, but not so much D7.

Mile23’s picture

Basically it's all the same reasons we should be re-running composer install inside the container.

...IN FACT....

We have to run composer install inside the container for this to work with #2744463: Add phpcs, coder 8.2.8 as --dev requirements for drupal/core.

Because we run a composer post-install event which we use to configure phpcs to use coder.

We could work-around since we already know the installed_paths directory is a requirement, and the plugin does that, but it'd be better to have the composer step in the container.

  • Mile23 committed cf4c465 on 2841472-phpcs-contrib
    Issue #2841472: Added todo for better test coverage.
    
  • Mile23 committed da94ab9 on 2841472-phpcs-contrib
    Issue #2841472: Report filepath is now set in report_file_path config....
Mile23’s picture

This adds the report_file_path config per #24 and updates the tests for the new dev environment.

We're basically postponed here for core 8's use-case while we figure out #2845802: Run composer install inside the container to allow for platform constraints, path-based config

After a certain point, core will perform the config of phpcs to use coder, so we won't need the installed_path config for D8.

Core D7 doesn't have a phpcs.xml (or a composer.json to depend on phcps), so it's unclear how to proceed.

Contrib for D8 could have a phpcs.xml file, but no composer.json, in which case it would inherit whatever versions core is using. That could lead to wrong reporting. If the contrib has a composer.json, it might need a helping hand with installed_path config. Or not. How should we proceed with a merged dev dependency that might be satisfied by core and installed by core?

We were unable to find a D7 contrib project with a phpcs.xml file, so it's not a use case at the moment. Obviously that can change.

In IRC we talked about having a global code quality metric where we could just run coder against all the projects. That's out of scope here, but it's an interesting idea. It kind of suffers from the same ambiguity as the above situations, though.

Mixologic’s picture

How should we proceed with a merged dev dependency that might be satisfied by core and installed by core?

I don't think that we need to support project specific versions of phpcs or coder. The only thing that should be configurable on a per project basis is phpcs.xml. We have "Drupal" coding standards, not project specific coding standards. A maintainer can enable/disable whatever rules they are ready to support, but we're not going to allow adding new rules that only a specific project supports. So, if core says we use coder 8.2.8 and phpcs version >=2.5.1, then thats what contrib is going to to inherit.

For d7 , we would either composer require coder and phpcs (since we do have a fake composer.json), or just download them somewhere else. Composer isn't an essential step in acquiring phpcs - its packaged as a phar, and coder is really just a git repo that we need to be downloaded to somewhere.

Mixologic’s picture

There was much IRC conversing. I think we landed on supporting just about everything.

https://docs.google.com/spreadsheets/d/1Oq-sM5yJmeAYPwFXRX2QFcQnNvqTGHgM... has more details about the combinations of d7/d8/core/contrib with coder/phpcs.xml and not, and how those ought to be handled.

Mixologic’s picture

Also, this should probably be marked as a dupe: #2589853: Integrate PHP CodeSniffer test with drupal.org

  • Mile23 committed e9db5c8 on 2841472-phpcs-contrib
    Issue #2841472: Added generic coder behavior to phpcs plugin. Not...

  • Mile23 committed 9982b45 on 2841472-phpcs-contrib
    Issue #2841472: Some tweaks to phpcs plugin, improved tests so they pass...

  • Mile23 committed e2acd21 on 2841472-phpcs-contrib
    Issue #2841472: Refactored Phpsc behavior to allow unit testing for...

  • Mile23 committed 1274745 on 2841472-phpcs-contrib
    Issue #2841472: Ensuring we dont generate changed file list for full...
Mile23’s picture

The principles that we decided on in IRC are: Everything gets sniffed, and if you want to control how your project is sniffed, add a phpcs.xml.dist file. If the project doesn't require coder or phpcs, we require the latest stable of drupal/coder. If the project has a phpcs.xml(.dist) file, we use that, otherwise we use the current Drupal standard.

OK, so there's a matrix of test cases you can see here: http://cgit.drupalcode.org/drupalci_testbot/tree/tests/DrupalCI/Tests/Pl...

They represent a bunch of use-cases.

The use cases are a matrix of the following components per project:

  1. Has composer requirement for phpcs/coder. This would be, eg, core after #2744463: Add phpcs, coder 8.2.8 as --dev requirements for drupal/core If the project doesn't have a requirement for coder, we require drupal/coder @stable and use it. If core requires one version and contrib requires another, they fight it out in Composer-land and our plugin has no opinion. If this becomes a problem, file a follow-up and we'll make it work.
  2. Has a phpcs.xml(.dist) file. If the project has a phcps config file (.dist or not), then we use it. If it doesn't, we use the Drupal standard with CLI options as per https://www.drupal.org/node/1587138
  3. Has modified files. We want to only sniff modified files if possible. If there are no modified files, we'll sniff the whole project. This would be for, eg, branch testing.
  4. Has a modified phpcs.xml(.dist) file. If the config file is modified, we always sniff the whole project. Note that 'modifying' the config file could be adding or removing. If it's removing the config file, then we honor the sniff_only_changed configuration, and try to only sniff the changed files.

While there's a way to cause this thing to stop the build based on CS errors (thus failing the test), that's off by default. You only end up with a phpcs report in your artifact folder.

What still needs to happen:

D7 tests, more contrib tests, and most of all, the following:

This plugin uses Composer inside the PHP container to resolve dependencies. This means it does not have access to the cache of metadata goodness which lives in the host. We need to map /opt/drupalci/composer-cache from the host to a similar place in the PHP container. Then Composer takes only a few seconds to figure out which drupal/coder to use, instead of 30.

  • Mixologic committed 26d0b7d on 2841472-phpcs-contrib
    Issue #2841472: Demonstrates why we need tests
    
  • Mixologic committed 46ae7a4 on 2841472-phpcs-contrib
    Issue #2841472: Utilizes the complete() interface
    
  • Mixologic committed 85b095c on 2841472-phpcs-contrib
    Issue #2841472: exchanges buildTaskPlugin entanglement for clunkier...
  • Mixologic committed a0f7d10 on 2841472-phpcs-contrib
    Issue #2841472: unconfigures report_file_path and makes it a static...

  • Mixologic committed 1f50448 on 2841472-phpcs-contrib
    Issue #2841472: Composer needs git inside to know whats inside of its...

  • Mile23 committed 0a95d0b on 2841472-phpcs-contrib
    Issue #2841472: Added 2 D7 contrib tests.
    

  • Mile23 committed d19e626 on 2841472-phpcs-contrib
    Issue #2841472: More test good.
    
Mile23’s picture

Status: Needs work » Needs review

Added more tests.

Needs a follow-up to determine what's installed using a better technique than looking for a phpcs executable.

Other than that I think we should move forward and learn more things before trying any more.

Mile23’s picture

Mixologic’s picture

Status: Needs review » Fixed

So yeah, this is in production, and producing checkstyle.xml files. Patch testing cant use it yet though because checkstyle makes jenkins faint.

Mixologic’s picture

Component: Coding Standards » Jobs and Job Handling

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.