Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Set ESLint rules as errors while preventing hooks/CI from failing #6945

Merged
merged 1 commit into from
Mar 22, 2017

Conversation

ghost
Copy link

@ghost ghost commented Jul 21, 2016

We are migrating old code to the new standards (see janitorial efforts at #8562). In the meantime, the new code is failing to adhere to our current code-style because there are rules set as warnings yet.

Until now, they could not be set to errors because doing so will break the git hooks and our CI tools. This PR aims to address that problem by only reporting errors in lines modified, instead of the whole code base. With this approach, we can enforce that all new code is always written following the up-to-date conventions, which will help the migration effort to be completed sooner.

The gist of this PR is just piping commands for several contexts:

eslint -f json | eslines

Caveats

Editor integration has proven harder than expected, and, at this point, it's not a goal to aim for it. This means that in editors all rules will be reported as errors.

For editors that allow calling custom commands for linting, there may be a way out and a solution may be found. I'm happy to help with this. Yet, as it needs to be considered in a case-by-case basis and it is time-consuming I'd rather do it after this PR is merged/aborted.


How to test locally

  • git clone [email protected]:Automattic/wp-calypso.git
  • git checkout -b add/eslint-formatters-calypso-testcase origin/add/eslint-formatters-calypso-testcase
  • npm install
  • make githooks

Test that it works:

  • open client/components/web-preview/index.jsx and remove the spaces after and before the parens at line 167. Do not introduce any other change.
  • git add client/components/web-preview/index.jsx
  • git commit: expected result is that commit hook will report an error in the line modified and won't let you commit (notice that the same error in lines not modified is considered a warning)

Test that it doesn't prevent you from commiting (if the errors are in lines not modified):

  • open client/components/web-preview/index.jsx and remove all debug statements and the debug importing. Do not introduce any other change.
  • git add client/components/web-preview/index.jsx
  • git commit: expected result is that commit hook will not report any error.

@nb
Copy link
Member

nb commented Jul 21, 2016

If we are adding the whole project to Calypso, why not just make it a dependency? I know we chatted about having the code as part as Calypso, but in this case it should live in lib/ somewhere and wouldn't need licenses, package.json and things like this.

bin/run-lint Outdated
const argsBranchName = [ 'rev-parse', '--abbrev-ref', 'HEAD' ];
const branchName = child_process.spawnSync( 'git', argsBranchName ).stdout.toString().trim();

if ( branchName === 'master' ) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a chance we can move this logic out of the run-lint script and somehow into the eslines code? I would like to keep running eslint simple, so that people can easily do it outside of Calypso. If we always need those format arguments, it will be much harder to integrate with editors.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the suggestion! At the moment, what I have done is to create a new formatter at eslint-formatters-calypso: it is only a wrapper for deciding which formatter to return depending on the git branch. I would need to check how different editors call eslint, but I guess passing a formatter like in eslint -f eslines . should be possible. However it may be, moving the logic inside eslint-formatters-calypso is a good idea.

As an aside: eslines has been rebranded to a more meaningful name: eslint-formatters-calypso, as its responsibilities have grown and changed. eslines is now the name of the smart and wrapper formatter inside that package.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does it need to be a formatter? Can’t it be an eslint plugin, so that we can include it in the configuration?

Copy link
Author

@ghost ghost Jul 29, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TL;DR:

  • It can't be a plugin, but we need something more than a formatter (at least, the ability to control exit codes). This is something we can easily do by piping eslint . | eslines.
  • Take advantage of this work from the editors seems easy, as they usually execute npm run lint for linting or allow to configure a command manually - I need to research more on this, though.

# More context about formatters VS plugins

An ESLint plugin bundles a set of rules, environment variables and processors. An ESLint formatter is something you pass as an argument to the CLI and there is no way to make it play together with the config file (see code - currentOptions.format is the command line option --format). The ESLint team has already had some discussions around this: move formatters into plugins or have the ability to pass options to formatters.

# More than a formatter

There are situations where people needs to configure the formatter (for example, to configure the colors or the file paths for a specific environment) or it needs to do more than an ESLint formatter is capable of.

To work around this, sometimes, people encapsulates the call to ESLint in its own script (eslint-nibble, eslint-pretty).

Other approaches that I have seen in practice while using ESLint directly are:

The recommended way is:

eslint -f json . | formatter --your-options

This is something we can benefit from as it would allow us to control the exit code (actually, we can already do this, as before it was migrated to formatter this is the approach I was taking).

The common way to pass info to the formatter is by using environment variables or a config file (we use also the config file).

There is some other hacky way that depends on the fact that ESLint uses optionator for parsing options from the command line. This is used by eslint-friendly-formatter. The way this works is:

  • ESLint uses optionator to parse command line arguments.

  • Optionator provides an API to do optionator.parse(process.argv) (see ESLint code) that returns an object like {opt1: 2, opt2:3, _: ['positional-param-1', 'positional-param-2']} with keys being the options and any other positional parameter added in the _ array.

  • ESLint uses the contents in the _ array as the input files (but discards any file that does not exist).

  • Optionator happen to follow the bash convention of using -- as a marker for end of options so any value that follows the marker is considered a positional parameter. example of the hack:

    command --opt1 2 --opt2 3 -- file-to-process-1 file-to-process-2 --opt3

    for this command, optionator.parse(process.argv) would return{opt1: 2, opt2:3, _: ['file-to-process-1', 'file-to-process-2', '--opt3']}.

  • eslint-friendly-formatter takes advantage of this hack (ESLint would not process --opt3 as that file does not exists) to retrieve the command line options after the -- marker and use them for its own configuration.

@seear
Copy link
Contributor

seear commented Jul 25, 2016

For the master branch, it would only break the build for ESLint parsing errors.

Something I didn't understand very well from the previous discussion around this—do we ever run CircleCI on master?

@blowery
Copy link
Contributor

blowery commented Jul 25, 2016

Something I didn't understand very well from the previous discussion around this—do we ever run CircleCI on master?

@seear yup, on every commit to master. Right now failures only get reported to project owners via email.

@ghost
Copy link
Author

ghost commented Jul 27, 2016

@seear @blowery CircleCI build runs on every merge (which may include more than one commit, depending on how the topic branch is merged into master - squashed or not). See for example these two random builds: one commit and a merge with several commits.

*
* ex: @@ -65,3 +65,3 @@ module.exports = {
*
* 65 is the starting point, 3 the number of lines affected,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

3 the number of lines affected

I think the counts for a hunk (the number after the comma) includes the context lines, not just the modified lines—see example below. So we will be including the context lines from the diff in the lint. Is that what we want? (Maybe it is.)

screen shot 2016-07-27 at 22 22 20

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ignore above comment, just spotted the -U0 passed to the diff command in git-diff 😄

@ghost
Copy link
Author

ghost commented Jul 29, 2016

@nb In response to #6945 (comment). Well, you already know I think it makes sense to distribute this as an independent repo/package. In importing this into Calypso repo, I asked myself a few questions:

  • where should it be placed? bin/ was the directory that made the most sense to me, as other building scripts are there. I would be happy to move it to other place - but, currently, I don't see any other place better than this. Any suggestion?
  • what files shall remain? I was deleting the LICENSE at the same time you were commenting, as I realized that should not be there. As for the other files (package.json, etc): I left them there because they are useful for development (to have an isolated npm run test for this package, for example).
  • how to call it? I was reviewing if we could import this as an ESLint plugin, but that is not possible. An argument that can be done is whether it should be imported as a npm local package and use it by calling ./node_packages/eslint-formatter-calypso/ or call it directly from the path ./bin/eslint-formatter-calypso. I don't have a strong opinion about this. If I chose the second option is because it seemed less intrusive with the current state of things - but I would be happy to change it. Do you think it would be better to import it as a npm package? Do you see any other option?

@ghost ghost changed the title Teach CircleCI to be smarter about linting Changing the linting strategy Jul 31, 2016
@ghost
Copy link
Author

ghost commented Jul 31, 2016

@nb this is ready for a second round. The git hooks pre-push and pre-commit have been updated to use eslines formatter and the feedback for the first round processed. The explanation in the PR has been updated too.

If this approach generates consensus, I would like to merge this PR into master after resolving any issues that may appear within this second round. Then, I would address anything left (for example: editors integration with ESLint, etc) within other PRs.

@ghost ghost mentioned this pull request Aug 6, 2016
@ghost ghost force-pushed the add/eslint-formatters-calypso-testcase branch from 863296a to f9d4a6c Compare August 13, 2016 12:40
@ghost
Copy link
Author

ghost commented Aug 13, 2016

@nb this branch has been rebased. I have also updated the pre-commit and pre-push git hooks to be more GUI friendly.

@ghost
Copy link
Author

ghost commented Aug 16, 2016

Reverted the commits introduced for test-case this PR in CircleCI. This is ready to be merged into master.

@lancewillett lancewillett added Framework [Status] Needs Review The PR is ready for review. This also triggers e2e canary tests and wp-desktop tests automatically. labels Sep 2, 2016
"test-differ": "./node_modules/.bin/tape tests/test-differ.js | ./node_modules/.bin/faucet",
"test-parsing-errors": "./node_modules/.bin/tape tests/test-parsing-errors.js | ./node_modules/.bin/faucet"
},
"dependencies": {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to run npm install in this directory in order for the command-line tool to work?

@oandregal oandregal removed the [Status] Needs Review The PR is ready for review. This also triggers e2e canary tests and wp-desktop tests automatically. label Feb 21, 2017
@oandregal
Copy link
Contributor

oandregal commented Feb 21, 2017

Current state: editor integration is a blocker. We need the editors to run a custom command for linting so we can pipe eslines to the result of eslint, as we did for CI and git hooks. I've tested Atom and WebStorm and they don't allow this.

So, either we provide a custom eslint that wraps the eslines utility or editors will report all rules as errors because we cannot filter them with eslines. As per previous conversations, neither alternative is good enough, so I'm giving by unconscious brain some time to come up with something else.

@oandregal oandregal force-pushed the add/eslint-formatters-calypso-testcase branch from 8d94e72 to 25b0c00 Compare March 9, 2017 13:51
oandregal added a commit that referenced this pull request Mar 9, 2017
The linting task will report:

- only parsing errors, when running on master branch
- for other branches, any ESLint error in

  * lines modified within that branch
  * any linted files for the rule no-unused-vars

pre-commit githook and CircleCI were modified to fail if linter reports any error
@matticbot matticbot added the [Size] XL Probably needs to be broken down into multiple smaller issues label Mar 9, 2017
@oandregal oandregal changed the title Changing the linting strategy Set all ESLint rules as errors while preventing hooks/CI from failing Mar 9, 2017
@oandregal oandregal changed the title Set all ESLint rules as errors while preventing hooks/CI from failing Set ESLint rules as errors while preventing hooks/CI from failing Mar 9, 2017
@oandregal oandregal force-pushed the add/eslint-formatters-calypso-testcase branch 2 times, most recently from 8098f1e to a522b04 Compare March 21, 2017 11:26
@oandregal oandregal mentioned this pull request Mar 21, 2017
@oandregal
Copy link
Contributor

oandregal commented Mar 21, 2017

I was about merging this, but on rebasing and shrinkwrapping again, I found that some tests were not passing and the cause was unrelated to this PR. I prefer separate concerns and solve these issues in a separate PR before this is merged. Proposal to fix it in #12356

After that is merged, this needs a rebase.

The gist of this PR is:

	eslint -f json | eslines

With these changes in place, the linting task will report:

* only parsing errors, when running on master branch.
* for other branches, any ESLint error in

	* lines modified within that branch
	* any linted files for the rule no-unused-vars
@@ -1,7 +1,5 @@
#!/bin/sh

PATH="$PATH:/usr/local/bin"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nosolosw For those of us using GUI git apps like Tower.app, removing PATH causes commit to stop working as the app no longer has PATH info to run the commands in .git/hooks/pre-commit. Can you revert this line?

More info: https://www.git-tower.com/help/mac/faq-and-tips/faq#faq-hook-scripts

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure @raoulwegat , can you test #12548 works and green-lit it?

@matticbot matticbot added the [Size] XL Probably needs to be broken down into multiple smaller issues label Mar 27, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Framework OSS Citizen [Size] XL Probably needs to be broken down into multiple smaller issues
Projects
None yet
Development

Successfully merging this pull request may close these issues.