-
Notifications
You must be signed in to change notification settings - Fork 0
/
reuse_lint.nim
171 lines (155 loc) · 6.04 KB
/
reuse_lint.nim
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
# This file is part of osh-tool.
# <https://github.com/hoijui/osh-tool>
#
# SPDX-FileCopyrightText: 2022-2023 Robin Vobruba <[email protected]>
#
# SPDX-License-Identifier: AGPL-3.0-or-later
import options
import regex
import strformat
import strutils
import system
import tables
import ../check
import ../check_config
import ../state
import ../util/leightweight
import ../util/fs
import std/logging
import std/osproc
import std/streams
#const IDS = @[srcFileNameBase(), "rl", "reusel", "reuse_lint"]
const ID = srcFileNameBase()
const REUSE_CMD = "reuse"
const REUSE_TOOL_URL = "https://reuse.software/"
const HIGH_COMPLIANCE = 0.7
const MIN_COMPLIANCE = 0.2
type ReuseLintCheck = ref object of Check
type ReuseLintCheckGenerator = ref object of CheckGenerator
method name*(this: ReuseLintCheck): string =
return "REUSE Licensing info"
method description*(this: ReuseLintCheck): string =
return """Checks that complete SPDX licensing info is given \
for all files in the project. \
We do this using the Free Software Foundations REUSE tool.
NOTE: This is related to the License exists check."""
method why*(this: ReuseLintCheck): string =
return """Copyright and licensing is difficult,
especially when reusing software from different projects
that are released under various different licenses.
[REUSE](https://reuse.software/) was started by
the [Free Software Foundation Europe](https://fsfe.org/) (FSFE)
to provide a set of recommendations to make licensing your Free Software projects easier.
Not only do these recommendations make it easier for you
to declare the licenses under which your works are released,
but they also make it easier for a computer
to understand how your project is licensed.
Propper licensing information may prevent or help in potential legal disputes.
It also helps anyone using your source or derivates of it,
to understand their rights.
"""
method sourcePath*(this: ReuseLintCheck): string =
return fs.srcFileName()
method requirements*(this: ReuseLintCheck): CheckReqs =
return {
CheckReq.ExternalTool,
}
method getSignificanceFactors*(this: ReuseLintCheck): CheckSignificance =
return CheckSignificance(
weight: 0.5,
openness: 1.0,
hardware: 0.0,
quality: 0.3,
machineReadability: 1.0,
)
method run*(this: ReuseLintCheck, state: var State): CheckResult =
let config = state.config.checks[ID]
try:
debug fmt"Now running '{REUSE_CMD}' ..."
let process = osproc.startProcess(
command = REUSE_CMD,
workingDir = state.config.projRoot,
args = ["lint"],
env = nil,
options = {poUsePath})
process.inputStream.close() # NOTE **Essential** - This prevents hanging/freezing when reading stdout below
process.errorStream.close() # NOTE **Essential** - This prevents hanging/freezing when reading stdout below
let (lines, exCode) = process.readLines()
process.close()
debug fmt"'{REUSE_CMD}' run done."
if exCode == 0:
newCheckResult(config, CheckResultKind.Perfect)
else:
var msg_lines = newSeq[string]()
msg_lines.add("For more details then this list, and help with fixing these issues,")
msg_lines.add("please use the REUSE tool, available in as Linux package `reuse`,")
msg_lines.add(fmt"or under <{REUSE_TOOL_URL}>.")
msg_lines.add("")
var secSummary = false
var secMissingInfo = false
var issues = newSeq[CheckIssue]()
var m: RegexMatch2
var coverage = 0.0
var coverageDimensions = 0
for line in lines:
if secSummary:
if match(line, re2"^[*] (.*: .+)$", m):
let mainLinePart = line[m.group(0)];
msg_lines.add(mainLinePart)
# Samples for interesting instances of mainLinePart:
# - "Files with copyright information: 30 / 31"
# - "Files with license information: 30 / 31"
if match(mainLinePart, re2"^Files with (copyright|license) information: (\d+) / (\d+)$", m):
let haveInfo = parseInt(mainLinePart[m.group(1)]);
let total = parseInt(mainLinePart[m.group(2)]);
coverage += haveInfo.float / total.float
coverageDimensions += 1
elif secMissingInfo:
if match(line, re2"^[*] (.+)$", m):
let file = line[m.group(0)];
issues.add(CheckIssue(
severity: CheckIssueSeverity.Low,
msg: some(fmt"File with missing copyright and/or license info: {file}")
))
if match(line, re2"^# (.+)$", m):
# Section header
let name = line[m.group(0)];
secSummary = false
secMissingInfo = false
if not secSummary and name == "SUMMARY":
secSummary = true
if not secMissingInfo and name == "MISSING COPYRIGHT AND LICENSING INFORMATION":
secMissingInfo = true
if coverageDimensions > 0:
coverage = coverage / coverageDimensions.float
msg_lines.add("")
msg_lines.add(fmt"Total coverage (roughly): {toPercentStr(coverage)}%")
msg_lines.add("")
msg_lines.add("Please get to a perfect REUSE state by using the REUSE-tool locally")
msg_lines.add(fmt"(after installing): `{REUSE_CMD} lint`")
let msg = some(msg_lines.join("\n"))
let kind = if coverage < MIN_COMPLIANCE:
CheckResultKind.Bad
elif coverage < HIGH_COMPLIANCE:
CheckResultKind.Acceptable
else:
CheckResultKind.Ok
issues.insert(CheckIssue(
severity: CheckIssueSeverity.Middle,
msg: msg
), 0)
CheckResult(
config: config,
kind: kind,
issues: issues
)
except OSError as err:
let msg = fmt("Failed to run '{REUSE_CMD}'; make sure it is in your PATH: {err.msg}")
newCheckResult(config, CheckResultKind.Bad, CheckIssueSeverity.High, some(msg))
method id*(this: ReuseLintCheckGenerator): string =
return ID
method generate*(this: ReuseLintCheckGenerator, config: CheckConfig = this.defaultConfig()): Check =
this.ensureNonConfig(config)
ReuseLintCheck()
proc createGenerator*(): CheckGenerator =
ReuseLintCheckGenerator()