mirror of
git://git.yoctoproject.org/layerindex-web.git
synced 2025-07-19 20:59:01 +02:00
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:
parent
369b03c6db
commit
e071ebab29
20
rrs/migrations/0017_maintenanceplan_maintainer_style.py
Normal file
20
rrs/migrations/0017_maintenanceplan_maintainer_style.py
Normal 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),
|
||||
),
|
||||
]
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -67,6 +67,104 @@ def get_commit_info(info, logger):
|
|||
|
||||
return (author_name, author_email, date, title)
|
||||
|
||||
|
||||
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.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"):
|
||||
if RecipeMaintainerHistory.objects.filter(layerbranch=layerbranch, sha1=commit):
|
||||
continue
|
||||
|
||||
logger.debug("Analysing commit %s ..." % (commit))
|
||||
|
||||
(author_name, author_email, date, title) = \
|
||||
get_commit_info(utils.runcmd("git show " + commit, repodir,
|
||||
logger=logger), logger)
|
||||
|
||||
author = Maintainer.create_or_update(author_name, author_email)
|
||||
rms = RecipeMaintainerHistory(title=title, date=date, author=author,
|
||||
sha1=commit, layerbranch=layerbranch)
|
||||
rms.save()
|
||||
|
||||
utils.runcmd("git checkout %s -f" % commit,
|
||||
repodir, logger=logger)
|
||||
|
||||
lines = [line.strip() for line in open(maintainers_full_path)]
|
||||
for line in lines:
|
||||
res = get_recipe_maintainer(line, logger)
|
||||
if res:
|
||||
(pn, name, email) = res
|
||||
qry = Recipe.objects.filter(pn = pn, layerbranch = layerbranch)
|
||||
|
||||
if qry:
|
||||
m = Maintainer.create_or_update(name, email)
|
||||
|
||||
rm = RecipeMaintainer()
|
||||
rm.recipe = qry[0]
|
||||
rm.maintainer = m
|
||||
rm.history = rms
|
||||
rm.save()
|
||||
|
||||
logger.debug("%s: Change maintainer to %s in commit %s." % \
|
||||
(pn, m.name, commit))
|
||||
else:
|
||||
logger.debug("%s: Not found in %s." % \
|
||||
(pn, layerbranch))
|
||||
|
||||
# set missing recipes to no maintainer
|
||||
for recipe in layerbranch.recipe_set.all():
|
||||
if not RecipeMaintainer.objects.filter(recipe = recipe, history = rms):
|
||||
rm = RecipeMaintainer()
|
||||
rm.recipe = recipe
|
||||
link_maintainer = RecipeMaintenanceLink.link_maintainer(recipe.pn, rms)
|
||||
if link_maintainer:
|
||||
rm.maintainer = link_maintainer.maintainer
|
||||
else:
|
||||
rm.maintainer = no_maintainer
|
||||
rm.history = rms
|
||||
rm.save()
|
||||
if link_maintainer:
|
||||
logger.debug("%s: linked to maintainer for %s" % (recipe.pn, link_maintainer.recipe.pn))
|
||||
else:
|
||||
logger.debug("%s: Not found maintainer in commit %s set to 'No maintainer'." % \
|
||||
(recipe.pn, rms.sha1))
|
||||
|
||||
# set new recipes to no maintainer if don't have one
|
||||
rms = RecipeMaintainerHistory.get_last(layerbranch)
|
||||
for recipe in layerbranch.recipe_set.all():
|
||||
if not RecipeMaintainer.objects.filter(recipe = recipe, history = rms):
|
||||
rm = RecipeMaintainer()
|
||||
rm.recipe = recipe
|
||||
link_maintainer = RecipeMaintenanceLink.link_maintainer(recipe.pn, rms)
|
||||
if link_maintainer:
|
||||
rm.maintainer = link_maintainer.maintainer
|
||||
else:
|
||||
rm.maintainer = no_maintainer
|
||||
rm.history = rms
|
||||
rm.save()
|
||||
if link_maintainer:
|
||||
logger.debug("%s: New recipe linked to maintainer for %s" % (recipe.pn, link_maintainer.recipe.pn))
|
||||
else:
|
||||
logger.debug("%s: New recipe not found maintainer set to 'No maintainer'." % \
|
||||
(recipe.pn))
|
||||
if options.dry_run:
|
||||
raise DryRunRollbackException
|
||||
except DryRunRollbackException:
|
||||
pass
|
||||
|
||||
"""
|
||||
Recreate Maintainership history from the beginning
|
||||
"""
|
||||
|
@ -83,8 +181,6 @@ def maintainer_history(options, logger):
|
|||
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:
|
||||
|
@ -100,97 +196,14 @@ def maintainer_history(options, logger):
|
|||
repodir = os.path.join(fetchdir, urldir)
|
||||
layerdir = os.path.join(repodir, layerbranch.vcs_subdir)
|
||||
|
||||
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
|
||||
|
||||
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)
|
||||
|
||||
try:
|
||||
with transaction.atomic():
|
||||
for commit in commits.strip().split("\n"):
|
||||
if RecipeMaintainerHistory.objects.filter(layerbranch=layerbranch, sha1=commit):
|
||||
continue
|
||||
|
||||
logger.debug("Analysing commit %s ..." % (commit))
|
||||
|
||||
(author_name, author_email, date, title) = \
|
||||
get_commit_info(utils.runcmd("git show " + commit, repodir,
|
||||
logger=logger), logger)
|
||||
|
||||
author = Maintainer.create_or_update(author_name, author_email)
|
||||
rms = RecipeMaintainerHistory(title=title, date=date, author=author,
|
||||
sha1=commit, layerbranch=layerbranch)
|
||||
rms.save()
|
||||
|
||||
utils.runcmd("git checkout %s -f" % commit,
|
||||
repodir, logger=logger)
|
||||
|
||||
lines = [line.strip() for line in open(maintainers_full_path)]
|
||||
for line in lines:
|
||||
res = get_recipe_maintainer(line, logger)
|
||||
if res:
|
||||
(pn, name, email) = res
|
||||
qry = Recipe.objects.filter(pn = pn, layerbranch = layerbranch)
|
||||
|
||||
if qry:
|
||||
m = Maintainer.create_or_update(name, email)
|
||||
|
||||
rm = RecipeMaintainer()
|
||||
rm.recipe = qry[0]
|
||||
rm.maintainer = m
|
||||
rm.history = rms
|
||||
rm.save()
|
||||
|
||||
logger.debug("%s: Change maintainer to %s in commit %s." % \
|
||||
(pn, m.name, commit))
|
||||
else:
|
||||
logger.debug("%s: Not found in %s." % \
|
||||
(pn, layerbranch))
|
||||
|
||||
# set missing recipes to no maintainer
|
||||
for recipe in layerbranch.recipe_set.all():
|
||||
if not RecipeMaintainer.objects.filter(recipe = recipe, history = rms):
|
||||
rm = RecipeMaintainer()
|
||||
rm.recipe = recipe
|
||||
link_maintainer = RecipeMaintenanceLink.link_maintainer(recipe.pn, rms)
|
||||
if link_maintainer:
|
||||
rm.maintainer = link_maintainer.maintainer
|
||||
else:
|
||||
rm.maintainer = no_maintainer
|
||||
rm.history = rms
|
||||
rm.save()
|
||||
if link_maintainer:
|
||||
logger.debug("%s: linked to maintainer for %s" % (recipe.pn, link_maintainer.recipe.pn))
|
||||
else:
|
||||
logger.debug("%s: Not found maintainer in commit %s set to 'No maintainer'." % \
|
||||
(recipe.pn, rms.sha1))
|
||||
|
||||
# set new recipes to no maintainer if don't have one
|
||||
rms = RecipeMaintainerHistory.get_last(layerbranch)
|
||||
for recipe in layerbranch.recipe_set.all():
|
||||
if not RecipeMaintainer.objects.filter(recipe = recipe, history = rms):
|
||||
rm = RecipeMaintainer()
|
||||
rm.recipe = recipe
|
||||
link_maintainer = RecipeMaintenanceLink.link_maintainer(recipe.pn, rms)
|
||||
if link_maintainer:
|
||||
rm.maintainer = link_maintainer.maintainer
|
||||
else:
|
||||
rm.maintainer = no_maintainer
|
||||
rm.history = rms
|
||||
rm.save()
|
||||
if link_maintainer:
|
||||
logger.debug("%s: New recipe linked to maintainer for %s" % (recipe.pn, link_maintainer.recipe.pn))
|
||||
else:
|
||||
logger.debug("%s: New recipe not found maintainer set to 'No maintainer'." % \
|
||||
(recipe.pn))
|
||||
if options.dry_run:
|
||||
raise DryRunRollbackException
|
||||
except DryRunRollbackException:
|
||||
pass
|
||||
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)
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Reference in New Issue
Block a user