git @ Cat's Eye Technologies Chrysoberyl / master script / release-distfile
master

Tree @master (Download .tar.gz)

release-distfile @masterraw · history · blame

#!/usr/bin/env python3

# SPDX-FileCopyrightText: Chris Pressey, the original author of this work, has dedicated it to the public domain.
# For more information, please refer to <https://unlicense.org/>
# SPDX-License-Identifier: Unlicense

"""
Create a release distfile from the latest tag in the current repository.
"""

from argparse import ArgumentParser
import json
import logging
import os
from os import path
import re
import sys
import subprocess


logging.basicConfig(level=logging.INFO)
logger = logging.getLogger()

DEFAULT_DEST_DIR =  os.path.join(
    os.getenv('HOME'), 'canonical', 'static.catseye.tc', 'distfiles'
)


def match_tag(distro, tag):
    match = re.match(r'^rel_(\d+)_(\d+)_(\d+)_(\d+)$', tag)
    if match:
        v_maj = match.group(1)
        v_min = match.group(2)
        r_maj = match.group(3)
        r_min = match.group(4)
        filename = '%s-%s.%s-%s.%s' % (
            distro, v_maj, v_min, r_maj, r_min
        )
        return (v_maj, v_min, r_maj, r_min, filename)

    match = re.match(r'^rel_(\d+)_(\d+)$', tag)
    if match:
        v_maj = match.group(1)
        v_min = match.group(2)
        filename = '%s-%s.%s' % (distro, v_maj, v_min)
        return (v_maj, v_min, "0", "0", filename)

    match = re.match(r'^v?(\d+)\.(\d+)\-(\d+)\.(\d+)$', tag)
    if match:
        v_maj = match.group(1)
        v_min = match.group(2)
        r_maj = match.group(3)
        r_min = match.group(4)
        filename = '%s-%s.%s-%s.%s' % (
            distro, v_maj, v_min, r_maj, r_min
        )
        return (v_maj, v_min, r_maj, r_min, filename)

    match = re.match(r'^v?(\d+)\.(\d+)$', tag)
    if match:
        v_maj = match.group(1)
        v_min = match.group(2)
        filename = '%s-%s.%s' % (distro, v_maj, v_min)
        return (v_maj, v_min, "0", "0", filename)

    raise ValueError("Not a release tag that I understand: %s" % tag)


def do_it(command, **kwargs):
    subprocess.check_call(command, shell=True, **kwargs)


def get_it(command):
    return subprocess.Popen(
        command, shell=True, stdout=subprocess.PIPE
    ).communicate()[0].decode('utf-8')


def each_tag():
    # borrowed from http://stackoverflow.com/a/24830212, could probably be simplified
    output = get_it(
        "git for-each-ref --format='%(*committerdate:raw)%(committerdate:raw) "
        "%(refname) %(*objectname) %(objectname)' refs/tags | sort -n | awk '{ print $3; }'"
    )
    for line in output.split('\n'):
        if line:
            match = re.match(r'^refs/tags/(.*?)$', line)
            if match:
               yield match.group(1)


def main(args):
    argparser = ArgumentParser()

    argparser.add_argument('distro', type=str)
    argparser.add_argument(
        '--dest-dir', metavar="DIR", type=str, default=DEFAULT_DEST_DIR,
        help="Directory into which to write the zipball"
    )
    argparser.add_argument(
        '--dry-run', action='store_true', default=False,
        help="Don't actually write the zipball, only show what we would do"
    )
    argparser.add_argument(
        '--entry', metavar="KEY", type=str, default=None,
        help="Name of distribution entry in Chrysoberyl to update"
    )
    argparser.add_argument(
        '--force', action='store_true', default=False,
        help="Overwrite existing zipball if present"
    )
    argparser.add_argument(
        '--tag', metavar="TAG", type=str, default=None,
        help="Specify particular tag to use instead of finding latest"
    )

    options = argparser.parse_args(sys.argv[1:])

    distributions_filename = path.join(path.dirname(path.realpath(sys.argv[0])), '..', 'distribution', 'distributions.json')
    with open(distributions_filename, 'r') as f:
        distributions = json.loads(f.read())

    latest_tag = options.tag
    if latest_tag is None:
        for tag in each_tag():
            latest_tag = tag
    logger.info("Latest tag: %s", latest_tag)

    (v_maj, v_min, r_maj, r_min, base_projectame) = match_tag(options.distro, latest_tag)
    zip_filename = '{}.zip'.format(base_projectame)
    logger.info("Versioning: %s", repr((v_maj, v_min, r_maj, r_min, zip_filename)))

    dest_dir = options.dest_dir
    if not os.path.exists(dest_dir):
        raise IOError("{} not available".format(dest_dir))

    full_filename = os.path.join(dest_dir, zip_filename)
    if os.path.exists(full_filename) and not options.force:
        raise IOError("{} exists".format(full_filename))
    do_it('ls -l {}/{}* || echo "OKAY: This is the first zipball for this entry."'.format(dest_dir, options.distro))
    command = "git archive --prefix={}/ --format=zip {} -o {}".format(base_projectame, latest_tag, full_filename)
    logger.info("command: '%s'", command)
    if not options.dry_run:
        do_it(command)
        do_it("unzip -v {}".format(full_filename))

    if options.entry is not None:
        version = "{}.{}".format(v_maj, v_min)
        revision = "{}.{}".format(r_maj, r_min)
        url = 'https://catseye.tc/distfiles/{}'.format(zip_filename)
        info = {
            'version': version,
            'revision': revision,
            'url': url,
        }
        distributions["{} distribution".format(options.entry)]['releases'].append(info)
        if not options.dry_run:
            with open(distributions_filename, 'w') as f:
                f.write(json.dumps(distributions, indent=4, sort_keys=True))

    sys.exit(0)


if __name__ == '__main__':
    main(sys.argv[1:])