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
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
|
"""Various file-based header checks."""
import re
from .. import results, sources
from . import GentooRepoCheck
copyright_regex = re.compile(
r'^# Copyright (?P<begin>\d{4}-)?(?P<end>\d{4}) (?P<holder>.+)$')
class _FileHeaderResult(results.Result):
"""Generic file header result."""
def __init__(self, line, **kwargs):
super().__init__(**kwargs)
self.line = line
class InvalidCopyright(_FileHeaderResult, results.AliasResult, results.Error):
"""File with invalid copyright.
The file does not start with a valid copyright line. Each ebuild or eclass
file must start with a copyright line of the form::
# Copyright YEARS MAIN-CONTRIBUTOR [OTHER-CONTRIBUTOR]... [and others]
Files in the Gentoo repository must use::
# Copyright YEARS Gentoo Authors
"""
_name = 'InvalidCopyright'
@property
def desc(self):
return f'invalid copyright: {self.line!r}'
class OldGentooCopyright(_FileHeaderResult, results.AliasResult, results.Warning):
"""File with old Gentoo Foundation copyright.
The file still assigns copyright to the Gentoo Foundation even though
it has been committed after the new copyright policy was approved
(2018-10-21).
Ebuilds and eclasses in Gentoo repository must use 'Gentoo Authors'
instead. Files in other repositories may specify an explicit copyright
holder instead.
"""
_name = 'OldGentooCopyright'
@property
def desc(self):
return f'old copyright, update to "Gentoo Authors": {self.line!r}'
class NonGentooAuthorsCopyright(_FileHeaderResult, results.AliasResult, results.Error):
"""File with copyright stating owner other than "Gentoo Authors".
The file specifies explicit copyright owner, while the Gentoo repository
policy specifies that all ebuilds and eclasses must use "Gentoo Authors".
If the owner is not listed in metadata/AUTHORS, addition can be requested
via bugs.gentoo.org.
"""
_name = 'NonGentooAuthorsCopyright'
@property
def desc(self):
return f'copyright line must state "Gentoo Authors": {self.line!r}'
class InvalidLicenseHeader(_FileHeaderResult, results.AliasResult, results.Error):
"""File with invalid license header.
The file does not have with a valid license header.
Ebuilds and eclasses in the Gentoo repository must use::
# Distributed under the terms of the GNU General Public License v2
"""
_name = 'InvalidLicenseHeader'
@property
def desc(self):
if self.line:
return f'invalid license header: {self.line!r}'
return 'missing license header'
class _HeaderCheck(GentooRepoCheck):
"""Scan files for incorrect copyright/license headers."""
_invalid_copyright = InvalidCopyright
_old_copyright = OldGentooCopyright
_non_gentoo_authors = NonGentooAuthorsCopyright
_invalid_license = InvalidLicenseHeader
known_results = frozenset([
_invalid_copyright, _old_copyright, _non_gentoo_authors, _invalid_license,
])
_item_attr = 'pkg'
license_header = '# Distributed under the terms of the GNU General Public License v2'
def args(self, item):
return {self._item_attr: item}
def feed(self, item):
if item.lines:
line = item.lines[0].strip()
if mo := copyright_regex.match(line):
# Copyright policy is active since 2018-10-21, so it applies
# to all ebuilds committed in 2019 and later
if int(mo.group('end')) >= 2019:
if mo.group('holder') == 'Gentoo Foundation':
yield self._old_copyright(line, **self.args(item))
# Gentoo policy requires 'Gentoo Authors'
elif mo.group('holder') != 'Gentoo Authors':
yield self._non_gentoo_authors(line, **self.args(item))
else:
yield self._invalid_copyright(line, **self.args(item))
try:
line = item.lines[1].strip('\n')
except IndexError:
line = ''
if line != self.license_header:
yield self._invalid_license(line, **self.args(item))
class EbuildInvalidCopyright(InvalidCopyright, results.VersionResult):
__doc__ = InvalidCopyright.__doc__
class EbuildOldGentooCopyright(OldGentooCopyright, results.VersionResult):
__doc__ = OldGentooCopyright.__doc__
class EbuildNonGentooAuthorsCopyright(NonGentooAuthorsCopyright, results.VersionResult):
__doc__ = NonGentooAuthorsCopyright.__doc__
class EbuildInvalidLicenseHeader(InvalidLicenseHeader, results.VersionResult):
__doc__ = InvalidLicenseHeader.__doc__
class EbuildHeaderCheck(_HeaderCheck):
"""Scan ebuild for incorrect copyright/license headers."""
_source = sources.EbuildFileRepoSource
_invalid_copyright = EbuildInvalidCopyright
_old_copyright = EbuildOldGentooCopyright
_non_gentoo_authors = EbuildNonGentooAuthorsCopyright
_invalid_license = EbuildInvalidLicenseHeader
known_results = frozenset([
_invalid_copyright, _old_copyright, _non_gentoo_authors, _invalid_license,
])
_item_attr = 'pkg'
class EclassInvalidCopyright(InvalidCopyright, results.EclassResult):
__doc__ = InvalidCopyright.__doc__
@property
def desc(self):
return f'{self.eclass}: {super().desc}'
class EclassOldGentooCopyright(OldGentooCopyright, results.EclassResult):
__doc__ = OldGentooCopyright.__doc__
@property
def desc(self):
return f'{self.eclass}: {super().desc}'
class EclassNonGentooAuthorsCopyright(NonGentooAuthorsCopyright, results.EclassResult):
__doc__ = NonGentooAuthorsCopyright.__doc__
@property
def desc(self):
return f'{self.eclass}: {super().desc}'
class EclassInvalidLicenseHeader(InvalidLicenseHeader, results.EclassResult):
__doc__ = InvalidLicenseHeader.__doc__
@property
def desc(self):
return f'{self.eclass}: {super().desc}'
class EclassHeaderCheck(_HeaderCheck):
"""Scan eclasses for incorrect copyright/license headers."""
_source = sources.EclassRepoSource
_invalid_copyright = EclassInvalidCopyright
_old_copyright = EclassOldGentooCopyright
_non_gentoo_authors = EclassNonGentooAuthorsCopyright
_invalid_license = EclassInvalidLicenseHeader
known_results = frozenset([
_invalid_copyright, _old_copyright, _non_gentoo_authors, _invalid_license,
])
_item_attr = 'eclass'
|