Skip to content

Commit 19eb35c

Browse files
committed
Initial commit
0 parents  commit 19eb35c

File tree

10 files changed

+305
-0
lines changed

10 files changed

+305
-0
lines changed

.codeclimate.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
engines:
2+
golangci-lint:
3+
enabled: false

.dockerignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
.git

Dockerfile

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
FROM golang:bullseye
2+
LABEL maintainer="Victor \"Vito\" Gama <[email protected]>"
3+
4+
WORKDIR /usr/src/app/
5+
6+
COPY engine.json /
7+
8+
# Install dependencies:
9+
RUN apt-get update && apt-get -y install build-essential curl git ruby
10+
RUN curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b /usr/local/bin v1.45.2
11+
12+
RUN adduser -u 9000 --shell /bin/false app
13+
USER app
14+
15+
COPY . ./
16+
17+
VOLUME /code
18+
WORKDIR /code
19+
20+
CMD ["/usr/src/app/entrypoint.sh"]

LICENSE

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
Copyright (c) 2015 Gympass.
2+
3+
Permission is hereby granted, free of charge, to any person obtaining a copy
4+
of this software and associated documentation files (the "Software"), to deal
5+
in the Software without restriction, including without limitation the rights
6+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7+
copies of the Software, and to permit persons to whom the Software is
8+
furnished to do so, subject to the following conditions:
9+
10+
The above copyright notice and this permission notice shall be included in
11+
all copies or substantial portions of the Software.
12+
13+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19+
THE SOFTWARE.

Makefile

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
.PHONY: image
2+
3+
IMAGE_NAME ?= codeclimate/codeclimate-golangci
4+
5+
image:
6+
docker build --rm -t $(IMAGE_NAME) .

README.md

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# Code Climate golangci Engine
2+
3+
`codeclimate-golangci` is a Code Climate engine that executes [`golangci-lint`](https://github.com/golangci/golangci-lint) and reports findings to Code Climate.
4+
5+
### Installation & Usage
6+
7+
1. If you haven't already, [install the Code Climate CLI](https://github.com/codeclimate/codeclimate).
8+
2. Run `codeclimate engines:enable golangci`. This command both installs the engine and enables it in your `.codeclimate.yml` file.
9+
3. Configure `golangci-lint` through its [configuration file](https://golangci-lint.run/usage/configuration/).
10+
3. You're ready to analyze! Browse into your project's folder and run `codeclimate analyze`.
11+
12+
### Configuration
13+
14+
You can enable the engine using your `.codeclimate.yml`:
15+
16+
```yaml
17+
engines:
18+
golangci:
19+
enabled: true
20+
```
21+
22+
Then, configure `golangci-lint` through its [configuration file](https://golangci-lint.run/usage/configuration/).
23+
24+
### Need help?
25+
26+
For help with `codeclimate-golangci`, please open an issue on this repository.
27+
28+
### License
29+
30+
```
31+
Copyright (c) 2015 Gympass.
32+
33+
Permission is hereby granted, free of charge, to any person obtaining a copy
34+
of this software and associated documentation files (the "Software"), to deal
35+
in the Software without restriction, including without limitation the rights
36+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
37+
copies of the Software, and to permit persons to whom the Software is
38+
furnished to do so, subject to the following conditions:
39+
40+
The above copyright notice and this permission notice shall be included in
41+
all copies or substantial portions of the Software.
42+
43+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
44+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
45+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
46+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
47+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
48+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
49+
THE SOFTWARE.
50+
51+
```

engine.json

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"name": "golangci-lint",
3+
"description": "Fast linters Runner for Go",
4+
"languages": ["Go"],
5+
"maintainer": {
6+
"name": "Victor Gama",
7+
"email": "[email protected]"
8+
}
9+
"version": "da5a2077",
10+
"spec_version": "0.0.1"
11+
}

entrypoint.sh

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
#!/bin/bash
2+
3+
set -e
4+
/usr/local/bin/golangci-lint run --out-format "json" ./... \
5+
| ruby /usr/src/app/src/engine.rb

src/categories.rb

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
# frozen_string_literal: true
2+
3+
class Engine
4+
VALID_CATEGORIES = ['Bug Risk', 'Clarity', 'Compatibility', 'Complexity', 'Duplication', 'Performance', 'Security',
5+
'Style'].freeze
6+
CATEGORIES = {
7+
'deadcode' => ['Clarity'],
8+
'errcheck' => ['Bug Risk'],
9+
'gosimple' => ['Style'],
10+
'govet' => ['Bug Risk'],
11+
'ineffassign' => ['Clarity'],
12+
'staticcheck' => ['Bug Risk'],
13+
'structcheck' => ['Clarity'],
14+
'typecheck' => ['Bug Risk'],
15+
'unused' => ['Clarity'],
16+
'varcheck' => ['Clarity'],
17+
'asciicheck' => ['Clarity', 'Bug Risk'],
18+
'bidichk' => ['Bug Risk'],
19+
'bodyclose' => ['Performance', 'Bug Risk'],
20+
'containedctx' => ['Style'],
21+
'contextcheck' => ['Bug Risk'],
22+
'cyclop' => ['Complexity'],
23+
'decorder' => %w[Clarity Style],
24+
'depguard' => ['Style'],
25+
'dupl' => ['Duplication'],
26+
'dogsled' => ['Style'],
27+
'durationcheck' => ['Bug Risk'],
28+
'errchkjson' => ['Bug Risk'],
29+
'errname' => ['Style'],
30+
'errorlint' => ['Bug Risk'],
31+
'execinquery' => ['Security', 'Bug Risk'],
32+
'exhaustive' => ['Bug Risk'],
33+
'exhaustivestruct' => ['Style', 'Bug Risk'],
34+
'exportloopref' => ['Bug Risk'],
35+
'forbidigo' => ['Style'],
36+
'forcetypeassert' => ['Style', 'Bug Risk'],
37+
'funlen' => ['Complexity'],
38+
'gci' => %w[Compatibility Style],
39+
'gochecknoglobals' => ['Style'],
40+
'gochecknoinits' => ['Style'],
41+
'gocognit' => ['Complexity'],
42+
'goconst' => ['Style'],
43+
'gocritic' => ['Bug Risk', 'Performance', 'Style'],
44+
'gocyclo' => ['Complexity'],
45+
'godot' => ['Style'],
46+
'godox' => ['Style', 'Bug Risk'],
47+
'goerr113' => ['Style'],
48+
'gofmt' => ['Style'],
49+
'gofumpt' => ['Style'],
50+
'goheader' => ['Style'],
51+
'goimports' => ['Style'],
52+
'golint' => ['Style'],
53+
'gomnd' => %w[Style Clarity],
54+
'gomoddirectives' => ['Style'],
55+
'gomodguard' => %w[Style Compatibility],
56+
'goprintffuncname' => ['Style'],
57+
'gosec' => ['Security', 'Bug Risk'],
58+
'grouper' => ['Style'],
59+
'ifshort' => ['Style'],
60+
'importas' => ['Style'],
61+
'interfacer' => ['Style'],
62+
'ireturn' => ['Style'],
63+
'lll' => ['Style'],
64+
'maintidx' => %w[Style Complexity],
65+
'makezero' => ['Style', 'Bug Risk', 'Performance'],
66+
'maligned' => ['Performance'],
67+
'misspell' => ['Style'],
68+
'nakedret' => ['Style', 'Bug Risk'],
69+
'nestif' => %w[Style Clarity],
70+
'nilerr' => ['Bug Risk'],
71+
'nilnil' => ['Style', 'Bug Risk'],
72+
'nlreturn' => ['Style'],
73+
'noctx' => ['Performance', 'Bug Risk'],
74+
'nolintlint' => ['Style'],
75+
'paralleltest' => ['Style'],
76+
'prealloc' => ['Performance'],
77+
'predeclared' => %w[Style Clarity],
78+
'promlinter' => ['Style'],
79+
'revive' => ['Style'],
80+
'rowserrcheck' => ['Bug Risk'],
81+
'scopelint' => ['Bug Risk'],
82+
'sqlclosecheck' => ['Bug Risk', 'Performance'],
83+
'stylecheck' => ['Style'],
84+
'tagliatelle' => ['Style'],
85+
'tenv' => ['Style', 'Bug Risk'],
86+
'testpackage' => ['Style'],
87+
'thelper' => ['Style'],
88+
'tparallel' => ['Style'],
89+
'unconvert' => ['Style'],
90+
'unparam' => %w[Style Clarity],
91+
'varnamelen' => ['Style'],
92+
'wastedassign' => ['Style'],
93+
'whitespace' => ['Style'],
94+
'wrapcheck' => ['Style'],
95+
'wsl' => ['Style']
96+
}.freeze
97+
end

src/engine.rb

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
# frozen_string_literal: true
2+
3+
require_relative 'categories'
4+
require 'json'
5+
require 'digest/sha1'
6+
7+
class Engine
8+
class << self
9+
def run
10+
CATEGORIES.each_pair do |linter, categories|
11+
if categories.empty?
12+
warn "Error: Linter #{linter} does not have categories."
13+
exit!
14+
end
15+
16+
diff = categories - (categories & VALID_CATEGORIES)
17+
unless diff.empty?
18+
warn "Error: Linter #{linter} have invalid categories: #{diff}"
19+
exit!
20+
end
21+
end
22+
23+
data = $stdin.read
24+
begin
25+
data = JSON.parse(data)
26+
rescue JSON::ParserError
27+
warn 'Error: Received invalid input:'
28+
warn data
29+
exit!
30+
end
31+
32+
puts data['Issues']&.map { |i| convert_issue(i) }&.join("\0")
33+
end
34+
35+
ISSUE_IDENTIFIER_REGEXP = /^([^\s]+): (.*)/.freeze
36+
37+
def convert_issue(i)
38+
text = i['Text']
39+
# Data coming from linters is not standardised, so it may be quite
40+
# complicated to extract a check_name and description from it. Here we
41+
# try to obtain something that resembles an identifier in a best effort
42+
# fashion.
43+
check_name = text
44+
linter_name = i['FromLinter']
45+
46+
unless (m = ISSUE_IDENTIFIER_REGEXP.match(text)).nil?
47+
check_name = m[1]
48+
end
49+
50+
{
51+
type: :issue,
52+
check_name: check_name,
53+
description: "#{linter_name}: #{text}",
54+
categories: categories_for_linter(linter_name),
55+
fingerprint: fingerprint_issue(i),
56+
location: locate_issue(i)
57+
}.to_json
58+
end
59+
60+
def categories_for_linter(linter)
61+
CATEGORIES[linter]
62+
end
63+
64+
def locate_issue(i)
65+
pos = i['Pos']
66+
{
67+
path: pos['Filename'],
68+
positions: {
69+
begin: {
70+
line: pos['Line'],
71+
column: pos['Column']
72+
},
73+
end: {
74+
line: pos['Line'] + i['SourceLines'].length,
75+
column: i['SourceLines'].last.length
76+
}
77+
}
78+
}
79+
end
80+
81+
def fingerprint_issue(i)
82+
data = [
83+
i.dig('Pos', 'Filename'),
84+
i['Text'],
85+
i['SourceLines'].first
86+
].join('')
87+
Digest::SHA1.hexdigest data
88+
end
89+
end
90+
end
91+
92+
Engine.run

0 commit comments

Comments
 (0)