def maintainer_history(options, logger):
    fetchdir = settings.LAYER_FETCH_DIR
    if options.plan:
        maintplans = MaintenancePlan.objects.filter(id=int(options.plan))
        if not maintplans.exists():
            logger.error('No maintenance plan with ID %s found' % options.plan)
            sys.exit(1)
    else:
        maintplans = MaintenancePlan.objects.filter(updates_enabled=True)
        if not maintplans.exists():
            logger.error('No enabled maintenance plans found')
            sys.exit(1)

    lockfn = os.path.join(fetchdir, "layerindex.lock")
    lockfile = utils.lock_file(lockfn)
    if not lockfile:
        logger.error("Layer index lock timeout expired")
        sys.exit(1)
    try:
        for maintplan in maintplans:
            for item in maintplan.maintenanceplanlayerbranch_set.all():
                layerbranch = item.layerbranch
                if options.fullreload and not options.dry_run:
                    RecipeMaintainerHistory.objects.filter(
                        layerbranch=layerbranch).delete()
                urldir = str(layerbranch.layer.get_fetch_dir())
                repodir = os.path.join(fetchdir, urldir)
                layerdir = os.path.join(repodir, layerbranch.vcs_subdir)

                if maintplan.maintainer_style == 'I':
                    # maintainers.inc
                    maintainers_inc_history(options, logger, maintplan,
                                            layerbranch, repodir, layerdir)
                elif maintplan.maintainer_style == 'L':
                    # Layer-wide, don't need to do anything
                    logger.debug(
                        'Skipping maintainer processing for %s - plan %s maintainer style is layer-wide'
                        % (layerbranch, maintplan))
                else:
                    raise Exception(
                        'Unknown maintainer style %s for maintenance plan %s' %
                        (maintplan.maintainer_style, maintplan))
    finally:
        utils.unlock_file(lockfile)
def upgrade_history(options, logger):
    from rrs.models import MaintenancePlan, RecipeUpgrade, Release, Milestone

    if options.plan:
        maintplans = MaintenancePlan.objects.filter(id=int(options.plan))
        if not maintplans.exists():
            logger.error('No maintenance plan with ID %s found' % options.plan)
            sys.exit(1)
    else:
        maintplans = MaintenancePlan.objects.filter(updates_enabled=True)
        if not maintplans.exists():
            logger.error('No enabled maintenance plans found')
            sys.exit(1)

    lockfn = os.path.join(fetchdir, "layerindex.lock")
    lockfile = utils.lock_file(lockfn)
    if not lockfile:
        logger.error("Layer index lock timeout expired")
        sys.exit(1)
    try:
        for maintplan in maintplans:
            # Check releases and milestones
            current = date.today()
            current_release = Release.get_by_date(maintplan, current)
            if current_release:
                current_milestone = Milestone.get_by_release_and_date(
                    current_release, current)
                if not current_milestone:
                    logger.warning(
                        '%s: there is no milestone defined in the latest release (%s) that covers the current date, so up-to-date data will not be visible in the web interface.'
                        % (maintplan, current_release))
            else:
                logger.warning(
                    '%s: there is no release defined that covers the current date, so up-to-date data will not be visible in the web interface.'
                    % maintplan)

            for maintplanbranch in maintplan.maintenanceplanlayerbranch_set.all(
            ):
                layerbranch = maintplanbranch.layerbranch
                if options.fullreload and not options.dry_run:
                    RecipeUpgrade.objects.filter(
                        recipe__layerbranch=layerbranch).delete()
                layer = layerbranch.layer
                urldir = layer.get_fetch_dir()
                repodir = os.path.join(fetchdir, urldir)
                layerdir = os.path.join(repodir, layerbranch.vcs_subdir)

                if options.commit:
                    initial = False
                    since = options.commit
                    since_option = '%s^..%s' % (options.commit, options.commit)
                elif maintplanbranch.upgrade_rev and not options.fullreload:
                    initial = False
                    since = maintplanbranch.upgrade_date
                    since_option = '%s..origin/master' % maintplanbranch.upgrade_rev
                else:
                    initial = True
                    since = options.since
                    since_option = '--since="%s" origin/master' % since

                repo = git.Repo(repodir)
                assert repo.bare == False

                commits = utils.runcmd(
                    "git log %s --format='%%H %%ct' --reverse" % since_option,
                    repodir,
                    logger=logger)
                commit_list = commits.split('\n')

                bitbake_map = {}
                # Filter out some bad commits
                bitbake_commits = utils.runcmd(
                    "git rev-list fef18b445c0cb6b266cd939b9c78d7cbce38663f^..39780b1ccbd76579db0fc6fb9369c848a3bafa9d^",
                    bitbakepath,
                    logger=logger)
                bitbake_commit_list = bitbake_commits.splitlines()
                for commit in bitbake_commit_list:
                    bitbake_map[
                        commit] = '39780b1ccbd76579db0fc6fb9369c848a3bafa9d'

                if initial:
                    logger.debug("Adding initial upgrade history ....")

                    ct, ctepoch = commit_list.pop(0).split()
                    ctdate = datetime.fromtimestamp(int(ctepoch))
                    run_internal(maintplanbranch,
                                 ct,
                                 ctdate,
                                 options,
                                 logger,
                                 bitbake_map,
                                 initial=True)

                if layerbranch.vcs_subdir:
                    layersubdir_start = layerbranch.vcs_subdir
                    if not layersubdir_start.endswith('/'):
                        layersubdir_start += '/'
                else:
                    layersubdir_start = ''
                logger.debug("Adding upgrade history from %s to %s ..." %
                             (since, datetime.today().strftime("%Y-%m-%d")))
                for item in commit_list:
                    if item:
                        ct, ctepoch = item.split()
                        ctdate = datetime.fromtimestamp(int(ctepoch))
                        commitobj = repo.commit(ct)
                        touches_recipe = False
                        for parent in commitobj.parents:
                            diff = parent.diff(commitobj)
                            for diffitem in diff:
                                if layersubdir_start and not (
                                        diffitem.a_path.startswith(
                                            layersubdir_start) or diffitem.
                                        b_path.startswith(layersubdir_start)):
                                    # Not in this layer, skip it
                                    continue
                                if diffitem.a_path.endswith(
                                    ('.bb',
                                     '.inc')) or diffitem.b_path.endswith(
                                         ('.bb', '.inc')):
                                    # We need to look at this commit
                                    touches_recipe = True
                                    break
                            if touches_recipe:
                                break
                        if not touches_recipe:
                            # No recipes in the layer changed in this commit
                            # NOTE: Whilst it's possible that a change to a class might alter what's
                            # in the recipe, we can ignore that since we are only concerned with actual
                            # upgrades which would always require some sort of change to the recipe
                            # or an include file, so we can safely skip commits that don't do that
                            logger.debug("Skipping commit %s" % ct)
                            continue
                        logger.debug("Analysing commit %s ..." % ct)
                        run_internal(maintplanbranch, ct, ctdate, options,
                                     logger, bitbake_map)
                        if not options.dry_run:
                            maintplanbranch.upgrade_rev = ct
                            maintplanbranch.upgrade_date = ctdate
                            maintplanbranch.save()
    finally:
        utils.unlock_file(lockfile)
    if options.plan:
        maintplans = MaintenancePlan.objects.filter(id=int(options.plan))
        if not maintplans.exists():
            logger.error('No maintenance plan with ID %s found' % options.plan)
            sys.exit(1)
    else:
        maintplans = MaintenancePlan.objects.filter(updates_enabled=True)
        if not maintplans.exists():
            logger.error('No enabled maintenance plans found')
            sys.exit(1)

    logger.debug("Starting upstream history...")

    lockfn = os.path.join(fetchdir, "layerindex.lock")
    lockfile = utils.lock_file(lockfn)
    if not lockfile:
        logger.error("Layer index lock timeout expired")
        sys.exit(1)
    try:
        origsyspath = sys.path

        for maintplan in maintplans:
            for item in maintplan.maintenanceplanlayerbranch_set.all():
                layerbranch = item.layerbranch
                try:
                    with transaction.atomic():
                        sys.path = origsyspath

                        layer = layerbranch.layer
                        urldir = layer.get_fetch_dir()
def upgrade_history(options, logger):
    from rrs.models import MaintenancePlan, RecipeUpgrade, Release, Milestone, RecipeUpgradeGroupRule

    logger.debug('=== BEGIN; cmdline: %s' % (' '.join(sys.argv)))

    if options.plan:
        maintplans = MaintenancePlan.objects.filter(id=int(options.plan))
        if not maintplans.exists():
            logger.error('No maintenance plan with ID %s found' % options.plan)
            sys.exit(1)
    else:
        maintplans = MaintenancePlan.objects.filter(updates_enabled=True)
        if not maintplans.exists():
            logger.error('No enabled maintenance plans found')
            sys.exit(1)

    if options.regroup:
        for maintplan in maintplans:
            for maintplanbranch in maintplan.maintenanceplanlayerbranch_set.all(
            ):
                layerbranch = maintplanbranch.layerbranch
                if RecipeUpgradeGroupRule.objects.filter(
                        layerbranch=layerbranch).exists():
                    for ru in RecipeUpgrade.objects.filter(
                            recipesymbol__layerbranch=layerbranch):
                        if ru.regroup():
                            ru.save()
        sys.exit(0)

    lockfn = os.path.join(fetchdir, "layerindex.lock")
    lockfile = utils.lock_file(lockfn)
    if not lockfile:
        logger.error("Layer index lock timeout expired")
        sys.exit(1)
    try:
        for maintplan in maintplans:
            # Check releases and milestones
            current = date.today()
            current_release = Release.get_by_date(maintplan, current)
            if current_release:
                current_milestone = Milestone.get_by_release_and_date(
                    current_release, current)
                if not current_milestone:
                    logger.warning(
                        '%s: there is no milestone defined in the latest release (%s) that covers the current date, so up-to-date data will not be visible in the web interface.'
                        % (maintplan, current_release))
            else:
                logger.warning(
                    '%s: there is no release defined that covers the current date, so up-to-date data will not be visible in the web interface.'
                    % maintplan)

            for maintplanbranch in maintplan.maintenanceplanlayerbranch_set.all(
            ):
                layerbranch = maintplanbranch.layerbranch
                if options.fullreload and not options.dry_run:
                    logger.debug('fullreload: deleting upgrade objects')
                    if options.filter_files:
                        RecipeUpgrade.objects.filter(
                            recipesymbol__layerbranch=layerbranch,
                            filepath__startswith=options.filter_files).delete(
                            )
                    else:
                        RecipeUpgrade.objects.filter(
                            recipesymbol__layerbranch=layerbranch).delete()
                layer = layerbranch.layer
                urldir = layer.get_fetch_dir()
                repodir = os.path.join(fetchdir, urldir)
                layerdir = os.path.join(repodir, layerbranch.vcs_subdir)

                if options.commit:
                    initial = False
                    since = options.commit
                    since_option = [
                        '%s^..%s' % (options.commit, options.commit)
                    ]
                elif maintplanbranch.upgrade_rev and not options.fullreload:
                    initial = False
                    since = maintplanbranch.upgrade_date
                    since_option = [
                        '%s..origin/master' % maintplanbranch.upgrade_rev
                    ]
                else:
                    initial = True
                    since = options.since
                    since_option = ['--since=%s' % since, 'origin/master']

                repo = git.Repo(repodir)
                if repo.bare:
                    logger.error('Repository %s is bare, not supported' %
                                 repodir)
                    continue

                commits = utils.runcmd(['git', 'log'] + since_option +
                                       ['--format=%H %ct', '--reverse'],
                                       repodir,
                                       logger=logger)
                commit_list = commits.split('\n')

                bitbake_map = {}

                def remap_range(start, end, replacewith=None):
                    if replacewith is None:
                        replacewith = end
                    bitbake_commits = utils.runcmd(
                        ['git', 'rev-list',
                         '%s^..%s^' % (start, end)],
                        bitbakepath,
                        logger=logger)
                    bitbake_commit_list = bitbake_commits.splitlines()
                    for commit in bitbake_commit_list:
                        bitbake_map[commit] = replacewith

                # Filter out some bad commits
                remap_range('fef18b445c0cb6b266cd939b9c78d7cbce38663f',
                            '39780b1ccbd76579db0fc6fb9369c848a3bafa9d')
                remap_range('5796ed550d127853808f38257f8dcc8c1cf59342',
                            '547128731e62b36d2271c4390b3fee2b16c535dc')

                if options.stop_commit and (options.stop_commit not in [
                        x.split()[0] for x in commit_list
                ]):
                    logger.error('Stop commit %s is not in repository %s' %
                                 (options.stop_commit, repodir))
                    sys.exit(1)

                if initial:
                    logger.debug("Adding initial upgrade history ....")

                    ct, ctepoch = commit_list.pop(0).split()
                    ctdate = datetime.fromtimestamp(int(ctepoch))
                    run_internal(maintplanbranch,
                                 ct,
                                 ctdate,
                                 options,
                                 logger,
                                 bitbake_map,
                                 initial=True)

                if layerbranch.vcs_subdir:
                    layersubdir_start = layerbranch.vcs_subdir
                    if not layersubdir_start.endswith('/'):
                        layersubdir_start += '/'
                else:
                    layersubdir_start = ''
                logger.debug("Adding upgrade history from %s to %s ..." %
                             (since, datetime.today().strftime("%Y-%m-%d")))
                for item in commit_list:
                    if item:
                        ct, ctepoch = item.split()
                        if ct == options.stop_commit:
                            logger.debug('Stopping at requested commit %s' %
                                         ct)
                            break
                        ctdate = datetime.fromtimestamp(int(ctepoch))
                        commitobj = repo.commit(ct)
                        touches_recipe = False
                        for parent in commitobj.parents:
                            diff = parent.diff(commitobj)
                            for diffitem in diff:
                                if layersubdir_start and not (
                                        diffitem.a_path.startswith(
                                            layersubdir_start) or diffitem.
                                        b_path.startswith(layersubdir_start)):
                                    # Not in this layer, skip it
                                    continue
                                if options.filter_files and not (
                                        diffitem.a_path.startswith(
                                            options.filter_files)
                                        or diffitem.b_path.startswith(
                                            options.filter_files)):
                                    # Doesn't match path filter
                                    continue
                                if diffitem.a_path.endswith(
                                    ('.bb',
                                     '.inc')) or diffitem.b_path.endswith(
                                         ('.bb', '.inc')):
                                    # We need to look at this commit
                                    touches_recipe = True
                                    break
                            if touches_recipe:
                                break
                        if not touches_recipe:
                            # No recipes in the layer changed in this commit
                            # NOTE: Whilst it's possible that a change to a class might alter what's
                            # in the recipe, we can ignore that since we are only concerned with actual
                            # upgrades which would always require some sort of change to the recipe
                            # or an include file, so we can safely skip commits that don't do that
                            logger.debug("Skipping commit %s" % ct)
                            continue
                        logger.debug("Analysing commit %s ..." % ct)
                        run_internal(maintplanbranch, ct, ctdate, options,
                                     logger, bitbake_map)
                        if not (options.dry_run or options.filter_files):
                            maintplanbranch.upgrade_rev = ct
                            maintplanbranch.upgrade_date = ctdate
                            maintplanbranch.save()
    finally:
        utils.unlock_file(lockfile)