codeclimate_diff 0.1.10
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.codeclimate.yml +16 -0
- data/.codeclimate_diff.yml +4 -0
- data/.rspec +3 -0
- data/.rubocop.yml +16 -0
- data/CHANGELOG.md +5 -0
- data/Gemfile +14 -0
- data/Gemfile.lock +95 -0
- data/README.md +190 -0
- data/Rakefile +12 -0
- data/codeclimate_diff.gemspec +37 -0
- data/codeclimate_diff_baseline.json +61 -0
- data/exe/codeclimate_diff +32 -0
- data/lib/codeclimate_diff/codeclimate_wrapper.rb +27 -0
- data/lib/codeclimate_diff/downloader.rb +41 -0
- data/lib/codeclimate_diff/issue_sorter.rb +75 -0
- data/lib/codeclimate_diff/result_printer.rb +94 -0
- data/lib/codeclimate_diff/runner.rb +104 -0
- data/lib/codeclimate_diff/version.rb +5 -0
- data/lib/codeclimate_diff.rb +12 -0
- data/sig/codeclimate_diff.rbs +4 -0
- metadata +135 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: a23228123e37e3d3cec3d4ffaf33e22d56389ae6d620034a93670e91a9e2d6b5
|
4
|
+
data.tar.gz: 888dc1e3758dc9272b3ac4f163361628cff56c435357d23c9ae23c94e5064ab0
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 6e00f89bfe0a1a3e9fce4dc05ad0a58faa0b47afe36a89aa22dfefd7716683e82efda84719eb74f63f25bb97973fff8e6c11bec33fc7d0f36692a593b6042ec6
|
7
|
+
data.tar.gz: f748b2e9af9ac14a80e9949b5688f857b4b2f15adc5396ea2eadaf314180152948be6d35245c3c49dc69152f1a44336ca040484b0a894dc0a779ee0ac804357c
|
data/.codeclimate.yml
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
---
|
2
|
+
version: "2"
|
3
|
+
plugins:
|
4
|
+
rubocop:
|
5
|
+
enabled: true
|
6
|
+
channel: rubocop-1-36-0
|
7
|
+
reek:
|
8
|
+
enabled: true
|
9
|
+
|
10
|
+
checks:
|
11
|
+
method-complexity:
|
12
|
+
config:
|
13
|
+
threshold: 10 # defaults to 5. Cognitive complexity rather than cyclomatic complexity
|
14
|
+
|
15
|
+
exclude_patterns:
|
16
|
+
- "**/spec/"
|
data/.rspec
ADDED
data/.rubocop.yml
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
AllCops:
|
2
|
+
TargetRubyVersion: 2.6
|
3
|
+
|
4
|
+
Style/StringLiterals:
|
5
|
+
Enabled: true
|
6
|
+
EnforcedStyle: double_quotes
|
7
|
+
|
8
|
+
Style/StringLiteralsInInterpolation:
|
9
|
+
Enabled: true
|
10
|
+
EnforcedStyle: double_quotes
|
11
|
+
|
12
|
+
Layout/LineLength:
|
13
|
+
Max: 120
|
14
|
+
|
15
|
+
Style/Documentation:
|
16
|
+
Enabled: false
|
data/CHANGELOG.md
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
source "https://rubygems.org"
|
4
|
+
|
5
|
+
# Specify your gem's dependencies in codeclimate_diff.gemspec
|
6
|
+
gemspec
|
7
|
+
|
8
|
+
gem "colorize"
|
9
|
+
gem "json"
|
10
|
+
gem "pry-byebug"
|
11
|
+
gem "rake", "~> 13.0"
|
12
|
+
gem "rest-client"
|
13
|
+
gem "rspec", "~> 3.0"
|
14
|
+
gem "rubocop", "~> 1.21"
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
codeclimate_diff (0.1.10)
|
5
|
+
colorize
|
6
|
+
json
|
7
|
+
optparse
|
8
|
+
pry-byebug
|
9
|
+
rest-client
|
10
|
+
|
11
|
+
GEM
|
12
|
+
remote: https://rubygems.org/
|
13
|
+
specs:
|
14
|
+
ast (2.4.2)
|
15
|
+
byebug (11.1.3)
|
16
|
+
coderay (1.1.3)
|
17
|
+
colorize (0.8.1)
|
18
|
+
diff-lcs (1.5.0)
|
19
|
+
domain_name (0.5.20190701)
|
20
|
+
unf (>= 0.0.5, < 1.0.0)
|
21
|
+
http-accept (1.7.0)
|
22
|
+
http-cookie (1.0.5)
|
23
|
+
domain_name (~> 0.5)
|
24
|
+
json (2.6.2)
|
25
|
+
method_source (1.0.0)
|
26
|
+
mime-types (3.4.1)
|
27
|
+
mime-types-data (~> 3.2015)
|
28
|
+
mime-types-data (3.2022.0105)
|
29
|
+
netrc (0.11.0)
|
30
|
+
optparse (0.3.1)
|
31
|
+
parallel (1.22.1)
|
32
|
+
parser (3.1.2.1)
|
33
|
+
ast (~> 2.4.1)
|
34
|
+
pry (0.14.1)
|
35
|
+
coderay (~> 1.1)
|
36
|
+
method_source (~> 1.0)
|
37
|
+
pry-byebug (3.10.1)
|
38
|
+
byebug (~> 11.0)
|
39
|
+
pry (>= 0.13, < 0.15)
|
40
|
+
rainbow (3.1.1)
|
41
|
+
rake (13.0.6)
|
42
|
+
regexp_parser (2.6.0)
|
43
|
+
rest-client (2.1.0)
|
44
|
+
http-accept (>= 1.7.0, < 2.0)
|
45
|
+
http-cookie (>= 1.0.2, < 2.0)
|
46
|
+
mime-types (>= 1.16, < 4.0)
|
47
|
+
netrc (~> 0.8)
|
48
|
+
rexml (3.2.5)
|
49
|
+
rspec (3.11.0)
|
50
|
+
rspec-core (~> 3.11.0)
|
51
|
+
rspec-expectations (~> 3.11.0)
|
52
|
+
rspec-mocks (~> 3.11.0)
|
53
|
+
rspec-core (3.11.0)
|
54
|
+
rspec-support (~> 3.11.0)
|
55
|
+
rspec-expectations (3.11.1)
|
56
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
57
|
+
rspec-support (~> 3.11.0)
|
58
|
+
rspec-mocks (3.11.1)
|
59
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
60
|
+
rspec-support (~> 3.11.0)
|
61
|
+
rspec-support (3.11.1)
|
62
|
+
rubocop (1.36.0)
|
63
|
+
json (~> 2.3)
|
64
|
+
parallel (~> 1.10)
|
65
|
+
parser (>= 3.1.2.1)
|
66
|
+
rainbow (>= 2.2.2, < 4.0)
|
67
|
+
regexp_parser (>= 1.8, < 3.0)
|
68
|
+
rexml (>= 3.2.5, < 4.0)
|
69
|
+
rubocop-ast (>= 1.20.1, < 2.0)
|
70
|
+
ruby-progressbar (~> 1.7)
|
71
|
+
unicode-display_width (>= 1.4.0, < 3.0)
|
72
|
+
rubocop-ast (1.22.0)
|
73
|
+
parser (>= 3.1.1.0)
|
74
|
+
ruby-progressbar (1.11.0)
|
75
|
+
unf (0.1.4)
|
76
|
+
unf_ext
|
77
|
+
unf_ext (0.0.8.2)
|
78
|
+
unicode-display_width (2.3.0)
|
79
|
+
|
80
|
+
PLATFORMS
|
81
|
+
ruby
|
82
|
+
x86_64-darwin-19
|
83
|
+
|
84
|
+
DEPENDENCIES
|
85
|
+
codeclimate_diff!
|
86
|
+
colorize
|
87
|
+
json
|
88
|
+
pry-byebug
|
89
|
+
rake (~> 13.0)
|
90
|
+
rest-client
|
91
|
+
rspec (~> 3.0)
|
92
|
+
rubocop (~> 1.21)
|
93
|
+
|
94
|
+
BUNDLED WITH
|
95
|
+
2.3.7
|
data/README.md
ADDED
@@ -0,0 +1,190 @@
|
|
1
|
+
# CodeclimateDiff
|
2
|
+
|
3
|
+
This tool lets you see how changes in your branch will affect the code quality (what issues you've added, fixed, and what issues are outstanding in the files you've touched that could be fixed while you're in the area.)
|
4
|
+
|
5
|
+
It runs the https://hub.docker.com/r/codeclimate/codeclimate docker image under the hood, which pays attention to all the normal Code Climate configurations.
|
6
|
+
|
7
|
+
|
8
|
+
## Initial setup
|
9
|
+
|
10
|
+
1. Make sure docker is installed and running
|
11
|
+
|
12
|
+
2. Add a `.codeclimate.yml` config file eg:
|
13
|
+
```yml
|
14
|
+
---
|
15
|
+
version: "2"
|
16
|
+
plugins:
|
17
|
+
rubocop:
|
18
|
+
enabled: true
|
19
|
+
channel: rubocop-1-36-0
|
20
|
+
reek:
|
21
|
+
enabled: true
|
22
|
+
|
23
|
+
exclude_patterns:
|
24
|
+
- config/
|
25
|
+
- db/
|
26
|
+
- dist/
|
27
|
+
- features/
|
28
|
+
- public/
|
29
|
+
- "**/node_modules/"
|
30
|
+
- script/
|
31
|
+
- "**/spec/"
|
32
|
+
- "**/test/"
|
33
|
+
- "**/tests/"
|
34
|
+
- Tests/
|
35
|
+
- "**/vendor/"
|
36
|
+
- "**/*_test.go"
|
37
|
+
- "**/*.d.ts"
|
38
|
+
- "**/*.min.js"
|
39
|
+
- "**/*.min.css"
|
40
|
+
- "**/__tests__/"
|
41
|
+
- "**/__mocks__/"
|
42
|
+
- "/.gitlab/"
|
43
|
+
- coverage/ . # simple cov
|
44
|
+
```
|
45
|
+
|
46
|
+
3. Add a `.reek.yml` config file eg:
|
47
|
+
|
48
|
+
See https://github.com/troessner/reek#working-with-rails
|
49
|
+
```yml
|
50
|
+
detectors:
|
51
|
+
IrresponsibleModule:
|
52
|
+
enabled: false
|
53
|
+
|
54
|
+
LongParameterList:
|
55
|
+
max_params: 4 # defaults to 3. You want this number realistic but stretchy so we can move it down
|
56
|
+
|
57
|
+
TooManyStatements:
|
58
|
+
max_statements: 10 # defaults to 5. You want this number realistic but stretchy so we can move it down
|
59
|
+
|
60
|
+
directories:
|
61
|
+
"app/controllers":
|
62
|
+
IrresponsibleModule:
|
63
|
+
enabled: false
|
64
|
+
NestedIterators:
|
65
|
+
max_allowed_nesting: 2
|
66
|
+
UnusedPrivateMethod:
|
67
|
+
enabled: false
|
68
|
+
InstanceVariableAssumption:
|
69
|
+
enabled: false
|
70
|
+
"app/helpers":
|
71
|
+
IrresponsibleModule:
|
72
|
+
enabled: false
|
73
|
+
UtilityFunction:
|
74
|
+
enabled: false
|
75
|
+
FeatureEnvy:
|
76
|
+
enabled: false
|
77
|
+
"app/mailers":
|
78
|
+
InstanceVariableAssumption:
|
79
|
+
enabled: false
|
80
|
+
"app/models":
|
81
|
+
InstanceVariableAssumption:
|
82
|
+
enabled: false
|
83
|
+
```
|
84
|
+
|
85
|
+
4. Add a `.codeclimate_diff.yml` configuration file
|
86
|
+
```
|
87
|
+
main_branch_name: master # defaults to main
|
88
|
+
threshold_to_run_on_all_files: 8 # when you reach a certain number of files changed, it becomes faster to analyze all files rather than analyze them one by one.
|
89
|
+
```
|
90
|
+
|
91
|
+
5. Install the gem
|
92
|
+
|
93
|
+
Add this line to your application's Gemfile:
|
94
|
+
|
95
|
+
```ruby
|
96
|
+
gem 'codeclimate_diff', github: 'boost/codeclimate_diff'
|
97
|
+
```
|
98
|
+
|
99
|
+
Install the gem:
|
100
|
+
|
101
|
+
```bash
|
102
|
+
$ bundle install
|
103
|
+
```
|
104
|
+
|
105
|
+
Then generate the executable:
|
106
|
+
|
107
|
+
$ bundle binstubs codeclimate_diff
|
108
|
+
|
109
|
+
|
110
|
+
6. Run the baseline and commit the result to the repo
|
111
|
+
|
112
|
+
```
|
113
|
+
./bin/codeclimate_diff --baseline
|
114
|
+
```
|
115
|
+
|
116
|
+
## Usage
|
117
|
+
|
118
|
+
1. Create a feature branch for your work, and reset the baseline + commit (5 mins)
|
119
|
+
|
120
|
+
2. Do some work
|
121
|
+
|
122
|
+
3. Check if you've added any issues (about 10 secs per code file changed on your branch):
|
123
|
+
|
124
|
+
```bash
|
125
|
+
# runs on each file changed in your branch (about 10 secs per code file changed on your branch)
|
126
|
+
./bin/codeclimate_diff
|
127
|
+
|
128
|
+
OR
|
129
|
+
|
130
|
+
# filters the changed files in your branch futher by a grep pattern
|
131
|
+
./bin/codeclimate_diff --pattern places
|
132
|
+
|
133
|
+
OR
|
134
|
+
|
135
|
+
# only shows the new and fixed issues
|
136
|
+
./bin/codeclimate_diff --new-only
|
137
|
+
|
138
|
+
OR
|
139
|
+
|
140
|
+
# always analyzes all files rather than the changed files one by one, even if below the 'threshold_to_run_on_all_files' setting.
|
141
|
+
# NOTE: similar code issues will only work 100% correctly if you use this setting (otherwise it might miss a similarity with a file you didn't change and think you fixed it)
|
142
|
+
./bin/codeclimate_diff --all
|
143
|
+
```
|
144
|
+
|
145
|
+
4. Now you have time to fix the issues, horray!
|
146
|
+
|
147
|
+
|
148
|
+
## Setting it up to download the latest baseline from your CI Pipeline (Gitlab only)
|
149
|
+
|
150
|
+
Gitlab has a codeclimate template you can add to your pipeline that runs on main builds and then runs on your branch and outputs a difference (see https://docs.gitlab.com/ee/ci/testing/code_quality.html).
|
151
|
+
|
152
|
+
With a few tweaks to your CI configuration, we can pull down the main build baseline from the job so we don't have to do it locally.
|
153
|
+
|
154
|
+
1. In your Gitlab CI Configuration where you include the `Code-Quality.gitlab-ci.yml` template:
|
155
|
+
|
156
|
+
```yml
|
157
|
+
include:
|
158
|
+
- template: Code-Quality.gitlab-ci.yml
|
159
|
+
|
160
|
+
# add this bit:
|
161
|
+
code_quality:
|
162
|
+
artifacts:
|
163
|
+
paths: [gl-code-quality-report.json] . # without this, the artifact can't be downloaded
|
164
|
+
```
|
165
|
+
|
166
|
+
2. Add your project settings to the `.codecimate_diff.yml` configuration file:
|
167
|
+
```yml
|
168
|
+
main_branch_name: main
|
169
|
+
|
170
|
+
# settings to pull down the baseline from the pipeline in Gitlab before checking your branch
|
171
|
+
gitlab:
|
172
|
+
download_baseline_from_pipeline: true # If false or excluded, you will need to generate the baseline manually
|
173
|
+
project_id: '<project id>'
|
174
|
+
host: https://gitlab.digitalnz.org/
|
175
|
+
baseline_filename: 'gl-code-quality-report.json'
|
176
|
+
```
|
177
|
+
|
178
|
+
3. Create a personal access token with `read_api` access and save it in the `CODECLIMATE_DIFF_GITLAB_PERSONAL_ACCESS_TOKEN` env variable
|
179
|
+
|
180
|
+
Now when you run it on the changed files in your branch, it will download the latest baseline first!
|
181
|
+
|
182
|
+
## Development
|
183
|
+
|
184
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
185
|
+
|
186
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
187
|
+
|
188
|
+
## Contributing
|
189
|
+
|
190
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/codeclimate_diff.
|
data/Rakefile
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "lib/codeclimate_diff/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = "codeclimate_diff"
|
7
|
+
spec.version = CodeclimateDiff::VERSION
|
8
|
+
spec.authors = ["Isabel Anastasiadis"]
|
9
|
+
spec.email = ["isabel@boost.co.nz"]
|
10
|
+
|
11
|
+
spec.summary = "A developer command line tool to see how your branch has affected code quality"
|
12
|
+
spec.required_ruby_version = ">= 2.6.0"
|
13
|
+
|
14
|
+
spec.metadata["source_code_uri"] = "https://github.com/boost/codeclimate_diff"
|
15
|
+
spec.metadata["changelog_uri"] = "https://github.com/boost/codeclimate_diff/CHANGELOG.md"
|
16
|
+
|
17
|
+
# Specify which files should be added to the gem when it is released.
|
18
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
19
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
20
|
+
`git ls-files -z`.split("\x0").reject do |f|
|
21
|
+
(f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
|
22
|
+
end
|
23
|
+
end
|
24
|
+
spec.bindir = "exe"
|
25
|
+
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
26
|
+
spec.require_paths = ["lib"]
|
27
|
+
|
28
|
+
# Uncomment to register a new dependency of your gem
|
29
|
+
spec.add_dependency("colorize")
|
30
|
+
spec.add_dependency("json")
|
31
|
+
spec.add_dependency("optparse")
|
32
|
+
spec.add_dependency("pry-byebug")
|
33
|
+
spec.add_dependency("rest-client")
|
34
|
+
|
35
|
+
# For more information and examples about making a new gem, check out our
|
36
|
+
# guide at: https://bundler.io/guides/creating_gem.html
|
37
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
[{"engine_name":"structure","fingerprint":"f8ccebfc07d1a0c9fa77b6325a61b82c","categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `refresh_baseline_if_configured` has a Cognitive Complexity of 13 (exceeds 10 allowed). Consider refactoring.","location":{"path":"lib/codeclimate_diff/downloader.rb","lines":{"begin":7,"end":39}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue"},
|
2
|
+
{"engine_name":"structure","fingerprint":"4890c3e24aa5f22bc928ccddd6a60a14","categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `sort_issues` has 26 lines of code (exceeds 25 allowed). Consider refactoring.","location":{"path":"lib/codeclimate_diff/issue_sorter.rb","lines":{"begin":33,"end":73}},"other_locations":[],"remediation_points":624000,"severity":"minor","type":"issue"},
|
3
|
+
{"engine_name":"structure","fingerprint":"5989a7d02618f9eba49266f1c5473861","categories":["Complexity"],"check_name":"argument_count","content":{"body":""},"description":"Method `print_category` has 5 arguments (exceeds 4 allowed). Consider refactoring.","location":{"path":"lib/codeclimate_diff/result_printer.rb","lines":{"begin":20,"end":20}},"other_locations":[],"remediation_points":375000,"severity":"minor","type":"issue"},
|
4
|
+
{"engine_name":"structure","fingerprint":"79ec8f3ef3737357ed2c43ab0a065ef3","categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `calculate_issues_in_changed_files` has a Cognitive Complexity of 20 (exceeds 10 allowed). Consider refactoring.","location":{"path":"lib/codeclimate_diff/runner.rb","lines":{"begin":39,"end":69}},"other_locations":[],"remediation_points":1150000,"severity":"minor","type":"issue"},
|
5
|
+
{"name":"ruby.parse.succeeded","type":"measurement","value":7,"engine_name":"structure"},
|
6
|
+
{"engine_name":"reek","fingerprint":"054589dd54a8aef188941b688d099978","type":"issue","check_name":"IrresponsibleModule","description":"CodeclimateDiff::CodeclimateWrapper has no descriptive comment","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/codeclimate_wrapper.rb","lines":{"begin":7,"end":7}},"remediation_points":350000,"content":{"body":"Classes and modules are the units of reuse and release. It is therefore considered good practice to annotate every class and module with a brief comment outlining its responsibilities.\n\n## Example\n\nGiven\n\n```Ruby\nclass Dummy\n # Do things...\nend\n```\n\nReek would emit the following warning:\n\n```\ntest.rb -- 1 warning:\n [1]:Dummy has no descriptive comment (IrresponsibleModule)\n```\n\nFixing this is simple - just an explaining comment:\n\n```Ruby\n# The Dummy class is responsible for ...\nclass Dummy\n # Do things...\nend\n```\n"},"severity":"minor"},
|
7
|
+
{"engine_name":"reek","fingerprint":"0ab3c6e125bef6686b3c821c59933e08","type":"issue","check_name":"DuplicateMethodCall","description":"CodeclimateDiff::Downloader#self.refresh_baseline_if_configured calls 'CodeclimateDiff.configuration' 6 times","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/downloader.rb","lines":{"begin":8,"end":22}},"remediation_points":350000,"content":{"body":"Duplication occurs when two fragments of code look nearly identical, or when two fragments of code have nearly identical effects at some conceptual level.\n\nReek implements a check for _Duplicate Method Call_.\n\n## Example\n\nHere's a very much simplified and contrived example. The following method will report a warning:\n\n```Ruby\ndef double_thing()\n @other.thing + @other.thing\nend\n```\n\nOne quick approach to silence Reek would be to refactor the code thus:\n\n```Ruby\ndef double_thing()\n thing = @other.thing\n thing + thing\nend\n```\n\nA slightly different approach would be to replace all calls of `double_thing` by calls to `@other.double_thing`:\n\n```Ruby\nclass Other\n def double_thing()\n thing + thing\n end\nend\n```\n\nThe approach you take will depend on balancing other factors in your code.\n"},"severity":"minor"},
|
8
|
+
{"engine_name":"reek","fingerprint":"585621642e3917aabb584a8dee9dc5b9","type":"issue","check_name":"DuplicateMethodCall","description":"CodeclimateDiff::Downloader#self.refresh_baseline_if_configured calls 'CodeclimateDiff.configuration[\"gitlab\"]' 5 times","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/downloader.rb","lines":{"begin":8,"end":22}},"remediation_points":350000,"content":{"body":"Duplication occurs when two fragments of code look nearly identical, or when two fragments of code have nearly identical effects at some conceptual level.\n\nReek implements a check for _Duplicate Method Call_.\n\n## Example\n\nHere's a very much simplified and contrived example. The following method will report a warning:\n\n```Ruby\ndef double_thing()\n @other.thing + @other.thing\nend\n```\n\nOne quick approach to silence Reek would be to refactor the code thus:\n\n```Ruby\ndef double_thing()\n thing = @other.thing\n thing + thing\nend\n```\n\nA slightly different approach would be to replace all calls of `double_thing` by calls to `@other.double_thing`:\n\n```Ruby\nclass Other\n def double_thing()\n thing + thing\n end\nend\n```\n\nThe approach you take will depend on balancing other factors in your code.\n"},"severity":"minor"},
|
9
|
+
{"engine_name":"reek","fingerprint":"a90c8153eeb16896f80233d15786037a","type":"issue","check_name":"DuplicateMethodCall","description":"CodeclimateDiff::Downloader#self.refresh_baseline_if_configured calls 'puts \"Using current baseline.\"' 2 times","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/downloader.rb","lines":{"begin":33,"end":38}},"remediation_points":350000,"content":{"body":"Duplication occurs when two fragments of code look nearly identical, or when two fragments of code have nearly identical effects at some conceptual level.\n\nReek implements a check for _Duplicate Method Call_.\n\n## Example\n\nHere's a very much simplified and contrived example. The following method will report a warning:\n\n```Ruby\ndef double_thing()\n @other.thing + @other.thing\nend\n```\n\nOne quick approach to silence Reek would be to refactor the code thus:\n\n```Ruby\ndef double_thing()\n thing = @other.thing\n thing + thing\nend\n```\n\nA slightly different approach would be to replace all calls of `double_thing` by calls to `@other.double_thing`:\n\n```Ruby\nclass Other\n def double_thing()\n thing + thing\n end\nend\n```\n\nThe approach you take will depend on balancing other factors in your code.\n"},"severity":"minor"},
|
10
|
+
{"engine_name":"reek","fingerprint":"4dade4b82ae3a3c900dd5cbd4baa48c0","type":"issue","check_name":"DuplicateMethodCall","description":"CodeclimateDiff::Downloader#self.refresh_baseline_if_configured calls 'response.body' 2 times","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/downloader.rb","lines":{"begin":29,"end":32}},"remediation_points":350000,"content":{"body":"Duplication occurs when two fragments of code look nearly identical, or when two fragments of code have nearly identical effects at some conceptual level.\n\nReek implements a check for _Duplicate Method Call_.\n\n## Example\n\nHere's a very much simplified and contrived example. The following method will report a warning:\n\n```Ruby\ndef double_thing()\n @other.thing + @other.thing\nend\n```\n\nOne quick approach to silence Reek would be to refactor the code thus:\n\n```Ruby\ndef double_thing()\n thing = @other.thing\n thing + thing\nend\n```\n\nA slightly different approach would be to replace all calls of `double_thing` by calls to `@other.double_thing`:\n\n```Ruby\nclass Other\n def double_thing()\n thing + thing\n end\nend\n```\n\nThe approach you take will depend on balancing other factors in your code.\n"},"severity":"minor"},
|
11
|
+
{"engine_name":"reek","fingerprint":"eb7c72ed624bdafbdbfee6ceed64db54","type":"issue","check_name":"DuplicateMethodCall","description":"CodeclimateDiff::Downloader#self.refresh_baseline_if_configured calls 'response.code' 2 times","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/downloader.rb","lines":{"begin":28,"end":32}},"remediation_points":350000,"content":{"body":"Duplication occurs when two fragments of code look nearly identical, or when two fragments of code have nearly identical effects at some conceptual level.\n\nReek implements a check for _Duplicate Method Call_.\n\n## Example\n\nHere's a very much simplified and contrived example. The following method will report a warning:\n\n```Ruby\ndef double_thing()\n @other.thing + @other.thing\nend\n```\n\nOne quick approach to silence Reek would be to refactor the code thus:\n\n```Ruby\ndef double_thing()\n thing = @other.thing\n thing + thing\nend\n```\n\nA slightly different approach would be to replace all calls of `double_thing` by calls to `@other.double_thing`:\n\n```Ruby\nclass Other\n def double_thing()\n thing + thing\n end\nend\n```\n\nThe approach you take will depend on balancing other factors in your code.\n"},"severity":"minor"},
|
12
|
+
{"engine_name":"reek","fingerprint":"f54441e8533c9bf76e64b72596931f02","type":"issue","check_name":"IrresponsibleModule","description":"CodeclimateDiff::Downloader has no descriptive comment","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/downloader.rb","lines":{"begin":6,"end":6}},"remediation_points":350000,"content":{"body":"Classes and modules are the units of reuse and release. It is therefore considered good practice to annotate every class and module with a brief comment outlining its responsibilities.\n\n## Example\n\nGiven\n\n```Ruby\nclass Dummy\n # Do things...\nend\n```\n\nReek would emit the following warning:\n\n```\ntest.rb -- 1 warning:\n [1]:Dummy has no descriptive comment (IrresponsibleModule)\n```\n\nFixing this is simple - just an explaining comment:\n\n```Ruby\n# The Dummy class is responsible for ...\nclass Dummy\n # Do things...\nend\n```\n"},"severity":"minor"},
|
13
|
+
{"engine_name":"reek","fingerprint":"d2ddd39e281760ccf0b0918fe08e7e7c","type":"issue","check_name":"TooManyStatements","description":"CodeclimateDiff::Downloader#self.refresh_baseline_if_configured has approx 19 statements","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/downloader.rb","lines":{"begin":7,"end":7}},"remediation_points":500000,"content":{"body":"A method with `Too Many Statements` is any method that has a large number of lines.\n\n`Too Many Statements` warns about any method that has more than 5 statements. Reek's smell detector for `Too Many Statements` counts +1 for every simple statement in a method and +1 for every statement within a control structure (`if`, `else`, `case`, `when`, `for`, `while`, `until`, `begin`, `rescue`) but it doesn't count the control structure itself.\n\nSo the following method would score +6 in Reek's statement-counting algorithm:\n\n```Ruby\ndef parse(arg, argv, \u0026error)\n if !(val = arg) and (argv.empty? or /\\A-/ =~ (val = argv[0]))\n return nil, block, nil # +1\n end\n opt = (val = parse_arg(val, \u0026error))[1] # +2\n val = conv_arg(*val) # +3\n if opt and !arg\n argv.shift # +4\n else\n val[0] = nil # +5\n end\n val # +6\nend\n```\n\n(You might argue that the two assigments within the first @if@ should count as statements, and that perhaps the nested assignment should count as +2.)\n"},"severity":"minor"},
|
14
|
+
{"engine_name":"reek","fingerprint":"211287e4b5857d557ac53a56826d6fa0","type":"issue","check_name":"UncommunicativeVariableName","description":"CodeclimateDiff::Downloader#self.refresh_baseline_if_configured has the variable name 'e'","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/downloader.rb","lines":{"begin":36,"end":36}},"remediation_points":150000,"content":{"body":"An `Uncommunicative Variable Name` is a variable name that doesn't communicate its intent well enough.\n\nPoor names make it hard for the reader to build a mental picture of what's going on in the code. They can also be mis-interpreted; and they hurt the flow of reading, because the reader must slow down to interpret the names.\n"},"severity":"minor"},
|
15
|
+
{"engine_name":"reek","fingerprint":"6387eea8958bb1f9b03eb1a4d1a7d3d7","type":"issue","check_name":"DuplicateMethodCall","description":"CodeclimateDiff::IssueSorter#self.remove_closest_match_from_list calls 'issue[\"description\"] == issue_to_match[\"description\"]' 2 times","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/issue_sorter.rb","lines":{"begin":10,"end":21}},"remediation_points":350000,"content":{"body":"Duplication occurs when two fragments of code look nearly identical, or when two fragments of code have nearly identical effects at some conceptual level.\n\nReek implements a check for _Duplicate Method Call_.\n\n## Example\n\nHere's a very much simplified and contrived example. The following method will report a warning:\n\n```Ruby\ndef double_thing()\n @other.thing + @other.thing\nend\n```\n\nOne quick approach to silence Reek would be to refactor the code thus:\n\n```Ruby\ndef double_thing()\n thing = @other.thing\n thing + thing\nend\n```\n\nA slightly different approach would be to replace all calls of `double_thing` by calls to `@other.double_thing`:\n\n```Ruby\nclass Other\n def double_thing()\n thing + thing\n end\nend\n```\n\nThe approach you take will depend on balancing other factors in your code.\n"},"severity":"minor"},
|
16
|
+
{"engine_name":"reek","fingerprint":"26e97835cc4b485f5758c3fcf6c9aaea","type":"issue","check_name":"DuplicateMethodCall","description":"CodeclimateDiff::IssueSorter#self.remove_closest_match_from_list calls 'issue[\"description\"]' 2 times","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/issue_sorter.rb","lines":{"begin":10,"end":21}},"remediation_points":350000,"content":{"body":"Duplication occurs when two fragments of code look nearly identical, or when two fragments of code have nearly identical effects at some conceptual level.\n\nReek implements a check for _Duplicate Method Call_.\n\n## Example\n\nHere's a very much simplified and contrived example. The following method will report a warning:\n\n```Ruby\ndef double_thing()\n @other.thing + @other.thing\nend\n```\n\nOne quick approach to silence Reek would be to refactor the code thus:\n\n```Ruby\ndef double_thing()\n thing = @other.thing\n thing + thing\nend\n```\n\nA slightly different approach would be to replace all calls of `double_thing` by calls to `@other.double_thing`:\n\n```Ruby\nclass Other\n def double_thing()\n thing + thing\n end\nend\n```\n\nThe approach you take will depend on balancing other factors in your code.\n"},"severity":"minor"},
|
17
|
+
{"engine_name":"reek","fingerprint":"22d5da8bce52124b5a9c808004f82019","type":"issue","check_name":"DuplicateMethodCall","description":"CodeclimateDiff::IssueSorter#self.remove_closest_match_from_list calls 'issue[\"fingerprint\"] == issue_to_match[\"fingerprint\"]' 2 times","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/issue_sorter.rb","lines":{"begin":8,"end":20}},"remediation_points":350000,"content":{"body":"Duplication occurs when two fragments of code look nearly identical, or when two fragments of code have nearly identical effects at some conceptual level.\n\nReek implements a check for _Duplicate Method Call_.\n\n## Example\n\nHere's a very much simplified and contrived example. The following method will report a warning:\n\n```Ruby\ndef double_thing()\n @other.thing + @other.thing\nend\n```\n\nOne quick approach to silence Reek would be to refactor the code thus:\n\n```Ruby\ndef double_thing()\n thing = @other.thing\n thing + thing\nend\n```\n\nA slightly different approach would be to replace all calls of `double_thing` by calls to `@other.double_thing`:\n\n```Ruby\nclass Other\n def double_thing()\n thing + thing\n end\nend\n```\n\nThe approach you take will depend on balancing other factors in your code.\n"},"severity":"minor"},
|
18
|
+
{"engine_name":"reek","fingerprint":"cc510276d86e62d3aaf8388d5bfa2b19","type":"issue","check_name":"DuplicateMethodCall","description":"CodeclimateDiff::IssueSorter#self.remove_closest_match_from_list calls 'issue[\"fingerprint\"]' 2 times","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/issue_sorter.rb","lines":{"begin":8,"end":20}},"remediation_points":350000,"content":{"body":"Duplication occurs when two fragments of code look nearly identical, or when two fragments of code have nearly identical effects at some conceptual level.\n\nReek implements a check for _Duplicate Method Call_.\n\n## Example\n\nHere's a very much simplified and contrived example. The following method will report a warning:\n\n```Ruby\ndef double_thing()\n @other.thing + @other.thing\nend\n```\n\nOne quick approach to silence Reek would be to refactor the code thus:\n\n```Ruby\ndef double_thing()\n thing = @other.thing\n thing + thing\nend\n```\n\nA slightly different approach would be to replace all calls of `double_thing` by calls to `@other.double_thing`:\n\n```Ruby\nclass Other\n def double_thing()\n thing + thing\n end\nend\n```\n\nThe approach you take will depend on balancing other factors in your code.\n"},"severity":"minor"},
|
19
|
+
{"engine_name":"reek","fingerprint":"0b23ee28cb8bdc33b6f2f06a482b5267","type":"issue","check_name":"DuplicateMethodCall","description":"CodeclimateDiff::IssueSorter#self.remove_closest_match_from_list calls 'issue_to_match[\"description\"]' 2 times","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/issue_sorter.rb","lines":{"begin":10,"end":21}},"remediation_points":350000,"content":{"body":"Duplication occurs when two fragments of code look nearly identical, or when two fragments of code have nearly identical effects at some conceptual level.\n\nReek implements a check for _Duplicate Method Call_.\n\n## Example\n\nHere's a very much simplified and contrived example. The following method will report a warning:\n\n```Ruby\ndef double_thing()\n @other.thing + @other.thing\nend\n```\n\nOne quick approach to silence Reek would be to refactor the code thus:\n\n```Ruby\ndef double_thing()\n thing = @other.thing\n thing + thing\nend\n```\n\nA slightly different approach would be to replace all calls of `double_thing` by calls to `@other.double_thing`:\n\n```Ruby\nclass Other\n def double_thing()\n thing + thing\n end\nend\n```\n\nThe approach you take will depend on balancing other factors in your code.\n"},"severity":"minor"},
|
20
|
+
{"engine_name":"reek","fingerprint":"30a514924733303a35f7ae8c8de71a95","type":"issue","check_name":"DuplicateMethodCall","description":"CodeclimateDiff::IssueSorter#self.remove_closest_match_from_list calls 'issue_to_match[\"fingerprint\"]' 2 times","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/issue_sorter.rb","lines":{"begin":8,"end":20}},"remediation_points":350000,"content":{"body":"Duplication occurs when two fragments of code look nearly identical, or when two fragments of code have nearly identical effects at some conceptual level.\n\nReek implements a check for _Duplicate Method Call_.\n\n## Example\n\nHere's a very much simplified and contrived example. The following method will report a warning:\n\n```Ruby\ndef double_thing()\n @other.thing + @other.thing\nend\n```\n\nOne quick approach to silence Reek would be to refactor the code thus:\n\n```Ruby\ndef double_thing()\n thing = @other.thing\n thing + thing\nend\n```\n\nA slightly different approach would be to replace all calls of `double_thing` by calls to `@other.double_thing`:\n\n```Ruby\nclass Other\n def double_thing()\n thing + thing\n end\nend\n```\n\nThe approach you take will depend on balancing other factors in your code.\n"},"severity":"minor"},
|
21
|
+
{"engine_name":"reek","fingerprint":"e1a894e2e680a8948d6a5bee61911741","type":"issue","check_name":"DuplicateMethodCall","description":"CodeclimateDiff::IssueSorter#self.remove_closest_match_from_list calls 'list.delete_at(index)' 2 times","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/issue_sorter.rb","lines":{"begin":14,"end":25}},"remediation_points":350000,"content":{"body":"Duplication occurs when two fragments of code look nearly identical, or when two fragments of code have nearly identical effects at some conceptual level.\n\nReek implements a check for _Duplicate Method Call_.\n\n## Example\n\nHere's a very much simplified and contrived example. The following method will report a warning:\n\n```Ruby\ndef double_thing()\n @other.thing + @other.thing\nend\n```\n\nOne quick approach to silence Reek would be to refactor the code thus:\n\n```Ruby\ndef double_thing()\n thing = @other.thing\n thing + thing\nend\n```\n\nA slightly different approach would be to replace all calls of `double_thing` by calls to `@other.double_thing`:\n\n```Ruby\nclass Other\n def double_thing()\n thing + thing\n end\nend\n```\n\nThe approach you take will depend on balancing other factors in your code.\n"},"severity":"minor"},
|
22
|
+
{"engine_name":"reek","fingerprint":"2fefd889dbaf81b11b2bea400c8d6deb","type":"issue","check_name":"DuplicateMethodCall","description":"CodeclimateDiff::IssueSorter#self.remove_closest_match_from_list calls 'list.index' 2 times","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/issue_sorter.rb","lines":{"begin":7,"end":19}},"remediation_points":350000,"content":{"body":"Duplication occurs when two fragments of code look nearly identical, or when two fragments of code have nearly identical effects at some conceptual level.\n\nReek implements a check for _Duplicate Method Call_.\n\n## Example\n\nHere's a very much simplified and contrived example. The following method will report a warning:\n\n```Ruby\ndef double_thing()\n @other.thing + @other.thing\nend\n```\n\nOne quick approach to silence Reek would be to refactor the code thus:\n\n```Ruby\ndef double_thing()\n thing = @other.thing\n thing + thing\nend\n```\n\nA slightly different approach would be to replace all calls of `double_thing` by calls to `@other.double_thing`:\n\n```Ruby\nclass Other\n def double_thing()\n thing + thing\n end\nend\n```\n\nThe approach you take will depend on balancing other factors in your code.\n"},"severity":"minor"},
|
23
|
+
{"engine_name":"reek","fingerprint":"ca6ea3faac869baf424f72669048c899","type":"issue","check_name":"DuplicateMethodCall","description":"CodeclimateDiff::IssueSorter#self.sort_issues calls 'baseline_issues.count' 2 times","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/issue_sorter.rb","lines":{"begin":50,"end":53}},"remediation_points":350000,"content":{"body":"Duplication occurs when two fragments of code look nearly identical, or when two fragments of code have nearly identical effects at some conceptual level.\n\nReek implements a check for _Duplicate Method Call_.\n\n## Example\n\nHere's a very much simplified and contrived example. The following method will report a warning:\n\n```Ruby\ndef double_thing()\n @other.thing + @other.thing\nend\n```\n\nOne quick approach to silence Reek would be to refactor the code thus:\n\n```Ruby\ndef double_thing()\n thing = @other.thing\n thing + thing\nend\n```\n\nA slightly different approach would be to replace all calls of `double_thing` by calls to `@other.double_thing`:\n\n```Ruby\nclass Other\n def double_thing()\n thing + thing\n end\nend\n```\n\nThe approach you take will depend on balancing other factors in your code.\n"},"severity":"minor"},
|
24
|
+
{"engine_name":"reek","fingerprint":"fe0d4f54334ad664a42b1018ae44a8ca","type":"issue","check_name":"DuplicateMethodCall","description":"CodeclimateDiff::IssueSorter#self.sort_issues calls 'current_issues.count' 2 times","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/issue_sorter.rb","lines":{"begin":50,"end":53}},"remediation_points":350000,"content":{"body":"Duplication occurs when two fragments of code look nearly identical, or when two fragments of code have nearly identical effects at some conceptual level.\n\nReek implements a check for _Duplicate Method Call_.\n\n## Example\n\nHere's a very much simplified and contrived example. The following method will report a warning:\n\n```Ruby\ndef double_thing()\n @other.thing + @other.thing\nend\n```\n\nOne quick approach to silence Reek would be to refactor the code thus:\n\n```Ruby\ndef double_thing()\n thing = @other.thing\n thing + thing\nend\n```\n\nA slightly different approach would be to replace all calls of `double_thing` by calls to `@other.double_thing`:\n\n```Ruby\nclass Other\n def double_thing()\n thing + thing\n end\nend\n```\n\nThe approach you take will depend on balancing other factors in your code.\n"},"severity":"minor"},
|
25
|
+
{"engine_name":"reek","fingerprint":"26083f936211b5167cc5f6809b9c9ade","type":"issue","check_name":"DuplicateMethodCall","description":"CodeclimateDiff::IssueSorter#self.sort_issues calls 'issue[\"fingerprint\"] == fingerprint' 2 times","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/issue_sorter.rb","lines":{"begin":47,"end":48}},"remediation_points":350000,"content":{"body":"Duplication occurs when two fragments of code look nearly identical, or when two fragments of code have nearly identical effects at some conceptual level.\n\nReek implements a check for _Duplicate Method Call_.\n\n## Example\n\nHere's a very much simplified and contrived example. The following method will report a warning:\n\n```Ruby\ndef double_thing()\n @other.thing + @other.thing\nend\n```\n\nOne quick approach to silence Reek would be to refactor the code thus:\n\n```Ruby\ndef double_thing()\n thing = @other.thing\n thing + thing\nend\n```\n\nA slightly different approach would be to replace all calls of `double_thing` by calls to `@other.double_thing`:\n\n```Ruby\nclass Other\n def double_thing()\n thing + thing\n end\nend\n```\n\nThe approach you take will depend on balancing other factors in your code.\n"},"severity":"minor"},
|
26
|
+
{"engine_name":"reek","fingerprint":"e879665bca8ca2a6bc3f789248ff8b39","type":"issue","check_name":"DuplicateMethodCall","description":"CodeclimateDiff::IssueSorter#self.sort_issues calls 'issue[\"fingerprint\"]' 3 times","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/issue_sorter.rb","lines":{"begin":44,"end":48}},"remediation_points":350000,"content":{"body":"Duplication occurs when two fragments of code look nearly identical, or when two fragments of code have nearly identical effects at some conceptual level.\n\nReek implements a check for _Duplicate Method Call_.\n\n## Example\n\nHere's a very much simplified and contrived example. The following method will report a warning:\n\n```Ruby\ndef double_thing()\n @other.thing + @other.thing\nend\n```\n\nOne quick approach to silence Reek would be to refactor the code thus:\n\n```Ruby\ndef double_thing()\n thing = @other.thing\n thing + thing\nend\n```\n\nA slightly different approach would be to replace all calls of `double_thing` by calls to `@other.double_thing`:\n\n```Ruby\nclass Other\n def double_thing()\n thing + thing\n end\nend\n```\n\nThe approach you take will depend on balancing other factors in your code.\n"},"severity":"minor"},
|
27
|
+
{"engine_name":"reek","fingerprint":"d90684590df5d9e0fe73c8b42d49fb87","type":"issue","check_name":"IrresponsibleModule","description":"CodeclimateDiff::IssueSorter has no descriptive comment","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/issue_sorter.rb","lines":{"begin":4,"end":4}},"remediation_points":350000,"content":{"body":"Classes and modules are the units of reuse and release. It is therefore considered good practice to annotate every class and module with a brief comment outlining its responsibilities.\n\n## Example\n\nGiven\n\n```Ruby\nclass Dummy\n # Do things...\nend\n```\n\nReek would emit the following warning:\n\n```\ntest.rb -- 1 warning:\n [1]:Dummy has no descriptive comment (IrresponsibleModule)\n```\n\nFixing this is simple - just an explaining comment:\n\n```Ruby\n# The Dummy class is responsible for ...\nclass Dummy\n # Do things...\nend\n```\n"},"severity":"minor"},
|
28
|
+
{"engine_name":"reek","fingerprint":"980375b6536d455e110ca4c16fb8385a","type":"issue","check_name":"NestedIterators","description":"CodeclimateDiff::IssueSorter#self.sort_issues contains iterators nested 2 deep","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/issue_sorter.rb","lines":{"begin":47,"end":61}},"remediation_points":500000,"content":{"body":"A `Nested Iterator` occurs when a block contains another block.\n\n## Example\n\nGiven\n\n```Ruby\nclass Duck\n class \u003c\u003c self\n def duck_names\n %i!tick trick track!.each do |surname|\n %i!duck!.each do |last_name|\n puts \"full name is #{surname} #{last_name}\"\n end\n end\n end\n end\nend\n```\n\nReek would report the following warning:\n\n```\ntest.rb -- 1 warning:\n [5]:Duck#duck_names contains iterators nested 2 deep (NestedIterators)\n```\n"},"severity":"minor"},
|
29
|
+
{"engine_name":"reek","fingerprint":"c43a4fd2dc6f9f2900cf0b0a0702294e","type":"issue","check_name":"TooManyStatements","description":"CodeclimateDiff::IssueSorter#self.remove_closest_match_from_list has approx 9 statements","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/issue_sorter.rb","lines":{"begin":5,"end":5}},"remediation_points":500000,"content":{"body":"A method with `Too Many Statements` is any method that has a large number of lines.\n\n`Too Many Statements` warns about any method that has more than 5 statements. Reek's smell detector for `Too Many Statements` counts +1 for every simple statement in a method and +1 for every statement within a control structure (`if`, `else`, `case`, `when`, `for`, `while`, `until`, `begin`, `rescue`) but it doesn't count the control structure itself.\n\nSo the following method would score +6 in Reek's statement-counting algorithm:\n\n```Ruby\ndef parse(arg, argv, \u0026error)\n if !(val = arg) and (argv.empty? or /\\A-/ =~ (val = argv[0]))\n return nil, block, nil # +1\n end\n opt = (val = parse_arg(val, \u0026error))[1] # +2\n val = conv_arg(*val) # +3\n if opt and !arg\n argv.shift # +4\n else\n val[0] = nil # +5\n end\n val # +6\nend\n```\n\n(You might argue that the two assigments within the first @if@ should count as statements, and that perhaps the nested assignment should count as +2.)\n"},"severity":"minor"},
|
30
|
+
{"engine_name":"reek","fingerprint":"c8711ff1c512044d0e938787698e5f45","type":"issue","check_name":"TooManyStatements","description":"CodeclimateDiff::IssueSorter#self.sort_issues has approx 22 statements","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/issue_sorter.rb","lines":{"begin":33,"end":33}},"remediation_points":500000,"content":{"body":"A method with `Too Many Statements` is any method that has a large number of lines.\n\n`Too Many Statements` warns about any method that has more than 5 statements. Reek's smell detector for `Too Many Statements` counts +1 for every simple statement in a method and +1 for every statement within a control structure (`if`, `else`, `case`, `when`, `for`, `while`, `until`, `begin`, `rescue`) but it doesn't count the control structure itself.\n\nSo the following method would score +6 in Reek's statement-counting algorithm:\n\n```Ruby\ndef parse(arg, argv, \u0026error)\n if !(val = arg) and (argv.empty? or /\\A-/ =~ (val = argv[0]))\n return nil, block, nil # +1\n end\n opt = (val = parse_arg(val, \u0026error))[1] # +2\n val = conv_arg(*val) # +3\n if opt and !arg\n argv.shift # +4\n else\n val[0] = nil # +5\n end\n val # +6\nend\n```\n\n(You might argue that the two assigments within the first @if@ should count as statements, and that perhaps the nested assignment should count as +2.)\n"},"severity":"minor"},
|
31
|
+
{"engine_name":"reek","fingerprint":"7b8cb5858d38666042143f4f0ad0e173","type":"issue","check_name":"ControlParameter","description":"CodeclimateDiff::ResultPrinter#self.print_category is controlled by argument 'color'","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/result_printer.rb","lines":{"begin":23,"end":23}},"remediation_points":500000,"content":{"body":"`Control Parameter` is a special case of `Control Couple`\n\n## Example\n\nA simple example would be the \"quoted\" parameter in the following method:\n\n```Ruby\ndef write(quoted)\n if quoted\n write_quoted @value\n else\n write_unquoted @value\n end\nend\n```\n\nFixing those problems is out of the scope of this document but an easy solution could be to remove the \"write\" method alltogether and to move the calls to \"write_quoted\" / \"write_unquoted\" in the initial caller of \"write\".\n"},"severity":"minor"},
|
32
|
+
{"engine_name":"reek","fingerprint":"83771ef70121c9192872a09a66ee7b0c","type":"issue","check_name":"ControlParameter","description":"CodeclimateDiff::ResultPrinter#self.print_result is controlled by argument 'show_preexisting'","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/result_printer.rb","lines":{"begin":52,"end":52}},"remediation_points":500000,"content":{"body":"`Control Parameter` is a special case of `Control Couple`\n\n## Example\n\nA simple example would be the \"quoted\" parameter in the following method:\n\n```Ruby\ndef write(quoted)\n if quoted\n write_quoted @value\n else\n write_unquoted @value\n end\nend\n```\n\nFixing those problems is out of the scope of this document but an easy solution could be to remove the \"write\" method alltogether and to move the calls to \"write_quoted\" / \"write_unquoted\" in the initial caller of \"write\".\n"},"severity":"minor"},
|
33
|
+
{"engine_name":"reek","fingerprint":"f188df8774fe06f10f92ea3ced203a90","type":"issue","check_name":"DuplicateMethodCall","description":"CodeclimateDiff::ResultPrinter#self.print_issues calls 'issue[\"check_name\"]' 2 times","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/result_printer.rb","lines":{"begin":36,"end":43}},"remediation_points":350000,"content":{"body":"Duplication occurs when two fragments of code look nearly identical, or when two fragments of code have nearly identical effects at some conceptual level.\n\nReek implements a check for _Duplicate Method Call_.\n\n## Example\n\nHere's a very much simplified and contrived example. The following method will report a warning:\n\n```Ruby\ndef double_thing()\n @other.thing + @other.thing\nend\n```\n\nOne quick approach to silence Reek would be to refactor the code thus:\n\n```Ruby\ndef double_thing()\n thing = @other.thing\n thing + thing\nend\n```\n\nA slightly different approach would be to replace all calls of `double_thing` by calls to `@other.double_thing`:\n\n```Ruby\nclass Other\n def double_thing()\n thing + thing\n end\nend\n```\n\nThe approach you take will depend on balancing other factors in your code.\n"},"severity":"minor"},
|
34
|
+
{"engine_name":"reek","fingerprint":"da9c16763266a2ee7c4acd27b473f431","type":"issue","check_name":"DuplicateMethodCall","description":"CodeclimateDiff::ResultPrinter#self.print_issues calls 'issue[\"engine_name\"]' 2 times","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/result_printer.rb","lines":{"begin":36,"end":42}},"remediation_points":350000,"content":{"body":"Duplication occurs when two fragments of code look nearly identical, or when two fragments of code have nearly identical effects at some conceptual level.\n\nReek implements a check for _Duplicate Method Call_.\n\n## Example\n\nHere's a very much simplified and contrived example. The following method will report a warning:\n\n```Ruby\ndef double_thing()\n @other.thing + @other.thing\nend\n```\n\nOne quick approach to silence Reek would be to refactor the code thus:\n\n```Ruby\ndef double_thing()\n thing = @other.thing\n thing + thing\nend\n```\n\nA slightly different approach would be to replace all calls of `double_thing` by calls to `@other.double_thing`:\n\n```Ruby\nclass Other\n def double_thing()\n thing + thing\n end\nend\n```\n\nThe approach you take will depend on balancing other factors in your code.\n"},"severity":"minor"},
|
35
|
+
{"engine_name":"reek","fingerprint":"e9462c1d7525075446ba64f4bf4ddc2b","type":"issue","check_name":"DuplicateMethodCall","description":"CodeclimateDiff::ResultPrinter#self.print_issues calls 'issue[\"severity\"]' 2 times","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/result_printer.rb","lines":{"begin":36,"end":44}},"remediation_points":350000,"content":{"body":"Duplication occurs when two fragments of code look nearly identical, or when two fragments of code have nearly identical effects at some conceptual level.\n\nReek implements a check for _Duplicate Method Call_.\n\n## Example\n\nHere's a very much simplified and contrived example. The following method will report a warning:\n\n```Ruby\ndef double_thing()\n @other.thing + @other.thing\nend\n```\n\nOne quick approach to silence Reek would be to refactor the code thus:\n\n```Ruby\ndef double_thing()\n thing = @other.thing\n thing + thing\nend\n```\n\nA slightly different approach would be to replace all calls of `double_thing` by calls to `@other.double_thing`:\n\n```Ruby\nclass Other\n def double_thing()\n thing + thing\n end\nend\n```\n\nThe approach you take will depend on balancing other factors in your code.\n"},"severity":"minor"},
|
36
|
+
{"engine_name":"reek","fingerprint":"a6d4b886f1137834a7895847fda2a405","type":"issue","check_name":"DuplicateMethodCall","description":"CodeclimateDiff::ResultPrinter#self.print_issues_in_category calls 'issue[\"location\"]' 2 times","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/result_printer.rb","lines":{"begin":10,"end":11}},"remediation_points":350000,"content":{"body":"Duplication occurs when two fragments of code look nearly identical, or when two fragments of code have nearly identical effects at some conceptual level.\n\nReek implements a check for _Duplicate Method Call_.\n\n## Example\n\nHere's a very much simplified and contrived example. The following method will report a warning:\n\n```Ruby\ndef double_thing()\n @other.thing + @other.thing\nend\n```\n\nOne quick approach to silence Reek would be to refactor the code thus:\n\n```Ruby\ndef double_thing()\n thing = @other.thing\n thing + thing\nend\n```\n\nA slightly different approach would be to replace all calls of `double_thing` by calls to `@other.double_thing`:\n\n```Ruby\nclass Other\n def double_thing()\n thing + thing\n end\nend\n```\n\nThe approach you take will depend on balancing other factors in your code.\n"},"severity":"minor"},
|
37
|
+
{"engine_name":"reek","fingerprint":"c426eed4fc2a91238bf2a338dc0ac631","type":"issue","check_name":"DuplicateMethodCall","description":"CodeclimateDiff::ResultPrinter#self.print_result calls 'fixed_issues.count' 2 times","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/result_printer.rb","lines":{"begin":71,"end":72}},"remediation_points":350000,"content":{"body":"Duplication occurs when two fragments of code look nearly identical, or when two fragments of code have nearly identical effects at some conceptual level.\n\nReek implements a check for _Duplicate Method Call_.\n\n## Example\n\nHere's a very much simplified and contrived example. The following method will report a warning:\n\n```Ruby\ndef double_thing()\n @other.thing + @other.thing\nend\n```\n\nOne quick approach to silence Reek would be to refactor the code thus:\n\n```Ruby\ndef double_thing()\n thing = @other.thing\n thing + thing\nend\n```\n\nA slightly different approach would be to replace all calls of `double_thing` by calls to `@other.double_thing`:\n\n```Ruby\nclass Other\n def double_thing()\n thing + thing\n end\nend\n```\n\nThe approach you take will depend on balancing other factors in your code.\n"},"severity":"minor"},
|
38
|
+
{"engine_name":"reek","fingerprint":"9f6643562758be7d65fc46249ba74212","type":"issue","check_name":"DuplicateMethodCall","description":"CodeclimateDiff::ResultPrinter#self.print_result calls 'new_issues.count' 2 times","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/result_printer.rb","lines":{"begin":63,"end":64}},"remediation_points":350000,"content":{"body":"Duplication occurs when two fragments of code look nearly identical, or when two fragments of code have nearly identical effects at some conceptual level.\n\nReek implements a check for _Duplicate Method Call_.\n\n## Example\n\nHere's a very much simplified and contrived example. The following method will report a warning:\n\n```Ruby\ndef double_thing()\n @other.thing + @other.thing\nend\n```\n\nOne quick approach to silence Reek would be to refactor the code thus:\n\n```Ruby\ndef double_thing()\n thing = @other.thing\n thing + thing\nend\n```\n\nA slightly different approach would be to replace all calls of `double_thing` by calls to `@other.double_thing`:\n\n```Ruby\nclass Other\n def double_thing()\n thing + thing\n end\nend\n```\n\nThe approach you take will depend on balancing other factors in your code.\n"},"severity":"minor"},
|
39
|
+
{"engine_name":"reek","fingerprint":"66c9f93f83039d5ff8b90e5f5f57156a","type":"issue","check_name":"DuplicateMethodCall","description":"CodeclimateDiff::ResultPrinter#self.print_result calls 'preexisting_issues.count' 2 times","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/result_printer.rb","lines":{"begin":54,"end":55}},"remediation_points":350000,"content":{"body":"Duplication occurs when two fragments of code look nearly identical, or when two fragments of code have nearly identical effects at some conceptual level.\n\nReek implements a check for _Duplicate Method Call_.\n\n## Example\n\nHere's a very much simplified and contrived example. The following method will report a warning:\n\n```Ruby\ndef double_thing()\n @other.thing + @other.thing\nend\n```\n\nOne quick approach to silence Reek would be to refactor the code thus:\n\n```Ruby\ndef double_thing()\n thing = @other.thing\n thing + thing\nend\n```\n\nA slightly different approach would be to replace all calls of `double_thing` by calls to `@other.double_thing`:\n\n```Ruby\nclass Other\n def double_thing()\n thing + thing\n end\nend\n```\n\nThe approach you take will depend on balancing other factors in your code.\n"},"severity":"minor"},
|
40
|
+
{"engine_name":"reek","fingerprint":"524d4bedfc2cef6b38a753be561516ef","type":"issue","check_name":"IrresponsibleModule","description":"CodeclimateDiff::ResultPrinter has no descriptive comment","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/result_printer.rb","lines":{"begin":7,"end":7}},"remediation_points":350000,"content":{"body":"Classes and modules are the units of reuse and release. It is therefore considered good practice to annotate every class and module with a brief comment outlining its responsibilities.\n\n## Example\n\nGiven\n\n```Ruby\nclass Dummy\n # Do things...\nend\n```\n\nReek would emit the following warning:\n\n```\ntest.rb -- 1 warning:\n [1]:Dummy has no descriptive comment (IrresponsibleModule)\n```\n\nFixing this is simple - just an explaining comment:\n\n```Ruby\n# The Dummy class is responsible for ...\nclass Dummy\n # Do things...\nend\n```\n"},"severity":"minor"},
|
41
|
+
{"engine_name":"reek","fingerprint":"b96b36b5b941ac84266107f82a75ecb6","type":"issue","check_name":"LongParameterList","description":"CodeclimateDiff::ResultPrinter#self.print_category has 5 parameters","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/result_printer.rb","lines":{"begin":20,"end":20}},"remediation_points":500000,"content":{"body":"A `Long Parameter List` occurs when a method has a lot of parameters.\n\n## Example\n\nGiven\n\n```Ruby\nclass Dummy\n def long_list(foo,bar,baz,fling,flung)\n puts foo,bar,baz,fling,flung\n end\nend\n```\n\nReek would report the following warning:\n\n```\ntest.rb -- 1 warning:\n [2]:Dummy#long_list has 5 parameters (LongParameterList)\n```\n\nA common solution to this problem would be the introduction of parameter objects.\n"},"severity":"minor"},
|
42
|
+
{"engine_name":"reek","fingerprint":"24256875bc239c7ee72e7bf5162a5cf1","type":"issue","check_name":"NestedIterators","description":"CodeclimateDiff::ResultPrinter#self.print_issues contains iterators nested 2 deep","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/result_printer.rb","lines":{"begin":41,"end":41}},"remediation_points":500000,"content":{"body":"A `Nested Iterator` occurs when a block contains another block.\n\n## Example\n\nGiven\n\n```Ruby\nclass Duck\n class \u003c\u003c self\n def duck_names\n %i!tick trick track!.each do |surname|\n %i!duck!.each do |last_name|\n puts \"full name is #{surname} #{last_name}\"\n end\n end\n end\n end\nend\n```\n\nReek would report the following warning:\n\n```\ntest.rb -- 1 warning:\n [5]:Duck#duck_names contains iterators nested 2 deep (NestedIterators)\n```\n"},"severity":"minor"},
|
43
|
+
{"engine_name":"reek","fingerprint":"dba788bce2d4037f442a07834141f748","type":"issue","check_name":"TooManyStatements","description":"CodeclimateDiff::ResultPrinter#self.print_call_to_action has approx 7 statements","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/result_printer.rb","lines":{"begin":79,"end":79}},"remediation_points":500000,"content":{"body":"A method with `Too Many Statements` is any method that has a large number of lines.\n\n`Too Many Statements` warns about any method that has more than 5 statements. Reek's smell detector for `Too Many Statements` counts +1 for every simple statement in a method and +1 for every statement within a control structure (`if`, `else`, `case`, `when`, `for`, `while`, `until`, `begin`, `rescue`) but it doesn't count the control structure itself.\n\nSo the following method would score +6 in Reek's statement-counting algorithm:\n\n```Ruby\ndef parse(arg, argv, \u0026error)\n if !(val = arg) and (argv.empty? or /\\A-/ =~ (val = argv[0]))\n return nil, block, nil # +1\n end\n opt = (val = parse_arg(val, \u0026error))[1] # +2\n val = conv_arg(*val) # +3\n if opt and !arg\n argv.shift # +4\n else\n val[0] = nil # +5\n end\n val # +6\nend\n```\n\n(You might argue that the two assigments within the first @if@ should count as statements, and that perhaps the nested assignment should count as +2.)\n"},"severity":"minor"},
|
44
|
+
{"engine_name":"reek","fingerprint":"847c6524aee19aaaa5c9f75d2af55034","type":"issue","check_name":"TooManyStatements","description":"CodeclimateDiff::ResultPrinter#self.print_issues has approx 10 statements","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/result_printer.rb","lines":{"begin":35,"end":35}},"remediation_points":500000,"content":{"body":"A method with `Too Many Statements` is any method that has a large number of lines.\n\n`Too Many Statements` warns about any method that has more than 5 statements. Reek's smell detector for `Too Many Statements` counts +1 for every simple statement in a method and +1 for every statement within a control structure (`if`, `else`, `case`, `when`, `for`, `while`, `until`, `begin`, `rescue`) but it doesn't count the control structure itself.\n\nSo the following method would score +6 in Reek's statement-counting algorithm:\n\n```Ruby\ndef parse(arg, argv, \u0026error)\n if !(val = arg) and (argv.empty? or /\\A-/ =~ (val = argv[0]))\n return nil, block, nil # +1\n end\n opt = (val = parse_arg(val, \u0026error))[1] # +2\n val = conv_arg(*val) # +3\n if opt and !arg\n argv.shift # +4\n else\n val[0] = nil # +5\n end\n val # +6\nend\n```\n\n(You might argue that the two assigments within the first @if@ should count as statements, and that perhaps the nested assignment should count as +2.)\n"},"severity":"minor"},
|
45
|
+
{"engine_name":"reek","fingerprint":"0e1b58c41f946011380b520b782497f6","type":"issue","check_name":"TooManyStatements","description":"CodeclimateDiff::ResultPrinter#self.print_issues_in_category has approx 7 statements","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/result_printer.rb","lines":{"begin":8,"end":8}},"remediation_points":500000,"content":{"body":"A method with `Too Many Statements` is any method that has a large number of lines.\n\n`Too Many Statements` warns about any method that has more than 5 statements. Reek's smell detector for `Too Many Statements` counts +1 for every simple statement in a method and +1 for every statement within a control structure (`if`, `else`, `case`, `when`, `for`, `while`, `until`, `begin`, `rescue`) but it doesn't count the control structure itself.\n\nSo the following method would score +6 in Reek's statement-counting algorithm:\n\n```Ruby\ndef parse(arg, argv, \u0026error)\n if !(val = arg) and (argv.empty? or /\\A-/ =~ (val = argv[0]))\n return nil, block, nil # +1\n end\n opt = (val = parse_arg(val, \u0026error))[1] # +2\n val = conv_arg(*val) # +3\n if opt and !arg\n argv.shift # +4\n else\n val[0] = nil # +5\n end\n val # +6\nend\n```\n\n(You might argue that the two assigments within the first @if@ should count as statements, and that perhaps the nested assignment should count as +2.)\n"},"severity":"minor"},
|
46
|
+
{"engine_name":"reek","fingerprint":"659011304f57ba6821ee6ecd179e2fe3","type":"issue","check_name":"TooManyStatements","description":"CodeclimateDiff::ResultPrinter#self.print_result has approx 12 statements","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/result_printer.rb","lines":{"begin":51,"end":51}},"remediation_points":500000,"content":{"body":"A method with `Too Many Statements` is any method that has a large number of lines.\n\n`Too Many Statements` warns about any method that has more than 5 statements. Reek's smell detector for `Too Many Statements` counts +1 for every simple statement in a method and +1 for every statement within a control structure (`if`, `else`, `case`, `when`, `for`, `while`, `until`, `begin`, `rescue`) but it doesn't count the control structure itself.\n\nSo the following method would score +6 in Reek's statement-counting algorithm:\n\n```Ruby\ndef parse(arg, argv, \u0026error)\n if !(val = arg) and (argv.empty? or /\\A-/ =~ (val = argv[0]))\n return nil, block, nil # +1\n end\n opt = (val = parse_arg(val, \u0026error))[1] # +2\n val = conv_arg(*val) # +3\n if opt and !arg\n argv.shift # +4\n else\n val[0] = nil # +5\n end\n val # +6\nend\n```\n\n(You might argue that the two assigments within the first @if@ should count as statements, and that perhaps the nested assignment should count as +2.)\n"},"severity":"minor"},
|
47
|
+
{"engine_name":"reek","fingerprint":"eebfb3495de5ce6c32cdbdedd3733169","type":"issue","check_name":"BooleanParameter","description":"CodeclimateDiff::Runner#self.run_diff_on_branch has boolean parameter 'always_analyze_all_files'","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/runner.rb","lines":{"begin":89,"end":89}},"remediation_points":500000,"content":{"body":"`Boolean Parameter` is a special case of `Control Couple`, where a method parameter is defaulted to true or false. A _Boolean Parameter_ effectively permits a method's caller to decide which execution path to take. This is a case of bad cohesion. You're creating a dependency between methods that is not really necessary, thus increasing coupling.\n\n## Example\n\nGiven\n\n```Ruby\nclass Dummy\n def hit_the_switch(switch = true)\n if switch\n puts 'Hitting the switch'\n # do other things...\n else\n puts 'Not hitting the switch'\n # do other things...\n end\n end\nend\n```\n\nReek would emit the following warning:\n\n```\ntest.rb -- 3 warnings:\n [1]:Dummy#hit_the_switch has boolean parameter 'switch' (BooleanParameter)\n [2]:Dummy#hit_the_switch is controlled by argument switch (ControlParameter)\n```\n\nNote that both smells are reported, `Boolean Parameter` and `Control Parameter`.\n\n## Getting rid of the smell\n\nThis is highly dependent on your exact architecture, but looking at the example above what you could do is:\n\n* Move everything in the `if` branch into a separate method\n* Move everything in the `else` branch into a separate method\n* Get rid of the `hit_the_switch` method alltogether\n* Make the decision what method to call in the initial caller of `hit_the_switch`\n"},"severity":"minor"},
|
48
|
+
{"engine_name":"reek","fingerprint":"f69f9414e9b677df0fe0fa88a56e09e6","type":"issue","check_name":"BooleanParameter","description":"CodeclimateDiff::Runner#self.run_diff_on_branch has boolean parameter 'show_preexisting'","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/runner.rb","lines":{"begin":89,"end":89}},"remediation_points":500000,"content":{"body":"`Boolean Parameter` is a special case of `Control Couple`, where a method parameter is defaulted to true or false. A _Boolean Parameter_ effectively permits a method's caller to decide which execution path to take. This is a case of bad cohesion. You're creating a dependency between methods that is not really necessary, thus increasing coupling.\n\n## Example\n\nGiven\n\n```Ruby\nclass Dummy\n def hit_the_switch(switch = true)\n if switch\n puts 'Hitting the switch'\n # do other things...\n else\n puts 'Not hitting the switch'\n # do other things...\n end\n end\nend\n```\n\nReek would emit the following warning:\n\n```\ntest.rb -- 3 warnings:\n [1]:Dummy#hit_the_switch has boolean parameter 'switch' (BooleanParameter)\n [2]:Dummy#hit_the_switch is controlled by argument switch (ControlParameter)\n```\n\nNote that both smells are reported, `Boolean Parameter` and `Control Parameter`.\n\n## Getting rid of the smell\n\nThis is highly dependent on your exact architecture, but looking at the example above what you could do is:\n\n* Move everything in the `if` branch into a separate method\n* Move everything in the `else` branch into a separate method\n* Get rid of the `hit_the_switch` method alltogether\n* Make the decision what method to call in the initial caller of `hit_the_switch`\n"},"severity":"minor"},
|
49
|
+
{"engine_name":"reek","fingerprint":"c9d1604f71aa792148908051a6c30a03","type":"issue","check_name":"ControlParameter","description":"CodeclimateDiff::Runner#self.calculate_issues_in_changed_files is controlled by argument 'always_analyze_all_files'","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/runner.rb","lines":{"begin":43,"end":45}},"remediation_points":500000,"content":{"body":"`Control Parameter` is a special case of `Control Couple`\n\n## Example\n\nA simple example would be the \"quoted\" parameter in the following method:\n\n```Ruby\ndef write(quoted)\n if quoted\n write_quoted @value\n else\n write_unquoted @value\n end\nend\n```\n\nFixing those problems is out of the scope of this document but an easy solution could be to remove the \"write\" method alltogether and to move the calls to \"write_quoted\" / \"write_unquoted\" in the initial caller of \"write\".\n"},"severity":"minor"},
|
50
|
+
{"engine_name":"reek","fingerprint":"3ff6caf742702e28c65517be34dc4a90","type":"issue","check_name":"DuplicateMethodCall","description":"CodeclimateDiff::Runner#self.calculate_issues_in_changed_files calls 'JSON.parse(result)' 2 times","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/runner.rb","lines":{"begin":49,"end":60}},"remediation_points":350000,"content":{"body":"Duplication occurs when two fragments of code look nearly identical, or when two fragments of code have nearly identical effects at some conceptual level.\n\nReek implements a check for _Duplicate Method Call_.\n\n## Example\n\nHere's a very much simplified and contrived example. The following method will report a warning:\n\n```Ruby\ndef double_thing()\n @other.thing + @other.thing\nend\n```\n\nOne quick approach to silence Reek would be to refactor the code thus:\n\n```Ruby\ndef double_thing()\n thing = @other.thing\n thing + thing\nend\n```\n\nA slightly different approach would be to replace all calls of `double_thing` by calls to `@other.double_thing`:\n\n```Ruby\nclass Other\n def double_thing()\n thing + thing\n end\nend\n```\n\nThe approach you take will depend on balancing other factors in your code.\n"},"severity":"minor"},
|
51
|
+
{"engine_name":"reek","fingerprint":"2ace891a3d054cd1d6b178902b0aa4b9","type":"issue","check_name":"DuplicateMethodCall","description":"CodeclimateDiff::Runner#self.calculate_issues_in_changed_files calls 'JSON.parse(result).each' 2 times","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/runner.rb","lines":{"begin":49,"end":60}},"remediation_points":350000,"content":{"body":"Duplication occurs when two fragments of code look nearly identical, or when two fragments of code have nearly identical effects at some conceptual level.\n\nReek implements a check for _Duplicate Method Call_.\n\n## Example\n\nHere's a very much simplified and contrived example. The following method will report a warning:\n\n```Ruby\ndef double_thing()\n @other.thing + @other.thing\nend\n```\n\nOne quick approach to silence Reek would be to refactor the code thus:\n\n```Ruby\ndef double_thing()\n thing = @other.thing\n thing + thing\nend\n```\n\nA slightly different approach would be to replace all calls of `double_thing` by calls to `@other.double_thing`:\n\n```Ruby\nclass Other\n def double_thing()\n thing + thing\n end\nend\n```\n\nThe approach you take will depend on balancing other factors in your code.\n"},"severity":"minor"},
|
52
|
+
{"engine_name":"reek","fingerprint":"ec3e2cc4884fd40b46e5fcd406caed9d","type":"issue","check_name":"DuplicateMethodCall","description":"CodeclimateDiff::Runner#self.calculate_issues_in_changed_files calls 'changed_file_issues.append(issue)' 2 times","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/runner.rb","lines":{"begin":53,"end":63}},"remediation_points":350000,"content":{"body":"Duplication occurs when two fragments of code look nearly identical, or when two fragments of code have nearly identical effects at some conceptual level.\n\nReek implements a check for _Duplicate Method Call_.\n\n## Example\n\nHere's a very much simplified and contrived example. The following method will report a warning:\n\n```Ruby\ndef double_thing()\n @other.thing + @other.thing\nend\n```\n\nOne quick approach to silence Reek would be to refactor the code thus:\n\n```Ruby\ndef double_thing()\n thing = @other.thing\n thing + thing\nend\n```\n\nA slightly different approach would be to replace all calls of `double_thing` by calls to `@other.double_thing`:\n\n```Ruby\nclass Other\n def double_thing()\n thing + thing\n end\nend\n```\n\nThe approach you take will depend on balancing other factors in your code.\n"},"severity":"minor"},
|
53
|
+
{"engine_name":"reek","fingerprint":"974a71566e027ab872e3496e1782f85a","type":"issue","check_name":"DuplicateMethodCall","description":"CodeclimateDiff::Runner#self.calculate_issues_in_changed_files calls 'issue[\"type\"] != \"issue\"' 2 times","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/runner.rb","lines":{"begin":50,"end":61}},"remediation_points":350000,"content":{"body":"Duplication occurs when two fragments of code look nearly identical, or when two fragments of code have nearly identical effects at some conceptual level.\n\nReek implements a check for _Duplicate Method Call_.\n\n## Example\n\nHere's a very much simplified and contrived example. The following method will report a warning:\n\n```Ruby\ndef double_thing()\n @other.thing + @other.thing\nend\n```\n\nOne quick approach to silence Reek would be to refactor the code thus:\n\n```Ruby\ndef double_thing()\n thing = @other.thing\n thing + thing\nend\n```\n\nA slightly different approach would be to replace all calls of `double_thing` by calls to `@other.double_thing`:\n\n```Ruby\nclass Other\n def double_thing()\n thing + thing\n end\nend\n```\n\nThe approach you take will depend on balancing other factors in your code.\n"},"severity":"minor"},
|
54
|
+
{"engine_name":"reek","fingerprint":"ececcf3944ab70f551f1c57fc5f8989f","type":"issue","check_name":"DuplicateMethodCall","description":"CodeclimateDiff::Runner#self.calculate_issues_in_changed_files calls 'issue[\"type\"]' 2 times","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/runner.rb","lines":{"begin":50,"end":61}},"remediation_points":350000,"content":{"body":"Duplication occurs when two fragments of code look nearly identical, or when two fragments of code have nearly identical effects at some conceptual level.\n\nReek implements a check for _Duplicate Method Call_.\n\n## Example\n\nHere's a very much simplified and contrived example. The following method will report a warning:\n\n```Ruby\ndef double_thing()\n @other.thing + @other.thing\nend\n```\n\nOne quick approach to silence Reek would be to refactor the code thus:\n\n```Ruby\ndef double_thing()\n thing = @other.thing\n thing + thing\nend\n```\n\nA slightly different approach would be to replace all calls of `double_thing` by calls to `@other.double_thing`:\n\n```Ruby\nclass Other\n def double_thing()\n thing + thing\n end\nend\n```\n\nThe approach you take will depend on balancing other factors in your code.\n"},"severity":"minor"},
|
55
|
+
{"engine_name":"reek","fingerprint":"cd318842f19b32d930607d78221cf5c5","type":"issue","check_name":"IrresponsibleModule","description":"CodeclimateDiff::Runner has no descriptive comment","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/runner.rb","lines":{"begin":12,"end":12}},"remediation_points":350000,"content":{"body":"Classes and modules are the units of reuse and release. It is therefore considered good practice to annotate every class and module with a brief comment outlining its responsibilities.\n\n## Example\n\nGiven\n\n```Ruby\nclass Dummy\n # Do things...\nend\n```\n\nReek would emit the following warning:\n\n```\ntest.rb -- 1 warning:\n [1]:Dummy has no descriptive comment (IrresponsibleModule)\n```\n\nFixing this is simple - just an explaining comment:\n\n```Ruby\n# The Dummy class is responsible for ...\nclass Dummy\n # Do things...\nend\n```\n"},"severity":"minor"},
|
56
|
+
{"engine_name":"reek","fingerprint":"eaaa4ef4913d7d7dedf4596bf06edb8b","type":"issue","check_name":"NestedIterators","description":"CodeclimateDiff::Runner#self.calculate_changed_filenames contains iterators nested 2 deep","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/runner.rb","lines":{"begin":33,"end":33}},"remediation_points":500000,"content":{"body":"A `Nested Iterator` occurs when a block contains another block.\n\n## Example\n\nGiven\n\n```Ruby\nclass Duck\n class \u003c\u003c self\n def duck_names\n %i!tick trick track!.each do |surname|\n %i!duck!.each do |last_name|\n puts \"full name is #{surname} #{last_name}\"\n end\n end\n end\n end\nend\n```\n\nReek would report the following warning:\n\n```\ntest.rb -- 1 warning:\n [5]:Duck#duck_names contains iterators nested 2 deep (NestedIterators)\n```\n"},"severity":"minor"},
|
57
|
+
{"engine_name":"reek","fingerprint":"e5fe8233608d51d516fe9c9662f3bce0","type":"issue","check_name":"NestedIterators","description":"CodeclimateDiff::Runner#self.calculate_issues_in_changed_files contains iterators nested 2 deep","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/runner.rb","lines":{"begin":60,"end":60}},"remediation_points":500000,"content":{"body":"A `Nested Iterator` occurs when a block contains another block.\n\n## Example\n\nGiven\n\n```Ruby\nclass Duck\n class \u003c\u003c self\n def duck_names\n %i!tick trick track!.each do |surname|\n %i!duck!.each do |last_name|\n puts \"full name is #{surname} #{last_name}\"\n end\n end\n end\n end\nend\n```\n\nReek would report the following warning:\n\n```\ntest.rb -- 1 warning:\n [5]:Duck#duck_names contains iterators nested 2 deep (NestedIterators)\n```\n"},"severity":"minor"},
|
58
|
+
{"engine_name":"reek","fingerprint":"545f4e2fbe74902028bcf6c047db975b","type":"issue","check_name":"TooManyStatements","description":"CodeclimateDiff::Runner#self.calculate_changed_filenames has approx 16 statements","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/runner.rb","lines":{"begin":13,"end":13}},"remediation_points":500000,"content":{"body":"A method with `Too Many Statements` is any method that has a large number of lines.\n\n`Too Many Statements` warns about any method that has more than 5 statements. Reek's smell detector for `Too Many Statements` counts +1 for every simple statement in a method and +1 for every statement within a control structure (`if`, `else`, `case`, `when`, `for`, `while`, `until`, `begin`, `rescue`) but it doesn't count the control structure itself.\n\nSo the following method would score +6 in Reek's statement-counting algorithm:\n\n```Ruby\ndef parse(arg, argv, \u0026error)\n if !(val = arg) and (argv.empty? or /\\A-/ =~ (val = argv[0]))\n return nil, block, nil # +1\n end\n opt = (val = parse_arg(val, \u0026error))[1] # +2\n val = conv_arg(*val) # +3\n if opt and !arg\n argv.shift # +4\n else\n val[0] = nil # +5\n end\n val # +6\nend\n```\n\n(You might argue that the two assigments within the first @if@ should count as statements, and that perhaps the nested assignment should count as +2.)\n"},"severity":"minor"},
|
59
|
+
{"engine_name":"reek","fingerprint":"911d8c2cd9713eb42fe09220efd48ca7","type":"issue","check_name":"TooManyStatements","description":"CodeclimateDiff::Runner#self.calculate_issues_in_changed_files has approx 18 statements","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/runner.rb","lines":{"begin":39,"end":39}},"remediation_points":500000,"content":{"body":"A method with `Too Many Statements` is any method that has a large number of lines.\n\n`Too Many Statements` warns about any method that has more than 5 statements. Reek's smell detector for `Too Many Statements` counts +1 for every simple statement in a method and +1 for every statement within a control structure (`if`, `else`, `case`, `when`, `for`, `while`, `until`, `begin`, `rescue`) but it doesn't count the control structure itself.\n\nSo the following method would score +6 in Reek's statement-counting algorithm:\n\n```Ruby\ndef parse(arg, argv, \u0026error)\n if !(val = arg) and (argv.empty? or /\\A-/ =~ (val = argv[0]))\n return nil, block, nil # +1\n end\n opt = (val = parse_arg(val, \u0026error))[1] # +2\n val = conv_arg(*val) # +3\n if opt and !arg\n argv.shift # +4\n else\n val[0] = nil # +5\n end\n val # +6\nend\n```\n\n(You might argue that the two assigments within the first @if@ should count as statements, and that perhaps the nested assignment should count as +2.)\n"},"severity":"minor"},
|
60
|
+
{"engine_name":"reek","fingerprint":"b3db1a66f15bef1e6a1a5d9bbb519f26","type":"issue","check_name":"TooManyStatements","description":"CodeclimateDiff::Runner#self.run_diff_on_branch has approx 7 statements","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/runner.rb","lines":{"begin":89,"end":89}},"remediation_points":500000,"content":{"body":"A method with `Too Many Statements` is any method that has a large number of lines.\n\n`Too Many Statements` warns about any method that has more than 5 statements. Reek's smell detector for `Too Many Statements` counts +1 for every simple statement in a method and +1 for every statement within a control structure (`if`, `else`, `case`, `when`, `for`, `while`, `until`, `begin`, `rescue`) but it doesn't count the control structure itself.\n\nSo the following method would score +6 in Reek's statement-counting algorithm:\n\n```Ruby\ndef parse(arg, argv, \u0026error)\n if !(val = arg) and (argv.empty? or /\\A-/ =~ (val = argv[0]))\n return nil, block, nil # +1\n end\n opt = (val = parse_arg(val, \u0026error))[1] # +2\n val = conv_arg(*val) # +3\n if opt and !arg\n argv.shift # +4\n else\n val[0] = nil # +5\n end\n val # +6\nend\n```\n\n(You might argue that the two assigments within the first @if@ should count as statements, and that perhaps the nested assignment should count as +2.)\n"},"severity":"minor"},
|
61
|
+
{"engine_name":"reek","fingerprint":"91c4af205f1f773f532585f9dd580548","type":"issue","check_name":"IrresponsibleModule","description":"CodeclimateDiff has no descriptive comment","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff.rb","lines":{"begin":6,"end":6}},"remediation_points":350000,"content":{"body":"Classes and modules are the units of reuse and release. It is therefore considered good practice to annotate every class and module with a brief comment outlining its responsibilities.\n\n## Example\n\nGiven\n\n```Ruby\nclass Dummy\n # Do things...\nend\n```\n\nReek would emit the following warning:\n\n```\ntest.rb -- 1 warning:\n [1]:Dummy has no descriptive comment (IrresponsibleModule)\n```\n\nFixing this is simple - just an explaining comment:\n\n```Ruby\n# The Dummy class is responsible for ...\nclass Dummy\n # Do things...\nend\n```\n"},"severity":"minor"}]
|
@@ -0,0 +1,32 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
$LOAD_PATH.unshift("#{__dir__}/../lib")
|
5
|
+
|
6
|
+
require "optparse"
|
7
|
+
require "codeclimate_diff/runner"
|
8
|
+
require "codeclimate_diff"
|
9
|
+
|
10
|
+
options = {}
|
11
|
+
OptionParser.new do |opts|
|
12
|
+
opts.banner = "Usage: codeclimate_diff [options]"
|
13
|
+
|
14
|
+
opts.on("-b", "--baseline", "Means it will regenerate the baseline.")
|
15
|
+
|
16
|
+
opts.on("-n", "--new-only",
|
17
|
+
"It will only show what you have changed and not existing issues in files you have touched.")
|
18
|
+
|
19
|
+
opts.on("-a", "--all",
|
20
|
+
"It will always analyze all files, and not the changed files one by one, even if below the 'threshold_to_run_on_all_files' setting.")
|
21
|
+
|
22
|
+
opts.on("-pPATTERN", "--pattern=PATTERN",
|
23
|
+
"Grep pattern to filter files. If provided, will filter the files changed on your branch further.")
|
24
|
+
end.parse!(into: options)
|
25
|
+
|
26
|
+
if options[:baseline]
|
27
|
+
CodeclimateDiff::Runner.generate_baseline
|
28
|
+
elsif options[:"new-only"]
|
29
|
+
CodeclimateDiff::Runner.run_diff_on_branch(options[:pattern], always_analyze_all_files: options[:all], show_preexisting: false)
|
30
|
+
else
|
31
|
+
CodeclimateDiff::Runner.run_diff_on_branch(options[:pattern], always_analyze_all_files: options[:all], show_preexisting: true)
|
32
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "json"
|
4
|
+
require "colorize"
|
5
|
+
|
6
|
+
module CodeclimateDiff
|
7
|
+
class CodeclimateWrapper
|
8
|
+
|
9
|
+
def run_codeclimate(filename = "")
|
10
|
+
docker_platform = CodeclimateDiff.configuration["docker_platform"] || "linux/amd64"
|
11
|
+
|
12
|
+
`docker run \
|
13
|
+
--interactive --tty --rm \
|
14
|
+
--env CODECLIMATE_CODE="$PWD" \
|
15
|
+
--volume "$PWD":/code \
|
16
|
+
--volume /var/run/docker.sock:/var/run/docker.sock \
|
17
|
+
--volume /tmp/cc:/tmp/cc \
|
18
|
+
--platform #{docker_platform} \
|
19
|
+
codeclimate/codeclimate analyze -f json #{filename}`
|
20
|
+
end
|
21
|
+
|
22
|
+
def pull_latest_image
|
23
|
+
puts "Downloading latest codeclimate docker image..."
|
24
|
+
`docker pull codeclimate/codeclimate`
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "rest-client"
|
4
|
+
|
5
|
+
module CodeclimateDiff
|
6
|
+
class Downloader
|
7
|
+
def self.refresh_baseline_if_configured
|
8
|
+
return unless CodeclimateDiff.configuration["gitlab"]
|
9
|
+
return unless CodeclimateDiff.configuration["gitlab"]["download_baseline_from_pipeline"]
|
10
|
+
|
11
|
+
personal_access_token = ENV.fetch("CODECLIMATE_DIFF_GITLAB_PERSONAL_ACCESS_TOKEN")
|
12
|
+
|
13
|
+
if !personal_access_token
|
14
|
+
puts "Missing environment variable 'CODECLIMATE_DIFF_GITLAB_PERSONAL_ACCESS_TOKEN'. Using current baseline."
|
15
|
+
return
|
16
|
+
end
|
17
|
+
|
18
|
+
puts "Downloading baseline file from gitlab..."
|
19
|
+
branch_name = CodeclimateDiff.configuration["main_branch_name"] || "main"
|
20
|
+
project_id = CodeclimateDiff.configuration["gitlab"]["project_id"]
|
21
|
+
host = CodeclimateDiff.configuration["gitlab"]["host"]
|
22
|
+
baseline_filename = CodeclimateDiff.configuration["gitlab"]["baseline_filename"]
|
23
|
+
|
24
|
+
# curl --output codeclimate_diff_baseline.json --header "PRIVATE-TOKEN: MYTOKEN" "https://gitlab.digitalnz.org/api/v4/projects/85/jobs/artifacts/main/raw/codeclimate_diff_baseline.json?job=code_quality"
|
25
|
+
url = "#{host}/api/v4/projects/#{project_id}/jobs/artifacts/#{branch_name}/raw/#{baseline_filename}?job=code_quality"
|
26
|
+
response = RestClient.get(url, { "PRIVATE-TOKEN": personal_access_token })
|
27
|
+
|
28
|
+
if response.code < 300
|
29
|
+
File.write("codeclimate_diff_baseline.json", response.body)
|
30
|
+
puts "Successfully updated the baseline."
|
31
|
+
else
|
32
|
+
puts "Downloading baseline file failed with status code #{response.code}: #{response.body}"
|
33
|
+
puts "Using current baseline."
|
34
|
+
end
|
35
|
+
|
36
|
+
rescue StandardError => e
|
37
|
+
puts e
|
38
|
+
puts "Using current baseline."
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module CodeclimateDiff
|
4
|
+
class IssueSorter
|
5
|
+
def self.remove_closest_match_from_list(issue_to_match, list)
|
6
|
+
# check for exact match first
|
7
|
+
index = list.index do |issue|
|
8
|
+
issue["fingerprint"] == issue_to_match["fingerprint"] &&
|
9
|
+
issue["location"]["lines"]["begin"] == issue_to_match["location"]["lines"]["begin"] &&
|
10
|
+
issue["description"] == issue_to_match["description"]
|
11
|
+
end
|
12
|
+
|
13
|
+
if index
|
14
|
+
list.delete_at(index)
|
15
|
+
return
|
16
|
+
end
|
17
|
+
|
18
|
+
# check for same method name (description often has method name or variable name in it)
|
19
|
+
index = list.index do |issue|
|
20
|
+
issue["fingerprint"] == issue_to_match["fingerprint"] &&
|
21
|
+
issue["description"] == issue_to_match["description"]
|
22
|
+
end
|
23
|
+
|
24
|
+
if index
|
25
|
+
list.delete_at(index)
|
26
|
+
return
|
27
|
+
end
|
28
|
+
|
29
|
+
# otherwise just remove the first one
|
30
|
+
list.pop
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.sort_issues(preexisting_issues, changed_file_issues)
|
34
|
+
puts "Sorting into :preexisting, :new and :fixed lists..."
|
35
|
+
|
36
|
+
result = {}
|
37
|
+
result[:preexisting] = []
|
38
|
+
result[:new] = []
|
39
|
+
result[:fixed] = []
|
40
|
+
|
41
|
+
# fingerprints are unique per issue type and file
|
42
|
+
# so there could be multiple if the same issue shows up multiple times
|
43
|
+
# plus line numbers and method names could have changed
|
44
|
+
unique_fingerprints = (preexisting_issues + changed_file_issues).map { |issue| issue["fingerprint"] }.uniq
|
45
|
+
|
46
|
+
unique_fingerprints.each do |fingerprint|
|
47
|
+
baseline_issues = preexisting_issues.filter { |issue| issue["fingerprint"] == fingerprint }
|
48
|
+
current_issues = changed_file_issues.filter { |issue| issue["fingerprint"] == fingerprint }
|
49
|
+
|
50
|
+
if baseline_issues.count == current_issues.count
|
51
|
+
# current issues are most up to date (line numbers could have changed etc.)
|
52
|
+
result[:preexisting] += current_issues
|
53
|
+
elsif current_issues.count < baseline_issues.count
|
54
|
+
# less issues than there were before
|
55
|
+
current_issues.each do |issue_to_match|
|
56
|
+
CodeclimateDiff.remove_closest_match_from_list(issue_to_match, baseline_issues)
|
57
|
+
end
|
58
|
+
result[:fixed] += baseline_issues
|
59
|
+
else
|
60
|
+
# more issues than there were before
|
61
|
+
baseline_issues.each do |issue_to_match|
|
62
|
+
CodeclimateDiff.remove_closest_match_from_list(issue_to_match, current_issues)
|
63
|
+
end
|
64
|
+
result[:new] += current_issues
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# do a check to make sure the maths works out
|
69
|
+
puts "#{preexisting_issues.count} issues in matching files in baseline"
|
70
|
+
puts "#{changed_file_issues.count} current issues in matching files"
|
71
|
+
|
72
|
+
result
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "json"
|
4
|
+
require "colorize"
|
5
|
+
|
6
|
+
module CodeclimateDiff
|
7
|
+
class ResultPrinter
|
8
|
+
def self.print_issues_in_category(issues_list)
|
9
|
+
issues_list.each do |issue|
|
10
|
+
filename = issue["location"]["path"]
|
11
|
+
line_number = issue["location"]["lines"]["begin"]
|
12
|
+
description = issue["description"]
|
13
|
+
|
14
|
+
print "\u2022 #{filename}:#{line_number}".encode("utf-8").bold
|
15
|
+
puts " #{description}"
|
16
|
+
end
|
17
|
+
puts "\n"
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.print_category(bullet_emoji, severity, engine_name, check_name, color)
|
21
|
+
message = "#{bullet_emoji} [#{severity}] #{engine_name} #{check_name}:".encode("utf-8")
|
22
|
+
|
23
|
+
case color
|
24
|
+
when "red"
|
25
|
+
puts message.red
|
26
|
+
when "yellow"
|
27
|
+
puts message.yellow
|
28
|
+
when "green"
|
29
|
+
puts message.green
|
30
|
+
else
|
31
|
+
puts message
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.print_issues(issues_list, color, bullet_emoji)
|
36
|
+
issue_categories = issues_list.map { |issue| [issue["engine_name"], issue["check_name"], issue["severity"]] }.uniq
|
37
|
+
issue_categories.each do |issue_category|
|
38
|
+
engine_name = issue_category[0]
|
39
|
+
check_name = issue_category[1]
|
40
|
+
severity = issue_category[2]
|
41
|
+
issues = issues_list.filter do |issue|
|
42
|
+
issue["engine_name"] == engine_name &&
|
43
|
+
issue["check_name"] == check_name &&
|
44
|
+
issue["severity"] == severity
|
45
|
+
end
|
46
|
+
print_category(bullet_emoji, severity, engine_name, check_name, color)
|
47
|
+
print_issues_in_category(issues)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.print_result(sorted_issues, show_preexisting)
|
52
|
+
if show_preexisting
|
53
|
+
preexisting_issues = sorted_issues[:preexisting]
|
54
|
+
if preexisting_issues.count.positive?
|
55
|
+
puts "\n#{preexisting_issues.count} preexisting issues in changed files:\n".bold.yellow
|
56
|
+
print_issues(preexisting_issues, "yellow", "\u2718")
|
57
|
+
else
|
58
|
+
puts "\n0 issues in changed files!".encode("utf-8").bold.green
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
new_issues = sorted_issues[:new]
|
63
|
+
if new_issues.count.positive?
|
64
|
+
puts "\n#{new_issues.count} new issues:\n".bold.red
|
65
|
+
print_issues(new_issues, "red", "\u2718")
|
66
|
+
else
|
67
|
+
puts "\n0 new issues :)\n".encode("utf-8").bold
|
68
|
+
end
|
69
|
+
|
70
|
+
fixed_issues = sorted_issues[:fixed]
|
71
|
+
if fixed_issues.count.positive?
|
72
|
+
puts "\n#{fixed_issues.count} fixed issues: \n".encode("utf-8").bold.green
|
73
|
+
print_issues(fixed_issues, "green", "\u2714")
|
74
|
+
else
|
75
|
+
puts "\n0 fixed issues\n".bold
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def self.print_call_to_action(sorted_issues)
|
80
|
+
fixed_count = sorted_issues[:fixed].count
|
81
|
+
new_count = sorted_issues[:new].count
|
82
|
+
outstanding_count = sorted_issues[:preexisting].count + new_count
|
83
|
+
if fixed_count > new_count
|
84
|
+
puts "\n\u{1F389}\u{1F389} Well done! You made the code even better!! \u{1F389}\u{1F389} \n".bold.green.encode("utf-8")
|
85
|
+
elsif new_count > fixed_count
|
86
|
+
puts "\n\ Uh oh, you've introduced more issues than you've fixed. Better fix that! \n".bold.red.encode("utf-8")
|
87
|
+
elsif outstanding_count.positive?
|
88
|
+
puts "\n\ Why don't you see if you can fix some of those outstanding issues while you're here? \n".bold.encode("utf-8")
|
89
|
+
else
|
90
|
+
puts "\n\u{1F389}\u{1F389} Nothing to do here, the code is immaculate!! \u{1F389}\u{1F389} \n".bold.green.encode("utf-8")
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "json"
|
4
|
+
require "colorize"
|
5
|
+
require "pry-byebug"
|
6
|
+
require_relative "./codeclimate_wrapper"
|
7
|
+
require_relative "./result_printer"
|
8
|
+
require_relative "./issue_sorter"
|
9
|
+
require_relative "./downloader"
|
10
|
+
|
11
|
+
module CodeclimateDiff
|
12
|
+
class Runner
|
13
|
+
def self.calculate_changed_filenames(pattern)
|
14
|
+
extra_grep_filter = pattern ? " | grep '#{pattern}'" : ""
|
15
|
+
branch_name = CodeclimateDiff.configuration["main_branch_name"] || "main"
|
16
|
+
all_files_changed_str = `git diff --name-only #{branch_name} | grep --extended-regexp '.js$|.rb$'#{extra_grep_filter}`
|
17
|
+
all_files_changed = all_files_changed_str.split("\n")
|
18
|
+
.filter { |filename| File.exist?(filename) }
|
19
|
+
|
20
|
+
# load the exclude patterns list from .codeclimate.yml
|
21
|
+
exclude_patterns = []
|
22
|
+
if File.exist?(".codeclimate.yml")
|
23
|
+
config = YAML.load_file(".codeclimate.yml")
|
24
|
+
exclude_patterns = config["exclude_patterns"]
|
25
|
+
end
|
26
|
+
|
27
|
+
files_and_directories_excluded = exclude_patterns.map { |exclude_pattern| Dir.glob(exclude_pattern) }.flatten
|
28
|
+
|
29
|
+
# filter out any files that match the excluded ones
|
30
|
+
all_files_changed.filter do |filename|
|
31
|
+
next if files_and_directories_excluded.include? filename
|
32
|
+
|
33
|
+
next if files_and_directories_excluded.any? { |excluded_filename| filename.start_with?(excluded_filename) }
|
34
|
+
|
35
|
+
true
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.calculate_issues_in_changed_files(changed_filenames, always_analyze_all_files)
|
40
|
+
changed_file_issues = []
|
41
|
+
|
42
|
+
threshold_to_run_on_all_files = CodeclimateDiff.configuration["threshold_to_run_on_all_files"] || 8
|
43
|
+
analyze_all_files = always_analyze_all_files || changed_filenames.count > threshold_to_run_on_all_files
|
44
|
+
if analyze_all_files
|
45
|
+
message = always_analyze_all_files ? "Analyzing all files..." : "The number of changed files is greater than the threshold '#{threshold_to_run_on_all_files}', so analyzing all files..."
|
46
|
+
puts message
|
47
|
+
|
48
|
+
result = CodeclimateWrapper.new.run_codeclimate
|
49
|
+
JSON.parse(result).each do |issue|
|
50
|
+
next if issue["type"] != "issue"
|
51
|
+
next unless changed_filenames.include? issue["location"]["path"]
|
52
|
+
|
53
|
+
changed_file_issues.append(issue)
|
54
|
+
end
|
55
|
+
|
56
|
+
else
|
57
|
+
changed_filenames.each do |filename|
|
58
|
+
puts "Analysing '#{filename}'..."
|
59
|
+
result = CodeclimateWrapper.new.run_codeclimate(filename)
|
60
|
+
JSON.parse(result).each do |issue|
|
61
|
+
next if issue["type"] != "issue"
|
62
|
+
|
63
|
+
changed_file_issues.append(issue)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
changed_file_issues
|
69
|
+
end
|
70
|
+
|
71
|
+
def self.calculate_preexisting_issues_in_changed_files(changed_filenames)
|
72
|
+
Downloader.refresh_baseline_if_configured
|
73
|
+
|
74
|
+
puts "Extracting relevant preexisting issues..."
|
75
|
+
all_issues = JSON.parse(File.read("./codeclimate_diff_baseline.json"))
|
76
|
+
|
77
|
+
all_issues.filter { |issue| issue.key?("location") && changed_filenames.include?(issue["location"]["path"]) }
|
78
|
+
end
|
79
|
+
|
80
|
+
def self.generate_baseline
|
81
|
+
CodeclimateWrapper.new.pull_latest_image
|
82
|
+
|
83
|
+
puts "Generating the baseline. Should take about 5 minutes..."
|
84
|
+
result = CodeclimateWrapper.new.run_codeclimate
|
85
|
+
File.write("codeclimate_diff_baseline.json", result)
|
86
|
+
puts "Done!"
|
87
|
+
end
|
88
|
+
|
89
|
+
def self.run_diff_on_branch(pattern, always_analyze_all_files: false, show_preexisting: true)
|
90
|
+
CodeclimateWrapper.new.pull_latest_image
|
91
|
+
|
92
|
+
changed_filenames = calculate_changed_filenames(pattern)
|
93
|
+
|
94
|
+
changed_file_issues = calculate_issues_in_changed_files(changed_filenames, always_analyze_all_files)
|
95
|
+
|
96
|
+
preexisting_issues = calculate_preexisting_issues_in_changed_files(changed_filenames)
|
97
|
+
|
98
|
+
sorted_issues = IssueSorter.sort_issues(preexisting_issues, changed_file_issues)
|
99
|
+
|
100
|
+
ResultPrinter.print_result(sorted_issues, show_preexisting)
|
101
|
+
ResultPrinter.print_call_to_action(sorted_issues)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
metadata
ADDED
@@ -0,0 +1,135 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: codeclimate_diff
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.10
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Isabel Anastasiadis
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2023-07-06 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: colorize
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: json
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: optparse
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: pry-byebug
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rest-client
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
description:
|
84
|
+
email:
|
85
|
+
- isabel@boost.co.nz
|
86
|
+
executables:
|
87
|
+
- codeclimate_diff
|
88
|
+
extensions: []
|
89
|
+
extra_rdoc_files: []
|
90
|
+
files:
|
91
|
+
- ".codeclimate.yml"
|
92
|
+
- ".codeclimate_diff.yml"
|
93
|
+
- ".rspec"
|
94
|
+
- ".rubocop.yml"
|
95
|
+
- CHANGELOG.md
|
96
|
+
- Gemfile
|
97
|
+
- Gemfile.lock
|
98
|
+
- README.md
|
99
|
+
- Rakefile
|
100
|
+
- codeclimate_diff.gemspec
|
101
|
+
- codeclimate_diff_baseline.json
|
102
|
+
- exe/codeclimate_diff
|
103
|
+
- lib/codeclimate_diff.rb
|
104
|
+
- lib/codeclimate_diff/codeclimate_wrapper.rb
|
105
|
+
- lib/codeclimate_diff/downloader.rb
|
106
|
+
- lib/codeclimate_diff/issue_sorter.rb
|
107
|
+
- lib/codeclimate_diff/result_printer.rb
|
108
|
+
- lib/codeclimate_diff/runner.rb
|
109
|
+
- lib/codeclimate_diff/version.rb
|
110
|
+
- sig/codeclimate_diff.rbs
|
111
|
+
homepage:
|
112
|
+
licenses: []
|
113
|
+
metadata:
|
114
|
+
source_code_uri: https://github.com/boost/codeclimate_diff
|
115
|
+
changelog_uri: https://github.com/boost/codeclimate_diff/CHANGELOG.md
|
116
|
+
post_install_message:
|
117
|
+
rdoc_options: []
|
118
|
+
require_paths:
|
119
|
+
- lib
|
120
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: 2.6.0
|
125
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
126
|
+
requirements:
|
127
|
+
- - ">="
|
128
|
+
- !ruby/object:Gem::Version
|
129
|
+
version: '0'
|
130
|
+
requirements: []
|
131
|
+
rubygems_version: 3.2.32
|
132
|
+
signing_key:
|
133
|
+
specification_version: 4
|
134
|
+
summary: A developer command line tool to see how your branch has affected code quality
|
135
|
+
test_files: []
|