rrs: add flag to MaintenancePlan to specify layer-wide maintainers

Most layers do not track maintenance on a per-recipe basis, and for
those layers we will hide some of the per-recipe maintainer features
and on the recipe detail show the layer maintainer(s) as the
maintainer(s) of the recipe.

Signed-off-by: Paul Eggleton <paul.eggleton@linux.intel.com>
This commit is contained in:
Paul Eggleton 2018-04-18 16:11:51 +12:00
parent 369b03c6db
commit e071ebab29
6 changed files with 149 additions and 94 deletions

View File

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.12 on 2018-04-18 02:26
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('rrs', '0016_rmh_layerbranch_nonnull'),
]
operations = [
migrations.AddField(
model_name='maintenanceplan',
name='maintainer_style',
field=models.CharField(choices=[('I', 'Per-recipe - maintainers.inc'), ('L', 'Layer-wide')], default='L', help_text='Maintainer tracking style for the layers within this plan', max_length=1),
),
]

View File

@ -18,6 +18,10 @@ from django.core.exceptions import ObjectDoesNotExist
class MaintenancePlan(models.Model):
MAINTENANCEPLAN_MAINTAINER_STYLE = (
('I', 'Per-recipe - maintainers.inc'),
('L', 'Layer-wide'),
)
name = models.CharField(max_length=50, unique=True)
description = models.TextField(blank=True)
updates_enabled = models.BooleanField('Enable updates', default=True, help_text='Enable automatically updating metadata for this plan via the update scripts')
@ -26,10 +30,14 @@ class MaintenancePlan(models.Model):
email_from = models.CharField(max_length=255, blank=True, help_text='Sender for automated emails')
email_to = models.CharField(max_length=255, blank=True, help_text='Recipient for automated emails (separate multiple addresses with ;)')
admin = models.ForeignKey(User, blank=True, null=True, help_text='Plan administrator')
maintainer_style = models.CharField(max_length=1, choices=MAINTENANCEPLAN_MAINTAINER_STYLE, default='L', help_text='Maintainer tracking style for the layers within this plan')
def get_default_release(self):
return self.release_set.last()
def per_recipe_maintainers(self):
return self.maintainer_style != 'L'
def __str__(self):
return '%s' % (self.name)

View File

@ -67,48 +67,21 @@ def get_commit_info(info, logger):
return (author_name, author_email, date, title)
"""
Recreate Maintainership history from the beginning
"""
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)
no_maintainer, _ = Maintainer.objects.get_or_create(name='No maintainer')
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)
def maintainers_inc_history(options, logger, maintplan, layerbranch, repodir, layerdir):
maintainers_full_path = os.path.join(layerdir, MAINTAINERS_INCLUDE_PATH)
if not os.path.exists(maintainers_full_path):
logger.debug('No maintainers.inc for %s, skipping' % layerbranch)
continue
logger.warning('Maintainer style is maintainers.inc for plan %s but no maintainers.inc exists in for %s' % (maintplan, layerbranch))
return
logger.debug('Checking maintainers.inc history for %s' % layerbranch)
commits = utils.runcmd("git log --format='%%H' --reverse --date=rfc origin/master %s"
% os.path.join(layerbranch.vcs_subdir, MAINTAINERS_INCLUDE_PATH),
repodir, logger=logger)
no_maintainer, _ = Maintainer.objects.get_or_create(name='No maintainer')
try:
with transaction.atomic():
for commit in commits.strip().split("\n"):
@ -191,6 +164,46 @@ def maintainer_history(options, logger):
raise DryRunRollbackException
except DryRunRollbackException:
pass
"""
Recreate Maintainership history from the beginning
"""
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)

View File

@ -536,6 +536,7 @@ class RecipeListView(ListView):
context['all_maintplans'] = MaintenancePlan.objects.all()
context['maintplan_name'] = self.maintplan_name
maintplan = get_object_or_404(MaintenancePlan, name=self.maintplan_name)
context['maintplan'] = maintplan
context['release_name'] = self.release_name
context['all_releases'] = Release.objects.filter(plan=maintplan).order_by('-end_date')
context['milestone_name'] = self.milestone_name
@ -676,6 +677,7 @@ class RecipeDetailView(DetailView):
maintplan = get_object_or_404(MaintenancePlan, name=self.maintplan_name)
context['maintplan_name'] = maintplan.name
context['maintplan'] = maintplan
release = Release.get_current(maintplan)
context['release_name'] = release.name
milestone = Milestone.get_current(release)

View File

@ -53,7 +53,13 @@
<li class="lead">Upstream version: <span>{{ upstream_version }}</span></li>
{% endif %}
<li class="divider-vertical"></li>
<li class="lead">Maintainer: <span><a href="{% url 'rrs_recipes' maintplan_name release_name milestone_name %}?maintainer_name={{ maintainer_name|urlencode }}">{{ maintainer_name }}</a></span></li>
<li class="lead">Maintainer:
{% if maintplan.per_recipe_maintainers %}
<span><a href="{% url 'rrs_recipes' maintplan_name release_name milestone_name %}?maintainer_name={{ maintainer_name|urlencode }}">{{ maintainer_name }}</a></span>
{% else %}
<span>{% for maintainer in recipe.layerbranch.active_maintainers %}{{ maintainer.name }}{% if not forloop.last %}, {% endif %}{% endfor %}</span>
{% endif %}
</li>
<li class="divider-vertical"></li>
</ul>
</div>

View File

@ -40,6 +40,7 @@
{% endfor %}
</ul>
</li>
{% if maintplan.per_recipe_maintainers %}
<li><p>and</p></li>
<li class="dropdown">
<a data-toggle="dropdown" href="#" class="dropdown-toggle" id="selected-maintainer">
@ -56,6 +57,7 @@
{% endfor %}
</ul>
</li>
{% endif %}
</ul>
<form id="form-search" class="pull-right input-append">
@ -80,7 +82,9 @@
<th class="upstream_status_column span2">Upstream status</th>
<th class="last_updated_column">Last Updated</th>
<th class="patches_column">Patches</th>
{% if maintplan.per_recipe_maintainers %}
<th class="maintainer_column">Maintainer</th>
{% endif %}
<th class="summary_column muted span5">Summary</th>
<th class="no_update_reason_column muted span5" style="display:none">No update reason</th>
</tr>
@ -104,7 +108,9 @@
</td>
<td class="last_updated_column">{{r.outdated}}</td>
<td class="patches_column">{% if r.patches_total %}<span {% if not r.patches_pending %}class="muted"{% endif %}>{{ r.patches_pending }}<span class="muted"> / {{ r.patches_total }}</span>{% endif %}</td>
{% if maintplan.per_recipe_maintainers %}
<td class="maintainer_column">{{ r.maintainer_name }}</td>
{% endif %}
<td class="summary_column">{{ r.summary }}</td>
<td class="no_update_reason_column" style="display:none">{{ r.no_update_reason }}</td>
</tr>