summaryrefslogtreecommitdiffstats
path: root/scripts/parse-git-changelog
blob: d74e83b43af8e220321870e08637d859d718d222 (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
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
#!/usr/bin/python3

from __future__ import print_function
import email
import re
import sys

def role_is_dd(role):
    """
    Check if a role is a DD role
    """
    return role.startswith("DD") or role.startswith("DN")

class DakOutput(object):
    """
    Output parsed record for dak
    """
    def __init__(self, pathname):
        self.out = open(pathname, 'w')
        self.out.write("Archive: ftp.debian.org\n")
        self.out.write("Uploader: Jonathan McDowell <noodles@earth.li>\n")
        self.out.write("Cc: keyring-maint@debian.org\n")

    def close(self):
        self.out.close()

    def write(self, state, operation):
        if operation['action'] == 'remove':
            if 'rt-ticket' in operation:
                if not role_is_dd(operation['role']):
                    self.out.write("\nAction: dm-remove\n" +
                            "Fingerprint: " + operation['key'] + "\n" +
                            "Reason: RT #" + operation['rt-ticket'] +
                            ", keyring commit " + state['commit'] + "\n")
        elif operation['action'] == 'replace':
            if not role_is_dd(operation['role']):
                self.out.write("\nAction: dm-migrate\n" +
                        "From: " + operation['old-key'] + "\n" +
                        "To: " + operation['new-key'] + "\n" +
                        "Reason: RT #" + operation['rt-ticket'] +
                        ", keyring commit " + state['commit'] + "\n")
        elif (operation['action'] == 'add' and
              role_is_dd(operation['role']) and
              operation['notes'] == 'Move from DM keyring'):
            if 'rt-ticket' in operation:
                self.out.write("\nAction: dm-remove\n" +
                               "Fingerprint: " + operation['key'] + "\n" +
                               "Reason: Moved to DD keyring, RT #" +
                               operation['rt-ticket'] + ", keyring commit " +
                               state['commit'] + "\n")


class RTOutput(object):
    """
    Output parsed records for RT
    """
    def __init__(self, pathname):
        self.out = open(pathname, 'w')

    def close(self):
        self.out.close()

    def write(self, state, operation):
        if operation['action'] == 'add':
            if 'rt-ticket' in operation:
                self.out.write("# Commit " + state['commit'] + "\n")
                if role_is_dd(operation['role']):
                    self.out.write("rt edit ticket/" + operation['rt-ticket'] +
                            " set queue=DSA\n")
                elif operation['role'] == 'DM':
                    self.out.write("rt correspond -s resolved -m " +
                        "'This key has now been added to the active DM keyring.' " +
                        operation['rt-ticket'] + "\n")
                else:
                    self.out.write("rt correspond -s resolved -m " +
                        "'This key has now been added to the " +
                        operation['role'] + " keyring.' " +
                        operation['rt-ticket'] + "\n")
        elif operation['action'] == 'remove':
            if 'rt-ticket' in operation:
                self.out.write("# Commit " + state['commit'] + "\n")
                if role_is_dd(operation['role']):
                    self.out.write("rt edit ticket/" + operation['rt-ticket'] +
                            " set queue=DSA\n")
                else:
                    self.out.write("rt edit ticket/" + operation['rt-ticket'] +
                            " set queue=Keyring\n" +
                            "rt correspond -s resolved -m "+
                            "'This key has now been removed from the active DM keyring.' " +
                            operation['rt-ticket'] + "\n")
        elif operation['action'] == 'replace':
            self.out.write("# Commit " + state['commit'] + "\n")
            if role_is_dd(operation['role']):
                self.out.write("rt edit ticket/" + operation['rt-ticket'] +
                        " set queue=Keyring\n" +
                        "rt correspond -s resolved -m " +
                        "'Your key has been replaced in the active keyring and LDAP updated with the new fingerprint.' " +
                        operation['rt-ticket'] + "\n")
            else:
                self.out.write("rt edit ticket/" + operation['rt-ticket'] +
                        " set queue=Keyring\n" +
                        "rt correspond -s resolved -m "+
                        "'Your key has been replaced in the active DM keyring.' " +
                        operation['rt-ticket'] + "\n")


class LDAPOutput(object):
    """
    Output parsed records for LDAP
    """
    def __init__(self, pathname):
        self.out = open(pathname, 'w')

    def close(self):
        self.out.close()

    def write(self, state, operation):
        if operation['action'] == 'replace':
            if role_is_dd(operation['role']):
                self.out.write(operation['username'] + " " + operation['old-key'] + " ")
                self.out.write(operation['new-key'] + "\n")


class Parser(object):
    def __init__(self):
        self.seenrt = {}

    def do_operation(self, state):
        operation = email.message_from_string(state['message'])

        if not 'action' in operation:
            print("NOTE : " + state['commit'] + " (" + state['summary'] + ") has no action")
            return None

        if operation['role'] == 'role':
            # At present we don't do anything with role keys
            return None

        if 'rt-ticket' in operation and operation['rt-ticket'] in self.seenrt:
            print("ERROR: RT " + operation['rt-ticket'] + " used in " +
                    self.seenrt[operation['rt-ticket']] + " and " +
                    state['commit'])
        else:
            self.seenrt[operation['rt-ticket']] = state['commit']

        if operation['action'] == 'add':
            if 'rt-ticket' in operation:
                if operation['role'] == 'DM':
                    try:
                        bts = operation['BTS'].strip()
                        bts = re.sub(r'https?://bugs.debian.org/(\d+)',
                            r'\1-done@bugs.debian.org', bts)
                        print("NOTE : Mail " + bts + " (new DM).")
                    except AttributeError:
                        print('NOTE : DM add for RT ticket %s lacks a BTS ticket.' % operation['RT-Ticket'])
                return operation
            else:
                print("TODO : Add with no RT ticket")
                return None
        elif operation['action'] == 'remove':
            if 'rt-ticket' in operation:
                return operation
            else:
                if 'username' in operation:
                    username = operation['username']
                elif 'key' in operation:
                    username = operation['key']
                elif 'old-key' in operation:
                    username = operation['old-key']
                elif 'subject' in operation:
                    username = operation['subject']
                print("TODO : Removal for " + username + " without RT ticket.")
                return None
        elif operation['action'] == 'replace':
            if role_is_dd(operation['role']):
                if not 'username' in operation:
                    operation['Username'] = 'FIXME'
                return operation
            else:
                return operation
        else:
            print("Error: Unknown action " + operation['action'])
            return None

    def main(self):
        state = {}
        opcount = 0
        dak = DakOutput("dak-update")
        rt = RTOutput("rt-update")
        ldap = LDAPOutput("ldap-update")

        for line in sys.stdin:
            line = line.rstrip()

            # Catch the start of a commit
            m = re.match("commit (.*)$", line)
            if m:
                if 'message' in state:
                    operation = self.do_operation(state)
                    if operation:
                        dak.write(state, operation)
                        rt.write(state, operation)
                        ldap.write(state, operation)
                    opcount += 1
                elif 'commit' in state:
                    if re.match("Import changes sent to keyring", state['summary']):
                        pass
                    elif re.match("Update changelog", state['summary']):
                        pass
                    else:
                        print("NOTE : " + state['commit'] + " (" + state['summary'] + ") is not an action.")
                state = {}
                state['commit'] = m.group(1)

            if not re.match("    ", line):
                continue

            line = line[4:]
            if not 'inaction' in state:
                if not 'summary' in state:
                    state['summary'] = line
                elif re.match("[a-zA-Z]*: ", line):
                    state['inaction'] = True
                    state['message'] = line + "\n"
            else:
                state['message'] += line + "\n"

        # Process the last commit, if applicable
        if 'message' in state:
            operation = self.do_operation(state)
            if operation:
                dak.write(state, operation)
                rt.write(state, operation)
                ldap.write(state, operation)
            opcount += 1

        ldap.close()
        rt.close()
        dak.close()

        print("Processed " + str(opcount) + " operations.")

if __name__ == '__main__':
    parser = Parser()
    parser.main()