summaryrefslogtreecommitdiff
path: root/scripts/check-copyright-year
blob: ef657e44da91b15ea978636b4023315f75f0b026 (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
#!/usr/bin/python
#
# Does the copyright statement include the year of the latest git commit?
#
# Copyright (C) 2012, 2014-2016  Codethink Limited
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; version 2 of the License.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program.  If not, see <http://www.gnu.org/licenses/>.

from __future__ import print_function

import datetime
import fnmatch
import re
import sys

import cliapp

class CheckCopyrightYear(cliapp.Application):

    pat = re.compile(r'^[ #/*]*Copyright\s+(\(C\)\s*)'
                     r'(?P<years>[0-9, -]+)')
    ignore = ['COPYING', 'morphlib/licensecheck.pl', 'cliapp/*']

    def add_settings(self):
        self.settings.boolean(['verbose', 'v'], 'be more verbose')

    def setup(self):
        self.all_ok = True
        self.uncommitted = self.get_uncommitted_files()
        self.this_year = datetime.datetime.now().year

    def cleanup(self):
        if not self.all_ok:
            print('ERROR: Some copyright years need fixing', file=sys.stderr)
            sys.exit(1)

    def get_uncommitted_files(self):
        filenames = set()
        status = self.runcmd(['git', 'status', '--porcelain', '-z'])
        tokens = status.rstrip('\0').split('\0')
        while tokens:
            tok = tokens.pop(0)
            filenames.add(tok[3:])
            if 'R' in tok[0:2]:
                filenames.add(tokens.pop(0))
        return filenames

    def process_input_line(self, filename, line):
        if any(fnmatch.fnmatch(filename, pattern) for pattern in self.ignore):
            return

        m = self.pat.match(line)
        if not m:
            return

        year = None
        if filename not in self.uncommitted:
            year = self.get_git_commit_year(filename)

        if year is None:
            # git does not have a commit date for the file, which might
            # happen if the file isn't committed yet. This happens during
            # development, and it's OK. It's not quite a lumberjack, but
            # let's not get into gender stereotypes here.
            year = self.this_year

        ok = False
        for start, end in self.get_copyright_years(m):
            if start <= year <= end:
                ok = True

        if ok:
            if self.settings['verbose']:
                self.output.write('OK %s\n' % filename)
        else:
            self.output.write('BAD %s:%s:%s\n' %
                              (filename, self.lineno, line.strip()))

        self.all_ok = self.all_ok and ok

    def get_git_commit_year(self, filename):
        out = self.runcmd(['git', 'log', '-1', '--format=format:%ad',
                           filename])
        if not out:
            return None
        words = out.split()
        return int(words[4])

    def get_copyright_years(self, match):
        years = match.group('years')
        groups = [s.strip() for s in years.split(',')]

        for group in groups:
            if '-' in group:
                start, end = group.split('-')
            else:
                start = end = group
            start = int(start)
            end = int(end)
            yield start, end


CheckCopyrightYear().run()