Blob Blame History Raw
#!/usr/bin/python -tt
import argparse
import koji
import os
import re
import sys
import traceback

from collections import namedtuple

SigEntry = namedtuple('SigEntry', ['cbstag', 'localdir', 'remotedir', 'desturl'])
KOJI_URL = 'https://cbs.centos.org/kojihub/'
WARNING = "WARNING: {0}: {1} {2}"
ERROR = "ERROR: {0}: {1} {2}"
TAGREGEX = r'(?P<signame>\w+)(?P<centosversion>\d)-(.*)-(?P<releasestage>candidate|testing|release)$'
MIRRORREGEX = r'(?P<centosversion>\d)/(?P<signame>\w+)/(?P<sigstructure>.*)'


def main(files=[]):

    kojiclient = koji.ClientSession(KOJI_URL)
    tags = [x.get('name') for x in kojiclient.listTags()]

    for filename in files:
        print '='*10 + ' Linting: {0} '.format(filename) + '='*10
        with open(filename) as thefile:
            lines = thefile.readlines()

            for lineno,line in enumerate(map(str.strip, lines)):
                if line.startswith('#'):
                    continue
                se = SigEntry(*line.split('|'))

                # Check that we put the right tags in the right list (testing -> buildlogs, release -> sign)
                # and that the CentOS Version matches for all the URLs
                try:
                    gs = re.match(TAGREGEX, se.cbstag.strip('/')).groupdict()
                    if gs['releasestage'] == 'release' and 'buildlogs' in filename:
                        msg = 'release tag in buildlogs list'
                        raise AssertionError

                    if gs['releasestage'] == 'testing' and 'sign' in filename:
                        msg = 'testing tag in sign list'
                        raise AssertionError

                    for attr in ['localdir','remotedir','desturl']:
                            match = re.match(MIRRORREGEX, se._asdict()[attr])
                            if not match:
                                msg = 'Malformed directory: {}\n {} does not match pattern: {}'.format(se._asdict()[attr], attr, MIRRORREGEX)
                                raise AssertionError
                            are = match.groupdict()

                            if are['centosversion'] != gs['centosversion']:
                                msg = 'version location of {0} does not match the CentOS Version of the CBS tag'.format(attr)
                                raise AssertionError

                except AttributeError, e:
                    msg = 'bad cbs tag!'
                    print e
                    print(ERROR.format(filename+':'+str(lineno), se.cbstag.strip('/'), msg))
                    sys.exit(-1)
                except AssertionError, e:
                    msg = msg or "Unkown error"
                    print(WARNING.format(filename+':'+str(lineno), se.cbstag.strip('/'), msg))
                    continue

                # Check that we have trailing slashes for the path components so that RSYNC is happy
                try:
                    for attr in ['cbstag','localdir','remotedir','desturl']:
                        assert(se._asdict()[attr].endswith('/'))
                except AssertionError, e:
                    msg = '{} is missing trailing slash!'.format(attr)
                    print(WARNING.format(filename+':'+str(lineno), se.cbstag.strip('/'), msg))
                    continue

                try:
                    assert(se.cbstag.strip('/') in tags)
                except AssertionError, e:
                    print(ERROR.format(filename+':'+str(lineno), se.cbstag.strip('/'), 'CBS Tag does not exist!'))
                    sys.exit(-1)

                # Check to be sure all the tags have RPMs in them
                try:
                    builds = kojiclient.getLatestBuilds(se.cbstag.strip('/'))
                    assert(len(builds) > 0)
                except AssertionError, e:
                    print(ERROR.format(str(lineno), se.cbstag.strip('/'), 'No RPMS in tag!'))
                    sys.exit(-1)

if __name__ == '__main__':
    parser = argparse.ArgumentParser(description="Check the content control entries for sanity. If called without arguments, cclint will look in the current directory for any files named *_list")

    parser.add_argument('files', metavar='content-control-file',
                        type=str, nargs="*", help="the file to check",
                        default=filter(lambda x: '_list' in x, os.listdir('.'))
                        )

    args = parser.parse_args()
    main(args.files)