lintus 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +9 -0
- data/README.md +28 -12
- data/action.yml +1 -1
- data/lib/lintus/cli.rb +12 -7
- data/lib/lintus/formatter/github.rb +1 -1
- data/lib/lintus/formatter/json.rb +8 -2
- data/lib/lintus/formatter/text.rb +1 -1
- data/lib/lintus/offense.rb +1 -3
- data/lib/lintus/progress.rb +45 -0
- data/lib/lintus/report.rb +8 -13
- data/lib/lintus/rule.rb +2 -16
- data/lib/lintus/runner.rb +7 -2
- data/lib/lintus/template.rb +8 -8
- data/lib/lintus/version.rb +1 -1
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 52defdb195114923372f370f0da8c0a2a676968a0feb338af762294f718fd006
|
|
4
|
+
data.tar.gz: 7dc54bf3c0e478c2233d3ae1a2ac84819959ee5cbcfa1ab06c9058b7aedd19ee
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a09d084dd3238c1ba62570cdc3c06b30ac96082a9e044f3b19d1fbe1cf1323ce3a300dddce3347ef387069b967bf35437e2a384da1ad6127d2f0e613f819c6a3
|
|
7
|
+
data.tar.gz: 1d13affd795e60d4a371294741e37d9defc08358d4e0c92751d516d8925f6387ab147829393c9b0519f79c6614cb4a91e3185f8c57acb1b5d1626d39b5023417
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
## [0.2.0] - 2026-09-21
|
|
4
|
+
|
|
5
|
+
- A progress counter on stderr while files are being checked.
|
|
6
|
+
- The JSON output lists every file with the probability each rule gave it, not only offenses.
|
|
7
|
+
- The starter config's documentation rule asks about the offense and scopes it with criteria,
|
|
8
|
+
so namespace wrappers no longer trip it.
|
|
9
|
+
- Removed rule `severity` and the `--fail-on` flag: an offense is an offense. A config that still
|
|
10
|
+
sets `severity` is rejected with a message naming the key.
|
|
11
|
+
|
|
3
12
|
## [0.1.0] - 2026-09-21
|
|
4
13
|
|
|
5
14
|
- Initial release.
|
data/README.md
CHANGED
|
@@ -19,7 +19,7 @@ rules:
|
|
|
19
19
|
|
|
20
20
|
```
|
|
21
21
|
$ lintus
|
|
22
|
-
app/jobs/retry_job.rb: [no_sleep_in_jobs] Background jobs must never block on sleep. (
|
|
22
|
+
app/jobs/retry_job.rb: [no_sleep_in_jobs] Background jobs must never block on sleep. (noul 0.94)
|
|
23
23
|
|
|
24
24
|
42 files inspected, 1 offense detected
|
|
25
25
|
```
|
|
@@ -81,13 +81,11 @@ rules:
|
|
|
81
81
|
- "app/**/*.rb"
|
|
82
82
|
exclude:
|
|
83
83
|
- "app/models/legacy/**"
|
|
84
|
-
severity: error
|
|
85
84
|
|
|
86
85
|
service_objects_are_documented:
|
|
87
86
|
description: Service objects carry a comment explaining what they do.
|
|
88
87
|
question: Does every class in this file have a comment describing its responsibility?
|
|
89
88
|
offense_when: false
|
|
90
|
-
severity: warning
|
|
91
89
|
paths:
|
|
92
90
|
- "app/services/**/*.rb"
|
|
93
91
|
```
|
|
@@ -102,7 +100,6 @@ rules:
|
|
|
102
100
|
| `threshold` | no | Probability above which the answer counts as `true`. Defaults to 0.5. Raise it for rules where a false positive is costly. |
|
|
103
101
|
| `paths` | no | Globs the rule applies to. Defaults to the top-level `paths`; with neither, every file. |
|
|
104
102
|
| `exclude` | no | Globs the rule never applies to, on top of the top-level `exclude`. |
|
|
105
|
-
| `severity` | no | `error` (default) or `warning`. Only errors fail the run, unless `--fail-on` says otherwise. |
|
|
106
103
|
| `offense_when` | no | `true` (default) or `false`. Set to `false` for rules phrased positively, such as "Does every class have a comment?". |
|
|
107
104
|
|
|
108
105
|
Rule ids are snake_case. They become the question identifiers in the Jev request and the
|
|
@@ -121,6 +118,22 @@ model. So prefer several narrow questions over one broad one: "Does this file ca
|
|
|
121
118
|
and "Does this file rescue `Exception`?" as two rules beat "Does this file do anything a job
|
|
122
119
|
should not?".
|
|
123
120
|
|
|
121
|
+
**Ask about the offense, not about compliance.** "Does every class have a comment?" turns
|
|
122
|
+
false on a single edge case, and there is always one: the namespace wrapper, the reopened
|
|
123
|
+
class, the one-line error subclass. "Is there a class without a comment?" is the same
|
|
124
|
+
question, but its `criteria` can now spell out which classes count. Keep `offense_when: false`
|
|
125
|
+
for questions that are genuinely easier to phrase positively.
|
|
126
|
+
|
|
127
|
+
**Name the unit and list what to ignore.** The model reads a question literally. When Lintus
|
|
128
|
+
first linted itself with "does every class or module have a comment?", every file was flagged
|
|
129
|
+
with a probability around 0.15: each one opens with an undocumented `module Lintus`. The fix
|
|
130
|
+
was not the threshold but the criteria, which now exclude wrappers whose body only nests other
|
|
131
|
+
definitions.
|
|
132
|
+
|
|
133
|
+
**Read the probabilities before touching the threshold.** Scores clustered far from 0.5 mean
|
|
134
|
+
the model is sure of its reading of the question. If that reading is not yours, reword. Move
|
|
135
|
+
the threshold only when the scores of clean and offending files overlap around it.
|
|
136
|
+
|
|
124
137
|
Lintus sends the model the file's path and its full content. A question can therefore refer to
|
|
125
138
|
the file name ("Is this a controller?") as well as the code.
|
|
126
139
|
|
|
@@ -140,11 +153,15 @@ Only files that at least one rule applies to are sent. Deleted files are never s
|
|
|
140
153
|
|
|
141
154
|
`--format text` is the default. `--format github` prints GitHub Actions workflow commands, so
|
|
142
155
|
each offense becomes an annotation on the file in the pull request; it is the default when
|
|
143
|
-
`GITHUB_ACTIONS` is set. `--format json` is for other tools
|
|
156
|
+
`GITHUB_ACTIONS` is set. `--format json` is for other tools, and it also lists the probability
|
|
157
|
+
every rule gave every file under `files`, offense or not, which is what you want when tuning
|
|
158
|
+
a rule's wording or threshold.
|
|
159
|
+
|
|
160
|
+
Exit status is `0` when clean, `1` when there are offenses, and `2` when a request failed or
|
|
161
|
+
the invocation was wrong.
|
|
144
162
|
|
|
145
|
-
|
|
146
|
-
(
|
|
147
|
-
invocation was wrong.
|
|
163
|
+
While a run is in flight, a counter on stderr shows how many files are done. Off a terminal
|
|
164
|
+
(CI logs, pipes) it is a single line announcing the run, so captured output stays clean.
|
|
148
165
|
|
|
149
166
|
## GitHub Actions
|
|
150
167
|
|
|
@@ -163,7 +180,7 @@ jobs:
|
|
|
163
180
|
- uses: actions/checkout@v4
|
|
164
181
|
with:
|
|
165
182
|
fetch-depth: 0 # needed to diff against the base branch
|
|
166
|
-
- uses: virolea/lintus@v0.
|
|
183
|
+
- uses: virolea/lintus@v0.2.0
|
|
167
184
|
with:
|
|
168
185
|
jev-api-key: ${{ secrets.JEV_API_KEY }}
|
|
169
186
|
```
|
|
@@ -180,7 +197,7 @@ With [pre-commit](https://pre-commit.com):
|
|
|
180
197
|
```yaml
|
|
181
198
|
repos:
|
|
182
199
|
- repo: https://github.com/virolea/lintus
|
|
183
|
-
rev: v0.
|
|
200
|
+
rev: v0.2.0
|
|
184
201
|
hooks:
|
|
185
202
|
- id: lintus
|
|
186
203
|
```
|
|
@@ -204,7 +221,6 @@ Either way `JEV_API_KEY` must be in the environment of the shell running the com
|
|
|
204
221
|
-c, --config PATH Config file to use instead of searching for one
|
|
205
222
|
-j, --jobs N Concurrent requests to the Jev API (default 4)
|
|
206
223
|
--api-key KEY Jev API key (default: $JEV_API_KEY)
|
|
207
|
-
--fail-on LEVEL error (default), warning, or never
|
|
208
224
|
-l, --list Show what would be checked without calling the API
|
|
209
225
|
```
|
|
210
226
|
|
|
@@ -223,7 +239,7 @@ The repository lints itself: see `.lintus.yml` and `.github/workflows/lintus.yml
|
|
|
223
239
|
Bump `lib/lintus/version.rb`, add the entry to `CHANGELOG.md`, and push a matching tag:
|
|
224
240
|
|
|
225
241
|
```bash
|
|
226
|
-
git tag v0.
|
|
242
|
+
git tag v0.2.0 && git push origin v0.2.0
|
|
227
243
|
```
|
|
228
244
|
|
|
229
245
|
The release workflow checks that the tag matches the version, runs the tests, and pushes the
|
data/action.yml
CHANGED
|
@@ -17,7 +17,7 @@ inputs:
|
|
|
17
17
|
required: false
|
|
18
18
|
default: ""
|
|
19
19
|
args:
|
|
20
|
-
description: Extra arguments for the lintus command, for example "--
|
|
20
|
+
description: Extra arguments for the lintus command, for example "--jobs 8".
|
|
21
21
|
required: false
|
|
22
22
|
default: ""
|
|
23
23
|
version:
|
data/lib/lintus/cli.rb
CHANGED
|
@@ -15,7 +15,7 @@ module Lintus
|
|
|
15
15
|
# `command` is what to do; `selection` is which files, with `ref` for --diff
|
|
16
16
|
# and `files` for paths given on the command line.
|
|
17
17
|
Options = Struct.new(
|
|
18
|
-
:command, :selection, :ref, :files, :config, :format, :jobs, :api_key, :list,
|
|
18
|
+
:command, :selection, :ref, :files, :config, :format, :jobs, :api_key, :list,
|
|
19
19
|
keyword_init: true
|
|
20
20
|
)
|
|
21
21
|
|
|
@@ -50,9 +50,9 @@ module Lintus
|
|
|
50
50
|
return list(tasks) if options.list
|
|
51
51
|
|
|
52
52
|
configure_api_key!(options)
|
|
53
|
-
report = runner.run(tasks)
|
|
53
|
+
report = with_progress(tasks.size) { |progress| runner.run(tasks) { |done| progress.update(done) } }
|
|
54
54
|
Formatter.for(options.format).new(stdout).render(report)
|
|
55
|
-
report.exit_status
|
|
55
|
+
report.exit_status
|
|
56
56
|
end
|
|
57
57
|
|
|
58
58
|
def parse(argv)
|
|
@@ -79,7 +79,7 @@ module Lintus
|
|
|
79
79
|
def default_options
|
|
80
80
|
Options.new(
|
|
81
81
|
command: :lint, selection: :all, format: @env["GITHUB_ACTIONS"] == "true" ? "github" : "text",
|
|
82
|
-
jobs: 4, api_key: @env["JEV_API_KEY"], list: false
|
|
82
|
+
jobs: 4, api_key: @env["JEV_API_KEY"], list: false
|
|
83
83
|
)
|
|
84
84
|
end
|
|
85
85
|
|
|
@@ -109,9 +109,6 @@ module Lintus
|
|
|
109
109
|
options.jobs = jobs
|
|
110
110
|
end
|
|
111
111
|
parser.on("--api-key KEY", "Jev API key (default: $JEV_API_KEY)") { |key| options.api_key = key }
|
|
112
|
-
parser.on("--fail-on LEVEL", %w[error warning never], "Exit non-zero on: error (default), warning, never") do |level|
|
|
113
|
-
options.fail_on = level
|
|
114
|
-
end
|
|
115
112
|
parser.on("-l", "--list", "List the files and rules that would be checked, without calling the API") do
|
|
116
113
|
options.list = true
|
|
117
114
|
end
|
|
@@ -138,6 +135,14 @@ module Lintus
|
|
|
138
135
|
end
|
|
139
136
|
end
|
|
140
137
|
|
|
138
|
+
def with_progress(total)
|
|
139
|
+
progress = Progress.new(stderr, total)
|
|
140
|
+
progress.start
|
|
141
|
+
yield progress
|
|
142
|
+
ensure
|
|
143
|
+
progress.finish
|
|
144
|
+
end
|
|
145
|
+
|
|
141
146
|
def configure_api_key!(options)
|
|
142
147
|
raise Error, "no API key: set JEV_API_KEY or pass --api-key" if options.api_key.nil? || options.api_key.empty?
|
|
143
148
|
|
|
@@ -8,7 +8,7 @@ module Lintus
|
|
|
8
8
|
private
|
|
9
9
|
|
|
10
10
|
def offense_line(offense)
|
|
11
|
-
command(
|
|
11
|
+
command("error", offense.message, file: offense.path, title: "lintus: #{offense.rule.id}")
|
|
12
12
|
end
|
|
13
13
|
|
|
14
14
|
def skipped_line(skipped)
|
|
@@ -11,16 +11,22 @@ module Lintus
|
|
|
11
11
|
summary: {
|
|
12
12
|
files_inspected: report.checked.size,
|
|
13
13
|
offenses: report.offenses.size,
|
|
14
|
-
errors: report.errors.size,
|
|
15
|
-
warnings: report.warnings.size,
|
|
16
14
|
skipped: report.skipped.size,
|
|
17
15
|
failures: report.failures.size
|
|
18
16
|
},
|
|
19
17
|
offenses: report.sorted_offenses.map(&:to_h),
|
|
18
|
+
files: report.checked.sort_by(&:path).map { |checked| file_entry(checked) },
|
|
20
19
|
skipped: report.skipped.map { |skipped| { path: skipped.path, reason: skipped.reason } },
|
|
21
20
|
failures: report.failures.map { |failure| { path: failure.path, error: failure.error.message } }
|
|
22
21
|
)
|
|
23
22
|
end
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
|
|
26
|
+
# Every rule asked about the file and the probability it got, offense or not.
|
|
27
|
+
def file_entry(checked)
|
|
28
|
+
{ path: checked.path, answers: checked.answers.transform_values { |noul| noul.round(3) } }
|
|
29
|
+
end
|
|
24
30
|
end
|
|
25
31
|
end
|
|
26
32
|
end
|
|
@@ -7,7 +7,7 @@ module Lintus
|
|
|
7
7
|
private
|
|
8
8
|
|
|
9
9
|
def offense_line(offense)
|
|
10
|
-
"#{offense.path}: [#{offense.rule.id}] #{offense.message} (
|
|
10
|
+
"#{offense.path}: [#{offense.rule.id}] #{offense.message} (noul #{offense.noul.round(2)})"
|
|
11
11
|
end
|
|
12
12
|
|
|
13
13
|
def skipped_line(skipped) = "#{skipped.path}: skipped, #{skipped.reason}"
|
data/lib/lintus/offense.rb
CHANGED
|
@@ -3,12 +3,10 @@
|
|
|
3
3
|
module Lintus
|
|
4
4
|
# A rule the model flagged on a file, with the probability it gave.
|
|
5
5
|
Offense = Struct.new(:path, :rule, :noul, keyword_init: true) do
|
|
6
|
-
def severity = rule.severity
|
|
7
|
-
def error? = rule.error?
|
|
8
6
|
def message = rule.description
|
|
9
7
|
|
|
10
8
|
def to_h
|
|
11
|
-
{ path: path, rule: rule.id,
|
|
9
|
+
{ path: path, rule: rule.id, message: message, noul: noul.round(3) }
|
|
12
10
|
end
|
|
13
11
|
end
|
|
14
12
|
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Lintus
|
|
4
|
+
# Tells the person waiting that something is happening. On a terminal it is a
|
|
5
|
+
# counter updated in place and erased when done; elsewhere (CI logs, pipes) it
|
|
6
|
+
# is a single line announcing the run, so nothing garbles captured output.
|
|
7
|
+
class Progress
|
|
8
|
+
def initialize(io, total)
|
|
9
|
+
@io = io
|
|
10
|
+
@total = total
|
|
11
|
+
@tty = io.respond_to?(:tty?) && io.tty?
|
|
12
|
+
@width = 0
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def start
|
|
16
|
+
return if @total.zero?
|
|
17
|
+
|
|
18
|
+
if @tty
|
|
19
|
+
draw(0)
|
|
20
|
+
else
|
|
21
|
+
@io.puts("Checking #{@total} #{@total == 1 ? "file" : "files"}...")
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def update(done)
|
|
26
|
+
draw(done) if @tty
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def finish
|
|
30
|
+
return unless @tty && @width.positive?
|
|
31
|
+
|
|
32
|
+
@io.print "\r#{" " * @width}\r"
|
|
33
|
+
@io.flush
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
private
|
|
37
|
+
|
|
38
|
+
def draw(done)
|
|
39
|
+
line = "Checking #{done}/#{@total} files"
|
|
40
|
+
@width = [@width, line.size].max
|
|
41
|
+
@io.print "\r#{line.ljust(@width)}"
|
|
42
|
+
@io.flush
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
data/lib/lintus/report.rb
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
module Lintus
|
|
4
4
|
# Everything a run produced: what was checked, what was flagged, what was skipped, what broke.
|
|
5
5
|
class Report
|
|
6
|
+
Checked = Struct.new(:path, :answers, keyword_init: true)
|
|
6
7
|
Skipped = Struct.new(:path, :reason, keyword_init: true)
|
|
7
8
|
Failure = Struct.new(:path, :error, keyword_init: true)
|
|
8
9
|
|
|
@@ -16,9 +17,11 @@ module Lintus
|
|
|
16
17
|
@mutex = Mutex.new
|
|
17
18
|
end
|
|
18
19
|
|
|
19
|
-
|
|
20
|
+
# `answers` maps every rule asked about the file to the probability it got,
|
|
21
|
+
# offense or not, so a run can be read for confidence and not just verdicts.
|
|
22
|
+
def record_checked(path, offenses, answers = {})
|
|
20
23
|
synchronize do
|
|
21
|
-
checked << path
|
|
24
|
+
checked << Checked.new(path: path, answers: answers)
|
|
22
25
|
self.offenses.concat(offenses)
|
|
23
26
|
end
|
|
24
27
|
end
|
|
@@ -31,21 +34,13 @@ module Lintus
|
|
|
31
34
|
synchronize { failures << Failure.new(path: path, error: error) }
|
|
32
35
|
end
|
|
33
36
|
|
|
34
|
-
def errors = offenses.select(&:error?)
|
|
35
|
-
def warnings = offenses.reject(&:error?)
|
|
36
|
-
|
|
37
37
|
def sorted_offenses = offenses.sort_by { |offense| [offense.path, offense.rule.id] }
|
|
38
38
|
|
|
39
|
-
# 2 when a file could not be checked, 1 when
|
|
40
|
-
def exit_status
|
|
39
|
+
# 2 when a file could not be checked, 1 when there are offenses, else 0.
|
|
40
|
+
def exit_status
|
|
41
41
|
return 2 if failures.any?
|
|
42
42
|
|
|
43
|
-
|
|
44
|
-
when "warning" then offenses
|
|
45
|
-
when "never" then []
|
|
46
|
-
else errors
|
|
47
|
-
end
|
|
48
|
-
failing.any? ? 1 : 0
|
|
43
|
+
offenses.any? ? 1 : 0
|
|
49
44
|
end
|
|
50
45
|
|
|
51
46
|
private
|
data/lib/lintus/rule.rb
CHANGED
|
@@ -8,11 +8,10 @@ module Lintus
|
|
|
8
8
|
# `offense_when` (true by default, so questions are phrased to describe the
|
|
9
9
|
# offense: "Does this file call sleep?").
|
|
10
10
|
class Rule
|
|
11
|
-
KEYS = %w[question description criteria threshold paths exclude
|
|
12
|
-
SEVERITIES = %w[error warning].freeze
|
|
11
|
+
KEYS = %w[question description criteria threshold paths exclude offense_when].freeze
|
|
13
12
|
ID_FORMAT = /\A[a-z][a-z0-9_]*\z/
|
|
14
13
|
|
|
15
|
-
attr_reader :id, :description, :question, :criteria, :threshold, :paths, :exclude, :
|
|
14
|
+
attr_reader :id, :description, :question, :criteria, :threshold, :paths, :exclude, :offense_when
|
|
16
15
|
|
|
17
16
|
def initialize(id, attrs, default_paths: [], default_exclude: [])
|
|
18
17
|
@id = validate_id(id)
|
|
@@ -24,7 +23,6 @@ module Lintus
|
|
|
24
23
|
@threshold = build_threshold(attrs["threshold"])
|
|
25
24
|
@paths = Schema.string_list(attrs.fetch("paths", default_paths))
|
|
26
25
|
@exclude = default_exclude + Schema.string_list(attrs["exclude"])
|
|
27
|
-
@severity = build_severity(attrs.fetch("severity", "error"))
|
|
28
26
|
@offense_when = build_offense_when(attrs.fetch("offense_when", true))
|
|
29
27
|
end
|
|
30
28
|
|
|
@@ -41,8 +39,6 @@ module Lintus
|
|
|
41
39
|
|
|
42
40
|
def offense?(answer) = answer.result == offense_when
|
|
43
41
|
|
|
44
|
-
def error? = severity == "error"
|
|
45
|
-
|
|
46
42
|
private
|
|
47
43
|
|
|
48
44
|
def validate_id(id)
|
|
@@ -86,16 +82,6 @@ module Lintus
|
|
|
86
82
|
threshold.to_f
|
|
87
83
|
end
|
|
88
84
|
|
|
89
|
-
def build_severity(severity)
|
|
90
|
-
severity = severity.to_s
|
|
91
|
-
unless SEVERITIES.include?(severity)
|
|
92
|
-
raise ConfigError,
|
|
93
|
-
"rule #{id}: `severity` must be one of #{SEVERITIES.join(", ")}"
|
|
94
|
-
end
|
|
95
|
-
|
|
96
|
-
severity
|
|
97
|
-
end
|
|
98
|
-
|
|
99
85
|
def build_offense_when(value)
|
|
100
86
|
raise ConfigError, "rule #{id}: `offense_when` must be true or false" unless [true, false].include?(value)
|
|
101
87
|
|
data/lib/lintus/runner.rb
CHANGED
|
@@ -23,17 +23,21 @@ module Lintus
|
|
|
23
23
|
end
|
|
24
24
|
end
|
|
25
25
|
|
|
26
|
-
# Checks the [file, rules] pairs from #plan concurrently.
|
|
26
|
+
# Checks the [file, rules] pairs from #plan concurrently. The block, if
|
|
27
|
+
# given, is called with the number of files done after each one finishes.
|
|
27
28
|
def run(tasks)
|
|
28
29
|
report = Report.new
|
|
29
30
|
queue = Queue.new
|
|
30
31
|
tasks.each { |task| queue << task }
|
|
31
32
|
queue.close
|
|
33
|
+
done = 0
|
|
34
|
+
counter = Mutex.new
|
|
32
35
|
|
|
33
36
|
workers = Array.new([jobs, tasks.size].min) do
|
|
34
37
|
Thread.new do
|
|
35
38
|
while (task = queue.pop)
|
|
36
39
|
check(*task, report)
|
|
40
|
+
counter.synchronize { yield(done += 1) } if block_given?
|
|
37
41
|
end
|
|
38
42
|
end
|
|
39
43
|
end
|
|
@@ -50,7 +54,8 @@ module Lintus
|
|
|
50
54
|
end
|
|
51
55
|
|
|
52
56
|
response = with_retries { perform(file, rules, content) }
|
|
53
|
-
|
|
57
|
+
answers = rules.to_h { |rule| [rule.id, response.answers[rule.id].noul] }
|
|
58
|
+
report.record_checked(file.path, offenses_for(file, rules, response.answers), answers)
|
|
54
59
|
rescue Jev::Error, Error => e
|
|
55
60
|
report.record_failure(file.path, e)
|
|
56
61
|
end
|
data/lib/lintus/template.rb
CHANGED
|
@@ -28,7 +28,6 @@ module Lintus
|
|
|
28
28
|
criteria:
|
|
29
29
|
"true": A debugger breakpoint or a throwaway print statement is present.
|
|
30
30
|
"false": Any output is deliberate program behaviour, or there is none.
|
|
31
|
-
severity: error
|
|
32
31
|
|
|
33
32
|
no_hardcoded_secrets:
|
|
34
33
|
description: Secrets must come from the environment or a credentials store, never source code.
|
|
@@ -37,14 +36,15 @@ module Lintus
|
|
|
37
36
|
"true": A literal credential value is written in the source.
|
|
38
37
|
"false": Credentials are read from configuration, or there are none.
|
|
39
38
|
threshold: 0.7
|
|
40
|
-
severity: error
|
|
41
39
|
|
|
42
|
-
|
|
43
|
-
description:
|
|
44
|
-
question:
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
40
|
+
classes_are_documented:
|
|
41
|
+
description: Classes should open with a short comment explaining their purpose.
|
|
42
|
+
question: Is there a class or module defined in this file whose definition is not preceded by a comment describing what it is for?
|
|
43
|
+
criteria:
|
|
44
|
+
"true": A class or module with a body of its own has no descriptive comment right above its definition.
|
|
45
|
+
"false": >-
|
|
46
|
+
Every class or module that carries behaviour has such a comment, or the file defines none.
|
|
47
|
+
Ignore namespace wrappers that only nest other definitions and one-line subclasses.
|
|
48
48
|
YAML
|
|
49
49
|
end
|
|
50
50
|
end
|
data/lib/lintus/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: lintus
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Vincent Rolea
|
|
@@ -67,6 +67,7 @@ files:
|
|
|
67
67
|
- lib/lintus/git.rb
|
|
68
68
|
- lib/lintus/glob.rb
|
|
69
69
|
- lib/lintus/offense.rb
|
|
70
|
+
- lib/lintus/progress.rb
|
|
70
71
|
- lib/lintus/report.rb
|
|
71
72
|
- lib/lintus/rule.rb
|
|
72
73
|
- lib/lintus/runner.rb
|