policial 0.0.3 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +64 -21
- data/Rakefile +2 -0
- data/lib/policial.rb +25 -8
- data/lib/policial/commit.rb +5 -2
- data/lib/policial/commit_file.rb +2 -0
- data/lib/policial/config_loader.rb +32 -0
- data/lib/policial/detective.rb +37 -0
- data/lib/policial/line.rb +3 -1
- data/lib/policial/patch.rb +2 -0
- data/lib/policial/pull_request.rb +11 -38
- data/lib/policial/pull_request_event.rb +6 -4
- data/lib/policial/style_checker.rb +18 -20
- data/lib/policial/style_guides/base.rb +38 -5
- data/lib/policial/style_guides/coffeescript.rb +42 -0
- data/lib/policial/style_guides/javascript.rb +45 -0
- data/lib/policial/style_guides/ruby.rb +51 -31
- data/lib/policial/style_guides/scss.rb +69 -0
- data/lib/policial/unchanged_line.rb +2 -0
- data/lib/policial/version.rb +3 -1
- data/lib/policial/violation.rb +14 -13
- metadata +41 -14
- data/lib/policial/accusation_policy.rb +0 -34
- data/lib/policial/commenter.rb +0 -25
- data/lib/policial/investigation.rb +0 -35
- data/lib/policial/octokit_client.rb +0 -10
- data/lib/policial/repo_config.rb +0 -36
- data/lib/policial/style_guides/unsupported.rb +0 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8cc78f631ce46e67c9b73b1b07aeb12ce9e1391a
|
4
|
+
data.tar.gz: 0e24d6c7a9cae9b3b0d1644dcdd937a7b751301c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7d40ae450bcc8fa6ca1fa029b3e642eeee559aed3012321044c2e77d17fc9c202f9a1838f7a365d56b57ef5b38a306d5e7dd52b60153d6f64a26bc8403e1f246
|
7
|
+
data.tar.gz: e5944b150dcd32d76dc3f82e86c799b3b6e359c76f8dbd36e3d2cb50c2b9baa9c1004653e8bd8df3f42526f8bf831f3d4756a4cd70d1eabb32e742a4f0cc5686
|
data/README.md
CHANGED
@@ -7,9 +7,7 @@
|
|
7
7
|
*Policial* is a gem that investigates pull requests and accuses style guide
|
8
8
|
violations. It is based on thoughtbot's
|
9
9
|
[Hound project](https://github.com/thoughtbot/hound).
|
10
|
-
|
11
|
-
rules by defining a `.rubocop.yml` file in you repo. Please see
|
12
|
-
[RuboCop's README](https://github.com/bbatsov/rubocop).
|
10
|
+
It currently supports Ruby, SCSS and CoffeeScript.
|
13
11
|
|
14
12
|
## Installation
|
15
13
|
|
@@ -29,53 +27,98 @@ Or install it yourself as:
|
|
29
27
|
|
30
28
|
## Usage
|
31
29
|
|
32
|
-
1.
|
33
|
-
|
30
|
+
1. First, instantiate a new Detective:
|
31
|
+
```ruby
|
32
|
+
detective = Policial::Detective.new
|
33
|
+
```
|
34
|
+
|
35
|
+
You might need to pass an Octokit client with your GitHub credentials.
|
36
|
+
For more information on this please check the
|
34
37
|
[Octokit README](https://github.com/octokit/octokit.rb).
|
35
38
|
|
36
39
|
```ruby
|
37
|
-
|
40
|
+
octokit = Octokit::Client.new(access_token: 'mygithubtoken666')
|
41
|
+
detective = Policial::Detective.new(octokit)
|
38
42
|
```
|
39
|
-
|
40
|
-
|
43
|
+
If you don't pass an Octokit client Policial will use the global Octokit
|
44
|
+
configuration.
|
41
45
|
|
42
|
-
2. Let's investigate! Start
|
43
|
-
investigation against. You can setup a pull request manually:
|
46
|
+
2. Let's investigate! Start by briefing your detective about the pull request it
|
47
|
+
will run an investigation against. You can setup a pull request manually:
|
44
48
|
|
45
49
|
```ruby
|
46
|
-
|
50
|
+
detective.brief(
|
47
51
|
repo: 'volmer/my_repo',
|
48
52
|
number: 3,
|
49
53
|
head_sha: 'headsha'
|
50
54
|
)
|
51
55
|
```
|
52
56
|
|
53
|
-
Or you can
|
57
|
+
Or you can brief it with a
|
54
58
|
[GitHub `pull_request` webhook](https://developer.github.com/webhooks):
|
55
59
|
|
56
60
|
```ruby
|
57
61
|
event = Policial::PullRequestEvent.new(webhook_payload)
|
58
|
-
|
62
|
+
detective.brief(event)
|
59
63
|
```
|
60
64
|
|
61
65
|
3. Now you can run the investigation:
|
62
66
|
|
63
67
|
```ruby
|
64
|
-
investigation = Policial::Investigation.new(pull_request)
|
65
|
-
|
66
68
|
# Let's investigate this pull request...
|
67
|
-
|
69
|
+
detective.investigate
|
68
70
|
|
69
71
|
# Want to know the violations found?
|
70
|
-
|
72
|
+
detective.violations
|
71
73
|
```
|
72
74
|
|
73
|
-
4.
|
75
|
+
4. Want to know the violations found?
|
74
76
|
```ruby
|
75
|
-
|
77
|
+
violations = detective.violations
|
78
|
+
# => [#<Policial::Violation:0x007ff0b5abad30 @filename="lib/test.rb", @line_number=1, ...>]
|
79
|
+
|
80
|
+
violations.first.message
|
81
|
+
"Prefer single-quoted strings when you don't need string interpolation or special symbols."
|
76
82
|
```
|
77
|
-
|
78
|
-
|
83
|
+
|
84
|
+
## Ruby
|
85
|
+
|
86
|
+
You can setup your Ruby code style rules with a `.rubocop.yml` file in
|
87
|
+
your repo. Please see [RuboCop's README](https://github.com/bbatsov/rubocop).
|
88
|
+
|
89
|
+
## CoffeeScript
|
90
|
+
|
91
|
+
You can setup your CoffeeScript code style rules with a `coffeelint.json`
|
92
|
+
file in your repo. For more information on how customize the linter rules please
|
93
|
+
visit the [Coffeelint website](https://coffelint.org).
|
94
|
+
|
95
|
+
## SCSS
|
96
|
+
|
97
|
+
SCSS linting is disabled by default. To enable it, you need to install the
|
98
|
+
[SCSS-Lint](https://github.com/brigade/scss-lint) gem:
|
99
|
+
|
100
|
+
```
|
101
|
+
gem install scss_lint
|
102
|
+
```
|
103
|
+
|
104
|
+
Or add the following to your `Gemfile` and run `bundle install`:
|
105
|
+
|
106
|
+
```ruby
|
107
|
+
gem 'scss_lint', require: false
|
108
|
+
```
|
109
|
+
|
110
|
+
The `require: false` is necessary because `scss-lint` monkey patches `Sass`.
|
111
|
+
More info [here](https://github.com/brigade/scss-lint#installation).
|
112
|
+
|
113
|
+
Now you can enable SCSS on Policial:
|
114
|
+
|
115
|
+
```ruby
|
116
|
+
Policial.style_guides << Policial::StyleGuides::Scss
|
117
|
+
```
|
118
|
+
|
119
|
+
You can setup your SCSS code style rules with a `.scss-lint.yml` file in your
|
120
|
+
repo. For more information on how customize the linter rules please
|
121
|
+
read [SCSS-Lint's README](https://github.com/brigade/scss-lint#configuration).
|
79
122
|
|
80
123
|
## Contributing
|
81
124
|
|
data/Rakefile
CHANGED
data/lib/policial.rb
CHANGED
@@ -1,20 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'octokit'
|
2
4
|
|
3
|
-
require 'policial/accusation_policy'
|
4
|
-
require 'policial/commenter'
|
5
5
|
require 'policial/commit'
|
6
6
|
require 'policial/commit_file'
|
7
|
-
require 'policial/
|
8
|
-
require 'policial/
|
7
|
+
require 'policial/config_loader'
|
8
|
+
require 'policial/detective'
|
9
9
|
require 'policial/line'
|
10
10
|
require 'policial/patch'
|
11
11
|
require 'policial/pull_request'
|
12
12
|
require 'policial/pull_request_event'
|
13
|
-
require 'policial/repo_config'
|
14
13
|
require 'policial/style_checker'
|
15
14
|
require 'policial/style_guides/base'
|
16
15
|
require 'policial/style_guides/ruby'
|
17
|
-
require 'policial/style_guides/
|
16
|
+
require 'policial/style_guides/scss'
|
17
|
+
require 'policial/style_guides/coffeescript'
|
18
|
+
require 'policial/style_guides/javascript'
|
18
19
|
require 'policial/unchanged_line'
|
19
20
|
require 'policial/version'
|
20
21
|
require 'policial/violation'
|
@@ -23,7 +24,23 @@ require 'policial/violation'
|
|
23
24
|
# so you can configure GitHub credentials, enable/disable style guides
|
24
25
|
# and more.
|
25
26
|
module Policial
|
26
|
-
|
27
|
+
DEFAULT_STYLE_GUIDES = [
|
28
|
+
Policial::StyleGuides::Ruby,
|
29
|
+
Policial::StyleGuides::CoffeeScript,
|
30
|
+
Policial::StyleGuides::JavaScript
|
31
|
+
].freeze
|
32
|
+
|
33
|
+
OPTIONAL_STYLE_GUIDES = [
|
34
|
+
Policial::StyleGuides::Scss
|
35
|
+
].freeze
|
36
|
+
|
37
|
+
module_function
|
38
|
+
|
39
|
+
def style_guides
|
40
|
+
@style_guides ||= DEFAULT_STYLE_GUIDES.dup
|
41
|
+
end
|
27
42
|
|
28
|
-
|
43
|
+
def style_guides=(style_guides)
|
44
|
+
@style_guides = style_guides
|
45
|
+
end
|
29
46
|
end
|
data/lib/policial/commit.rb
CHANGED
@@ -1,15 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Policial
|
2
4
|
# Public: A Commit in a GitHub repo.
|
3
5
|
class Commit
|
4
6
|
attr_reader :repo, :sha
|
5
7
|
|
6
|
-
def initialize(repo, sha)
|
8
|
+
def initialize(repo, sha, github_client)
|
7
9
|
@repo = repo
|
8
10
|
@sha = sha
|
11
|
+
@github_client = github_client
|
9
12
|
end
|
10
13
|
|
11
14
|
def file_content(filename)
|
12
|
-
contents =
|
15
|
+
contents = @github_client.contents(@repo, path: filename, ref: @sha)
|
13
16
|
|
14
17
|
if contents && contents.content
|
15
18
|
Base64.decode64(contents.content).force_encoding('UTF-8')
|
data/lib/policial/commit_file.rb
CHANGED
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Policial
|
4
|
+
# Public: Load and parse config files from GitHub repo.
|
5
|
+
class ConfigLoader
|
6
|
+
def initialize(commit)
|
7
|
+
@commit = commit
|
8
|
+
end
|
9
|
+
|
10
|
+
def raw(filename)
|
11
|
+
blank?(filename) ? '' : @commit.file_content(filename)
|
12
|
+
end
|
13
|
+
|
14
|
+
def json(filename)
|
15
|
+
JSON.parse(raw(filename))
|
16
|
+
rescue JSON::ParserError
|
17
|
+
{}
|
18
|
+
end
|
19
|
+
|
20
|
+
def yaml(filename)
|
21
|
+
YAML.load(raw(filename)) || {}
|
22
|
+
rescue Psych::SyntaxError
|
23
|
+
{}
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def blank?(string)
|
29
|
+
string.to_s.strip.empty?
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Policial
|
4
|
+
# Public: Starting with an Octokit client and a pull request,
|
5
|
+
# it checks all changes introduced looking for style guide violations.
|
6
|
+
class Detective
|
7
|
+
attr_accessor :violations
|
8
|
+
attr_reader :github_client, :pull_request
|
9
|
+
|
10
|
+
def initialize(github_client = nil)
|
11
|
+
@github_client = github_client || Octokit
|
12
|
+
end
|
13
|
+
|
14
|
+
def brief(event_or_attributes)
|
15
|
+
pull_request_attributes = extract_attributes(event_or_attributes)
|
16
|
+
return unless pull_request_attributes
|
17
|
+
@pull_request = PullRequest.new(
|
18
|
+
pull_request_attributes.merge(github_client: @github_client)
|
19
|
+
)
|
20
|
+
end
|
21
|
+
|
22
|
+
def investigate(options = {})
|
23
|
+
return unless pull_request
|
24
|
+
@violations ||= StyleChecker.new(pull_request, options).violations
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def extract_attributes(event_or_attributes)
|
30
|
+
if event_or_attributes.is_a?(PullRequestEvent)
|
31
|
+
event_or_attributes.pull_request_attributes
|
32
|
+
else
|
33
|
+
event_or_attributes
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
data/lib/policial/line.rb
CHANGED
@@ -1,7 +1,9 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Policial
|
2
4
|
# Public: a changed line in a commit file.
|
3
5
|
class Line
|
4
|
-
attr_reader :number, :patch_position
|
6
|
+
attr_reader :content, :number, :patch_position
|
5
7
|
|
6
8
|
def initialize(number, content, patch_position)
|
7
9
|
@number = number
|
data/lib/policial/patch.rb
CHANGED
@@ -1,21 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Policial
|
2
|
-
# Public: A
|
4
|
+
# Public: A GitHub Pull Request.
|
3
5
|
class PullRequest
|
4
6
|
attr_reader :repo, :number, :user
|
7
|
+
attr_accessor :github_client
|
5
8
|
|
6
|
-
def initialize(repo:, number:, head_sha:, user: nil)
|
7
|
-
@repo
|
8
|
-
@number
|
9
|
+
def initialize(repo:, number:, head_sha:, github_client:, user: nil)
|
10
|
+
@repo = repo
|
11
|
+
@number = number
|
9
12
|
@head_sha = head_sha
|
10
|
-
@user
|
11
|
-
|
12
|
-
|
13
|
-
def comments
|
14
|
-
@comments ||= fetch_comments
|
13
|
+
@user = user
|
14
|
+
@github_client = github_client
|
15
15
|
end
|
16
16
|
|
17
17
|
def files
|
18
|
-
@files ||=
|
18
|
+
@files ||= @github_client.pull_request_files(
|
19
19
|
@repo, @number
|
20
20
|
).map do |file|
|
21
21
|
build_commit_file(file)
|
@@ -23,7 +23,7 @@ module Policial
|
|
23
23
|
end
|
24
24
|
|
25
25
|
def head_commit
|
26
|
-
@head_commit ||= Commit.new(@repo, @head_sha)
|
26
|
+
@head_commit ||= Commit.new(@repo, @head_sha, @github_client)
|
27
27
|
end
|
28
28
|
|
29
29
|
private
|
@@ -31,32 +31,5 @@ module Policial
|
|
31
31
|
def build_commit_file(file)
|
32
32
|
CommitFile.new(file, head_commit)
|
33
33
|
end
|
34
|
-
|
35
|
-
def fetch_comments
|
36
|
-
paginate do |page|
|
37
|
-
Policial.octokit.pull_request_comments(
|
38
|
-
@repo,
|
39
|
-
@number,
|
40
|
-
page: page
|
41
|
-
)
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
private
|
46
|
-
|
47
|
-
def paginate
|
48
|
-
page, results, all_pages_fetched = 1, [], false
|
49
|
-
|
50
|
-
until all_pages_fetched
|
51
|
-
if (page_results = yield(page)).empty?
|
52
|
-
all_pages_fetched = true
|
53
|
-
else
|
54
|
-
results += page_results
|
55
|
-
page += 1
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
results
|
60
|
-
end
|
61
34
|
end
|
62
35
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'json'
|
2
4
|
|
3
5
|
module Policial
|
@@ -9,19 +11,19 @@ module Policial
|
|
9
11
|
@payload = payload
|
10
12
|
end
|
11
13
|
|
12
|
-
def
|
13
|
-
|
14
|
+
def pull_request_attributes
|
15
|
+
{
|
14
16
|
repo: @payload['repository']['full_name'],
|
15
17
|
number: @payload['number'],
|
16
18
|
head_sha: @payload['pull_request']['head']['sha'],
|
17
19
|
user: @payload['pull_request']['user']['login']
|
18
|
-
|
20
|
+
}
|
19
21
|
rescue NoMethodError
|
20
22
|
nil
|
21
23
|
end
|
22
24
|
|
23
25
|
def should_investigate?
|
24
|
-
!
|
26
|
+
!pull_request_attributes.nil? && (
|
25
27
|
@payload['action'] == 'opened' || @payload['action'] == 'synchronize'
|
26
28
|
)
|
27
29
|
end
|
@@ -1,10 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Policial
|
2
4
|
# Public: Filters files to reviewable subset, builds style guide based on file
|
3
5
|
# extension and delegates to style guide for line violations.
|
4
6
|
class StyleChecker
|
5
|
-
def initialize(pull_request)
|
7
|
+
def initialize(pull_request, options = {})
|
6
8
|
@pull_request = pull_request
|
7
9
|
@style_guides = {}
|
10
|
+
@options = options
|
8
11
|
end
|
9
12
|
|
10
13
|
def violations
|
@@ -13,36 +16,31 @@ module Policial
|
|
13
16
|
|
14
17
|
private
|
15
18
|
|
16
|
-
attr_reader :pull_request, :style_guides
|
17
|
-
|
18
19
|
def violations_in_checked_files
|
19
20
|
files_to_check.flat_map do |file|
|
20
|
-
style_guide
|
21
|
+
style_guides.flat_map do |style_guide|
|
22
|
+
if style_guide.investigate?(file.filename)
|
23
|
+
style_guide.violations_in_file(file)
|
24
|
+
else
|
25
|
+
[]
|
26
|
+
end
|
27
|
+
end
|
21
28
|
end
|
22
29
|
end
|
23
30
|
|
24
31
|
def files_to_check
|
25
|
-
pull_request.files.reject(&:removed?)
|
26
|
-
style_guide(file.filename).enabled?
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
def style_guide(filename)
|
31
|
-
style_guide_class = style_guide_class(filename)
|
32
|
-
style_guides[style_guide_class] ||= style_guide_class.new(config)
|
32
|
+
@pull_request.files.reject(&:removed?)
|
33
33
|
end
|
34
34
|
|
35
|
-
def
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
else
|
40
|
-
StyleGuides::Unsupported
|
35
|
+
def style_guides
|
36
|
+
Policial.style_guides.map do |klass|
|
37
|
+
@style_guides[klass] ||= klass.new(
|
38
|
+
config_loader, @options[klass::KEY] || {})
|
41
39
|
end
|
42
40
|
end
|
43
41
|
|
44
|
-
def
|
45
|
-
@
|
42
|
+
def config_loader
|
43
|
+
@config_loader ||= ConfigLoader.new(@pull_request.head_commit)
|
46
44
|
end
|
47
45
|
end
|
48
46
|
end
|
@@ -1,17 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Policial
|
2
4
|
module StyleGuides
|
3
5
|
# Public: Base to contain common style guide logic.
|
4
6
|
class Base
|
5
|
-
def initialize(
|
6
|
-
@
|
7
|
+
def initialize(config_loader, options = {})
|
8
|
+
@config_loader = config_loader
|
9
|
+
@options = options
|
10
|
+
end
|
11
|
+
|
12
|
+
def violations_in_file(_file)
|
13
|
+
raise NotImplementedError, "must implement ##{__method__}"
|
14
|
+
end
|
15
|
+
|
16
|
+
def exclude_file?(_filename)
|
17
|
+
raise NotImplementedError, "must implement ##{__method__}"
|
18
|
+
end
|
19
|
+
|
20
|
+
def filename_pattern
|
21
|
+
raise NotImplementedError, "must implement ##{__method__}"
|
7
22
|
end
|
8
23
|
|
24
|
+
def default_config_file
|
25
|
+
raise NotImplementedError, "must implement ##{__method__}"
|
26
|
+
end
|
27
|
+
|
28
|
+
def config_file
|
29
|
+
if @options[:config_file].to_s.strip.empty?
|
30
|
+
default_config_file
|
31
|
+
else
|
32
|
+
@options[:config_file]
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def investigate?(filename)
|
37
|
+
enabled? && matches_pattern?(filename) && !exclude_file?(filename)
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
9
42
|
def enabled?
|
10
|
-
@
|
43
|
+
@options[:enabled] != false
|
11
44
|
end
|
12
45
|
|
13
|
-
def
|
14
|
-
|
46
|
+
def matches_pattern?(filename)
|
47
|
+
!(filename =~ filename_pattern).nil?
|
15
48
|
end
|
16
49
|
end
|
17
50
|
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'coffeelint'
|
4
|
+
|
5
|
+
module Policial
|
6
|
+
module StyleGuides
|
7
|
+
# Public: Determine CoffeeScript style guide violations per-line.
|
8
|
+
class CoffeeScript < Base
|
9
|
+
KEY = :coffeescript
|
10
|
+
|
11
|
+
def violations_in_file(file)
|
12
|
+
errors = Coffeelint.lint(file.content, config)
|
13
|
+
violations(file, errors)
|
14
|
+
end
|
15
|
+
|
16
|
+
def exclude_file?(_filename)
|
17
|
+
false
|
18
|
+
end
|
19
|
+
|
20
|
+
def filename_pattern
|
21
|
+
/.+\.coffee\z/
|
22
|
+
end
|
23
|
+
|
24
|
+
def default_config_file
|
25
|
+
'coffeelint.json'
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def config
|
31
|
+
@config ||= @config_loader.json(config_file)
|
32
|
+
end
|
33
|
+
|
34
|
+
def violations(file, errors)
|
35
|
+
errors.map do |error|
|
36
|
+
Violation.new(
|
37
|
+
file, error['lineNumber'], error['message'], error['rule'])
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'eslintrb'
|
4
|
+
|
5
|
+
module Policial
|
6
|
+
module StyleGuides
|
7
|
+
# Public: Determine Javascript style guide violations per-line.
|
8
|
+
class JavaScript < Base
|
9
|
+
KEY = :javascript
|
10
|
+
|
11
|
+
def violations_in_file(file)
|
12
|
+
errors = Eslintrb.lint(file.content, config)
|
13
|
+
violations(file, errors)
|
14
|
+
end
|
15
|
+
|
16
|
+
def exclude_file?(_filename)
|
17
|
+
false
|
18
|
+
end
|
19
|
+
|
20
|
+
def filename_pattern
|
21
|
+
/.+\.js\z/
|
22
|
+
end
|
23
|
+
|
24
|
+
def default_config_file
|
25
|
+
'.eslintrc.json'
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def config
|
31
|
+
@config ||= begin
|
32
|
+
content = @config_loader.json(config_file)
|
33
|
+
content.empty? ? :defaults : content
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def violations(file, errors)
|
38
|
+
errors.map do |error|
|
39
|
+
Violation.new(
|
40
|
+
file, error['line'], error['message'], error['ruleId'])
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -1,34 +1,54 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'rubocop'
|
2
4
|
|
3
5
|
module Policial
|
4
6
|
module StyleGuides
|
5
7
|
# Public: Determine Ruby style guide violations per-line.
|
6
8
|
class Ruby < Base
|
7
|
-
|
8
|
-
|
9
|
+
KEY = :ruby
|
10
|
+
|
11
|
+
def violations_in_file(file)
|
12
|
+
offenses = team.inspect_file(parsed_source(file))
|
9
13
|
|
10
|
-
|
11
|
-
|
14
|
+
offenses.reject(&:disabled?).map do |offense|
|
15
|
+
Violation.new(file, offense.line, offense.message, offense.cop_name)
|
12
16
|
end
|
13
17
|
end
|
14
18
|
|
15
|
-
def
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
19
|
+
def exclude_file?(filename)
|
20
|
+
config.file_to_exclude?(filename)
|
21
|
+
end
|
22
|
+
|
23
|
+
def filename_pattern
|
24
|
+
/.+\.rb\z/
|
25
|
+
end
|
26
|
+
|
27
|
+
def default_config_file
|
28
|
+
RuboCop::ConfigLoader::DOTFILE
|
22
29
|
end
|
23
30
|
|
24
31
|
private
|
25
32
|
|
26
33
|
def team
|
27
|
-
|
34
|
+
cop_classes = if config['Rails']['Enabled']
|
35
|
+
RuboCop::Cop::Cop.all
|
36
|
+
else
|
37
|
+
RuboCop::Cop::Cop.non_rails
|
38
|
+
end
|
39
|
+
|
40
|
+
RuboCop::Cop::Team.new(cop_classes, config)
|
28
41
|
end
|
29
42
|
|
30
43
|
def parsed_source(file)
|
31
|
-
|
44
|
+
absolute_path =
|
45
|
+
File.join(config.base_dir_for_path_parameters, file.filename)
|
46
|
+
|
47
|
+
RuboCop::ProcessedSource.new(
|
48
|
+
file.content,
|
49
|
+
config['AllCops']['TargetRubyVersion'],
|
50
|
+
absolute_path
|
51
|
+
)
|
32
52
|
end
|
33
53
|
|
34
54
|
def config
|
@@ -36,31 +56,31 @@ module Policial
|
|
36
56
|
end
|
37
57
|
|
38
58
|
def custom_config
|
39
|
-
|
59
|
+
content = @config_loader.yaml(config_file)
|
60
|
+
filter(content)
|
40
61
|
|
41
|
-
|
42
|
-
RuboCop::
|
43
|
-
config.add_missing_namespaces
|
44
|
-
config.make_excludes_absolute
|
45
|
-
end
|
46
|
-
else
|
47
|
-
RuboCop::Config.new
|
62
|
+
tempfile_from(config_file, content.to_yaml) do |tempfile|
|
63
|
+
RuboCop::ConfigLoader.load_file(tempfile.path)
|
48
64
|
end
|
49
65
|
end
|
50
66
|
|
51
|
-
def
|
52
|
-
|
67
|
+
def tempfile_from(filename, content)
|
68
|
+
filename = File.basename(filename)
|
69
|
+
Tempfile.create(File.basename(filename), Dir.pwd) do |tempfile|
|
70
|
+
tempfile.write(content)
|
71
|
+
tempfile.rewind
|
72
|
+
|
73
|
+
yield(tempfile)
|
74
|
+
end
|
53
75
|
end
|
54
76
|
|
55
|
-
def
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
Violation.new(file, offense.line, offense.message)
|
77
|
+
def filter(config_hash)
|
78
|
+
config_hash.delete('require')
|
79
|
+
config_hash.delete('inherit_gem')
|
80
|
+
config_hash['inherit_from'] =
|
81
|
+
Array(config_hash['inherit_from']).select do |value|
|
82
|
+
value =~ /\A#{URI.regexp(%w(http https))}\z/
|
62
83
|
end
|
63
|
-
end.values
|
64
84
|
end
|
65
85
|
end
|
66
86
|
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Policial
|
4
|
+
module StyleGuides
|
5
|
+
# Public: Determine SCSS style guide violations per-line.
|
6
|
+
class Scss < Base
|
7
|
+
KEY = :scss
|
8
|
+
|
9
|
+
def violations_in_file(file)
|
10
|
+
absolute_path = File.expand_path(file.filename)
|
11
|
+
|
12
|
+
runner = new_runner
|
13
|
+
|
14
|
+
tempfile_from(file.filename, file.content) do |tempfile|
|
15
|
+
runner.run([{ file: tempfile, path: absolute_path }])
|
16
|
+
end
|
17
|
+
|
18
|
+
violations(runner, file)
|
19
|
+
end
|
20
|
+
|
21
|
+
def exclude_file?(filename)
|
22
|
+
config.excluded_file?(File.expand_path(filename))
|
23
|
+
end
|
24
|
+
|
25
|
+
def filename_pattern
|
26
|
+
/.+\.scss\z/
|
27
|
+
end
|
28
|
+
|
29
|
+
def default_config_file
|
30
|
+
require 'scss_lint'
|
31
|
+
SCSSLint::Config::FILE_NAME
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def config
|
37
|
+
require 'scss_lint'
|
38
|
+
@config ||= begin
|
39
|
+
content = @config_loader.raw(config_file)
|
40
|
+
tempfile_from(config_file, content) do |temp|
|
41
|
+
SCSSLint::Config.load(temp, merge_with_default: true)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def violations(runner, file)
|
47
|
+
runner.lints.map do |lint|
|
48
|
+
linter_name = lint.linter ? lint.linter.name : 'undefined'
|
49
|
+
Violation.new(
|
50
|
+
file, lint.location.line, lint.description, linter_name)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def new_runner
|
55
|
+
require 'scss_lint'
|
56
|
+
SCSSLint::Runner.new(config)
|
57
|
+
end
|
58
|
+
|
59
|
+
def tempfile_from(filename, content)
|
60
|
+
Tempfile.create(File.basename(filename), Dir.pwd) do |tempfile|
|
61
|
+
tempfile.write(content)
|
62
|
+
tempfile.rewind
|
63
|
+
|
64
|
+
yield(tempfile)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
data/lib/policial/version.rb
CHANGED
data/lib/policial/violation.rb
CHANGED
@@ -1,30 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Policial
|
2
|
-
# Public: Hold file, line, and
|
3
|
-
# guides.
|
4
|
+
# Public: Hold file, line, and message. Built by style guides.
|
4
5
|
class Violation
|
5
|
-
attr_reader :line_number, :
|
6
|
+
attr_reader :line_number, :message, :linter
|
6
7
|
|
7
|
-
def initialize(file, line_number, message)
|
8
|
-
@
|
9
|
-
@line = file.line_at(line_number)
|
8
|
+
def initialize(file, line_number, message, linter)
|
9
|
+
@file = file
|
10
10
|
@line_number = line_number
|
11
|
-
@
|
11
|
+
@message = message
|
12
|
+
@linter = linter
|
12
13
|
end
|
13
14
|
|
14
|
-
def
|
15
|
-
@
|
15
|
+
def filename
|
16
|
+
@file.filename
|
16
17
|
end
|
17
18
|
|
18
|
-
def
|
19
|
-
@
|
19
|
+
def line
|
20
|
+
@line ||= @file.line_at(line_number)
|
20
21
|
end
|
21
22
|
|
22
23
|
def patch_position
|
23
|
-
|
24
|
+
line.patch_position
|
24
25
|
end
|
25
26
|
|
26
27
|
def on_changed_line?
|
27
|
-
|
28
|
+
line.changed?
|
28
29
|
end
|
29
30
|
end
|
30
31
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: policial
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Volmer Soares
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2016-04-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: octokit
|
@@ -16,31 +16,59 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '3
|
19
|
+
version: '4.3'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '3
|
26
|
+
version: '4.3'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: rubocop
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '0.
|
33
|
+
version: '0.39'
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: '0.
|
40
|
+
version: '0.39'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: coffeelint
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1.14'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.14'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: eslintrb
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '2.0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '2.0'
|
41
69
|
description: Review pull requests for style guide violations.
|
42
70
|
email:
|
43
|
-
-
|
71
|
+
- volmer@radicaos.com
|
44
72
|
executables: []
|
45
73
|
extensions: []
|
46
74
|
extra_rdoc_files: []
|
@@ -49,21 +77,20 @@ files:
|
|
49
77
|
- README.md
|
50
78
|
- Rakefile
|
51
79
|
- lib/policial.rb
|
52
|
-
- lib/policial/accusation_policy.rb
|
53
|
-
- lib/policial/commenter.rb
|
54
80
|
- lib/policial/commit.rb
|
55
81
|
- lib/policial/commit_file.rb
|
56
|
-
- lib/policial/
|
82
|
+
- lib/policial/config_loader.rb
|
83
|
+
- lib/policial/detective.rb
|
57
84
|
- lib/policial/line.rb
|
58
|
-
- lib/policial/octokit_client.rb
|
59
85
|
- lib/policial/patch.rb
|
60
86
|
- lib/policial/pull_request.rb
|
61
87
|
- lib/policial/pull_request_event.rb
|
62
|
-
- lib/policial/repo_config.rb
|
63
88
|
- lib/policial/style_checker.rb
|
64
89
|
- lib/policial/style_guides/base.rb
|
90
|
+
- lib/policial/style_guides/coffeescript.rb
|
91
|
+
- lib/policial/style_guides/javascript.rb
|
65
92
|
- lib/policial/style_guides/ruby.rb
|
66
|
-
- lib/policial/style_guides/
|
93
|
+
- lib/policial/style_guides/scss.rb
|
67
94
|
- lib/policial/unchanged_line.rb
|
68
95
|
- lib/policial/version.rb
|
69
96
|
- lib/policial/violation.rb
|
@@ -87,7 +114,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
87
114
|
version: '0'
|
88
115
|
requirements: []
|
89
116
|
rubyforge_project:
|
90
|
-
rubygems_version: 2.
|
117
|
+
rubygems_version: 2.5.1
|
91
118
|
signing_key:
|
92
119
|
specification_version: 4
|
93
120
|
summary: Review pull requests for style guide violations
|
@@ -1,34 +0,0 @@
|
|
1
|
-
module Policial
|
2
|
-
# Public: Checks if a given pull request should be investigated or not.
|
3
|
-
class AccusationPolicy
|
4
|
-
def initialize(pull_request)
|
5
|
-
@pull_request = pull_request
|
6
|
-
end
|
7
|
-
|
8
|
-
def allowed_for?(violation)
|
9
|
-
unreported_violation_messages(violation).any?
|
10
|
-
end
|
11
|
-
|
12
|
-
private
|
13
|
-
|
14
|
-
def unreported_violation_messages(violation)
|
15
|
-
violation.messages - existing_violation_messages(violation)
|
16
|
-
end
|
17
|
-
|
18
|
-
def existing_violation_messages(violation)
|
19
|
-
previous_comments_on_line(violation).map(&:body)
|
20
|
-
.flat_map { |body| body.split('<br>') }
|
21
|
-
end
|
22
|
-
|
23
|
-
def previous_comments_on_line(violation)
|
24
|
-
existing_comments.select do |comment|
|
25
|
-
comment.path == violation.filename &&
|
26
|
-
comment.original_position == violation.patch_position
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
def existing_comments
|
31
|
-
@existing_comments ||= @pull_request.comments
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
data/lib/policial/commenter.rb
DELETED
@@ -1,25 +0,0 @@
|
|
1
|
-
module Policial
|
2
|
-
# Public: Comment violations on pull request.
|
3
|
-
class Commenter
|
4
|
-
def initialize(pull_request)
|
5
|
-
@pull_request = pull_request
|
6
|
-
end
|
7
|
-
|
8
|
-
def comment_violation(violation)
|
9
|
-
Policial.octokit.create_pull_request_comment(
|
10
|
-
@pull_request.repo,
|
11
|
-
@pull_request.number,
|
12
|
-
comment_body(violation),
|
13
|
-
@pull_request.head_commit.sha,
|
14
|
-
violation.filename,
|
15
|
-
violation.patch_position
|
16
|
-
)
|
17
|
-
end
|
18
|
-
|
19
|
-
private
|
20
|
-
|
21
|
-
def comment_body(violation)
|
22
|
-
violation.messages.join('<br/>')
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
@@ -1,35 +0,0 @@
|
|
1
|
-
module Policial
|
2
|
-
# Public: Starting with unparsed data coming from a pull request event,
|
3
|
-
# it checks all changes introduced looking for style guide violations. It
|
4
|
-
# also accuse all present violations through comments on all relevant lines
|
5
|
-
# in the pull request.
|
6
|
-
class Investigation
|
7
|
-
attr_accessor :violations, :pull_request
|
8
|
-
|
9
|
-
def initialize(pull_request)
|
10
|
-
@pull_request = pull_request
|
11
|
-
end
|
12
|
-
|
13
|
-
def run
|
14
|
-
@violations ||= StyleChecker.new(@pull_request).violations
|
15
|
-
end
|
16
|
-
|
17
|
-
def accuse
|
18
|
-
return if @violations.nil?
|
19
|
-
|
20
|
-
commenter = Commenter.new(@pull_request)
|
21
|
-
|
22
|
-
@violations.each do |violation|
|
23
|
-
if accusation_policy.allowed_for?(violation)
|
24
|
-
commenter.comment_violation(violation)
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
private
|
30
|
-
|
31
|
-
def accusation_policy
|
32
|
-
@accusation_policy ||= AccusationPolicy.new(@pull_request)
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
data/lib/policial/repo_config.rb
DELETED
@@ -1,36 +0,0 @@
|
|
1
|
-
module Policial
|
2
|
-
# Public: Load and parse config files from GitHub repo.
|
3
|
-
class RepoConfig
|
4
|
-
def initialize(commit)
|
5
|
-
@commit = commit
|
6
|
-
end
|
7
|
-
|
8
|
-
def enabled_for?(style_guide_class)
|
9
|
-
Policial::STYLE_GUIDES.include?(style_guide_class)
|
10
|
-
end
|
11
|
-
|
12
|
-
def for(style_guide_class)
|
13
|
-
config_file = style_guide_class.config_file
|
14
|
-
|
15
|
-
if config_file
|
16
|
-
load_file(config_file)
|
17
|
-
else
|
18
|
-
{}
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
private
|
23
|
-
|
24
|
-
def load_file(file)
|
25
|
-
config_file_content = @commit.file_content(file)
|
26
|
-
|
27
|
-
parse_yaml(config_file_content) || {}
|
28
|
-
end
|
29
|
-
|
30
|
-
def parse_yaml(content)
|
31
|
-
YAML.load(content)
|
32
|
-
rescue Psych::SyntaxError
|
33
|
-
{}
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|