codegrade 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/.gitignore +15 -0
- data/.rspec +2 -0
- data/Gemfile +3 -0
- data/LICENSE.txt +22 -0
- data/README.md +7 -0
- data/Rakefile +1 -0
- data/bin/codegrade +9 -0
- data/codegrade.gemspec +26 -0
- data/lib/codegrade.rb +10 -0
- data/lib/codegrade/commit.rb +43 -0
- data/lib/codegrade/formatter.rb +35 -0
- data/lib/codegrade/grader/commit_message.rb +236 -0
- data/lib/codegrade/grader/grader.rb +30 -0
- data/lib/codegrade/grader/rubocop.rb +38 -0
- data/lib/codegrade/offense.rb +22 -0
- data/lib/codegrade/version.rb +3 -0
- data/spec/models/grader/commit_message_spec.rb +337 -0
- data/spec/models/grader/grader_spec.rb +17 -0
- data/spec/models/grader/rubocop_spec.rb +33 -0
- data/spec/spec_helper.rb +11 -0
- metadata +141 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: e387a4ec843d880c7c20f2f251851de9797ad772
|
4
|
+
data.tar.gz: 6b22863716a4f1ea94f8c68020db34d32ddbb400
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 2c8d198ee979cf3d2a8b8851ea8269c3c6f8d41be20154ae39af2f17d2c8abb8dc53881d23d693acab4caabb589a76015708a2709440c7cb0f704a9ab0fb1a76
|
7
|
+
data.tar.gz: 123e9b9abfc205e094889c5bc6af56c810aa0c54fd9aaff6f81236a9851c7dc172333b6935cb9b07a78ab54313434fe44f001c4af21d66ba47740994f424e2a8
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Visuality
|
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
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/bin/codegrade
ADDED
data/codegrade.gemspec
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'codegrade/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "codegrade"
|
8
|
+
spec.version = Codegrade::VERSION
|
9
|
+
spec.authors = ["Michał Młoźniak", "Karol Słuszniak"]
|
10
|
+
spec.email = ["contact@visuality.pl"]
|
11
|
+
spec.summary = %q{Grade your git commits}
|
12
|
+
spec.description = %q{This tool grades your commit messages and all changes files}
|
13
|
+
spec.homepage = "http://www.visuality.pl"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency('bundler', '~> 1.7')
|
22
|
+
spec.add_development_dependency('rake', '~> 10.0')
|
23
|
+
spec.add_development_dependency('rspec', '~> 3.1')
|
24
|
+
spec.add_runtime_dependency('rubocop', '~> 0.27')
|
25
|
+
spec.add_runtime_dependency('rugged', '~> 0.21')
|
26
|
+
end
|
data/lib/codegrade.rb
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
require 'rubocop'
|
3
|
+
require 'rugged'
|
4
|
+
|
5
|
+
require 'codegrade/commit'
|
6
|
+
require 'codegrade/formatter'
|
7
|
+
require 'codegrade/offense'
|
8
|
+
require 'codegrade/grader/commit_message'
|
9
|
+
require 'codegrade/grader/grader'
|
10
|
+
require 'codegrade/grader/rubocop'
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Codegrade
|
2
|
+
class Commit
|
3
|
+
attr_reader :sha, :working_directory
|
4
|
+
|
5
|
+
def initialize(working_directory = '.', sha = nil)
|
6
|
+
@working_directory = File.expand_path(working_directory)
|
7
|
+
@sha = sha
|
8
|
+
end
|
9
|
+
|
10
|
+
def author
|
11
|
+
commit.author
|
12
|
+
end
|
13
|
+
|
14
|
+
def message
|
15
|
+
commit.message
|
16
|
+
end
|
17
|
+
|
18
|
+
def files
|
19
|
+
parse_git_tree(commit.tree, working_directory)
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def repo
|
25
|
+
@repo ||= Rugged::Repository.new(working_directory)
|
26
|
+
end
|
27
|
+
|
28
|
+
def commit
|
29
|
+
@commit ||= sha.nil? ? repo.last_commit : repo.lookup(sha)
|
30
|
+
end
|
31
|
+
|
32
|
+
def parse_git_tree(tree, path)
|
33
|
+
files = []
|
34
|
+
|
35
|
+
tree.walk_blobs do |root, entry|
|
36
|
+
path = File.expand_path(root, working_directory)
|
37
|
+
files.push(File.join(path, entry[:name]))
|
38
|
+
end
|
39
|
+
|
40
|
+
files
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Codegrade
|
2
|
+
class Formatter
|
3
|
+
def initialize(offenses)
|
4
|
+
@offenses = offenses
|
5
|
+
end
|
6
|
+
|
7
|
+
def print
|
8
|
+
group_by_categories
|
9
|
+
working_directory = File.expand_path('.')
|
10
|
+
|
11
|
+
@categories.each do |category, offenses|
|
12
|
+
puts "#{category} (#{offenses.size}):"
|
13
|
+
puts
|
14
|
+
|
15
|
+
offenses.each do |offense|
|
16
|
+
file = offense.file.gsub(working_directory, '')[1..-1]
|
17
|
+
puts "- #{file}:#{offense.line_number}:#{offense.column_number}"
|
18
|
+
end
|
19
|
+
|
20
|
+
puts
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def group_by_categories
|
27
|
+
@categories = Hash.new
|
28
|
+
|
29
|
+
@offenses.each do |offense|
|
30
|
+
@categories[offense.category] ||= []
|
31
|
+
@categories[offense.category].push(offense)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,236 @@
|
|
1
|
+
module Codegrade
|
2
|
+
module Grader
|
3
|
+
class CommitMessage
|
4
|
+
attr_reader :offenses
|
5
|
+
|
6
|
+
def initialize(message)
|
7
|
+
@message = message
|
8
|
+
end
|
9
|
+
|
10
|
+
def grade
|
11
|
+
clear_offenses
|
12
|
+
parse_commit_message
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def clear_offenses
|
18
|
+
@offenses = []
|
19
|
+
end
|
20
|
+
|
21
|
+
def parse_commit_message
|
22
|
+
lines = @message.split("\n")
|
23
|
+
|
24
|
+
paragraph, paragraph_start, paragraph_line = [], 1, 0
|
25
|
+
inside_punctation = false
|
26
|
+
|
27
|
+
lines.each_with_index do |line, index|
|
28
|
+
paragraph << line
|
29
|
+
paragraph_line += 1
|
30
|
+
|
31
|
+
line_number = index + 1
|
32
|
+
blank_line = blank?(line)
|
33
|
+
title_line = line_number == 1
|
34
|
+
end_of_paragraph = (index == lines.length - 1 ||
|
35
|
+
blank?(lines[index + 1])) && !title_line &&
|
36
|
+
paragraph.any? && !blank?(paragraph.last)
|
37
|
+
|
38
|
+
if line.start_with?('* ')
|
39
|
+
check_punctation_no_separating_line(inside_punctation, line_number)
|
40
|
+
|
41
|
+
inside_punctation = true
|
42
|
+
end
|
43
|
+
|
44
|
+
if title_line
|
45
|
+
check_title_leading_lowercase(line, line_number)
|
46
|
+
check_title_too_long(line, line_number)
|
47
|
+
check_title_trailing_dot(line, line_number)
|
48
|
+
end
|
49
|
+
|
50
|
+
if end_of_paragraph
|
51
|
+
if inside_punctation
|
52
|
+
check_punctation_leading_lowercase(paragraph, paragraph_start)
|
53
|
+
check_punctation_trailing_dot(paragraph, paragraph_start)
|
54
|
+
else
|
55
|
+
check_paragraph_leading_lowercase(paragraph, paragraph_start)
|
56
|
+
check_paragraph_no_trailing_dot(paragraph, paragraph_start)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
if blank_line
|
61
|
+
check_redundant_empty_line(paragraph, line_number)
|
62
|
+
|
63
|
+
paragraph, paragraph_start, paragraph_line = [], line_number + 1, 0
|
64
|
+
inside_punctation = false
|
65
|
+
else
|
66
|
+
check_title_multiple_lines(line_number)
|
67
|
+
|
68
|
+
check_line_trailing_whitespace(line, line_number)
|
69
|
+
check_line_too_long(line, line_number) unless title_line
|
70
|
+
end
|
71
|
+
|
72
|
+
if inside_punctation
|
73
|
+
if paragraph_line > 1
|
74
|
+
check_punctation_leading_whitespace(line, line_number)
|
75
|
+
end
|
76
|
+
else
|
77
|
+
if ! blank_line
|
78
|
+
check_line_leading_whitespace(line, line_number)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def check_title_leading_lowercase(line, line_number)
|
85
|
+
if line[0].downcase == line[0]
|
86
|
+
add_offense(
|
87
|
+
:category => 'title_leading_lowercase',
|
88
|
+
:line_number => line_number,
|
89
|
+
:column_number => 1)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def check_title_too_long(line, line_number)
|
94
|
+
if line.length > 50
|
95
|
+
add_offense(
|
96
|
+
:category => 'title_too_long',
|
97
|
+
:line_number => line_number,
|
98
|
+
:column_number => 51)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def check_title_trailing_dot(line, line_number)
|
103
|
+
if (m = line.match(/\.\s*$/))
|
104
|
+
add_offense(
|
105
|
+
:category => 'title_trailing_dot',
|
106
|
+
:line_number => line_number,
|
107
|
+
:column_number => m.begin(0))
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def check_title_multiple_lines(line_number)
|
112
|
+
if line_number == 2
|
113
|
+
add_offense(
|
114
|
+
:category => 'title_multiple_lines',
|
115
|
+
:line_number => line_number)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def check_redundant_empty_line(paragraph, line_number)
|
120
|
+
if paragraph.select { |line| !blank?(line) }.empty?
|
121
|
+
add_offense(
|
122
|
+
:category => 'redundant_empty_line',
|
123
|
+
:line_number => line_number)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def check_line_trailing_whitespace(line, line_number)
|
128
|
+
if (m = line.match(/\s+$/))
|
129
|
+
add_offense(
|
130
|
+
:category => 'line_trailing_whitespace',
|
131
|
+
:line_number => line_number,
|
132
|
+
:column_number => m.begin(0) + 1)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
def check_line_leading_whitespace(line, line_number)
|
137
|
+
if (m = line.match(/^\s+/))
|
138
|
+
add_offense(
|
139
|
+
:category => 'line_leading_whitespace',
|
140
|
+
:line_number => line_number,
|
141
|
+
:column_number => m.end(0))
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
def check_line_too_long(line, line_number)
|
146
|
+
if line.length > 70
|
147
|
+
add_offense(
|
148
|
+
:category => 'line_too_long',
|
149
|
+
:line_number => line_number,
|
150
|
+
:column_number => 71)
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
def check_paragraph_leading_lowercase(paragraph, paragraph_start)
|
155
|
+
line = strip(paragraph.first)
|
156
|
+
|
157
|
+
if line[0].downcase == line[0] &&
|
158
|
+
!link?(line.split[0])
|
159
|
+
add_offense(
|
160
|
+
:category => 'paragraph_leading_lowercase',
|
161
|
+
:line_number => paragraph_start,
|
162
|
+
:column_number => 1)
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
def check_paragraph_no_trailing_dot(paragraph, paragraph_start)
|
167
|
+
line = strip(paragraph.last)
|
168
|
+
|
169
|
+
if line[-1] != '.' &&
|
170
|
+
!link?(line.split[-1])
|
171
|
+
add_offense(
|
172
|
+
:category => 'paragraph_no_trailing_dot',
|
173
|
+
:line_number => paragraph_start + paragraph.length - 1,
|
174
|
+
:column_number => paragraph.last.length)
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
def check_punctation_no_separating_line(inside_punctation, line_number)
|
179
|
+
if inside_punctation
|
180
|
+
add_offense(
|
181
|
+
:category => 'punctation_no_separating_line',
|
182
|
+
:line_number => line_number)
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
def check_punctation_leading_whitespace(line, line_number)
|
187
|
+
if (m = line.match(/^\s*/)) && m.end(0) != 2
|
188
|
+
add_offense(
|
189
|
+
:category => 'punctation_leading_whitespace',
|
190
|
+
:line_number => line_number,
|
191
|
+
:column_number => m.end(0))
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
def check_punctation_leading_lowercase(paragraph, paragraph_start)
|
196
|
+
line = strip(paragraph.first)
|
197
|
+
|
198
|
+
if line[2].downcase == line[2] &&
|
199
|
+
!link?(line.split[0])
|
200
|
+
add_offense(
|
201
|
+
:category => 'punctation_leading_lowercase',
|
202
|
+
:line_number => paragraph_start,
|
203
|
+
:column_number => 3)
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
def check_punctation_trailing_dot(paragraph, paragraph_start)
|
208
|
+
line = strip(paragraph.last)
|
209
|
+
|
210
|
+
if line[-1] == '.' &&
|
211
|
+
!link?(line.split[-1])
|
212
|
+
add_offense(
|
213
|
+
:category => 'punctation_trailing_dot',
|
214
|
+
:line_number => paragraph_start + paragraph.length - 1,
|
215
|
+
:column_number => paragraph.last.length)
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
def add_offense(params)
|
220
|
+
@offenses << Codegrade::Offense.new(params)
|
221
|
+
end
|
222
|
+
|
223
|
+
def strip(text)
|
224
|
+
text.match(/(\S.*\S)/).to_s
|
225
|
+
end
|
226
|
+
|
227
|
+
def blank?(text)
|
228
|
+
text.match(/^\s*$/)
|
229
|
+
end
|
230
|
+
|
231
|
+
def link?(text)
|
232
|
+
text.include?('://') || text.start_with?('www.')
|
233
|
+
end
|
234
|
+
end
|
235
|
+
end
|
236
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Codegrade
|
2
|
+
module Grader
|
3
|
+
class Grader
|
4
|
+
attr_reader :commit
|
5
|
+
|
6
|
+
def initialize(commit)
|
7
|
+
@commit = commit
|
8
|
+
end
|
9
|
+
|
10
|
+
def grade
|
11
|
+
offenses = []
|
12
|
+
|
13
|
+
commit_message = CommitMessage.new(@commit.message)
|
14
|
+
commit_message.grade
|
15
|
+
offenses.concat(commit_message.offenses)
|
16
|
+
ruby_files.each do |file|
|
17
|
+
offenses.concat(Rubocop.new(file).grade)
|
18
|
+
end
|
19
|
+
|
20
|
+
offenses
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def ruby_files
|
26
|
+
commit.files.select { |filename| File.extname(filename) == '.rb' }
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'rubocop'
|
2
|
+
|
3
|
+
module Codegrade
|
4
|
+
module Grader
|
5
|
+
class Rubocop
|
6
|
+
attr_reader :config_store, :file
|
7
|
+
|
8
|
+
def initialize(file)
|
9
|
+
@config_store = RuboCop::ConfigStore.new
|
10
|
+
@file = file
|
11
|
+
end
|
12
|
+
|
13
|
+
def grade
|
14
|
+
processed_file = RuboCop::ProcessedSource.from_file(file)
|
15
|
+
config = config_store.for(processed_file.path)
|
16
|
+
team = RuboCop::Cop::Team.new(classes, config, {})
|
17
|
+
|
18
|
+
team.inspect_file(processed_file).map do |rubocop_offense|
|
19
|
+
Offense.new(category: rubocop_offense.message,
|
20
|
+
line_number: rubocop_offense.line,
|
21
|
+
column_number: rubocop_offense.real_column,
|
22
|
+
file: file,
|
23
|
+
source: rubocop_offense.location.source)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def classes
|
30
|
+
skipped_cops = ['Style/AlignParameters']
|
31
|
+
|
32
|
+
RuboCop::Cop::Cop.all.reject do |cop|
|
33
|
+
cop.rails? || skipped_cops.include?(cop.cop_name)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Codegrade
|
2
|
+
class Offense < OpenStruct
|
3
|
+
def initialize(params)
|
4
|
+
params = OpenStruct.new(params)
|
5
|
+
|
6
|
+
super(
|
7
|
+
:category => params[:category],
|
8
|
+
:line_number => params[:line_number],
|
9
|
+
:column_number => params[:column_number],
|
10
|
+
:file => params[:file],
|
11
|
+
:source => params[:source]
|
12
|
+
)
|
13
|
+
end
|
14
|
+
|
15
|
+
def message
|
16
|
+
m = category.split('_').join(' ')
|
17
|
+
m = m[0].upcase + m[1..-1]
|
18
|
+
|
19
|
+
m
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,337 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Codegrade::Grader::CommitMessage do
|
4
|
+
def find_offense(grader, conditions = {})
|
5
|
+
grader.offenses.find do |error|
|
6
|
+
all_met = true
|
7
|
+
|
8
|
+
conditions.each do |key, value|
|
9
|
+
if error[key] != value
|
10
|
+
all_met = false
|
11
|
+
break
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
all_met
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
context 'proper reference commit' do
|
20
|
+
let(:message) do
|
21
|
+
"Implement perfect commit
|
22
|
+
|
23
|
+
This is some text. It can have sentences as it's a
|
24
|
+
regular paragraph. It's close to unlimited.
|
25
|
+
|
26
|
+
http://www.google.pl/
|
27
|
+
|
28
|
+
* This is a first punctor with multiple lines
|
29
|
+
and an useful content
|
30
|
+
|
31
|
+
* This is a second, with just one line
|
32
|
+
|
33
|
+
Ending comment here."
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'returns no errors' do
|
37
|
+
grader = Codegrade::Grader::CommitMessage.new(message)
|
38
|
+
grader.grade
|
39
|
+
|
40
|
+
expect(grader.offenses).to be_empty
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
context 'title starting with small letter' do
|
45
|
+
let(:message) { "wrong commit" }
|
46
|
+
|
47
|
+
it 'returns relevant error' do
|
48
|
+
grader = Codegrade::Grader::CommitMessage.new(message)
|
49
|
+
grader.grade
|
50
|
+
|
51
|
+
expect(find_offense(grader,
|
52
|
+
:category => 'title_leading_lowercase',
|
53
|
+
:line_number => 1)).not_to be_nil
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
context 'title too long' do
|
58
|
+
let(:message) { "Wrong commit with more that 50 chars that is just wrong" }
|
59
|
+
|
60
|
+
it 'returns relevant error' do
|
61
|
+
grader = Codegrade::Grader::CommitMessage.new(message)
|
62
|
+
grader.grade
|
63
|
+
|
64
|
+
expect(grader.offenses.count).to eq 1
|
65
|
+
expect(find_offense(grader,
|
66
|
+
:category => 'title_too_long',
|
67
|
+
:line_number => 1)).not_to be_nil
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
context 'title ending with a dot' do
|
72
|
+
let(:message) { "Wrong commit with dot." }
|
73
|
+
|
74
|
+
it 'returns relevant error' do
|
75
|
+
grader = Codegrade::Grader::CommitMessage.new(message)
|
76
|
+
grader.grade
|
77
|
+
|
78
|
+
expect(grader.offenses.count).to eq 1
|
79
|
+
expect(find_offense(grader,
|
80
|
+
:category => 'title_trailing_dot',
|
81
|
+
:line_number => 1,
|
82
|
+
:column_number => 21)).not_to be_nil
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
context 'title with multiple lines' do
|
87
|
+
let(:message) { "Wrong commit with\ntitle in multiple lines." }
|
88
|
+
|
89
|
+
it 'returns relevant error' do
|
90
|
+
grader = Codegrade::Grader::CommitMessage.new(message)
|
91
|
+
grader.grade
|
92
|
+
|
93
|
+
expect(grader.offenses.count).to eq 1
|
94
|
+
expect(find_offense(grader,
|
95
|
+
:category => 'title_multiple_lines',
|
96
|
+
:line_number => 2)).not_to be_nil
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
context 'redundant empty lines' do
|
101
|
+
let(:message) do
|
102
|
+
"Commit
|
103
|
+
|
104
|
+
|
105
|
+
One redundant empty line above.\n \n
|
106
|
+
One more redundant blank line above.
|
107
|
+
|
108
|
+
|
109
|
+
|
110
|
+
Two here."
|
111
|
+
end
|
112
|
+
|
113
|
+
it 'returns relevant errors' do
|
114
|
+
grader = Codegrade::Grader::CommitMessage.new(message)
|
115
|
+
grader.grade
|
116
|
+
|
117
|
+
expect(grader.offenses.count).to eq 4
|
118
|
+
expect(find_offense(grader,
|
119
|
+
:category => 'redundant_empty_line',
|
120
|
+
:line_number => 3)).not_to be_nil
|
121
|
+
expect(find_offense(grader,
|
122
|
+
:category => 'redundant_empty_line',
|
123
|
+
:line_number => 6)).not_to be_nil
|
124
|
+
expect(find_offense(grader,
|
125
|
+
:category => 'redundant_empty_line',
|
126
|
+
:line_number => 9)).not_to be_nil
|
127
|
+
expect(find_offense(grader,
|
128
|
+
:category => 'redundant_empty_line',
|
129
|
+
:line_number => 10)).not_to be_nil
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
context 'line with trailing whitespace' do
|
134
|
+
let(:message) do
|
135
|
+
"Commit \n
|
136
|
+
Another line. \nLast line."
|
137
|
+
end
|
138
|
+
|
139
|
+
it 'returns relevant errors' do
|
140
|
+
grader = Codegrade::Grader::CommitMessage.new(message)
|
141
|
+
grader.grade
|
142
|
+
|
143
|
+
expect(grader.offenses.count).to eq 2
|
144
|
+
expect(find_offense(grader,
|
145
|
+
:category => 'line_trailing_whitespace',
|
146
|
+
:line_number => 1,
|
147
|
+
:column_number => 7)).not_to be_nil
|
148
|
+
expect(find_offense(grader,
|
149
|
+
:category => 'line_trailing_whitespace',
|
150
|
+
:line_number => 3,
|
151
|
+
:column_number => 14)).not_to be_nil
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
context 'line outside punctation with leading whitespace' do
|
156
|
+
let(:message) do
|
157
|
+
"Commit
|
158
|
+
|
159
|
+
Wrong indent.
|
160
|
+
|
161
|
+
Another
|
162
|
+
and one more.
|
163
|
+
|
164
|
+
* Punctation
|
165
|
+
OK"
|
166
|
+
end
|
167
|
+
|
168
|
+
it 'returns relevant errors' do
|
169
|
+
grader = Codegrade::Grader::CommitMessage.new(message)
|
170
|
+
grader.grade
|
171
|
+
|
172
|
+
expect(grader.offenses.count).to eq 3
|
173
|
+
expect(find_offense(grader,
|
174
|
+
:category => 'line_leading_whitespace',
|
175
|
+
:line_number => 3,
|
176
|
+
:column_number => 2)).not_to be_nil
|
177
|
+
expect(find_offense(grader,
|
178
|
+
:category => 'line_leading_whitespace',
|
179
|
+
:line_number => 5,
|
180
|
+
:column_number => 1)).not_to be_nil
|
181
|
+
expect(find_offense(grader,
|
182
|
+
:category => 'line_leading_whitespace',
|
183
|
+
:line_number => 6,
|
184
|
+
:column_number => 6)).not_to be_nil
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
context 'too long line outside of title' do
|
189
|
+
let(:message) do
|
190
|
+
"Commit
|
191
|
+
|
192
|
+
Another line that is longer than 70 characters and violates our sacred rules."
|
193
|
+
end
|
194
|
+
|
195
|
+
it 'returns relevant error' do
|
196
|
+
grader = Codegrade::Grader::CommitMessage.new(message)
|
197
|
+
grader.grade
|
198
|
+
|
199
|
+
expect(find_offense(grader,
|
200
|
+
:category => 'line_too_long',
|
201
|
+
:line_number => 3,
|
202
|
+
:column_number => 71)).not_to be_nil
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
context 'paragraph starting with small letter' do
|
207
|
+
let(:message) do
|
208
|
+
"Commit
|
209
|
+
|
210
|
+
wrong para.
|
211
|
+
|
212
|
+
Good.
|
213
|
+
|
214
|
+
wrong again."
|
215
|
+
end
|
216
|
+
|
217
|
+
it 'returns relevant errors' do
|
218
|
+
grader = Codegrade::Grader::CommitMessage.new(message)
|
219
|
+
grader.grade
|
220
|
+
|
221
|
+
expect(grader.offenses.count).to eq 3
|
222
|
+
expect(find_offense(grader,
|
223
|
+
:category => 'paragraph_leading_lowercase',
|
224
|
+
:line_number => 3)).not_to be_nil
|
225
|
+
expect(find_offense(grader,
|
226
|
+
:category => 'paragraph_leading_lowercase',
|
227
|
+
:line_number => 7)).not_to be_nil
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
context 'punctation with no separating line' do
|
232
|
+
let(:message) do
|
233
|
+
"Commit
|
234
|
+
|
235
|
+
* Punctation
|
236
|
+
OK
|
237
|
+
|
238
|
+
* Punctation
|
239
|
+
close 1
|
240
|
+
* Punctation
|
241
|
+
close 2"
|
242
|
+
end
|
243
|
+
|
244
|
+
it 'returns relevant errors' do
|
245
|
+
grader = Codegrade::Grader::CommitMessage.new(message)
|
246
|
+
grader.grade
|
247
|
+
|
248
|
+
expect(find_offense(grader,
|
249
|
+
:category => 'punctation_no_separating_line',
|
250
|
+
:line_number => 8)).not_to be_nil
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
context 'punctation with wrong leading whitespace' do
|
255
|
+
let(:message) do
|
256
|
+
"Commit
|
257
|
+
|
258
|
+
* Punctation
|
259
|
+
small
|
260
|
+
|
261
|
+
* Punctation
|
262
|
+
OK
|
263
|
+
|
264
|
+
* Punctation
|
265
|
+
big"
|
266
|
+
end
|
267
|
+
|
268
|
+
it 'returns relevant errors' do
|
269
|
+
grader = Codegrade::Grader::CommitMessage.new(message)
|
270
|
+
grader.grade
|
271
|
+
|
272
|
+
expect(find_offense(grader,
|
273
|
+
:category => 'punctation_leading_whitespace',
|
274
|
+
:line_number => 4,
|
275
|
+
:column_number => 0)).not_to be_nil
|
276
|
+
expect(find_offense(grader,
|
277
|
+
:category => 'punctation_leading_whitespace',
|
278
|
+
:line_number => 10,
|
279
|
+
:column_number => 3)).not_to be_nil
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|
283
|
+
context 'punctation starting with small letter' do
|
284
|
+
let(:message) do
|
285
|
+
"Commit
|
286
|
+
|
287
|
+
* punctation
|
288
|
+
|
289
|
+
* Punctation
|
290
|
+
|
291
|
+
* xunctation"
|
292
|
+
end
|
293
|
+
|
294
|
+
it 'returns relevant errors' do
|
295
|
+
grader = Codegrade::Grader::CommitMessage.new(message)
|
296
|
+
grader.grade
|
297
|
+
|
298
|
+
expect(grader.offenses.count).to eq 2
|
299
|
+
expect(find_offense(grader,
|
300
|
+
:category => 'punctation_leading_lowercase',
|
301
|
+
:line_number => 3,
|
302
|
+
:column_number => 3)).not_to be_nil
|
303
|
+
expect(find_offense(grader,
|
304
|
+
:category => 'punctation_leading_lowercase',
|
305
|
+
:line_number => 7,
|
306
|
+
:column_number => 3)).not_to be_nil
|
307
|
+
end
|
308
|
+
end
|
309
|
+
|
310
|
+
context 'punctation ending with a dot' do
|
311
|
+
let(:message) do
|
312
|
+
"Commit
|
313
|
+
|
314
|
+
* Punctation.
|
315
|
+
|
316
|
+
* Punctation
|
317
|
+
|
318
|
+
* Punctation
|
319
|
+
end."
|
320
|
+
end
|
321
|
+
|
322
|
+
it 'returns relevant errors' do
|
323
|
+
grader = Codegrade::Grader::CommitMessage.new(message)
|
324
|
+
grader.grade
|
325
|
+
|
326
|
+
expect(grader.offenses.count).to eq 2
|
327
|
+
expect(find_offense(grader,
|
328
|
+
:category => 'punctation_trailing_dot',
|
329
|
+
:line_number => 3,
|
330
|
+
:column_number => 13)).not_to be_nil
|
331
|
+
expect(find_offense(grader,
|
332
|
+
:category => 'punctation_trailing_dot',
|
333
|
+
:line_number => 8,
|
334
|
+
:column_number => 6)).not_to be_nil
|
335
|
+
end
|
336
|
+
end
|
337
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Codegrade::Grader::Grader do
|
4
|
+
it 'combines all grades in one array' do
|
5
|
+
allow_any_instance_of(Codegrade::Grader::CommitMessage).to receive(:grade)
|
6
|
+
allow_any_instance_of(Codegrade::Grader::CommitMessage).to receive(:offenses).and_return([Codegrade::Offense.new(category: 'Commit message error')])
|
7
|
+
allow_any_instance_of(Codegrade::Grader::Rubocop).to receive(:grade).and_return([Codegrade::Offense.new(category: 'Ruby syntax error')])
|
8
|
+
commit = Codegrade::Commit.new
|
9
|
+
allow(commit).to receive(:message).and_return('Some commit message')
|
10
|
+
allow(commit).to receive(:files).and_return(['file.rb'])
|
11
|
+
|
12
|
+
offenses = Codegrade::Grader::Grader.new(commit).grade
|
13
|
+
|
14
|
+
expect(offenses.map(&:category)).to include('Commit message error')
|
15
|
+
expect(offenses.map(&:category)).to include('Ruby syntax error')
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'tempfile'
|
3
|
+
|
4
|
+
describe Codegrade::Grader::Rubocop do
|
5
|
+
def make_tempfile(content)
|
6
|
+
file = Tempfile.new('file.rb')
|
7
|
+
file.write(content)
|
8
|
+
file.rewind
|
9
|
+
|
10
|
+
file
|
11
|
+
end
|
12
|
+
|
13
|
+
def close_tempfile(file)
|
14
|
+
file.close
|
15
|
+
file.unlink
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'detects errors in ruby code' do
|
19
|
+
file = make_tempfile(<<-EOD)
|
20
|
+
class Test
|
21
|
+
puts 'Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry standard dummy text ever since the 1500s.'
|
22
|
+
end
|
23
|
+
EOD
|
24
|
+
|
25
|
+
offenses = Codegrade::Grader::Rubocop.new(file).grade
|
26
|
+
offense = offenses.detect { |o| o.category =~ /Line is too long/ }
|
27
|
+
|
28
|
+
expect(offense.category).to eq('Line is too long. [165/80]')
|
29
|
+
expect(offense.line_number).to eq(2)
|
30
|
+
|
31
|
+
close_tempfile(file)
|
32
|
+
end
|
33
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
require_relative '../lib/codegrade'
|
2
|
+
|
3
|
+
RSpec.configure do |config|
|
4
|
+
config.expect_with :rspec do |expectations|
|
5
|
+
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
|
6
|
+
end
|
7
|
+
|
8
|
+
config.mock_with :rspec do |mocks|
|
9
|
+
mocks.verify_partial_doubles = true
|
10
|
+
end
|
11
|
+
end
|
metadata
ADDED
@@ -0,0 +1,141 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: codegrade
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Michał Młoźniak
|
8
|
+
- Karol Słuszniak
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2014-12-05 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: bundler
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - "~>"
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: '1.7'
|
21
|
+
type: :development
|
22
|
+
prerelease: false
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - "~>"
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: '1.7'
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: rake
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - "~>"
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: '10.0'
|
35
|
+
type: :development
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - "~>"
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '10.0'
|
42
|
+
- !ruby/object:Gem::Dependency
|
43
|
+
name: rspec
|
44
|
+
requirement: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - "~>"
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '3.1'
|
49
|
+
type: :development
|
50
|
+
prerelease: false
|
51
|
+
version_requirements: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - "~>"
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '3.1'
|
56
|
+
- !ruby/object:Gem::Dependency
|
57
|
+
name: rubocop
|
58
|
+
requirement: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - "~>"
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '0.27'
|
63
|
+
type: :runtime
|
64
|
+
prerelease: false
|
65
|
+
version_requirements: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - "~>"
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0.27'
|
70
|
+
- !ruby/object:Gem::Dependency
|
71
|
+
name: rugged
|
72
|
+
requirement: !ruby/object:Gem::Requirement
|
73
|
+
requirements:
|
74
|
+
- - "~>"
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '0.21'
|
77
|
+
type: :runtime
|
78
|
+
prerelease: false
|
79
|
+
version_requirements: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - "~>"
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: '0.21'
|
84
|
+
description: This tool grades your commit messages and all changes files
|
85
|
+
email:
|
86
|
+
- contact@visuality.pl
|
87
|
+
executables:
|
88
|
+
- codegrade
|
89
|
+
extensions: []
|
90
|
+
extra_rdoc_files: []
|
91
|
+
files:
|
92
|
+
- ".gitignore"
|
93
|
+
- ".rspec"
|
94
|
+
- Gemfile
|
95
|
+
- Gemfile.lock
|
96
|
+
- LICENSE.txt
|
97
|
+
- README.md
|
98
|
+
- Rakefile
|
99
|
+
- bin/codegrade
|
100
|
+
- codegrade.gemspec
|
101
|
+
- lib/codegrade.rb
|
102
|
+
- lib/codegrade/commit.rb
|
103
|
+
- lib/codegrade/formatter.rb
|
104
|
+
- lib/codegrade/grader/commit_message.rb
|
105
|
+
- lib/codegrade/grader/grader.rb
|
106
|
+
- lib/codegrade/grader/rubocop.rb
|
107
|
+
- lib/codegrade/offense.rb
|
108
|
+
- lib/codegrade/version.rb
|
109
|
+
- spec/models/grader/commit_message_spec.rb
|
110
|
+
- spec/models/grader/grader_spec.rb
|
111
|
+
- spec/models/grader/rubocop_spec.rb
|
112
|
+
- spec/spec_helper.rb
|
113
|
+
homepage: http://www.visuality.pl
|
114
|
+
licenses:
|
115
|
+
- MIT
|
116
|
+
metadata: {}
|
117
|
+
post_install_message:
|
118
|
+
rdoc_options: []
|
119
|
+
require_paths:
|
120
|
+
- lib
|
121
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
122
|
+
requirements:
|
123
|
+
- - ">="
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '0'
|
126
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
127
|
+
requirements:
|
128
|
+
- - ">="
|
129
|
+
- !ruby/object:Gem::Version
|
130
|
+
version: '0'
|
131
|
+
requirements: []
|
132
|
+
rubyforge_project:
|
133
|
+
rubygems_version: 2.2.2
|
134
|
+
signing_key:
|
135
|
+
specification_version: 4
|
136
|
+
summary: Grade your git commits
|
137
|
+
test_files:
|
138
|
+
- spec/models/grader/commit_message_spec.rb
|
139
|
+
- spec/models/grader/grader_spec.rb
|
140
|
+
- spec/models/grader/rubocop_spec.rb
|
141
|
+
- spec/spec_helper.rb
|