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
|
#!/usr/bin/env python3
#
# Copyright 2016 Chris Lamb <lamby@debian.org>
#
# This file 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, either version 3 of the License, or
# (at your option) any later version.
#
# This file 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 file. If not, see <https://www.gnu.org/licenses/>.
import re
import sys
import datetime
import requests
import subprocess
import dateutil.relativedelta
re_line = re.compile(
r'(?P<suffix>msg\d+.html).*\[DLA (?P<dla>[\d-]+)\] (?P<source>[^\s]+) security update.*'
)
re_version = re.compile(r'^Version.*: (?P<version>.*)')
re_rmadison = re.compile(r'^\s*(?P<source>[^\s]+)\s+\|\s+(?P<version>[^\s]+)\s+\|\s+(?P<suite>[^\s]+)')
session = requests.Session()
def get_dlas(year, month):
url = 'https://lists.debian.org/debian-lts-announce/{}/{:02}/'.format(
year,
month,
)
result = parse(session.get(url).content, re_line)
# Prepend URL as the indices have relative URIs
for x in result:
x['url'] = '{}{}'.format(url, x['suffix'])
return result
def get_dla(url):
return parse(session.get(url).content, re_version)
def main(*args):
dlas = {}
for idx in range(3):
dt = datetime.datetime.utcnow().replace(day=1) - \
dateutil.relativedelta.relativedelta(months=idx)
info("Getting announcements for {}/{:02} ...", dt.year, dt.month)
# Prefer later DLAs with reversed(..)
for x in reversed(get_dlas(dt.year, dt.month)):
# Only comment on the latest upload
if x['source'] in dlas:
continue
info("{source}: parsing announcement from {url} ...", **x)
x.update(get_dla(x['url'])[0])
dlas[x['source']] = x
if not dlas:
return 0
for _, x in sorted(udd(dlas.keys()).items()):
dla = dlas[x['source']]
if subprocess.call((
'dpkg', '--compare-versions', dla['version'], 'gt', x['version'],
)) == 0:
warn("{}: DLA-{} announced version {} but {} has {} <{}>".format(
dla['source'],
dla['dla'],
dla['version'],
x['suite'],
x['version'],
dla['url'],
))
return 0
def udd(sources):
result = {}
info("Querying UDD for {} packages ...", len(sources))
output = subprocess.check_output(
('rmadison', '--url=udd', '--suite=wheezy-security') + tuple(sources)
)
# Reverse to prefer later versions
for x in reversed(parse(output, re_rmadison)):
result.setdefault(x['source'], x)
return result
def warn(msg, *args, **kwargs):
print("W: " + msg.format(*args, **kwargs), file=sys.stderr)
def info(msg, *args, **kwargs):
print("I: " + msg.format(*args, **kwargs), file=sys.stderr)
def parse(content, pattern):
result = []
for x in content.splitlines():
m = pattern.search(x.decode('utf8'))
if m is None:
continue
result.append(m.groupdict())
return result
if __name__ == '__main__':
try:
sys.exit(main(*sys.argv[1:]))
except KeyboardInterrupt:
sys.exit(1)
|