-
Notifications
You must be signed in to change notification settings - Fork 73
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
Multiple Projects per Repo, Eslint Enhancements, and More... #179
Conversation
lintreview/tools/eslint.py
Outdated
@@ -17,14 +18,16 @@ class Eslint(Tool): | |||
def check_dependencies(self): | |||
"""See if ESLint is on the system path. | |||
""" | |||
return in_path('eslint') or npm_exists('eslint') | |||
return in_path('eslint') or npm_exists('eslint', cwd=self.get_working_dir()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
E501 line too long (84 > 79 characters)
lintreview/tools/eslint.py
Outdated
if npm_exists('eslint'): | ||
cmd = os.path.join(os.getcwd(), 'node_modules', '.bin', 'eslint') | ||
if npm_exists('eslint', cwd=self.get_working_dir()): | ||
cmd = os.path.join(self.get_working_dir(), 'node_modules', '.bin', 'eslint') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
E501 line too long (88 > 79 characters)
lintreview/tools/phpcs.py
Outdated
@@ -22,7 +22,7 @@ def check_dependencies(self): | |||
""" | |||
See if PHPCS is on the system path. | |||
""" | |||
return in_path('phpcs') or composer_exists('phpcs') | |||
return in_path('phpcs') or composer_exists('phpcs', cwd=self.get_working_dir()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
E501 line too long (87 > 79 characters)
settings.sample.py
Outdated
GITHUB_AUTHOR_NAME = 'lintreview' | ||
GITHUB_AUTHOR_EMAIL = '[email protected]' | ||
GITHUB_AUTHOR_NAME = env('LINTREVIEW_GITHUB_AUTHOR_NAME', 'lintreview') | ||
GITHUB_AUTHOR_EMAIL = env('LINTREVIEW_GITHUB_AUTHOR_EMAIL', '[email protected]') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
E501 line too long (85 > 79 characters)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be good to have updated tests for the changes to the linter tools.
lintreview/tools/eslint.py
Outdated
|
||
if self.options.get('install'): | ||
output = run_command( | ||
['npm', 'install'], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My concern with installing application dependencies is that many react/ember/angular applications have build dependencies that are in the 100-400MB range. Downloading all those dependencies on each review will add a significant runtime cost and potentially create bandwidth usage problems.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's optional, so the user can decide as they see fit.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, with node 8 and package-lock.json, installs are much quicker than they were in the past.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, installing the project's dependencies was how I resolved the issue of the linter generating erroneous "unresolved reference to PACKAGE" errors. I suppose one could disable that linting rule, but then you'd miss real cases of unresolved references.
One way to deal with the extra bandwidth might be to put in more logic around cleanup. Instead of deleting the repo after every lint, maybe it's deleted when the PR is closed or merged? Maybe there's a configurable cleanup frequency (e.g. every day, week, etc.)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the npm cache actually takes care of the issue of bandwidth, to a large degree
lintreview/tools/phpcs.py
Outdated
output = run_command( | ||
['composer', 'install'], | ||
ignore_error=True, | ||
cwd=self.get_working_dir()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have similar concerns with PHP dependencies as I have with running npm
install around time and cost.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you often find that your PHP applications require additional composer packages to install their PHPCS rules?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As with npm, it's optional.
As for additional composer packages, I'm not really familiar with PHPCS. Just wanted to demonstrate on a non-node linter. The point is that a team could add a PHP project to their existing projects without having to touch the linting bot.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have plans in the near future to extract the various tool runtime environments into small standalone docker images removing the need for a kitchen sink image entirely. I feel this change would not be necessary if phpcs was a separate image people could pull down as needed.
I would prefer to not add features that will be removed in the short term.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are you saying the linting bot would start docker containers for linting environments? Or that you would have different "flavors" of the linting bot docker image, each with different tools?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That the review jobs would spawn containers to run each tool on the repository being reviewed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Awesome! That's actually how I initially thought I was going to solve my problem when I started this work, but when I saw how easy it was to get the functionality I needed by just re-jiggering a few lines...
Spawning containers might solve some problems, but wouldn't you still need to have all your eslint config and plugin packages for all your projects pre-installed in the linter docker image? And update the image when your config changes? I also learned that this does not only apply to eslint. With PHPCS, for example, you can add new "sniffs" as composer packages.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you still need to have all your eslint config and plugin packages for all your projects pre-installed in the linter docker image?
Yes you would. It doesn't help address the 'unresolved imports' for eslint. But it would allow lintreview users to build their own docker images that contain the custom sniffs/packages they use.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that would be great for a lot of people. However, I also think many would much rather not have to maintain separate docker images. I'm already doing the work of keeping track of needed dependencies when I update my package.json/composer.json/etc.. I wouldn't want to then have to go and update another tool with those same changes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I hear you on the pain of setting up docker images. For the node based projects I think the install
option is one of the only ways to get around the unresolved imports due to how babel & eslint handle those checks. However, with PHP and other toolchains linting is far less invasive and changes far less frequently than in Javascript projects.
lintreview/utils.py
Outdated
@@ -19,26 +19,24 @@ def in_path(name): | |||
return False | |||
|
|||
|
|||
def npm_exists(name): | |||
def npm_exists(name, cwd=os.getcwd()): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This default value will capture the cwd() when the module is imported. I don't think that's what you want.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I must not fully understand how it works, then. Could you explain more? I could use None as default and set to os.getcwd() if None is passed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The current default value will be created when this module is imported, not each time the function is run. Using None
and getting cwd if none is passed will work as you want.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see, thank you.
Agreed that updated tests would be good. I hesitated in opening this PR because I knew I wouldn't have time to add them for a while. I'll try, when I get the chance. |
Codecov Report
@@ Coverage Diff @@
## master #179 +/- ##
==========================================
- Coverage 85.97% 85.65% -0.32%
==========================================
Files 40 40
Lines 2167 2196 +29
==========================================
+ Hits 1863 1881 +18
- Misses 304 315 +11
Continue to review full report at Codecov.
|
lintreview/tools/eslint.py
Outdated
@@ -51,7 +66,8 @@ def process_fixer(self, files): | |||
output = run_command( | |||
command, | |||
ignore_error=True, | |||
include_errors=False) | |||
include_errors=False, | |||
cwd=working_dir) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
F821 undefined name 'working_dir'
Pull in a part of #179 that allows eslint to configure which extensions of files are checked.
Pull in some changes from #179
Closing as I pulled in the desired pieces of this. Thank you! |
Purpose
My apologies for combining unrelated changes into a single PR. These are modifications I made for my own use, thought I'd share. I've described each change below. Feel free to merge, cherry pick, or completely discard.
Multiple Projects per Repo
Using a simple
_#
suffix to the name of[tool_*]
sections, it's now possible to run the same linter multiple times, using different settings for each run. This can be useful when you have multiple projects in the same repo, with each client using different linting settings. For example, you might have a node.js API and React client in the same repo, and different linting settings according to the best-practices of each. You can now do this using separate[tool_eslint_#client]
and[tool_eslint_#api]
sections. Since most linters support a .ignore file, you can have each project ignore the files of the other, which takes care of the concerns you laid out about having to consolidate the comments before posting. I've added documentation that clarifies how to use this. Note that whatever follows after the _# is irrelevant, so long as each section has a unique name.These changes, along with those in the next section, should address cases like #144, at least for those using eslint or PHPCS, which I updated. Minimal changes would be needed to update the other linters.
Ad-Hoc Linter Installations and Package Resolution
It really bugged me that you had to install all your linting tools into the linting bot. That means you have to keep updating the linting bot when you want to change your linting config (e.g. switch eslint configs from standard to airbnb or add a new plugin). Most languages have package managers these days, and most projects use those package managers to keep track of what the project's dependencies are. So why can't the bot just use a package.json or composer.json to get the tools it needs?
Well, now it can! Using the
working_dir
andinstall
options in supported linters, the bot will run the appropriate package manager's install command (e.g.npm install
for eslint orcomposer install
for phpcs) inworking-dir
before linting. For supported languages, this means that you don't need to install any linting tools inside the linting bot or update it when your config changes!This also resolves a linting issue I was having, where the bot would complain about unresolved references to imported packages (e.g. axios, sequelize, etc.).
Again, I updated eslint and phpcs, but it would be trivial to add support for the other tools that can be installed by package managers. Admittedly, this might not work for those who need access to private packages or something, but it will work for many use cases.
Minimal Docker Image
I like small Docker images. The image built using the Dockerfile in the repo is almost 1GB! So I made a minimal alpine image that only installs the packages necessary to run the linter. I also split out the linters in requirements.txt into a separate file, so that the alpine image doesn't install python linters by default.
Since I was doing this just for my own needs, I extended from the node alpine image, which others might not need. So this image should be revised to extend from the official alpine image instead. Then users can extend from the alpine version of lint-story to add only the tools they need (e.g. node, php and composer, python linters, etc.). I've published my working image under
sjawhar/lint-review:alpine
if you want to take a look.Eslint Extensions
Added the ability to specify the file extensions you want to use with eslint. They were hard-coded to only .js and .jsx. Now those are just the default. Also added documentation to that effect.
Settings and Environment variables
Made more things in settings.py configurable with environment variables, reducing the chance that the user will have to write their own settings.py.
Other thoughts
On a side note, there's a misleading bit of info the current documentation:
That's not actually true. The
[tools]
section can be completely omitted if you use[tool_*]
sections. You can see it in the code below. The linters in the tools section are used to populate a dictionary but are immediately overwritten by the[tool_*]
sections:lint-review/lintreview/config.py
Lines 172 to 187 in 131f3cf