aboutsummaryrefslogtreecommitdiff
blob: c08c4c8b4c771bf2d65ba2592bb5ad7b685d6ee5 (plain)
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'