policial 0.0.1
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 +7 -0
- data/LICENSE.txt +22 -0
- data/README.md +79 -0
- data/Rakefile +8 -0
- data/lib/policial.rb +28 -0
- data/lib/policial/accusation_policy.rb +34 -0
- data/lib/policial/commenter.rb +25 -0
- data/lib/policial/commit.rb +23 -0
- data/lib/policial/commit_file.rb +40 -0
- data/lib/policial/investigation.rb +35 -0
- data/lib/policial/line.rb +16 -0
- data/lib/policial/patch.rb +35 -0
- data/lib/policial/pull_request.rb +60 -0
- data/lib/policial/pull_request_event.rb +29 -0
- data/lib/policial/repo_config.rb +36 -0
- data/lib/policial/style_checker.rb +48 -0
- data/lib/policial/style_guides/base.rb +18 -0
- data/lib/policial/style_guides/ruby.rb +67 -0
- data/lib/policial/style_guides/unsupported.rb +10 -0
- data/lib/policial/unchanged_line.rb +15 -0
- data/lib/policial/version.rb +4 -0
- data/lib/policial/violation.rb +30 -0
- metadata +93 -0
    
        checksums.yaml
    ADDED
    
    | @@ -0,0 +1,7 @@ | |
| 1 | 
            +
            ---
         | 
| 2 | 
            +
            SHA1:
         | 
| 3 | 
            +
              metadata.gz: f9ea96661ceb59f07846827daf7ccf1d9f1ed2c6
         | 
| 4 | 
            +
              data.tar.gz: 26d33c00ee9d4dfa3e65196f3faa6d8c4bfad416
         | 
| 5 | 
            +
            SHA512:
         | 
| 6 | 
            +
              metadata.gz: 59ea217f976c38a00fb0a80bac2fd23c73f592622efcef1e8689db454ced0e54365da3e9a459f0b0a1a26d6e34ae5c4e5a4105d748f90ccb16e98a20b7e1bf8e
         | 
| 7 | 
            +
              data.tar.gz: 58be1670722255f6886aae39685cf7357406e4b1e74d97b752752faa682574b8d5914bd233ad9c32f8683197e33e79e38220062faf4144deb34a85ccd729ddd0
         | 
    
        data/LICENSE.txt
    ADDED
    
    | @@ -0,0 +1,22 @@ | |
| 1 | 
            +
            Copyright (c) 2014 Volmer Soares
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            MIT License
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            Permission is hereby granted, free of charge, to any person obtaining
         | 
| 6 | 
            +
            a copy of this software and associated documentation files (the
         | 
| 7 | 
            +
            "Software"), to deal in the Software without restriction, including
         | 
| 8 | 
            +
            without limitation the rights to use, copy, modify, merge, publish,
         | 
| 9 | 
            +
            distribute, sublicense, and/or sell copies of the Software, and to
         | 
| 10 | 
            +
            permit persons to whom the Software is furnished to do so, subject to
         | 
| 11 | 
            +
            the following conditions:
         | 
| 12 | 
            +
             | 
| 13 | 
            +
            The above copyright notice and this permission notice shall be
         | 
| 14 | 
            +
            included in all copies or substantial portions of the Software.
         | 
| 15 | 
            +
             | 
| 16 | 
            +
            THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
         | 
| 17 | 
            +
            EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
         | 
| 18 | 
            +
            MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
         | 
| 19 | 
            +
            NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
         | 
| 20 | 
            +
            LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
         | 
| 21 | 
            +
            OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
         | 
| 22 | 
            +
            WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
         | 
    
        data/README.md
    ADDED
    
    | @@ -0,0 +1,79 @@ | |
| 1 | 
            +
            # Policial
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            [](https://travis-ci.org/volmer/policial)
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            *Policial* is a gem that investigates pull requests and accuses style guide
         | 
| 6 | 
            +
            violations. It is based on thoughtbot's
         | 
| 7 | 
            +
            [Hound project](https://github.com/thoughtbot/hound).
         | 
| 8 | 
            +
            Currently it only investigates ruby code. You can setup your ruby code style
         | 
| 9 | 
            +
            rules by defining a `.rubocop.yml` file in you repo. Please see
         | 
| 10 | 
            +
            [RuboCop's README](https://github.com/bbatsov/rubocop).
         | 
| 11 | 
            +
             | 
| 12 | 
            +
            ## Installation
         | 
| 13 | 
            +
             | 
| 14 | 
            +
            Add this line to your application's Gemfile:
         | 
| 15 | 
            +
             | 
| 16 | 
            +
            ```ruby
         | 
| 17 | 
            +
            gem 'policial'
         | 
| 18 | 
            +
            ```
         | 
| 19 | 
            +
             | 
| 20 | 
            +
            And then execute:
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                $ bundle
         | 
| 23 | 
            +
             | 
| 24 | 
            +
            Or install it yourself as:
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                $ gem install policial
         | 
| 27 | 
            +
             | 
| 28 | 
            +
            ## Usage
         | 
| 29 | 
            +
             | 
| 30 | 
            +
            First, you need to set your GitHub credentials. For more information on
         | 
| 31 | 
            +
            this, please check [Octokit README](https://github.com/octokit/octokit.rb).
         | 
| 32 | 
            +
             | 
| 33 | 
            +
            ```ruby
         | 
| 34 | 
            +
            Octokit.configure do |c|
         | 
| 35 | 
            +
              c.access_tokein = 'mygithubtoken666'
         | 
| 36 | 
            +
            end
         | 
| 37 | 
            +
            ```
         | 
| 38 | 
            +
             | 
| 39 | 
            +
            You start with a pull request which Policial will run an investigation
         | 
| 40 | 
            +
            against. You can setup a pull request manually:
         | 
| 41 | 
            +
             | 
| 42 | 
            +
            ```ruby
         | 
| 43 | 
            +
            pull_request = Policial::PullRequest.new(
         | 
| 44 | 
            +
              repo: 'volmer/my_repo',
         | 
| 45 | 
            +
              number: 3,
         | 
| 46 | 
            +
              head_sha: 'headsha'
         | 
| 47 | 
            +
            )
         | 
| 48 | 
            +
            ```
         | 
| 49 | 
            +
             | 
| 50 | 
            +
            Or you can extract a pull request from a
         | 
| 51 | 
            +
            [GitHub `pull_request` webhook](https://developer.github.com/webhooks):
         | 
| 52 | 
            +
             | 
| 53 | 
            +
            ```ruby
         | 
| 54 | 
            +
            event = Policial::PullRequestEvent.new(webhook_payload)
         | 
| 55 | 
            +
            pull_request = event.pull_request
         | 
| 56 | 
            +
            ```
         | 
| 57 | 
            +
             | 
| 58 | 
            +
            Now you can start an investigation:
         | 
| 59 | 
            +
             | 
| 60 | 
            +
            ```ruby
         | 
| 61 | 
            +
            investigation = Policial::Investigation.new(pull_request)
         | 
| 62 | 
            +
             | 
| 63 | 
            +
            # Let's investigate this pull request...
         | 
| 64 | 
            +
            investigation.run
         | 
| 65 | 
            +
             | 
| 66 | 
            +
            # Want to know the violations found?
         | 
| 67 | 
            +
            investigation.violations
         | 
| 68 | 
            +
             | 
| 69 | 
            +
            # Hurry, post comments about those violations on the pull request!
         | 
| 70 | 
            +
            investigation.accuse
         | 
| 71 | 
            +
            ```
         | 
| 72 | 
            +
             | 
| 73 | 
            +
            ## Contributing
         | 
| 74 | 
            +
             | 
| 75 | 
            +
            1. Fork it ( https://github.com/volmer/policial/fork )
         | 
| 76 | 
            +
            2. Create your feature branch (`git checkout -b my-new-feature`)
         | 
| 77 | 
            +
            3. Commit your changes (`git commit -am 'Add some feature'`)
         | 
| 78 | 
            +
            4. Push to the branch (`git push origin my-new-feature`)
         | 
| 79 | 
            +
            5. Create a new Pull Request
         | 
    
        data/Rakefile
    ADDED
    
    
    
        data/lib/policial.rb
    ADDED
    
    | @@ -0,0 +1,28 @@ | |
| 1 | 
            +
            require 'octokit'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require 'policial/accusation_policy'
         | 
| 4 | 
            +
            require 'policial/commenter'
         | 
| 5 | 
            +
            require 'policial/commit'
         | 
| 6 | 
            +
            require 'policial/commit_file'
         | 
| 7 | 
            +
            require 'policial/investigation'
         | 
| 8 | 
            +
            require 'policial/line'
         | 
| 9 | 
            +
            require 'policial/patch'
         | 
| 10 | 
            +
            require 'policial/pull_request'
         | 
| 11 | 
            +
            require 'policial/pull_request_event'
         | 
| 12 | 
            +
            require 'policial/repo_config'
         | 
| 13 | 
            +
            require 'policial/style_checker'
         | 
| 14 | 
            +
            require 'policial/style_guides/base'
         | 
| 15 | 
            +
            require 'policial/style_guides/ruby'
         | 
| 16 | 
            +
            require 'policial/style_guides/unsupported'
         | 
| 17 | 
            +
            require 'policial/unchanged_line'
         | 
| 18 | 
            +
            require 'policial/version'
         | 
| 19 | 
            +
            require 'policial/violation'
         | 
| 20 | 
            +
             | 
| 21 | 
            +
            # Public: The global gem module. It exposes some module attribute accessors
         | 
| 22 | 
            +
            # so you can configure GitHub credentials, enable/disable style guides
         | 
| 23 | 
            +
            # and more.
         | 
| 24 | 
            +
            module Policial
         | 
| 25 | 
            +
              STYLE_GUIDES = [Policial::StyleGuides::Ruby]
         | 
| 26 | 
            +
             | 
| 27 | 
            +
              Octokit.auto_paginate = true
         | 
| 28 | 
            +
            end
         | 
| @@ -0,0 +1,34 @@ | |
| 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
         | 
| @@ -0,0 +1,25 @@ | |
| 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 | 
            +
                  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.line_number
         | 
| 16 | 
            +
                  )
         | 
| 17 | 
            +
                end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                private
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                def comment_body(violation)
         | 
| 22 | 
            +
                  violation.messages.join('<br/>')
         | 
| 23 | 
            +
                end
         | 
| 24 | 
            +
              end
         | 
| 25 | 
            +
            end
         | 
| @@ -0,0 +1,23 @@ | |
| 1 | 
            +
            module Policial
         | 
| 2 | 
            +
              # Public: A Commit in a GitHub repo.
         | 
| 3 | 
            +
              class Commit
         | 
| 4 | 
            +
                attr_reader :repo, :sha
         | 
| 5 | 
            +
             | 
| 6 | 
            +
                def initialize(repo, sha)
         | 
| 7 | 
            +
                  @repo = repo
         | 
| 8 | 
            +
                  @sha  = sha
         | 
| 9 | 
            +
                end
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                def file_content(filename)
         | 
| 12 | 
            +
                  contents = Octokit.contents(@repo, path: filename, ref: @sha)
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                  if contents && contents.content
         | 
| 15 | 
            +
                    Base64.decode64(contents.content).force_encoding('UTF-8')
         | 
| 16 | 
            +
                  else
         | 
| 17 | 
            +
                    ''
         | 
| 18 | 
            +
                  end
         | 
| 19 | 
            +
                rescue Octokit::NotFound
         | 
| 20 | 
            +
                  ''
         | 
| 21 | 
            +
                end
         | 
| 22 | 
            +
              end
         | 
| 23 | 
            +
            end
         | 
| @@ -0,0 +1,40 @@ | |
| 1 | 
            +
            module Policial
         | 
| 2 | 
            +
              # Public: A file in a commit.
         | 
| 3 | 
            +
              class CommitFile
         | 
| 4 | 
            +
                attr_reader :commit
         | 
| 5 | 
            +
             | 
| 6 | 
            +
                def initialize(file, commit)
         | 
| 7 | 
            +
                  @file   = file
         | 
| 8 | 
            +
                  @commit = commit
         | 
| 9 | 
            +
                end
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                def filename
         | 
| 12 | 
            +
                  @file.filename
         | 
| 13 | 
            +
                end
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                def content
         | 
| 16 | 
            +
                  @content ||= begin
         | 
| 17 | 
            +
                    @commit.file_content(filename) unless removed?
         | 
| 18 | 
            +
                  end
         | 
| 19 | 
            +
                end
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                def removed?
         | 
| 22 | 
            +
                  @file.status == 'removed'
         | 
| 23 | 
            +
                end
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                def line_at(line_number)
         | 
| 26 | 
            +
                  changed_lines.detect { |line| line.number == line_number } ||
         | 
| 27 | 
            +
                    UnchangedLine.new
         | 
| 28 | 
            +
                end
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                private
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                def changed_lines
         | 
| 33 | 
            +
                  @changed_lines ||= patch.changed_lines
         | 
| 34 | 
            +
                end
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                def patch
         | 
| 37 | 
            +
                  Patch.new(@file.patch)
         | 
| 38 | 
            +
                end
         | 
| 39 | 
            +
              end
         | 
| 40 | 
            +
            end
         | 
| @@ -0,0 +1,35 @@ | |
| 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.blank?
         | 
| 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
         | 
| @@ -0,0 +1,16 @@ | |
| 1 | 
            +
            module Policial
         | 
| 2 | 
            +
              # Public: a changed line in a commit file.
         | 
| 3 | 
            +
              class Line
         | 
| 4 | 
            +
                attr_reader :number, :patch_position
         | 
| 5 | 
            +
             | 
| 6 | 
            +
                def initialize(number, content, patch_position)
         | 
| 7 | 
            +
                  @number         = number
         | 
| 8 | 
            +
                  @content        = content
         | 
| 9 | 
            +
                  @patch_position = patch_position
         | 
| 10 | 
            +
                end
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                def changed?
         | 
| 13 | 
            +
                  true
         | 
| 14 | 
            +
                end
         | 
| 15 | 
            +
              end
         | 
| 16 | 
            +
            end
         | 
| @@ -0,0 +1,35 @@ | |
| 1 | 
            +
            module Policial
         | 
| 2 | 
            +
              # Public: a chunk of changed code in a commit file.
         | 
| 3 | 
            +
              class Patch
         | 
| 4 | 
            +
                RANGE_INFORMATION_LINE = /^@@ .+\+(?<line_number>\d+),/
         | 
| 5 | 
            +
                MODIFIED_LINE = /^\+(?!\+|\+)/
         | 
| 6 | 
            +
                NOT_REMOVED_LINE = /^[^-]/
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                def initialize(body)
         | 
| 9 | 
            +
                  @body = body || ''
         | 
| 10 | 
            +
                end
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                def changed_lines
         | 
| 13 | 
            +
                  line_number = 0
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                  @body.lines.each_with_index.with_object([]) do |(line, patch_pos), lines|
         | 
| 16 | 
            +
                    line_number =
         | 
| 17 | 
            +
                      parse_line(line, line_number, patch_pos, lines) || line_number
         | 
| 18 | 
            +
                  end
         | 
| 19 | 
            +
                end
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                private
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                def parse_line(line_content, line_number, patch_position, lines)
         | 
| 24 | 
            +
                  case line_content
         | 
| 25 | 
            +
                  when RANGE_INFORMATION_LINE
         | 
| 26 | 
            +
                    Regexp.last_match[:line_number].to_i
         | 
| 27 | 
            +
                  when MODIFIED_LINE
         | 
| 28 | 
            +
                    lines << Line.new(line_number, line_content, patch_position)
         | 
| 29 | 
            +
                    line_number + 1
         | 
| 30 | 
            +
                  when NOT_REMOVED_LINE
         | 
| 31 | 
            +
                    line_number + 1
         | 
| 32 | 
            +
                  end
         | 
| 33 | 
            +
                end
         | 
| 34 | 
            +
              end
         | 
| 35 | 
            +
            end
         | 
| @@ -0,0 +1,60 @@ | |
| 1 | 
            +
            module Policial
         | 
| 2 | 
            +
              # Public: A GibHub Pull Request.
         | 
| 3 | 
            +
              class PullRequest
         | 
| 4 | 
            +
                attr_reader :repo, :number, :user
         | 
| 5 | 
            +
             | 
| 6 | 
            +
                def initialize(repo:, number:, head_sha:, user: nil)
         | 
| 7 | 
            +
                  @repo     = repo
         | 
| 8 | 
            +
                  @number   = number
         | 
| 9 | 
            +
                  @head_sha = head_sha
         | 
| 10 | 
            +
                  @user     = user
         | 
| 11 | 
            +
                end
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                def comments
         | 
| 14 | 
            +
                  @comments ||= fetch_comments
         | 
| 15 | 
            +
                end
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                def files
         | 
| 18 | 
            +
                  @files ||= Octokit.pull_request_files(@repo, @number).map do |file|
         | 
| 19 | 
            +
                    build_commit_file(file)
         | 
| 20 | 
            +
                  end
         | 
| 21 | 
            +
                end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                def head_commit
         | 
| 24 | 
            +
                  @head_commit ||= Commit.new(@repo, @head_sha)
         | 
| 25 | 
            +
                end
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                private
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                def build_commit_file(file)
         | 
| 30 | 
            +
                  CommitFile.new(file, head_commit)
         | 
| 31 | 
            +
                end
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                def fetch_comments
         | 
| 34 | 
            +
                  paginate do |page|
         | 
| 35 | 
            +
                    Octokit.pull_request_comments(
         | 
| 36 | 
            +
                      @repo,
         | 
| 37 | 
            +
                      @number,
         | 
| 38 | 
            +
                      page: page
         | 
| 39 | 
            +
                    )
         | 
| 40 | 
            +
                  end
         | 
| 41 | 
            +
                end
         | 
| 42 | 
            +
             | 
| 43 | 
            +
                private
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                def paginate
         | 
| 46 | 
            +
                  page, results, all_pages_fetched = 1, [], false
         | 
| 47 | 
            +
             | 
| 48 | 
            +
                  until all_pages_fetched
         | 
| 49 | 
            +
                    if (page_results = yield(page)).empty?
         | 
| 50 | 
            +
                      all_pages_fetched = true
         | 
| 51 | 
            +
                    else
         | 
| 52 | 
            +
                      results += page_results
         | 
| 53 | 
            +
                      page += 1
         | 
| 54 | 
            +
                    end
         | 
| 55 | 
            +
                  end
         | 
| 56 | 
            +
             | 
| 57 | 
            +
                  results
         | 
| 58 | 
            +
                end
         | 
| 59 | 
            +
              end
         | 
| 60 | 
            +
            end
         | 
| @@ -0,0 +1,29 @@ | |
| 1 | 
            +
            require 'json'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Policial
         | 
| 4 | 
            +
              # Public: Parses a pull request event payload.
         | 
| 5 | 
            +
              class PullRequestEvent
         | 
| 6 | 
            +
                attr_reader :payload
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                def initialize(payload)
         | 
| 9 | 
            +
                  @payload = payload
         | 
| 10 | 
            +
                end
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                def pull_request
         | 
| 13 | 
            +
                  @pull_request ||= PullRequest.new(
         | 
| 14 | 
            +
                    repo: @payload['repository']['full_name'],
         | 
| 15 | 
            +
                    number: @payload['number'],
         | 
| 16 | 
            +
                    head_sha: @payload['pull_request']['head']['sha'],
         | 
| 17 | 
            +
                    user: @payload['pull_request']['user']['login']
         | 
| 18 | 
            +
                  )
         | 
| 19 | 
            +
                rescue NoMethodError
         | 
| 20 | 
            +
                  nil
         | 
| 21 | 
            +
                end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                def should_investigate?
         | 
| 24 | 
            +
                  !pull_request.nil? && (
         | 
| 25 | 
            +
                    @payload['action'] == 'opened' || @payload['action'] == 'synchronize'
         | 
| 26 | 
            +
                  )
         | 
| 27 | 
            +
                end
         | 
| 28 | 
            +
              end
         | 
| 29 | 
            +
            end
         | 
| @@ -0,0 +1,36 @@ | |
| 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
         | 
| @@ -0,0 +1,48 @@ | |
| 1 | 
            +
            module Policial
         | 
| 2 | 
            +
              # Public: Filters files to reviewable subset, builds style guide based on file
         | 
| 3 | 
            +
              # extension and delegates to style guide for line violations.
         | 
| 4 | 
            +
              class StyleChecker
         | 
| 5 | 
            +
                def initialize(pull_request)
         | 
| 6 | 
            +
                  @pull_request = pull_request
         | 
| 7 | 
            +
                  @style_guides = {}
         | 
| 8 | 
            +
                end
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                def violations
         | 
| 11 | 
            +
                  @violations ||= violations_in_checked_files.select(&:on_changed_line?)
         | 
| 12 | 
            +
                end
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                private
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                attr_reader :pull_request, :style_guides
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                def violations_in_checked_files
         | 
| 19 | 
            +
                  files_to_check.flat_map do |file|
         | 
| 20 | 
            +
                    style_guide(file.filename).violations_in_file(file)
         | 
| 21 | 
            +
                  end
         | 
| 22 | 
            +
                end
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                def files_to_check
         | 
| 25 | 
            +
                  pull_request.files.reject(&:removed?).select do |file|
         | 
| 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)
         | 
| 33 | 
            +
                end
         | 
| 34 | 
            +
             | 
| 35 | 
            +
                def style_guide_class(filename)
         | 
| 36 | 
            +
                  case filename
         | 
| 37 | 
            +
                  when /.+\.rb\z/
         | 
| 38 | 
            +
                    StyleGuides::Ruby
         | 
| 39 | 
            +
                  else
         | 
| 40 | 
            +
                    StyleGuides::Unsupported
         | 
| 41 | 
            +
                  end
         | 
| 42 | 
            +
                end
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                def config
         | 
| 45 | 
            +
                  @config ||= RepoConfig.new(pull_request.head_commit)
         | 
| 46 | 
            +
                end
         | 
| 47 | 
            +
              end
         | 
| 48 | 
            +
            end
         | 
| @@ -0,0 +1,18 @@ | |
| 1 | 
            +
            module Policial
         | 
| 2 | 
            +
              module StyleGuides
         | 
| 3 | 
            +
                # Public: Base to contain common style guide logic.
         | 
| 4 | 
            +
                class Base
         | 
| 5 | 
            +
                  def initialize(repo_config)
         | 
| 6 | 
            +
                    @repo_config = repo_config
         | 
| 7 | 
            +
                  end
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                  def enabled?
         | 
| 10 | 
            +
                    @repo_config.enabled_for?(self.class)
         | 
| 11 | 
            +
                  end
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                  def violations_in_file(_file)
         | 
| 14 | 
            +
                    fail NotImplementedError, "must implement ##{__method__}"
         | 
| 15 | 
            +
                  end
         | 
| 16 | 
            +
                end
         | 
| 17 | 
            +
              end
         | 
| 18 | 
            +
            end
         | 
| @@ -0,0 +1,67 @@ | |
| 1 | 
            +
            require 'rubocop'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Policial
         | 
| 4 | 
            +
              module StyleGuides
         | 
| 5 | 
            +
                # Public: Determine Ruby style guide violations per-line.
         | 
| 6 | 
            +
                class Ruby < Base
         | 
| 7 | 
            +
                  class << self
         | 
| 8 | 
            +
                    attr_writer :config_file
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                    def config_file
         | 
| 11 | 
            +
                      @config_file || RuboCop::ConfigLoader::DOTFILE
         | 
| 12 | 
            +
                    end
         | 
| 13 | 
            +
                  end
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                  def violations_in_file(file)
         | 
| 16 | 
            +
                    if config.file_to_exclude?(file.filename)
         | 
| 17 | 
            +
                      []
         | 
| 18 | 
            +
                    else
         | 
| 19 | 
            +
                      offenses = team.inspect_file(parsed_source(file))
         | 
| 20 | 
            +
                      build_violations(offenses, file)
         | 
| 21 | 
            +
                    end
         | 
| 22 | 
            +
                  end
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                  private
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                  def team
         | 
| 27 | 
            +
                    RuboCop::Cop::Team.new(RuboCop::Cop::Cop.all, config, rubocop_options)
         | 
| 28 | 
            +
                  end
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                  def parsed_source(file)
         | 
| 31 | 
            +
                    RuboCop::ProcessedSource.new(file.content, file.filename)
         | 
| 32 | 
            +
                  end
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                  def config
         | 
| 35 | 
            +
                    @config ||= RuboCop::ConfigLoader.merge_with_default(custom_config, '')
         | 
| 36 | 
            +
                  end
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                  def custom_config
         | 
| 39 | 
            +
                    custom = @repo_config.for(self.class)
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                    if custom.is_a?(Hash)
         | 
| 42 | 
            +
                      RuboCop::Config.new(custom, '').tap do |config|
         | 
| 43 | 
            +
                        config.add_missing_namespaces
         | 
| 44 | 
            +
                        config.make_excludes_absolute
         | 
| 45 | 
            +
                      end
         | 
| 46 | 
            +
                    else
         | 
| 47 | 
            +
                      RuboCop::Config.new
         | 
| 48 | 
            +
                    end
         | 
| 49 | 
            +
                  end
         | 
| 50 | 
            +
             | 
| 51 | 
            +
                  def rubocop_options
         | 
| 52 | 
            +
                    { debug: true } if config['ShowCopNames']
         | 
| 53 | 
            +
                  end
         | 
| 54 | 
            +
             | 
| 55 | 
            +
                  def build_violations(offenses, file)
         | 
| 56 | 
            +
                    offenses.each_with_object({}) do |offense, violations|
         | 
| 57 | 
            +
                      if violations[offense.line]
         | 
| 58 | 
            +
                        violations[offense.line].add_messages([offense.message])
         | 
| 59 | 
            +
                      else
         | 
| 60 | 
            +
                        violations[offense.line] =
         | 
| 61 | 
            +
                          Violation.new(file, offense.line, offense.message)
         | 
| 62 | 
            +
                      end
         | 
| 63 | 
            +
                    end.values
         | 
| 64 | 
            +
                  end
         | 
| 65 | 
            +
                end
         | 
| 66 | 
            +
              end
         | 
| 67 | 
            +
            end
         | 
| @@ -0,0 +1,30 @@ | |
| 1 | 
            +
            module Policial
         | 
| 2 | 
            +
              # Public: Hold file, line, and violation message values. Built by style
         | 
| 3 | 
            +
              # guides.
         | 
| 4 | 
            +
              class Violation
         | 
| 5 | 
            +
                attr_reader :line_number, :filename
         | 
| 6 | 
            +
             | 
| 7 | 
            +
                def initialize(file, line_number, message)
         | 
| 8 | 
            +
                  @filename    = file.filename
         | 
| 9 | 
            +
                  @line        = file.line_at(line_number)
         | 
| 10 | 
            +
                  @line_number = line_number
         | 
| 11 | 
            +
                  @messages    = [message]
         | 
| 12 | 
            +
                end
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                def add_messages(messages)
         | 
| 15 | 
            +
                  @messages += messages
         | 
| 16 | 
            +
                end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                def messages
         | 
| 19 | 
            +
                  @messages.uniq
         | 
| 20 | 
            +
                end
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                def patch_position
         | 
| 23 | 
            +
                  @line.patch_position
         | 
| 24 | 
            +
                end
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                def on_changed_line?
         | 
| 27 | 
            +
                  @line.changed?
         | 
| 28 | 
            +
                end
         | 
| 29 | 
            +
              end
         | 
| 30 | 
            +
            end
         | 
    
        metadata
    ADDED
    
    | @@ -0,0 +1,93 @@ | |
| 1 | 
            +
            --- !ruby/object:Gem::Specification
         | 
| 2 | 
            +
            name: policial
         | 
| 3 | 
            +
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            +
              version: 0.0.1
         | 
| 5 | 
            +
            platform: ruby
         | 
| 6 | 
            +
            authors:
         | 
| 7 | 
            +
            - Volmer Soares
         | 
| 8 | 
            +
            autorequire: 
         | 
| 9 | 
            +
            bindir: bin
         | 
| 10 | 
            +
            cert_chain: []
         | 
| 11 | 
            +
            date: 2014-12-21 00:00:00.000000000 Z
         | 
| 12 | 
            +
            dependencies:
         | 
| 13 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 14 | 
            +
              name: octokit
         | 
| 15 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 16 | 
            +
                requirements:
         | 
| 17 | 
            +
                - - "~>"
         | 
| 18 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 19 | 
            +
                    version: '3.7'
         | 
| 20 | 
            +
              type: :runtime
         | 
| 21 | 
            +
              prerelease: false
         | 
| 22 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 23 | 
            +
                requirements:
         | 
| 24 | 
            +
                - - "~>"
         | 
| 25 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 26 | 
            +
                    version: '3.7'
         | 
| 27 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 28 | 
            +
              name: rubocop
         | 
| 29 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 30 | 
            +
                requirements:
         | 
| 31 | 
            +
                - - "~>"
         | 
| 32 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 33 | 
            +
                    version: '0.2'
         | 
| 34 | 
            +
              type: :runtime
         | 
| 35 | 
            +
              prerelease: false
         | 
| 36 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 37 | 
            +
                requirements:
         | 
| 38 | 
            +
                - - "~>"
         | 
| 39 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 40 | 
            +
                    version: '0.2'
         | 
| 41 | 
            +
            description: Review pull requests for style guide violations.
         | 
| 42 | 
            +
            email:
         | 
| 43 | 
            +
            - volmerius@gmail.com
         | 
| 44 | 
            +
            executables: []
         | 
| 45 | 
            +
            extensions: []
         | 
| 46 | 
            +
            extra_rdoc_files: []
         | 
| 47 | 
            +
            files:
         | 
| 48 | 
            +
            - LICENSE.txt
         | 
| 49 | 
            +
            - README.md
         | 
| 50 | 
            +
            - Rakefile
         | 
| 51 | 
            +
            - lib/policial.rb
         | 
| 52 | 
            +
            - lib/policial/accusation_policy.rb
         | 
| 53 | 
            +
            - lib/policial/commenter.rb
         | 
| 54 | 
            +
            - lib/policial/commit.rb
         | 
| 55 | 
            +
            - lib/policial/commit_file.rb
         | 
| 56 | 
            +
            - lib/policial/investigation.rb
         | 
| 57 | 
            +
            - lib/policial/line.rb
         | 
| 58 | 
            +
            - lib/policial/patch.rb
         | 
| 59 | 
            +
            - lib/policial/pull_request.rb
         | 
| 60 | 
            +
            - lib/policial/pull_request_event.rb
         | 
| 61 | 
            +
            - lib/policial/repo_config.rb
         | 
| 62 | 
            +
            - lib/policial/style_checker.rb
         | 
| 63 | 
            +
            - lib/policial/style_guides/base.rb
         | 
| 64 | 
            +
            - lib/policial/style_guides/ruby.rb
         | 
| 65 | 
            +
            - lib/policial/style_guides/unsupported.rb
         | 
| 66 | 
            +
            - lib/policial/unchanged_line.rb
         | 
| 67 | 
            +
            - lib/policial/version.rb
         | 
| 68 | 
            +
            - lib/policial/violation.rb
         | 
| 69 | 
            +
            homepage: https://github.com/volmer/policial
         | 
| 70 | 
            +
            licenses:
         | 
| 71 | 
            +
            - MIT
         | 
| 72 | 
            +
            metadata: {}
         | 
| 73 | 
            +
            post_install_message: 
         | 
| 74 | 
            +
            rdoc_options: []
         | 
| 75 | 
            +
            require_paths:
         | 
| 76 | 
            +
            - lib
         | 
| 77 | 
            +
            required_ruby_version: !ruby/object:Gem::Requirement
         | 
| 78 | 
            +
              requirements:
         | 
| 79 | 
            +
              - - ">="
         | 
| 80 | 
            +
                - !ruby/object:Gem::Version
         | 
| 81 | 
            +
                  version: '0'
         | 
| 82 | 
            +
            required_rubygems_version: !ruby/object:Gem::Requirement
         | 
| 83 | 
            +
              requirements:
         | 
| 84 | 
            +
              - - ">="
         | 
| 85 | 
            +
                - !ruby/object:Gem::Version
         | 
| 86 | 
            +
                  version: '0'
         | 
| 87 | 
            +
            requirements: []
         | 
| 88 | 
            +
            rubyforge_project: 
         | 
| 89 | 
            +
            rubygems_version: 2.4.5
         | 
| 90 | 
            +
            signing_key: 
         | 
| 91 | 
            +
            specification_version: 4
         | 
| 92 | 
            +
            summary: Review pull requests for style guide violations
         | 
| 93 | 
            +
            test_files: []
         |