diff options
-rw-r--r-- | crocoite/test_tools.py | 28 | ||||
-rw-r--r-- | crocoite/tools.py | 71 | ||||
-rw-r--r-- | setup.py | 1 |
3 files changed, 99 insertions, 1 deletions
diff --git a/crocoite/test_tools.py b/crocoite/test_tools.py index 947d020..c320ad9 100644 --- a/crocoite/test_tools.py +++ b/crocoite/test_tools.py @@ -25,8 +25,9 @@ import pytest from warcio.archiveiterator import ArchiveIterator from warcio.warcwriter import WARCWriter from warcio.statusandheaders import StatusAndHeaders +from pkg_resources import parse_version -from .tools import mergeWarc +from .tools import mergeWarc, Errata, FixableErrata from .util import packageUrl @pytest.fixture @@ -195,3 +196,28 @@ def test_resp_revisit_other_url(writer): output.seek(0) recordsEqual (makeGolden (writer, records), ArchiveIterator (output)) +def test_errata_contains(): + """ Test version matching """ + e = Errata('some-uuid', 'description', ['a<1.0']) + assert {'a': parse_version('0.1')} in e + assert {'a': parse_version('1.0')} not in e + assert {'b': parse_version('1.0')} not in e + + e = Errata('some-uuid', 'description', ['a<1.0,>0.1']) + assert {'a': parse_version('0.1')} not in e + assert {'a': parse_version('0.2')} in e + assert {'a': parse_version('1.0')} not in e + + # a AND b + e = Errata('some-uuid', 'description', ['a<1.0', 'b>1.0']) + assert {'a': parse_version('0.1')} not in e + assert {'b': parse_version('1.1')} not in e + assert {'a': parse_version('0.1'), 'b': parse_version('1.1')} in e + +def test_errata_fixable (): + e = Errata('some-uuid', 'description', ['a<1.0', 'b>1.0']) + assert not e.fixable + + e = FixableErrata('some-uuid', 'description', ['a<1.0', 'b>1.0']) + assert e.fixable + diff --git a/crocoite/tools.py b/crocoite/tools.py index e2dc6a7..84c6f44 100644 --- a/crocoite/tools.py +++ b/crocoite/tools.py @@ -26,6 +26,7 @@ import shutil, sys, os, logging, argparse, json from io import BytesIO from warcio.archiveiterator import ArchiveIterator from warcio.warcwriter import WARCWriter +from pkg_resources import parse_version, parse_requirements from .util import packageUrl, getSoftwareInfo def mergeWarc (files, output): @@ -122,3 +123,73 @@ def extractScreenshot (): else: print ('not overwriting {}'.format (outpath)) +class Errata: + __slots__ = ('uuid', 'description', 'affects') + + def __init__ (self, uuid, description, affects): + self.uuid = uuid + self.description = description + # slightly abusing setuptool’s version parsing/matching here + self.affects = list (parse_requirements(affects)) + + def __contains__ (self, pkg): + """ + Return True if the versions in pkg are affected by this errata + + pkg must be a mapping from project_name to version + """ + matchedAll = [] + for a in self.affects: + haveVersion = pkg.get (a.project_name, None) + matchedAll.append (haveVersion is not None and haveVersion in a) + return all (matchedAll) + + def __repr__ (self): + return '{}({!r}, {!r}, {!r})'.format (self.__class__.__name__, + self.uuid, self.description, self.affects) + + @property + def fixable (self): + return getattr (self, 'applyFix', None) is not None + + def toDict (self): + return {'uuid': self.uuid, + 'description': self.description, + 'affects': list (map (str, self.affects)), + 'fixable': self.fixable} + +class FixableErrata(Errata): + def applyFix (self, records): + raise NotImplementedError () # pragma: no cover + +bugs = [ + Errata (uuid='34a176b3-ad3d-430f-a082-68087f304572', + description='Generated by version < 1.0. No erratas are supported for this version.', + affects=['crocoite<1.0'], + ), + ] + +def makeReport (fd): + for record in ArchiveIterator (fd): + if record.rec_type == 'warcinfo': + try: + data = json.load (record.raw_stream) + haveVersions = dict ([(pkg['projectName'], parse_version(pkg['version'])) for pkg in data['software']['self']]) + yield from filter (lambda b: haveVersions in b, bugs) + except json.decoder.JSONDecodeError: + pass + +def errata (): + parser = argparse.ArgumentParser(description='Show/fix erratas for WARCs generated by {}.'.format (__package__)) + parser.add_argument('input', type=argparse.FileType ('rb'), help='Input WARC') + + args = parser.parse_args() + + hasErrata = False + for item in makeReport (args.input): + json.dump (item.toDict (), sys.stdout) + sys.stdout.write ('\n') + sys.stdout.flush () + hasErrata = True + return int (hasErrata) + @@ -26,6 +26,7 @@ setup( 'crocoite-irc-dashboard = crocoite.cli:dashboard', 'crocoite-merge-warc = crocoite.tools:mergeWarcCli', 'crocoite-extract-screenshot = crocoite.tools:extractScreenshot', + 'crocoite-errata = crocoite.tools:errata', ], }, package_data={ |