Skip to content

Commit

Permalink
Create GitHub reporter (#188)
Browse files Browse the repository at this point in the history
Hello, we want to use Slim Lint in our GitHub Actions CI pipeline so I
would like to add a GitHub reporter. The name of it is taken from the
[Rubocop GitHub Actions
formatter](https://docs.rubocop.org/rubocop/1.66/formatters.html#github-actions-formatter)
which does the same thing for Rubocop.

I'm also adding a sample GH Actions workflow snippet that follows the
new [Rails GH CI
convention](https://github.com/rails/rails/blob/6df3358abf5fb99ede40d45ef7a643b97e1a802d/railties/lib/rails/generators/rails/app/templates/github/ci.yml.tt#L46)
to the README. This setup creates Pull Request annotations on Lint
failures that look like this:


![image](https://github.com/user-attachments/assets/a6818d73-4bae-498f-9194-77e27efd4bba)
  • Loading branch information
borama authored Oct 5, 2024
1 parent cf1f80b commit b4d1a56
Show file tree
Hide file tree
Showing 3 changed files with 171 additions and 0 deletions.
30 changes: 30 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ your [SCM hooks](https://github.com/sds/overcommit).
* [Linters](#linters)
* [Editor Integration](#editor-integration)
* [Git Integration](#git-integration)
* [GitHub Integration](#github-integration)
* [Rake Integration](#rake-integration)
* [Contributing](#contributing)
* [Changelog](#changelog)
Expand Down Expand Up @@ -181,6 +182,35 @@ If you'd like to integrate `slim-lint` into your Git workflow, check out
[overcommit](https://github.com/sds/overcommit), a powerful and flexible
Git hook manager.

## Github Integration

To run `slim-lint` in your [GitHub Actions](https://docs.github.com/en/actions) CI pipeline,
use the `github` reporter, for example:

```yml
on:
pull_request:
push:
branches: [ main ]
jobs:
lint:
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: .ruby-version
bundler-cache: true
- name: Lint Slim templates for consistent style
run: bundle exec slim-lint -r github app/views
```

On lint failures, this setup will create annotations in your pull requests on GitHub.

## Rake Integration

To execute `slim-lint` via a [Rake](https://github.com/ruby/rake) task, make
Expand Down
36 changes: 36 additions & 0 deletions lib/slim_lint/reporter/github_reporter.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# frozen_string_literal: true

module SlimLint
# Outputs lints in a format suitable for GitHub Actions.
# See https://docs.github.com/en/actions/reference/workflow-commands-for-github-actions/.
class Reporter::GithubReporter < Reporter
def display_report(report)
sorted_lints = report.lints.sort_by { |l| [l.filename, l.line] }

sorted_lints.each do |lint|
print_type(lint)
print_location(lint)
print_message(lint)
end
end

private

def print_type(lint)
if lint.error?
log.log '::error ', false
else
log.log '::warning ', false
end
end

def print_location(lint)
log.log "file=#{lint.filename},line=#{lint.line},", false
end

def print_message(lint)
log.log 'title=Slim Lint', false
log.log "::#{lint.message}"
end
end
end
105 changes: 105 additions & 0 deletions spec/slim_lint/reporter/github_reporter_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
# frozen_string_literal: true

require 'spec_helper'

describe SlimLint::Reporter::GithubReporter do
describe '#display_report' do
let(:io) { StringIO.new }
let(:output) { io.string }
let(:logger) { SlimLint::Logger.new(io) }
let(:report) { SlimLint::Report.new(lints, []) }
let(:reporter) { described_class.new(logger) }

subject { reporter.display_report(report) }

context 'when there are no lints' do
let(:lints) { [] }

it 'prints nothing' do
subject
output.should be_empty
end
end

context 'when there are lints' do
let(:filenames) { ['some-filename.slim', 'other-filename.slim'] }
let(:lines) { [502, 724] }
let(:descriptions) { ['Description of lint 1', 'Description of lint 2'] }
let(:severities) { [:warning] * 2 }
let(:linter) { double(name: 'SomeLinter') }

let(:lints) do
filenames.each_with_index.map do |filename, index|
SlimLint::Lint.new(linter, filename, lines[index], descriptions[index], severities[index])
end
end

it 'prints each lint on its own line' do
subject
output.count("\n").should == 2
end

it 'prints a trailing newline' do
subject
output[-1].should == "\n"
end

it 'prints the filename in the "file" parameter for each lint' do
subject
filenames.each do |filename|
output.scan(/file=#{filename}/).count.should == 1
end
end

it 'prints the line number in the "line" parameter for each lint' do
subject
lines.each do |line|
output.scan(/line=#{line}/).count.should == 1
end
end

it 'prints a "Slim Lint" annotation title for each lint' do
subject
output.scan(/title=Slim Lint/).count.should == 2
end

it 'prints the description for each lint at the end of the line' do
subject
descriptions.each do |description|
output.scan(/::#{description}$/).count.should == 1
end
end

context 'when lints are warnings' do
it 'prints the warning severity annotation at the beginning of each line' do
subject
output.split("\n").each do |line|
line.scan(/^::warning /).count.should == 1
end
end
end

context 'when lints are errors' do
let(:severities) { [:error] * 2 }

it 'prints the error severity annotation at the beginning of each line' do
subject
output.split("\n").each do |line|
line.scan(/^::error /).count.should == 1
end
end
end

context 'when lint has no associated linter' do
let(:linter) { nil }

it 'prints the description for each lint' do
subject
descriptions.each do |description|
output.scan(/#{description}/).count.should == 1
end
end
end
end
end
end

0 comments on commit b4d1a56

Please sign in to comment.