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):
|
class MaintenancePlan(models.Model):
|
||||||
|
MAINTENANCEPLAN_MAINTAINER_STYLE = (
|
||||||
|
('I', 'Per-recipe - maintainers.inc'),
|
||||||
|
('L', 'Layer-wide'),
|
||||||
|
)
|
||||||
name = models.CharField(max_length=50, unique=True)
|
name = models.CharField(max_length=50, unique=True)
|
||||||
description = models.TextField(blank=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')
|
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_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 ;)')
|
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')
|
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):
|
def get_default_release(self):
|
||||||
return self.release_set.last()
|
return self.release_set.last()
|
||||||
|
|
||||||
|
def per_recipe_maintainers(self):
|
||||||
|
return self.maintainer_style != 'L'
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return '%s' % (self.name)
|
return '%s' % (self.name)
|
||||||
|
|
||||||
|
|
|
@ -67,6 +67,104 @@ def get_commit_info(info, logger):
|
||||||
|
|
||||||
return (author_name, author_email, date, title)
|
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
|
Recreate Maintainership history from the beginning
|
||||||
"""
|
"""
|
||||||
|
@ -83,8 +181,6 @@ def maintainer_history(options, logger):
|
||||||
logger.error('No enabled maintenance plans found')
|
logger.error('No enabled maintenance plans found')
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
no_maintainer, _ = Maintainer.objects.get_or_create(name='No maintainer')
|
|
||||||
|
|
||||||
lockfn = os.path.join(fetchdir, "layerindex.lock")
|
lockfn = os.path.join(fetchdir, "layerindex.lock")
|
||||||
lockfile = utils.lock_file(lockfn)
|
lockfile = utils.lock_file(lockfn)
|
||||||
if not lockfile:
|
if not lockfile:
|
||||||
|
@ -100,97 +196,14 @@ def maintainer_history(options, logger):
|
||||||
repodir = os.path.join(fetchdir, urldir)
|
repodir = os.path.join(fetchdir, urldir)
|
||||||
layerdir = os.path.join(repodir, layerbranch.vcs_subdir)
|
layerdir = os.path.join(repodir, layerbranch.vcs_subdir)
|
||||||
|
|
||||||
maintainers_full_path = os.path.join(layerdir, MAINTAINERS_INCLUDE_PATH)
|
if maintplan.maintainer_style == 'I':
|
||||||
if not os.path.exists(maintainers_full_path):
|
# maintainers.inc
|
||||||
logger.debug('No maintainers.inc for %s, skipping' % layerbranch)
|
maintainers_inc_history(options, logger, maintplan, layerbranch, repodir, layerdir)
|
||||||
continue
|
elif maintplan.maintainer_style == 'L':
|
||||||
|
# Layer-wide, don't need to do anything
|
||||||
commits = utils.runcmd("git log --format='%%H' --reverse --date=rfc origin/master %s"
|
logger.debug('Skipping maintainer processing for %s - plan %s maintainer style is layer-wide' % (layerbranch, maintplan))
|
||||||
% os.path.join(layerbranch.vcs_subdir, MAINTAINERS_INCLUDE_PATH),
|
else:
|
||||||
repodir, logger=logger)
|
raise Exception('Unknown maintainer style %s for maintenance plan %s' % (maintplan.maintainer_style, maintplan))
|
||||||
|
|
||||||
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
|
|
||||||
finally:
|
finally:
|
||||||
utils.unlock_file(lockfile)
|
utils.unlock_file(lockfile)
|
||||||
|
|
||||||
|
|
|
@ -536,6 +536,7 @@ class RecipeListView(ListView):
|
||||||
context['all_maintplans'] = MaintenancePlan.objects.all()
|
context['all_maintplans'] = MaintenancePlan.objects.all()
|
||||||
context['maintplan_name'] = self.maintplan_name
|
context['maintplan_name'] = self.maintplan_name
|
||||||
maintplan = get_object_or_404(MaintenancePlan, name=self.maintplan_name)
|
maintplan = get_object_or_404(MaintenancePlan, name=self.maintplan_name)
|
||||||
|
context['maintplan'] = maintplan
|
||||||
context['release_name'] = self.release_name
|
context['release_name'] = self.release_name
|
||||||
context['all_releases'] = Release.objects.filter(plan=maintplan).order_by('-end_date')
|
context['all_releases'] = Release.objects.filter(plan=maintplan).order_by('-end_date')
|
||||||
context['milestone_name'] = self.milestone_name
|
context['milestone_name'] = self.milestone_name
|
||||||
|
@ -676,6 +677,7 @@ class RecipeDetailView(DetailView):
|
||||||
|
|
||||||
maintplan = get_object_or_404(MaintenancePlan, name=self.maintplan_name)
|
maintplan = get_object_or_404(MaintenancePlan, name=self.maintplan_name)
|
||||||
context['maintplan_name'] = maintplan.name
|
context['maintplan_name'] = maintplan.name
|
||||||
|
context['maintplan'] = maintplan
|
||||||
release = Release.get_current(maintplan)
|
release = Release.get_current(maintplan)
|
||||||
context['release_name'] = release.name
|
context['release_name'] = release.name
|
||||||
milestone = Milestone.get_current(release)
|
milestone = Milestone.get_current(release)
|
||||||
|
|
|
@ -53,7 +53,13 @@
|
||||||
<li class="lead">Upstream version: <span>{{ upstream_version }}</span></li>
|
<li class="lead">Upstream version: <span>{{ upstream_version }}</span></li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<li class="divider-vertical"></li>
|
<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>
|
<li class="divider-vertical"></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -40,6 +40,7 @@
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
|
{% if maintplan.per_recipe_maintainers %}
|
||||||
<li><p>and</p></li>
|
<li><p>and</p></li>
|
||||||
<li class="dropdown">
|
<li class="dropdown">
|
||||||
<a data-toggle="dropdown" href="#" class="dropdown-toggle" id="selected-maintainer">
|
<a data-toggle="dropdown" href="#" class="dropdown-toggle" id="selected-maintainer">
|
||||||
|
@ -56,6 +57,7 @@
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
|
{% endif %}
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<form id="form-search" class="pull-right input-append">
|
<form id="form-search" class="pull-right input-append">
|
||||||
|
@ -80,7 +82,9 @@
|
||||||
<th class="upstream_status_column span2">Upstream status</th>
|
<th class="upstream_status_column span2">Upstream status</th>
|
||||||
<th class="last_updated_column">Last Updated</th>
|
<th class="last_updated_column">Last Updated</th>
|
||||||
<th class="patches_column">Patches</th>
|
<th class="patches_column">Patches</th>
|
||||||
|
{% if maintplan.per_recipe_maintainers %}
|
||||||
<th class="maintainer_column">Maintainer</th>
|
<th class="maintainer_column">Maintainer</th>
|
||||||
|
{% endif %}
|
||||||
<th class="summary_column muted span5">Summary</th>
|
<th class="summary_column muted span5">Summary</th>
|
||||||
<th class="no_update_reason_column muted span5" style="display:none">No update reason</th>
|
<th class="no_update_reason_column muted span5" style="display:none">No update reason</th>
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -104,7 +108,9 @@
|
||||||
</td>
|
</td>
|
||||||
<td class="last_updated_column">{{r.outdated}}</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>
|
<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>
|
<td class="maintainer_column">{{ r.maintainer_name }}</td>
|
||||||
|
{% endif %}
|
||||||
<td class="summary_column">{{ r.summary }}</td>
|
<td class="summary_column">{{ r.summary }}</td>
|
||||||
<td class="no_update_reason_column" style="display:none">{{ r.no_update_reason }}</td>
|
<td class="no_update_reason_column" style="display:none">{{ r.no_update_reason }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
Loading…
Reference in New Issue
Block a user