| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169 | #!/usr/bin/env python"""Query the github API for the git tags of a project, and return a list ofversion tags for recent releases, or the default release.The default release is the most recent non-RC version.Recent is a list of unique major.minor versions, where each is the mostrecent version in the series.For example, if the list of versions is:    1.8.0-rc2    1.8.0-rc1    1.7.1    1.7.0    1.7.0-rc1    1.6.2    1.6.1`default` would return `1.7.1` and`recent -n 3` would return `1.8.0-rc2 1.7.1 1.6.2`"""from __future__ import absolute_importfrom __future__ import print_functionfrom __future__ import unicode_literalsimport argparseimport itertoolsimport operatorimport sysfrom collections import namedtupleimport requestsGITHUB_API = 'https://api.github.com/repos'class Version(namedtuple('_Version', 'major minor patch rc edition')):    @classmethod    def parse(cls, version):        edition = None        version = version.lstrip('v')        version, _, rc = version.partition('-')        if rc:            if 'rc' not in rc:                edition = rc                rc = None            elif '-' in rc:                edition, rc = rc.split('-')        major, minor, patch = version.split('.', 3)        return cls(major, minor, patch, rc, edition)    @property    def major_minor(self):        return self.major, self.minor    @property    def order(self):        """Return a representation that allows this object to be sorted        correctly with the default comparator.        """        # rc releases should appear before official releases        rc = (0, self.rc) if self.rc else (1, )        return (int(self.major), int(self.minor), int(self.patch)) + rc    def __str__(self):        rc = '-{}'.format(self.rc) if self.rc else ''        edition = '-{}'.format(self.edition) if self.edition else ''        return '.'.join(map(str, self[:3])) + edition + rcBLACKLIST = [  # List of versions known to be broken and should not be used    Version.parse('18.03.0-ce-rc2'),]def group_versions(versions):    """Group versions by `major.minor` releases.    Example:        >>> group_versions([                Version(1, 0, 0),                Version(2, 0, 0, 'rc1'),                Version(2, 0, 0),                Version(2, 1, 0),            ])        [            [Version(1, 0, 0)],            [Version(2, 0, 0), Version(2, 0, 0, 'rc1')],            [Version(2, 1, 0)],        ]    """    return list(        list(releases)        for _, releases        in itertools.groupby(versions, operator.attrgetter('major_minor'))    )def get_latest_versions(versions, num=1):    """Return a list of the most recent versions for each major.minor version    group.    """    versions = group_versions(versions)    num = min(len(versions), num)    return [versions[index][0] for index in range(num)]def get_default(versions):    """Return a :class:`Version` for the latest non-rc version."""    for version in versions:        if not version.rc:            return versiondef get_versions(tags):    for tag in tags:        try:            v = Version.parse(tag['name'])            if v not in BLACKLIST:                yield v        except ValueError:            print("Skipping invalid tag: {name}".format(**tag), file=sys.stderr)def get_github_releases(projects):    """Query the Github API for a list of version tags and return them in    sorted order.    See https://developer.github.com/v3/repos/#list-tags    """    versions = []    for project in projects:        url = '{}/{}/tags'.format(GITHUB_API, project)        response = requests.get(url)        response.raise_for_status()        versions.extend(get_versions(response.json()))    return sorted(versions, reverse=True, key=operator.attrgetter('order'))def parse_args(argv):    parser = argparse.ArgumentParser(description=__doc__)    parser.add_argument('project', help="Github project name (ex: docker/docker)")    parser.add_argument('command', choices=['recent', 'default'])    parser.add_argument('-n', '--num', type=int, default=2,                        help="Number of versions to return from `recent`")    return parser.parse_args(argv)def main(argv=None):    args = parse_args(argv)    versions = get_github_releases(args.project.split(','))    if args.command == 'recent':        print(' '.join(map(str, get_latest_versions(versions, args.num))))    elif args.command == 'default':        print(get_default(versions))    else:        raise ValueError("Unknown command {}".format(args.command))if __name__ == "__main__":    main()
 |