Merge lp:~smoser/vmbuilder/mfdiff.moved-to-git into lp:~ubuntu-on-ec2/vmbuilder/mfdiff
- mfdiff.moved-to-git
- Merge into mfdiff
Proposed by
Scott Moser
Status: | Merged |
---|---|
Merged at revision: | 21 |
Proposed branch: | lp:~smoser/vmbuilder/mfdiff.moved-to-git |
Merge into: | lp:~ubuntu-on-ec2/vmbuilder/mfdiff |
Diff against target: |
672 lines (+10/-643) 4 files modified
BUGS (+0/-4) README (+10/-14) TODO (+0/-9) mfdiff (+0/-616) |
To merge this branch: | bzr merge lp:~smoser/vmbuilder/mfdiff.moved-to-git |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Dan Watkins (community) | Approve | ||
Ubuntu on EC2 | Pending | ||
Review via email: mp+347711@code.launchpad.net |
Commit message
mfdiff has moved its revision control to git.
Its new upstream home is:
https:/
To clone, either:
git clone https:/
or
git clone ssh://git.
Description of the change
To post a comment you must log in.
Revision history for this message
Dan Watkins (oddbloke) : | # |
review:
Approve
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === removed file 'BUGS' |
2 | --- BUGS 2011-07-19 20:33:52 +0000 |
3 | +++ BUGS 1970-01-01 00:00:00 +0000 |
4 | @@ -1,4 +0,0 @@ |
5 | -Known bugs: |
6 | - - if the version of a binary package in 'to' or 'from' is *not* found in the |
7 | - current archive, then it will not handle this. This situation occurs |
8 | - when the binary version is not current in -updates. |
9 | |
10 | === modified file 'README' |
11 | --- README 2017-05-30 15:48:32 +0000 |
12 | +++ README 2018-06-08 17:34:35 +0000 |
13 | @@ -1,14 +1,10 @@ |
14 | -"Manifest Diff" (mfdiff) is a tool for determining what changed between |
15 | -2 builds by looking at their manifests. A manifest is simply a |
16 | -list of package and versions in a build, obtainable by: |
17 | - dpkg-query', '-W', '--showformat=${Package} ${Version}\n' |
18 | - |
19 | -Example of running: |
20 | - mf1=mfs/releases/trusty/release-20160314/ubuntu-14.04-server-cloudimg-amd64.manifest |
21 | - mf2=mfs/releases/trusty/release-20170517/ubuntu-14.04-server-cloudimg-amd64.manifest |
22 | - ./mfdiff amd64 trusty "${mf1}" "${mf2}" |
23 | - |
24 | - |
25 | -sync manifests from cloud-images.ubuntu.com with: |
26 | - rsync -av cloud-images.ubuntu.com::cloud-images/ --prune-empty-dirs mfs \ |
27 | - --filter "+ */" --filter "+ *.manifest" --filter "- *" |
28 | +mfdiff has moved to git. |
29 | + |
30 | +mfdiff has moved its revision control to git. |
31 | +Its new upstream home is: |
32 | + https://code.launchpad.net/~cloud-images-release-managers/cloud-images/+git/mfdiff |
33 | + |
34 | +To clone, either: |
35 | + git clone https://git.launchpad.net/~cloud-images-release-managers/cloud-images/+git/mfdiff |
36 | +or |
37 | + git clone ssh://git.launchpad.net/~cloud-images-release-managers/cloud-images/+git/mfdiff |
38 | |
39 | === removed file 'TODO' |
40 | --- TODO 2010-10-21 19:57:03 +0000 |
41 | +++ TODO 1970-01-01 00:00:00 +0000 |
42 | @@ -1,9 +0,0 @@ |
43 | -- use apt.VersionCompare() to determine if changelog entry is newer |
44 | - or older than desired. this would allow for changelogs missing exact |
45 | - versions. should raise an error, but continue |
46 | -- for linux packages (possibly more generically) |
47 | - new: ['linux-image-2.6.32-25-virtual', 'linux-image-2.6.32-309-ec2'] |
48 | - removed: ['linux-image-2.6.32-24-virtual', 'linux-image-2.6.32-308-ec2'] |
49 | - both linux-image-2.6.32-309-ec2 and linux-image-2.6.32-308-ec2 |
50 | - came from source of 'linux-ec2'. need to detect that as a 'changed' |
51 | - and collect relavant changelogs |
52 | |
53 | === removed file 'mfdiff' |
54 | --- mfdiff 2017-08-15 13:26:43 +0000 |
55 | +++ mfdiff 1970-01-01 00:00:00 +0000 |
56 | @@ -1,616 +0,0 @@ |
57 | -#!/usr/bin/env python |
58 | -""" |
59 | -Given two manifest files for a particular release/arch, find the |
60 | -packages which have been added, removed, and changed. Print the |
61 | -changelog entries for the changed packages limited to changes between |
62 | -the two versions. |
63 | -""" |
64 | - |
65 | -# vi:ts=4 expandtab |
66 | -# |
67 | -# Copyright (C) 2010, 2017 Canonical Ltd. |
68 | -# |
69 | -# Authors: Scott Moser <scott.moser@canonical.com> |
70 | -# Robert C Jennings <robert.jennings@canonical.com> |
71 | -# |
72 | -# This program is free software: you can redistribute it and/or modify |
73 | -# it under the terms of the GNU General Public License as published by |
74 | -# the Free Software Foundation, version 3 of the License. |
75 | -# |
76 | -# This program is distributed in the hope that it will be useful, |
77 | -# but WITHOUT ANY WARRANTY; without even the implied warranty of |
78 | -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
79 | -# GNU General Public License for more details. |
80 | -# |
81 | -# You should have received a copy of the GNU General Public License |
82 | -# along with this program. If not, see <http://www.gnu.org/licenses/>. |
83 | - |
84 | -import codecs |
85 | -import locale |
86 | -import logging |
87 | -import os |
88 | -import os.path |
89 | -import re |
90 | -import shutil |
91 | -import sys |
92 | -import tempfile |
93 | -from optparse import OptionParser |
94 | - |
95 | -import apt |
96 | -import requests |
97 | -from debian.changelog import Changelog |
98 | -from six import PY3, iteritems |
99 | - |
100 | -try: |
101 | - from six import viewkeys |
102 | -except ImportError: |
103 | - # The version of six in trusty doesn't have viewkeys, so pull in the six |
104 | - # code from a more recent version if we can't get it directly from six. |
105 | - import operator |
106 | - if PY3: |
107 | - viewkeys = operator.methodcaller("keys") |
108 | - else: |
109 | - viewkeys = operator.methodcaller("viewkeys") |
110 | - |
111 | -try: |
112 | - from apt import VersionCompare as version_compare |
113 | -except ImportError: |
114 | - from apt_pkg import version_compare as version_compare |
115 | - |
116 | - |
117 | -class MissingChangelogError(Exception): |
118 | - "The Changelog file could not be found on the server" |
119 | - pass |
120 | - |
121 | - |
122 | -class ChangelogMissingVersion(Exception): |
123 | - "The Changelog is missing starting and/or ending versions for the search" |
124 | - pass |
125 | - |
126 | - |
127 | -class UnknownSourceVersionError(Exception): |
128 | - "The binary package did not have a source version listed" |
129 | - pass |
130 | - |
131 | - |
132 | -def get_bin2src(packages, cache): |
133 | - """ |
134 | - Find the source package names for a given binary package list |
135 | - |
136 | - :param list packages: List of binary packages |
137 | - :param :class:`apt.Cache` cache: Open/updated apt cache |
138 | - :return: List mapping binary package name to source package name |
139 | - :rtype: dict |
140 | - """ |
141 | - |
142 | - ret = {} |
143 | - logging.debug('Finding source package names for all binary packages') |
144 | - for binpkg in packages: |
145 | - pkg_name = binpkg.split(':')[0] |
146 | - ret[binpkg] = cache[pkg_name].versions[0].source_name |
147 | - return ret |
148 | - |
149 | - |
150 | -def get_changelog(source, version, changelog_cache_d): |
151 | - """ |
152 | - Download changelog for source / version and returns path to that |
153 | - |
154 | - :param str source: Source package name |
155 | - :param str version: Source package version |
156 | - :param str changelog_cache_d: path to store cached changelogs |
157 | - :raises MissingChangelogError: If changelog file could not be downloaded |
158 | - :return: changelog file for source package & version |
159 | - :rtype: str |
160 | - """ |
161 | - |
162 | - cache_f = "%s/changelog.%s_%s" % (changelog_cache_d, source, version) |
163 | - |
164 | - if os.path.isfile(cache_f): |
165 | - logging.debug("Using cached changelog for %s:%s", source, version) |
166 | - return cache_f |
167 | - |
168 | - furls = [] |
169 | - num_colon_m = re.compile("[0-9]:") |
170 | - |
171 | - cache_tmp_fd, cache_tmp_path = tempfile.mkstemp( |
172 | - ".changelog.%s_%s" % (source, version), |
173 | - prefix="." + tempfile.gettempprefix(), dir=".") |
174 | - cache_tmp = os.fdopen(cache_tmp_fd, "w") |
175 | - for pile in ("main", "universe", "multiverse", "restricted"): |
176 | - pre = source[0:1] |
177 | - # packages starting with 'lib' are special |
178 | - if source.startswith("lib"): |
179 | - pre = source[0:4] |
180 | - |
181 | - # packages with '1:' versions have different paths |
182 | - # update-manager at version 1:0.134.11. |
183 | - # is main/u/update-manager/update-manager_0.134.11/ |
184 | - # rather than main/u/update-manager/update-manager_1:0.134.11 |
185 | - url_version = version |
186 | - if num_colon_m.match(version): |
187 | - url_version = version[2:] |
188 | - |
189 | - # Changelog URL example http://changelogs.ubuntu.com/changelogs/\ |
190 | - # pool/main/h/hal/hal_0.5.5.1-1ubuntu2/changelog |
191 | - changelog_url = "http://changelogs.ubuntu.com/changelogs/pool/" \ |
192 | - "%s/%s/%s/%s_%s/changelog" % \ |
193 | - (pile, pre, source, source, url_version) |
194 | - |
195 | - changelog = requests.get(changelog_url) |
196 | - if changelog.status_code == 200: |
197 | - cache_tmp.write(changelog.content) |
198 | - cache_tmp.close() |
199 | - shutil.copy2(cache_tmp_path, cache_f) |
200 | - os.unlink(cache_tmp_path) |
201 | - return cache_f |
202 | - else: |
203 | - logging.error("missing %s: %s", source, changelog_url) |
204 | - furls.append(changelog_url) |
205 | - if os.path.exists(cache_tmp_path): |
206 | - os.unlink(cache_tmp_path) |
207 | - raise MissingChangelogError("Failed to find changelog for %s at version " |
208 | - "%s.\n tried %s" % (source, version, |
209 | - ' '.join(furls))) |
210 | - |
211 | - |
212 | -def manifest_to_dict(filename): |
213 | - """ |
214 | - Parse manifest file to create a package / version mapping |
215 | - |
216 | - :param str filename: Name of package manifest file |
217 | - :return: List of package versions by name |
218 | - :rtype: dict |
219 | - """ |
220 | - |
221 | - ret = {} |
222 | - logging.debug('Reading package manifest from %s', filename) |
223 | - with open(filename, "r") as manifest: |
224 | - for line in manifest: |
225 | - (pkg, ver) = line.split() |
226 | - ret[pkg] = ver |
227 | - return ret |
228 | - |
229 | - |
230 | -def open_apt_cache(arch, release, cache_d=None): |
231 | - """ |
232 | - Create, update, and open an apt cache. |
233 | - |
234 | - This creates an apt cache directory and write a sources.list file |
235 | - before updating and opening the cache. The caller is responsible |
236 | - for closing the cache. |
237 | - |
238 | - :param str arch: Package architecture |
239 | - :param str release: Ubuntu release name (e.g. Xenial) |
240 | - :param str cache_d: apt cache path |
241 | - :returns: tuple of Open/updated apt cache and cache path name |
242 | - :rtype: tuple(:class:`apt.Cache`, str) |
243 | - """ |
244 | - |
245 | - if not cache_d: |
246 | - cache_d = "./cache.%s-%s" % (release, arch) |
247 | - logging.info("Using %s as the apt cache directory", cache_d) |
248 | - |
249 | - if arch in ['amd64', 'i386']: |
250 | - mirror = "http://archive.ubuntu.com/ubuntu/" |
251 | - else: |
252 | - mirror = "http://ports.ubuntu.com/ubuntu-ports/" |
253 | - logging.debug('Configuring apt cache using mirror %s', mirror) |
254 | - |
255 | - pockets = (release, "%s-updates" % release, "%s-security" % release, |
256 | - "%s-proposed" % release, ) |
257 | - components = ("main", "universe") |
258 | - srclines = [] |
259 | - for pocket in pockets: |
260 | - srcline = "deb %s %s %s" % (mirror, pocket, ' '.join(components)) |
261 | - logging.debug('Adding source: %s', srcline) |
262 | - srclines.append(srcline) |
263 | - try: |
264 | - os.makedirs("%s/etc/apt" % cache_d) |
265 | - except OSError as oserror: |
266 | - if os.errno.EEXIST != oserror.errno: |
267 | - raise |
268 | - with open("%s/etc/apt/sources.list" % cache_d, "w") as asl: |
269 | - asl.write('\n'.join(srclines)) |
270 | - |
271 | - apt.apt_pkg.config.set("Apt::Architecture", arch) |
272 | - |
273 | - logging.debug('Using host apt keys for signature verification') |
274 | - apt.apt_pkg.config.set("Dir::Etc::Trusted", "/etc/apt/trusted.gpg") |
275 | - apt.apt_pkg.config.set("Dir::Etc::TrustedParts", |
276 | - "/etc/apt/trusted.gpg.d/") |
277 | - |
278 | - cache = apt.Cache(rootdir=cache_d) |
279 | - logging.info('Updating apt cache') |
280 | - cache.update() |
281 | - logging.info('Update of apt cache complete') |
282 | - cache.open() |
283 | - return cache, cache_d |
284 | - |
285 | - |
286 | -def render_block(block): |
287 | - """ |
288 | - Render a changelog block to something printable (dropping blank lines) |
289 | - |
290 | - :param :class:`debian.changelog.ChangeBlock` block: Changelog block |
291 | - :return: String containing the changelog block text |
292 | - :rtype: str |
293 | - """ |
294 | - return '\n'.join([x for x in block.changes() if x]) |
295 | - |
296 | - |
297 | -def print_blocks(block_list): |
298 | - """ |
299 | - Print a Changelog block list |
300 | - |
301 | - :param list block_list: List of :class:`debian.changelog.ChangeBlock` |
302 | - """ |
303 | - |
304 | - for block in block_list: |
305 | - print(render_block(block).encode('utf-8').decode('utf-8')) |
306 | - |
307 | - |
308 | -def kernel_fixups(manifest_from, manifest_to): |
309 | - """ |
310 | - Fix up kernels so the pkg names match |
311 | - |
312 | - Kernel package names change from release to release so that they are |
313 | - co-installable, but we need to find matching package names in the |
314 | - two manifests to provide a list of changes between the versions of the |
315 | - package in each manifest. |
316 | - This function will return an altered version of manifest_from with kernel |
317 | - package names changed to match kernel package names in manifest_to. This |
318 | - will support later version comparisons. |
319 | - |
320 | - :param dict manifest_from: Dictionary mapping package to version for of |
321 | - the starting manifest |
322 | - :param dict manifest_to: Dictionary mapping package to version for the |
323 | - ending manifest |
324 | - :return: Starting manifest dictionary with altered kernel package names |
325 | - to match names in ending manifest |
326 | - :rtype dict: |
327 | - """ |
328 | - kfixups = {} |
329 | - kmatch = re.compile("linux-image-[0-9]") |
330 | - for pkg in manifest_to: |
331 | - # if this is a linux-image-* binary package do some hacks to make it |
332 | - # look like manifest_from is the same (format like |
333 | - # linux-image-2.6.32-32-virtual) |
334 | - if kmatch.match(pkg): |
335 | - logging.debug('Found kernel %s in manifest #2', pkg) |
336 | - img_type = pkg.split("-")[-1] |
337 | - if pkg in manifest_from: |
338 | - logging.debug('Found same kernel in manifest #1') |
339 | - continue |
340 | - for fpkg in manifest_from: |
341 | - if kmatch.match(fpkg) and fpkg.endswith("-%s" % img_type): |
342 | - logging.debug('Found similar kernel %s in manifest #1', |
343 | - fpkg) |
344 | - kfixups[pkg] = fpkg |
345 | - |
346 | - for pkg_to, pkg_from in iteritems(kfixups): |
347 | - logging.debug('Substituting kernel %s for %s in manifest #1 to ' |
348 | - 'enable version comparison', pkg_to, pkg_from) |
349 | - manifest_from[pkg_to] = manifest_from[pkg_from] |
350 | - del manifest_from[pkg_from] |
351 | - return manifest_from |
352 | - |
353 | - |
354 | -def find_added(manifest_from, manifest_to): |
355 | - "Find new packages in manifest_to" |
356 | - new = {} |
357 | - for pkg in sorted(viewkeys(manifest_to) - viewkeys(manifest_from)): |
358 | - logging.debug('New package: %s', pkg) |
359 | - new[pkg] = manifest_to[pkg] |
360 | - return new |
361 | - |
362 | - |
363 | -def find_removed(manifest_from, manifest_to): |
364 | - "Find packages removed from manifest_from" |
365 | - removed = {} |
366 | - for pkg in sorted(viewkeys(manifest_from) - viewkeys(manifest_to)): |
367 | - logging.debug('Removed package: %s', pkg) |
368 | - removed[pkg] = manifest_from[pkg] |
369 | - return removed |
370 | - |
371 | - |
372 | -def find_changed(manifest_from, manifest_to): |
373 | - "Find modified packages" |
374 | - changed = [] |
375 | - for pkg in sorted(viewkeys(manifest_from) & viewkeys(manifest_to)): |
376 | - if manifest_from[pkg] != manifest_to[pkg]: |
377 | - logging.debug('Changed package: %s', pkg) |
378 | - changed.append(pkg) |
379 | - return changed |
380 | - |
381 | - |
382 | -def map_source_to_binary(cache, packages): |
383 | - "Create a dictionary of source to list of binary packages" |
384 | - src2bins = {} |
385 | - for bin_pkg in packages: |
386 | - bin_name = bin_pkg.split(':')[0] |
387 | - if bin_name in cache: |
388 | - src2bins.setdefault( |
389 | - cache[bin_name].versions[0].source_name, []).append(bin_pkg) |
390 | - return src2bins |
391 | - |
392 | - |
393 | -def get_pkg_versions(cache, binary): |
394 | - "Get all known versions from the apt cache" |
395 | - pkg_name = binary.split(':')[0] |
396 | - try: |
397 | - return cache[pkg_name].versions |
398 | - except KeyError: |
399 | - raise Exception( |
400 | - "%s not in cache or did not have version info in cache" % |
401 | - pkg_name) |
402 | - |
403 | - |
404 | -def source_version_for_binary(cache, binary, binary_ver): |
405 | - "Find the source version data for a specific binary version" |
406 | - versions = get_pkg_versions(cache, binary) |
407 | - try: |
408 | - return versions[binary_ver].source_version |
409 | - except KeyError: |
410 | - # Strip the architecture name from the binary |
411 | - source_name = cache[binary.split(':')[0]].versions[0].source_name |
412 | - msg = ("Unable to determine source version for %s. " |
413 | - "Binary package %s/%s not in known source version " |
414 | - "list (%s)" % (source_name, binary, binary_ver, versions)) |
415 | - raise UnknownSourceVersionError(msg) |
416 | - |
417 | - |
418 | -def filter_changelog(changelog_path, version_low, version_high): |
419 | - """ |
420 | - Extract changelog entries within a version range |
421 | - |
422 | - The range of changelog entries returned will include all entries |
423 | - after version_low up to, and including, version_high. |
424 | - If either the starting or ending version are not found in the |
425 | - list of changelog entries the result will be incomplete and |
426 | - a non-empty error message is returned to indicate the issue. |
427 | - |
428 | - :param str changelog_path: File name of the changelog to process |
429 | - :return: list of changelog blocks and an error_msg if incomplete |
430 | - :rtype tuple(list, str): |
431 | - """ |
432 | - |
433 | - with open(changelog_path, "r") as fileptr: |
434 | - chlog = Changelog(fileptr.read()) |
435 | - change_blocks = [] |
436 | - start = False |
437 | - end = False |
438 | - error_msg = '' |
439 | - |
440 | - # The changelog blocks are in reverse order; we'll see high before low. |
441 | - for block in chlog: |
442 | - if block.version == version_high: |
443 | - start = True |
444 | - change_blocks = [] |
445 | - if block.version == version_low: |
446 | - end = True |
447 | - break |
448 | - change_blocks.append(block) |
449 | - if not start: |
450 | - error_msg = "Missing starting version {} in {}. " \ |
451 | - "Changlelog will be incomplete".format( |
452 | - version_high, changelog_path) |
453 | - logging.error(error_msg) |
454 | - if not end: |
455 | - if error_msg: |
456 | - # Start and end were not found, put a newline between their |
457 | - # error messages |
458 | - error_msg += '\n' |
459 | - error_msg += "Missing ending version {} in {}. " \ |
460 | - "Changelog output truncated".format( |
461 | - version_low, changelog_path) |
462 | - logging.error(error_msg) |
463 | - return change_blocks, error_msg |
464 | - |
465 | - |
466 | -def print_changelogs(apt_cache, apt_cache_d, manifest_from, manifest_to, |
467 | - changed): |
468 | - """ |
469 | - Print changelog entries for each changed package limited to |
470 | - changes in the package between the versions in the two manifests. |
471 | - |
472 | - :param :class:`apt.Cache` apt_cache: Open & up-to-date apt cache |
473 | - :param str apt_cache_d: apt cache path |
474 | - :param dict manifest_from: Packages and their versions in the |
475 | - first manifest file |
476 | - :param dict manifest_to: Packages and their versions in the |
477 | - second manifest file |
478 | - :param list changed: Packages which changed between the two manifests |
479 | - """ |
480 | - |
481 | - srcs = {} |
482 | - errors = [] |
483 | - src2bins = map_source_to_binary(apt_cache, changed) |
484 | - |
485 | - # Generate changelog data per unique source package |
486 | - for source_name in src2bins: |
487 | - srcs[source_name] = {"changelog_file": "", "changeblocks": []} |
488 | - src = srcs[source_name] |
489 | - |
490 | - # Use the first binary listed for a source package |
491 | - binary_name = src2bins[source_name][0] |
492 | - |
493 | - # Find the source version data for the binary in manifest #2 |
494 | - try: |
495 | - src['version_to'] = source_version_for_binary( |
496 | - apt_cache, binary_name, manifest_to[binary_name]) |
497 | - except UnknownSourceVersionError as excp: |
498 | - logging.error(str(excp)) |
499 | - errors.append(excp) |
500 | - continue |
501 | - |
502 | - # Find the source version data for the binary in manifest #1 |
503 | - binver_from = manifest_from[binary_name] |
504 | - try: |
505 | - src['version_from'] = source_version_for_binary( |
506 | - apt_cache, binary_name, binver_from) |
507 | - except UnknownSourceVersionError as excp: |
508 | - if manifest_to[binary_name] == src['version_to']: |
509 | - logging.info('Could not find source version data in apt ' |
510 | - 'cache. Assuming source %s version %s from ' |
511 | - 'binary %s', source_name, binver_from, |
512 | - binary_name) |
513 | - src['version_from'] = binver_from |
514 | - else: |
515 | - logging.error(str(excp)) |
516 | - errors.append(excp) |
517 | - continue |
518 | - |
519 | - # Check for version regression between manifests |
520 | - try: |
521 | - if version_compare(src['version_from'], src['version_to']) > 0: |
522 | - msg = "Package version regression {} -> {}".format( |
523 | - src['version_from'], src['version_to']) |
524 | - raise UnknownSourceVersionError(msg) |
525 | - except UnknownSourceVersionError as excp: |
526 | - errors.append(excp) |
527 | - continue |
528 | - |
529 | - # Get the changelog for this source package |
530 | - try: |
531 | - # Use the apt cache directory to store the changelog cache |
532 | - srcs[source_name]["changelog_file"] = get_changelog( |
533 | - source_name, src['version_to'], changelog_cache_d=apt_cache_d) |
534 | - except MissingChangelogError as excp: |
535 | - errors.append(excp) |
536 | - continue |
537 | - |
538 | - # Filter the changelog to a list of blocks between the two versions |
539 | - try: |
540 | - srcs[source_name]["changeblocks"], incomplete = filter_changelog( |
541 | - srcs[source_name]["changelog_file"], src['version_from'], |
542 | - src['version_to']) |
543 | - if incomplete: |
544 | - raise ChangelogMissingVersion(incomplete) |
545 | - except ChangelogMissingVersion as exp: |
546 | - errors.append(exp) |
547 | - continue |
548 | - |
549 | - # Print changelog ranges for changed packages |
550 | - for source_name in sorted(src2bins): |
551 | - binlist = sorted(src2bins[source_name]) |
552 | - binary = binlist[0] |
553 | - print("==== %s: %s => %s ====" % |
554 | - (source_name, manifest_from[binary], manifest_to[binary])) |
555 | - print("==== %s" % ' '.join(binlist)) |
556 | - print_blocks(srcs[source_name]["changeblocks"]) |
557 | - |
558 | - if errors: |
559 | - print("**** Errors ****") |
560 | - for error in errors: |
561 | - print(error) |
562 | - |
563 | - |
564 | -def parse_args(): |
565 | - """ |
566 | - Parse command line arguments |
567 | - |
568 | - :returns: options and remaining arguments from OptionParser.parse_args() |
569 | - :rtype list: |
570 | - """ |
571 | - |
572 | - parser = OptionParser(usage="Usage: {} arch suite manifest1 manifest2\n" |
573 | - "Compare two manifest files, and show " |
574 | - "changelog differences." |
575 | - .format(os.path.basename(sys.argv[0]))) |
576 | - parser.add_option("--cache-dir", dest="cache_d", |
577 | - help="cache dir for info", metavar="DIR", type="string", |
578 | - default=None) |
579 | - parser.add_option("-v", "--verbose", action="count", dest="loglevel", |
580 | - help="increase verbosity", default=0) |
581 | - |
582 | - (options, args) = parser.parse_args() |
583 | - |
584 | - if len(args) != 4: |
585 | - parser.error('you must provide arch, release, and 2 manifest files') |
586 | - |
587 | - return options, args |
588 | - |
589 | - |
590 | -def setup_logging(loglevel=0): |
591 | - """ |
592 | - Configure logging |
593 | - |
594 | - By default, log WARNING and higher messages |
595 | - :param int: loglevel 0: Warning, 1: Info, 2: Debug |
596 | - """ |
597 | - |
598 | - loglevel = [logging.WARNING, |
599 | - logging.INFO, |
600 | - logging.DEBUG][min(2, loglevel)] |
601 | - |
602 | - logging.basicConfig( |
603 | - level=loglevel, |
604 | - format="%(asctime)s %(name)s/%(levelname)s: %(message)s", |
605 | - stream=sys.stderr) |
606 | - |
607 | - |
608 | -def stdout_force_unicode(): |
609 | - """ |
610 | - Force output to UTF-8 at all times |
611 | - |
612 | - We want to output UTF-8 at all times to preserve Unicode characters |
613 | - in the changelog blocks. For Python 2 we will wrap sys.stdout with |
614 | - an instance of StreamWriter with our preferred coding. Python3 requires |
615 | - no changes. |
616 | - |
617 | - When writing output to the terminal we get the encoding of the |
618 | - terminal (utf-8 these days). When we redirect or pipe the output of |
619 | - the program it is generally not possible to know what the input |
620 | - encoding of the receiving program is, the encoding when redirecting |
621 | - to a file will be None (Python 2.7) or UTF-8 (Python 3) |
622 | - |
623 | - $ python2.7 -c "import sys; print sys.stdout.encoding" | cat |
624 | - None |
625 | - |
626 | - $ python3.4 -c "import sys; print(sys.stdout.encoding)" | cat |
627 | - UTF-8 |
628 | - |
629 | - Source: |
630 | - https://wiki.python.org/moin/PrintFails#print.2C_write_and_Unicode_in_pre-3.0_Python |
631 | - """ |
632 | - |
633 | - if sys.version_info[0] < 3: |
634 | - encoding = codecs.getwriter(locale.getpreferredencoding()) |
635 | - sys.stdout = encoding(sys.stdout) |
636 | - |
637 | - |
638 | -def main(): |
639 | - """ |
640 | - Given two manifest files for a particular release/arch, find the |
641 | - packages which have been added, removed, and changed. Print the |
642 | - changelog entries for the changed packages limited to changes between |
643 | - the two versions. |
644 | - """ |
645 | - |
646 | - stdout_force_unicode() |
647 | - options, (arch, release, manifest_from_filename, |
648 | - manifest_to_filename) = parse_args() |
649 | - |
650 | - setup_logging(options.loglevel) |
651 | - |
652 | - # index both manifests |
653 | - manifest_to = manifest_to_dict(manifest_to_filename) |
654 | - manifest_from = kernel_fixups(manifest_to_dict(manifest_from_filename), |
655 | - manifest_to) |
656 | - |
657 | - new = find_added(manifest_from, manifest_to) |
658 | - removed = find_removed(manifest_from, manifest_to) |
659 | - changed = find_changed(manifest_from, manifest_to) |
660 | - |
661 | - print("new: %s" % new) |
662 | - print("removed: %s" % removed) |
663 | - print("changed: %s" % changed) |
664 | - |
665 | - # if modified packages, download all changelogs from changelogs. |
666 | - if changed: |
667 | - cache, cache_d = open_apt_cache(arch, release, options.cache_d) |
668 | - print_changelogs(cache, cache_d, manifest_from, manifest_to, changed) |
669 | - |
670 | - |
671 | -if __name__ == "__main__": |
672 | - main() |