Add support for handling multiple branches

Change the data structure to support multiple branches. At the top level
there is a set list of Branch objects, and then a LayerBranch object
between each layer and the maintainers, dependencies, recipes and
machines, so that the set of each can be different per branch. The
branch is a session option, and can be selected via a drop-down that is
shown for all pages.

Additionally, with this change we avoid the need to run the update
script within a build environment set up with oe-init-build-env - since
we need a specific version of BitBake per branch we now use our own copy
of BitBake which is fetched by the script itself. The update script will
need to be called multiple times however - once per branch.

Signed-off-by: Paul Eggleton <paul.eggleton@linux.intel.com>
This commit is contained in:
Paul Eggleton 2013-03-04 23:17:00 +00:00
parent 0d52678697
commit 354e368aa2
20 changed files with 410 additions and 203 deletions

46
README
View File

@ -15,16 +15,12 @@ In order to make use of this application you will need:
* A database supported by Django (SQLite, MySQL, etc.). Django takes
care of creating the database itself, you just need to ensure that the
database server (if not using SQLite) is configured and running.
* On the machine that will run the update script (which does not have to
be the same machine as the web server, however it does still have to
have Django installed and have access to the database used by the web
* On the machine that will run the backend update script (which does not
have to be the same machine as the web server, however it does still
have to have Django installed, have the same or similar configuration
in settings.py and have access to the database used by the web
application):
* Python 2.6 or Python 2.7
* A copy of BitBake and OE-Core (or Poky, which includes both) -
the "danny" release or newer is required. It does not need to be
configured specially nor do all of the normal pre-requisites need to
be installed (it is only used for parsing recipes, not actual
building).
* GitPython (python-git) version 0.3.1 or later
* django-registration
* django-reversion
@ -43,31 +39,45 @@ Setup instructions:
python manage.py syncdb
You should answer "yes" when asked to create an admin account.
3. You can test the web application locally by setting DEBUG = True in
settings.py and running the following:
python manage.py runserver
Then visit http://127.0.0.1:8000/layerindex/ with your browser. This
should only be used for testing - for production you need to use a
proper web server and have DEBUG set to False.
Then visit http://127.0.0.1:8000/layerindex/ with your browser. As
with all Django applications there is an admin interface available
at http://127.0.0.1:8000/admin/ also.
4. You may wish to customise layerindex/about.html to suit your
NOTE: This local server should only be used for testing - for
production you need to use a proper web server and have DEBUG set
to False.
4. You'll need to add at least the openembedded-core layer to the
database (or some equivalent for use in basic BitBake configuration;
if it's not called "openembedded-core" then you'll need to set
CORE_LAYER_NAME in settings.py to match.) You can use the
"Submit Layer" feature within the interface itself (easiest) or the
admin interface to do this.
5. You may wish to customise layerindex/about.html to suit your
installation.
Usage
-----
On a regular basis you need to run the update script within an
environment set up for OE-Core build:
On a regular basis you need to run the update script:
$ cd path/to/oe-core
$ . ./oe-init-build-env
$ path/to/layerindex/update.py
path/to/layerindex/update.py
This will fetch all of the layer repositories, analyse their contents
and update the database with the results.
and update the database with the results. Note that if you set up more
than just the master branch in the database, you will need to run the
script once for each branch using -b (or --branch) to specify the
branch name. Run the script with --help for further information on
available options.
Maintenance

View File

@ -30,6 +30,25 @@
<div class="container">
<a class="brand" href="{% url layer_list %}">OpenEmbedded metadata index</a>
<ul class="nav">
{% block branch_selector %}
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
Branch: <b>{{ current_branch }}</b>
<b class="caret"></b>
</a>
<ul class="dropdown-menu">
{% for branch in all_branches %}
{% if branch.name = current_branch %}
<li><a href="{% url switch_branch branch.name %}"><b>{{ branch.name }}</b></a></li>
{% else %}
<li><a href="{% url switch_branch branch.name %}">{{ branch.name }}</a></li>
{% endif %}
{% endfor %}
</ul>
</li>
{% endblock %}
</ul>
<ul class="nav pull-right">
{% block submitlink %}<li><a href="{% url submit_layer %}">Submit layer</a></li>{% endblock %}
{% if user.is_authenticated %}

12
conf/local.conf Normal file
View File

@ -0,0 +1,12 @@
#
# This is a version of local.conf trimmed specially for parsing recipes
# within the OE Layer index update script. Since we're not doing any
# actual building and many variables are defaulted via bitbake.conf or
# the OE-Core basic distro config, we don't need to set very much.
# Just select something basic here:
MACHINE = "qemux86"
# OE-Core basic distro configuration
DISTRO = ""

View File

@ -14,34 +14,39 @@ class LayerMaintainerInline(admin.StackedInline):
class LayerDependencyInline(admin.StackedInline):
model = LayerDependency
fk_name = 'layer'
class BranchAdmin(CompareVersionAdmin):
model = Branch
class LayerItemAdmin(CompareVersionAdmin):
list_filter = ['status', 'layer_type']
save_as = True
search_fields = ['name', 'summary']
readonly_fields = ['vcs_last_fetch', 'vcs_last_rev', 'vcs_last_commit']
formfield_overrides = {
models.URLField: {'widget': TextInput(attrs={'size':'100'})},
models.CharField: {'widget': TextInput(attrs={'size':'100'})},
}
class LayerBranchAdmin(CompareVersionAdmin):
list_filter = ['layer__name']
readonly_fields = ['layer', 'branch', 'vcs_last_fetch', 'vcs_last_rev', 'vcs_last_commit']
inlines = [
LayerMaintainerInline,
LayerDependencyInline,
LayerMaintainerInline,
]
class LayerMaintainerAdmin(CompareVersionAdmin):
list_filter = ['status', 'layer__name']
list_filter = ['status', 'layerbranch__layer__name']
class LayerDependencyAdmin(CompareVersionAdmin):
list_filter = ['layer__name']
list_filter = ['layerbranch__layer__name']
class LayerNoteAdmin(CompareVersionAdmin):
list_filter = ['layer__name']
class RecipeAdmin(admin.ModelAdmin):
search_fields = ['filename', 'pn']
list_filter = ['layer__name']
list_filter = ['layerbranch__layer__name', 'layerbranch__branch__name']
readonly_fields = [fieldname for fieldname in Recipe._meta.get_all_field_names() if fieldname != 'recipefiledependency']
def has_add_permission(self, request, obj=None):
return False
@ -50,14 +55,16 @@ class RecipeAdmin(admin.ModelAdmin):
class MachineAdmin(admin.ModelAdmin):
search_fields = ['name']
list_filter = ['layer__name']
list_filter = ['layerbranch__layer__name', 'layerbranch__branch__name']
readonly_fields = Machine._meta.get_all_field_names()
def has_add_permission(self, request, obj=None):
return False
def has_delete_permission(self, request, obj=None):
return False
admin.site.register(Branch, BranchAdmin)
admin.site.register(LayerItem, LayerItemAdmin)
admin.site.register(LayerBranch, LayerBranchAdmin)
admin.site.register(LayerMaintainer, LayerMaintainerAdmin)
admin.site.register(LayerDependency, LayerDependencyAdmin)
admin.site.register(LayerNote, LayerNoteAdmin)

View File

@ -0,0 +1,16 @@
# layerindex-web - custom context processor
#
# Copyright (C) 2013 Intel Corporation
#
# Licensed under the MIT license, see COPYING.MIT for details
from layerindex.models import Branch
def layerindex_context(request):
current_branch = request.session.get('branch', None)
if not current_branch:
current_branch = 'master'
return {
'all_branches': Branch.objects.all(),
'current_branch': current_branch,
}

View File

@ -50,6 +50,13 @@
</div> <!-- end of container-fluid -->
<div class="container-fluid">
{% if not layerbranch %}
<div class="row-fluid">
<div class="alert alert-error">
This layer does not exist for branch {{ current_branch }}.
</div>
</div>
{% endif %}
<div class="row-fluid">
{% for note in layeritem.layernote_set.all %}
<div class="alert">
@ -95,24 +102,25 @@
{% if layeritem.vcs_subdir %}
<h4>Subdirectory</h4>
<p><span data-toggle="tooltip" title="Select subdirectory"><i class="icon-circle-arrow-right selectallicon" for="vcs_subdir" id="vcs_subdir_select"></i></span><span id="vcs_subdir" class="copyable">{{ layeritem.vcs_subdir }}</span>
{% if layeritem.tree_url %}
{% if layerbranch.tree_url %}
<span class="label label-info">
<a href="{{ layeritem.tree_url }}">web subdirectory</a>
<a href="{{ layerbranch.tree_url }}">web subdirectory</a>
</span>
{% endif %}
</p>
{% endif %}
{% if layeritem.vcs_last_commit %}
{% if layerbranch.vcs_last_commit %}
<p>
<span class="muted"><small>Last commit: {{ layeritem.vcs_last_commit|timesince }} ago</small></span>
<span class="muted"><small>Last commit: {{ layerbranch.vcs_last_commit|timesince }} ago</small></span>
</p>
{% endif %}
<h3>{% if layeritem.active_maintainers|length = 1 %}Maintainer{% else %}Maintainers{% endif %}</h3>
{% if layerbranch.active_maintainers.count > 0 %}
<h3>{% if layerbranch.active_maintainers|length = 1 %}Maintainer{% else %}Maintainers{% endif %}</h3>
<ul>
{% for maintainer in layeritem.active_maintainers %}
{% for maintainer in layerbranch.active_maintainers %}
<li>
{{ maintainer.name }}
{% if maintainer.responsibility %}
@ -125,16 +133,17 @@
</li>
{% endfor %}
</ul>
{% endif %}
</div> <!-- end of span7 -->
<div class="span4 pull-right description">
{% if layeritem.dependencies_set.all %}
{% if layerbranch.dependencies_set.count > 0 %}
<div class="well dependency-well">
<h3>Dependencies </h3>
<p>The {{ layeritem.name }} layer depends upon:</p>
<ul>
{% for dep in layeritem.dependencies_set.all %}
{% for dep in layerbranch.dependencies_set.all %}
<li><a href="{% url layer_item dep.dependency.name %}">{{ dep.dependency.name }}</a></li>
{% endfor %}
</ul>
@ -145,11 +154,11 @@
</div> <!-- end of row-fluid -->
</div> <!-- end of container-fluid -->
{% if layeritem.status = 'N' %}
{% if layeritem.status = 'N' and layerbranch %}
<div class="container-fluid" style="margin-bottom:1em;">
{% if layeritem.vcs_web_file_base_url %}
<span class="label label-important">
<a href="{{ layeritem.test_file_url }}">test file link</a>
<a href="{{ layerbranch.test_file_url }}">test file link</a>
</span>
{% else %}
<span class="label">
@ -158,7 +167,7 @@
{% endif %}
{% if layeritem.vcs_web_tree_base_url %}
<span class="label label-important">
<a href="{{ layeritem.test_tree_url }}">test tree link</a>
<a href="{{ layerbranch.test_tree_url }}">test tree link</a>
</span>
{% else %}
<span class="label">
@ -167,7 +176,7 @@
{% endif %}
</div>
{% else %}
{% if layeritem.machine_set.count > 0 %}
{% if layerbranch.machine_set.count > 0 %}
<div class="container-fluid" style="margin-bottom:1em;">
<div class="row-fluid">
<div class="navbar">
@ -178,7 +187,7 @@
<table class="table table-bordered">
<tbody>
{% for machine in layeritem.machine_set.all %}
{% for machine in layerbranch.machine_set.all %}
<tr>
<td><a href="{{ machine.vcs_web_url }}">{{ machine.name }}</a></td>
<td>{{ machine.description }}</td>
@ -190,6 +199,7 @@
</div>
{% endif %}
{% if layerbranch.recipe_set.count > 0 %}
<div class="container-fluid" style="margin-bottom:1em;">
<div class="row-fluid">
<div class="navbar">
@ -217,7 +227,7 @@
</tr>
</thead>
<tbody>
{% for recipe in layeritem.sorted_recipes %}
{% for recipe in layerbranch.sorted_recipes %}
<tr>
<td><a href="{% url recipe recipe.id %}">{{ recipe.name }}</a></td>
<td>{{ recipe.pv }}</td>
@ -229,6 +239,7 @@
</div>
</div>
{% endif %}
{% endif %}
{% endautoescape %}

View File

@ -180,35 +180,35 @@
if( repoval.startsWith('git://git.openembedded.org/') ) {
reponame = repoval.replace(/^.*\//, '')
$('#id_vcs_web_url').val('http://cgit.openembedded.org/cgit.cgi/' + reponame)
$('#id_vcs_web_tree_base_url').val('http://cgit.openembedded.org/cgit.cgi/' + reponame + '/tree/')
$('#id_vcs_web_file_base_url').val('http://cgit.openembedded.org/cgit.cgi/' + reponame + '/tree/')
$('#id_vcs_web_tree_base_url').val('http://cgit.openembedded.org/cgit.cgi/' + reponame + '/tree/%path%?h=%branch%')
$('#id_vcs_web_file_base_url').val('http://cgit.openembedded.org/cgit.cgi/' + reponame + '/tree/%path%?h=%branch%')
}
else if( repoval.indexOf('git.yoctoproject.org/') > -1 ) {
reponame = repoval.replace(/^.*\//, '')
$('#id_vcs_web_url').val('http://git.yoctoproject.org/cgit/cgit.cgi/' + reponame)
$('#id_vcs_web_tree_base_url').val('http://git.yoctoproject.org/cgit/cgit.cgi/' + reponame + '/tree/')
$('#id_vcs_web_file_base_url').val('http://git.yoctoproject.org/cgit/cgit.cgi/' + reponame + '/tree/')
$('#id_vcs_web_tree_base_url').val('http://git.yoctoproject.org/cgit/cgit.cgi/' + reponame + '/tree/%path%?h=%branch%')
$('#id_vcs_web_file_base_url').val('http://git.yoctoproject.org/cgit/cgit.cgi/' + reponame + '/tree/%path%?h=%branch%')
}
else if( repoval.indexOf('github.com/') > -1 ) {
reponame = repoval.replace(/^.*github.com\//, '')
reponame = reponame.replace(/.git$/, '')
$('#id_vcs_web_url').val('http://github.com/' + reponame)
$('#id_vcs_web_tree_base_url').val('http://github.com/' + reponame + '/tree/master/')
$('#id_vcs_web_file_base_url').val('http://github.com/' + reponame + '/blob/master/')
$('#id_vcs_web_tree_base_url').val('http://github.com/' + reponame + '/tree/%branch%/')
$('#id_vcs_web_file_base_url').val('http://github.com/' + reponame + '/blob/%branch%/')
}
else if( repoval.indexOf('gitorious.org/') > -1 ) {
reponame = repoval.replace(/^.*gitorious.org\//, '')
reponame = reponame.replace(/.git$/, '')
$('#id_vcs_web_url').val('http://gitorious.org/' + reponame)
$('#id_vcs_web_tree_base_url').val('http://gitorious.org/' + reponame + '/trees/master/')
$('#id_vcs_web_file_base_url').val('http://gitorious.org/' + reponame + '/blobs/master/')
$('#id_vcs_web_tree_base_url').val('http://gitorious.org/' + reponame + '/trees/%branch%/')
$('#id_vcs_web_file_base_url').val('http://gitorious.org/' + reponame + '/blobs/%branch%/')
}
else if( repoval.indexOf('bitbucket.org/') > -1 ) {
reponame = repoval.replace(/^.*bitbucket.org\//, '')
reponame = reponame.replace(/.git$/, '')
$('#id_vcs_web_url').val('http://bitbucket.org/' + reponame)
$('#id_vcs_web_tree_base_url').val('http://bitbucket.org/' + reponame + '/src/master/%path%?at=master')
$('#id_vcs_web_file_base_url').val('http://bitbucket.org/' + reponame + '/src/master/%path%?at=master')
$('#id_vcs_web_tree_base_url').val('http://bitbucket.org/' + reponame + '/src/%branch%/%path%?at=%branch%')
$('#id_vcs_web_file_base_url').val('http://bitbucket.org/' + reponame + '/src/%branch%/%path%?at=%branch%')
}
};

View File

@ -0,0 +1,10 @@
[
{
"model": "layerindex.branch",
"pk": 1,
"fields": {
"name": "master",
"bitbake_branch": "master"
}
}
]

View File

@ -4,7 +4,7 @@
#
# Licensed under the MIT license, see COPYING.MIT for details
from layerindex.models import LayerItem, LayerMaintainer, LayerNote
from layerindex.models import LayerItem, LayerBranch, LayerMaintainer, LayerNote
from django import forms
from django.core.validators import URLValidator, RegexValidator, email_re
from django.forms.models import inlineformset_factory
@ -42,7 +42,7 @@ class BaseLayerMaintainerFormSet(forms.models.BaseInlineFormSet):
f.required = True
return f
LayerMaintainerFormSet = inlineformset_factory(LayerItem, LayerMaintainer, form=LayerMaintainerForm, formset=BaseLayerMaintainerFormSet, can_delete=False, extra=10, max_num=10)
LayerMaintainerFormSet = inlineformset_factory(LayerBranch, LayerMaintainer, form=LayerMaintainerForm, formset=BaseLayerMaintainerFormSet, can_delete=False, extra=10, max_num=10)
class EditLayerForm(forms.ModelForm):
# Additional form fields
@ -53,10 +53,11 @@ class EditLayerForm(forms.ModelForm):
model = LayerItem
fields = ('name', 'layer_type', 'summary', 'description', 'vcs_url', 'vcs_subdir', 'vcs_web_url', 'vcs_web_tree_base_url', 'vcs_web_file_base_url', 'usage_url', 'mailing_list_url')
def __init__(self, user, *args, **kwargs):
def __init__(self, user, layerbranch, *args, **kwargs):
super(self.__class__, self).__init__(*args, **kwargs)
self.layerbranch = layerbranch
if self.instance.pk:
self.fields['deps'].initial = [d.dependency.pk for d in self.instance.dependencies_set.all()]
self.fields['deps'].initial = [d.dependency.pk for d in self.layerbranch.dependencies_set.all()]
del self.fields['captcha']
else:
self.fields['deps'].initial = [l.pk for l in LayerItem.objects.filter(name='openembedded-core')]

View File

@ -19,7 +19,7 @@
{% block content %}
{% autoescape on %}
{% if layer_list %}
{% if layerbranch_list %}
<div class="row-fluid">
<div class="span9 offset1">
@ -68,22 +68,22 @@
</thead>
<tbody>
{% for layeritem in layer_list %}
<tr class="layertype_{{ layeritem.layer_type }}">
<td><a href="{% url layer_item layeritem.name %}">{{ layeritem.name }}</a></td>
<td>{{ layeritem.summary }}</td>
<td>{{ layeritem.get_layer_type_display }}</td>
{% for layerbranch in layerbranch_list %}
<tr class="layertype_{{ layerbranch.layer.layer_type }}">
<td><a href="{% url layer_item layerbranch.layer.name %}">{{ layerbranch.layer.name }}</a></td>
<td>{{ layerbranch.layer.summary }}</td>
<td>{{ layerbranch.layer.get_layer_type_display }}</td>
<td class="showRollie">
{{ layeritem.vcs_url }}
{% if layeritem.vcs_web_url %}
<a class="rollie" href="{{ layeritem.vcs_web_url }}">
{{ layerbranch.layer.vcs_url }}
{% if layerbranch.layer.vcs_web_url %}
<a class="rollie" href="{{ layerbranch.layer.vcs_web_url }}">
<span class="label label-info">
web repo
</span>
</a>
{% endif %}
{% if layeritem.tree_url %}
<a class="rollie" href="{{ layeritem.tree_url }}">
{% if layerbranch.tree_url %}
<a class="rollie" href="{{ layerbranch.tree_url }}">
<span class="label label-info">
tree
</span>

View File

@ -52,7 +52,7 @@
<tr>
<td><a href="{{ machine.vcs_web_url }}">{{ machine.name }}</a></td>
<td>{{ machine.description }}</td>
<td><a href="{% url layer_item machine.layer.name %}">{{ machine.layer.name }}</a></td>
<td><a href="{% url layer_item machine.layerbranch.layer.name %}">{{ machine.layerbranch.layer.name }}</a></td>
</tr>
{% endfor %}
</tbody>

View File

@ -9,6 +9,18 @@ from datetime import datetime
from django.contrib.auth.models import User
from django.core.urlresolvers import reverse
import os.path
import re
class Branch(models.Model):
name = models.CharField(max_length=50)
bitbake_branch = models.CharField(max_length=50)
class Meta:
verbose_name_plural = "Branches"
def __unicode__(self):
return self.name
class LayerItem(models.Model):
LAYER_STATUS_CHOICES = (
@ -27,9 +39,6 @@ class LayerItem(models.Model):
layer_type = models.CharField(max_length=1, choices=LAYER_TYPE_CHOICES)
summary = models.CharField(max_length=200, help_text='One-line description of the layer')
description = models.TextField()
vcs_last_fetch = models.DateTimeField('Last successful fetch', blank=True, null=True)
vcs_last_rev = models.CharField('Last revision fetched', max_length=80, blank=True)
vcs_last_commit = models.DateTimeField('Last commit date', blank=True, null=True)
vcs_subdir = models.CharField('Repository subdirectory', max_length=40, blank=True, help_text='Subdirectory within the repository where the layer is located, if not in the root (usually only used if the repository contains more than one layer)')
vcs_url = models.CharField('Repository URL', max_length=200, help_text='Fetch/clone URL of the repository')
vcs_web_url = models.URLField('Repository web interface URL', blank=True, help_text='URL of the web interface for browsing the repository, if any')
@ -47,40 +56,21 @@ class LayerItem(models.Model):
def change_status(self, newstatus, username):
self.status = newstatus
def _handle_url_path(self, base_url, path):
if base_url:
if self.vcs_subdir:
extra_path = self.vcs_subdir + '/' + path
else:
extra_path = path
if '%path%' in base_url:
if extra_path:
url = re.sub(r'\[([^\]]*%path%[^\]]*)\]', '\\1', base_url)
return url.replace('%path%', extra_path)
else:
url = re.sub(r'\[([^\]]*%path%[^\]]*)\]', '', base_url)
return url
else:
return base_url + extra_path
def get_layerbranch(self, branchname):
res = list(self.layerbranch_set.filter(branch__name=branchname)[:1])
if res:
return res[0]
return None
def tree_url(self, path = ''):
return self._handle_url_path(self.vcs_web_tree_base_url, path)
def file_url(self, path = ''):
return self._handle_url_path(self.vcs_web_file_base_url, path)
def test_tree_url(self):
return self.tree_url('conf')
def test_file_url(self):
return self.file_url('conf/layer.conf')
def sorted_recipes(self):
return self.recipe_set.order_by('filename')
def active_maintainers(self):
return self.layermaintainer_set.filter(status='A')
matches = None
for layerbranch in self.layerbranch_set.all():
branchmatches = layerbranch.layermaintainer_set.filter(status='A')
if matches:
matches |= branchmatches
else:
matches = branchmatches
return matches
def user_can_edit(self, user):
if user.is_authenticated():
@ -96,12 +86,62 @@ class LayerItem(models.Model):
return self.name
class LayerBranch(models.Model):
layer = models.ForeignKey(LayerItem)
branch = models.ForeignKey(Branch)
vcs_last_fetch = models.DateTimeField('Last successful fetch', blank=True, null=True)
vcs_last_rev = models.CharField('Last revision fetched', max_length=80, blank=True)
vcs_last_commit = models.DateTimeField('Last commit date', blank=True, null=True)
class Meta:
verbose_name_plural = "Layer branches"
def sorted_recipes(self):
return self.recipe_set.order_by('filename')
def active_maintainers(self):
return self.layermaintainer_set.filter(status='A')
def _handle_url_path(self, base_url, path):
if base_url:
if self.layer.vcs_subdir:
extra_path = self.layer.vcs_subdir + '/' + path
else:
extra_path = path
url = base_url.replace('%branch%', self.branch.name)
if '%path%' in base_url:
if extra_path:
url = re.sub(r'\[([^\]]*%path%[^\]]*)\]', '\\1', url)
return url.replace('%path%', extra_path)
else:
url = re.sub(r'\[([^\]]*%path%[^\]]*)\]', '', url)
return url
else:
return url + extra_path
return None
def tree_url(self, path = ''):
return self._handle_url_path(self.layer.vcs_web_tree_base_url, path)
def file_url(self, path = ''):
return self._handle_url_path(self.layer.vcs_web_file_base_url, path)
def test_tree_url(self):
return self.tree_url('conf')
def test_file_url(self):
return self.file_url('conf/layer.conf')
def __unicode__(self):
return "%s: %s" % (self.layer.name, self.branch.name)
class LayerMaintainer(models.Model):
MAINTAINER_STATUS_CHOICES = (
('A', 'Active'),
('I', 'Inactive'),
)
layer = models.ForeignKey(LayerItem)
layerbranch = models.ForeignKey(LayerBranch)
name = models.CharField(max_length=255)
email = models.CharField(max_length=255)
responsibility = models.CharField(max_length=200, blank=True, help_text='Specific area(s) this maintainer is responsible for, if not the entire layer')
@ -111,18 +151,18 @@ class LayerMaintainer(models.Model):
respstr = ""
if self.responsibility:
respstr = " (%s)" % self.responsibility
return "%s: %s <%s>%s" % (self.layer.name, self.name, self.email, respstr)
return "%s: %s <%s>%s" % (self.layerbranch.layer.name, self.name, self.email, respstr)
class LayerDependency(models.Model):
layer = models.ForeignKey(LayerItem, related_name='dependencies_set')
layerbranch = models.ForeignKey(LayerBranch, related_name='dependencies_set')
dependency = models.ForeignKey(LayerItem, related_name='dependents_set')
class Meta:
verbose_name_plural = "Layer dependencies"
def __unicode__(self):
return "%s depends on %s" % (self.layer.name, self.dependency.name)
return "%s depends on %s" % (self.layerbranch.layer.name, self.dependency.name)
class LayerNote(models.Model):
@ -134,7 +174,7 @@ class LayerNote(models.Model):
class Recipe(models.Model):
layer = models.ForeignKey(LayerItem)
layerbranch = models.ForeignKey(LayerBranch)
filename = models.CharField(max_length=255)
filepath = models.CharField(max_length=255, blank=True)
pn = models.CharField(max_length=40, blank=True)
@ -146,7 +186,7 @@ class Recipe(models.Model):
homepage = models.URLField(blank=True)
def vcs_web_url(self):
url = self.layer.file_url(os.path.join(self.filepath, self.filename))
url = self.layerbranch.file_url(os.path.join(self.filepath, self.filename))
return url or ''
def full_path(self):
@ -170,7 +210,7 @@ class Recipe(models.Model):
class RecipeFileDependency(models.Model):
recipe = models.ForeignKey(Recipe)
layer = models.ForeignKey(LayerItem, related_name='+')
layerbranch = models.ForeignKey(LayerBranch, related_name='+')
path = models.CharField(max_length=255, db_index=True)
class Meta:
@ -181,13 +221,13 @@ class RecipeFileDependency(models.Model):
class Machine(models.Model):
layer = models.ForeignKey(LayerItem)
layerbranch = models.ForeignKey(LayerBranch)
name = models.CharField(max_length=255)
description = models.CharField(max_length=255)
def vcs_web_url(self):
url = self.layer.file_url(os.path.join('conf/machine/%s.conf' % self.name))
url = self.layerbranch.file_url(os.path.join('conf/machine/%s.conf' % self.name))
return url or ''
def __unicode__(self):
return '%s (%s)' % (self.name, self.layer.name)
return '%s (%s)' % (self.name, self.layerbranch.layer.name)

View File

@ -1,2 +1,2 @@
{% for recipe in recipe_list %}{{ recipe.layer.name }} {{ recipe.pn }} {{ recipe.pv }} {{ recipe.full_path }}
{% for recipe in recipe_list %}{{ recipe.layerbranch.layer.name }} {{ recipe.pn }} {{ recipe.pv }} {{ recipe.full_path }}
{% endfor %}

View File

@ -32,6 +32,12 @@
<li class="active"><a href="#">Recipe</a></li>
</ul>
{% if recipe.layerbranch.branch.name != current_branch %}
<div class="alert alert-error">
This recipe record is for a different branch than the one you have selected. To search for the equivalent recipe on the {{ current_branch }} branch, click <a href="{% url recipe_search %}?q={{ recipe.name }}">here</a>.
</div>
{% endif %}
<table class="table table-striped table-bordered">
<tbody>
<tr>
@ -74,7 +80,7 @@
</tr>
<tr>
<th>Layer</th>
<td><a href="{% url layer_item recipe.layer.name %}">{{ recipe.layer.name }}</a></td>
<td><a href="{% url layer_item recipe.layerbranch.layer.name %}">{{ recipe.layerbranch.layer.name }}</a> ({{ recipe.layerbranch.branch.name}} branch)</td>
</tr>
</tbody>
</table>

View File

@ -54,7 +54,7 @@
<td><a href="{% url recipe recipe.id %}">{{ recipe.name }}</a></td>
<td>{{ recipe.pv }}</td>
<td>{{ recipe.short_desc }}</td>
<td><a href="{% url layer_item recipe.layer.name %}">{{ recipe.layer.name }}</a></td>
<td><a href="{% url layer_item recipe.layerbranch.layer.name %}">{{ recipe.layerbranch.layer.name }}</a></td>
</tr>
{% endfor %}
</tbody>

View File

@ -21,6 +21,10 @@
{% endblock %}
{% block branch_selector %}
<li class="disabled"><a href="#">Branch: <b>master</b></a></li>
{% endblock %}
{% block formtop %}
<div class="submitformintro">
<p>Please enter the details for the layer you wish to add to the index.</p>

View File

@ -108,7 +108,7 @@ def update_recipe_file(data, path, recipe, layerdir_start, repodir):
RecipeFileDependency.objects.filter(recipe=recipe).delete()
for filedep in filedeps:
recipedep = RecipeFileDependency()
recipedep.layer = recipe.layer
recipedep.layerbranch = recipe.layerbranch
recipedep.recipe = recipe
recipedep.path = filedep
recipedep.save()
@ -132,30 +132,19 @@ def parse_layer_conf(layerdir, data):
data = bb.cooker._parse(os.path.join(layerdir, "conf", "layer.conf"), data)
data.expandVarref('LAYERDIR')
def setup_bitbake_path(basepath):
# Set path to bitbake lib dir
bitbakedir_env = os.environ.get('BITBAKEDIR', '')
if bitbakedir_env and os.path.exists(bitbakedir_env + '/lib/bb'):
bitbakepath = bitbakedir_env
elif basepath and os.path.exists(basepath + '/bitbake/lib/bb'):
bitbakepath = basepath + '/bitbake'
elif basepath and os.path.exists(basepath + '/../bitbake/lib/bb'):
bitbakepath = os.path.abspath(basepath + '/../bitbake')
else:
# look for bitbake/bin dir in PATH
bitbakepath = None
for pth in os.environ['PATH'].split(':'):
if os.path.exists(os.path.join(pth, '../lib/bb')):
bitbakepath = os.path.abspath(os.path.join(pth, '..'))
break
if not bitbakepath:
if basepath:
logger.error("Unable to find bitbake by searching BITBAKEDIR, specified path '%s' or its parent, or PATH" % basepath)
else:
logger.error("Unable to find bitbake by searching BITBAKEDIR or PATH")
sys.exit(1)
return bitbakepath
def get_branch(branchname):
from layerindex.models import Branch
res = list(Branch.objects.filter(name=branchname)[:1])
if res:
return res[0]
return None
def get_layer(layername):
from layerindex.models import LayerItem
res = list(LayerItem.objects.filter(name=layername)[:1])
if res:
return res[0]
return None
def main():
if LooseVersion(git.__version__) < '0.3.1':
@ -167,6 +156,9 @@ def main():
usage = """
%prog [options]""")
parser.add_option("-b", "--branch",
help = "Specify branch to update",
action="store", dest="branch", default='master')
parser.add_option("-l", "--layer",
help = "Specify layers to update (use commas to separate multiple). Default is all published layers.",
action="store", dest="layers")
@ -196,39 +188,16 @@ def main():
from django.core.management import setup_environ
from django.conf import settings
from layerindex.models import LayerItem, Recipe, RecipeFileDependency, Machine
from layerindex.models import LayerItem, LayerBranch, Recipe, RecipeFileDependency, Machine
from django.db import transaction
import settings
setup_environ(settings)
if len(sys.argv) > 1:
basepath = os.path.abspath(sys.argv[1])
else:
basepath = None
bitbakepath = setup_bitbake_path(None)
# Skip sanity checks
os.environ['BB_ENV_EXTRAWHITE'] = 'DISABLE_SANITY_CHECKS'
os.environ['DISABLE_SANITY_CHECKS'] = '1'
sys.path.extend([bitbakepath + '/lib'])
import bb.tinfoil
import bb.cooker
tinfoil = bb.tinfoil.Tinfoil()
tinfoil.prepare(config_only = True)
logger.setLevel(options.loglevel)
# Clear the default value of SUMMARY so that we can use DESCRIPTION instead if it hasn't been set
tinfoil.config_data.setVar('SUMMARY', '')
# Clear the default value of DESCRIPTION so that we can see where it's not set
tinfoil.config_data.setVar('DESCRIPTION', '')
# Clear the default value of HOMEPAGE ('unknown')
tinfoil.config_data.setVar('HOMEPAGE', '')
# Set a blank value for LICENSE so that it doesn't cause the parser to die (e.g. with meta-ti -
# why won't they just fix that?!)
tinfoil.config_data.setVar('LICENSE', '')
branch = get_branch(options.branch)
if not branch:
logger.error("Specified branch %s is not valid" % options.branch)
sys.exit(1)
fetchdir = settings.LAYER_FETCH_DIR
if not fetchdir:
@ -251,6 +220,8 @@ def main():
fetchedrepos = []
failedrepos = []
bitbakepath = os.path.join(fetchdir, 'bitbake')
if not options.nofetch:
# Fetch latest metadata from repositories
for layer in layerquery:
@ -264,13 +235,57 @@ def main():
if not os.path.exists(repodir):
out = runcmd("git clone %s %s" % (layer.vcs_url, urldir), fetchdir)
else:
out = runcmd("git pull", repodir)
out = runcmd("git fetch", repodir)
except Exception as e:
logger.error("fetch failed: %s" % str(e))
failedrepos.append(layer.vcs_url)
continue
fetchedrepos.append(layer.vcs_url)
logger.info("Fetching bitbake from remote repository %s" % settings.BITBAKE_REPO_URL)
if not os.path.exists(bitbakepath):
out = runcmd("git clone %s %s" % (settings.BITBAKE_REPO_URL, 'bitbake'), fetchdir)
else:
out = runcmd("git fetch", bitbakepath)
# Check out the branch of BitBake appropriate for this branch and clean out any stale files (e.g. *.pyc)
out = runcmd("git checkout origin/%s" % branch.bitbake_branch, bitbakepath)
out = runcmd("git clean -f -x", bitbakepath)
# Skip sanity checks
os.environ['BB_ENV_EXTRAWHITE'] = 'DISABLE_SANITY_CHECKS'
os.environ['DISABLE_SANITY_CHECKS'] = '1'
# Ensure we have OE-Core set up to get some base configuration
core_layer = get_layer(settings.CORE_LAYER_NAME)
if not core_layer:
logger.error("Unable to find core layer %s in database; check CORE_LAYER_NAME setting" % settings.CORE_LAYER_NAME)
sys.exit(1)
core_urldir = sanitise_path(core_layer.vcs_url)
core_repodir = os.path.join(fetchdir, core_urldir)
core_layerdir = os.path.join(core_repodir, core_layer.vcs_subdir)
out = runcmd("git checkout origin/%s" % options.branch, core_repodir)
out = runcmd("git clean -f -x", core_repodir)
os.environ['BBPATH'] = str("%s:%s" % (os.path.realpath('.'), core_layerdir))
sys.path.extend([bitbakepath + '/lib'])
import bb.tinfoil
import bb.cooker
tinfoil = bb.tinfoil.Tinfoil()
tinfoil.prepare(config_only = True)
logger.setLevel(options.loglevel)
# Clear the default value of SUMMARY so that we can use DESCRIPTION instead if it hasn't been set
tinfoil.config_data.setVar('SUMMARY', '')
# Clear the default value of DESCRIPTION so that we can see where it's not set
tinfoil.config_data.setVar('DESCRIPTION', '')
# Clear the default value of HOMEPAGE ('unknown')
tinfoil.config_data.setVar('HOMEPAGE', '')
# Set a blank value for LICENSE so that it doesn't cause the parser to die (e.g. with meta-ti -
# why won't they just fix that?!)
tinfoil.config_data.setVar('LICENSE', '')
# Process and extract data from each layer
for layer in layerquery:
transaction.enter_transaction_management()
@ -282,23 +297,51 @@ def main():
logger.info("Skipping update of layer %s as fetch of repository %s failed" % (layer.name, layer.vcs_url))
transaction.rollback()
continue
# Collect repo info
repo = git.Repo(repodir)
assert repo.bare == False
topcommit = repo.commit('master')
try:
topcommit = repo.commit('origin/%s' % options.branch)
except:
logger.info("Skipping update of layer %s - branch %s doesn't exist" % (layer.name, options.branch))
transaction.rollback()
continue
if layer.vcs_subdir:
# Find latest commit in subdirectory
# A bit odd to do it this way but apparently there's no other way in the GitPython API
for commit in repo.iter_commits(paths=layer.vcs_subdir):
for commit in repo.iter_commits('origin/%s' % options.branch, paths=layer.vcs_subdir):
topcommit = commit
break
layerbranch = layer.get_layerbranch(options.branch)
if not layerbranch:
# LayerBranch doesn't exist for this branch, create it
layerbranch = LayerBranch()
layerbranch.layer = layer
layerbranch.branch = branch
layerbranch.save()
layerdir = os.path.join(repodir, layer.vcs_subdir)
layerdir_start = os.path.normpath(layerdir) + os.sep
layerrecipes = Recipe.objects.filter(layer=layer)
layermachines = Machine.objects.filter(layer=layer)
if layer.vcs_last_rev != topcommit.hexsha or options.reload:
logger.info("Collecting data for layer %s" % layer.name)
layerrecipes = Recipe.objects.filter(layerbranch=layerbranch)
layermachines = Machine.objects.filter(layerbranch=layerbranch)
if layerbranch.vcs_last_rev != topcommit.hexsha or options.reload:
# Check out appropriate branch
out = runcmd("git checkout origin/%s" % options.branch, repodir)
if not os.path.exists(layerdir):
if options.branch == 'master':
logger.error("Subdirectory for layer %s does not exist on master branch!" % layer.name)
transaction.rollback()
continue
else:
logger.info("Skipping update of layer %s for branch %s - subdirectory does not exist on this branch" % (layer.name, options.branch))
transaction.rollback()
continue
logger.info("Collecting data for layer %s on branch %s" % (layer.name, options.branch))
# Parse layer.conf files for this layer and its dependencies
# This is necessary not just because BBPATH needs to be set in order
@ -308,16 +351,16 @@ def main():
config_data_copy = bb.data.createCopy(tinfoil.config_data)
parse_layer_conf(layerdir, config_data_copy)
for dep in layer.dependencies_set.all():
for dep in layerbranch.dependencies_set.all():
depurldir = sanitise_path(dep.dependency.vcs_url)
deprepodir = os.path.join(fetchdir, depurldir)
deplayerdir = os.path.join(deprepodir, dep.dependency.vcs_subdir)
parse_layer_conf(deplayerdir, config_data_copy)
config_data_copy.delVar('LAYERDIR')
if layer.vcs_last_rev and not options.reload:
if layerbranch.vcs_last_rev and not options.reload:
try:
diff = repo.commit(layer.vcs_last_rev).diff(topcommit)
diff = repo.commit(layerbranch.vcs_last_rev).diff(topcommit)
except Exception as e:
logger.warn("Unable to get diff from last commit hash for layer %s - falling back to slow update: %s" % (layer.name, str(e)))
diff = None
@ -350,7 +393,7 @@ def main():
(filepath, filename) = split_bb_file_path(path, subdir_start)
if filename:
recipe = Recipe()
recipe.layer = layer
recipe.layerbranch = layerbranch
recipe.filename = filename
recipe.filepath = filepath
update_recipe_file(config_data_copy, os.path.join(layerdir, filepath), recipe, layerdir_start, repodir)
@ -360,7 +403,7 @@ def main():
machinename = check_machine_conf(path)
if machinename:
machine = Machine()
machine.layer = layer
machine.layerbranch = layerbranch
machine.name = machinename
update_machine_conf_file(os.path.join(repodir, path), machine)
machine.save()
@ -385,7 +428,7 @@ def main():
machine = results[0]
update_machine_conf_file(os.path.join(repodir, path), machine)
machine.save()
deps = RecipeFileDependency.objects.filter(layer=layer).filter(path=path)
deps = RecipeFileDependency.objects.filter(layerbranch=layerbranch).filter(path=path)
for dep in deps:
dirtyrecipes.add(dep.recipe)
@ -400,7 +443,7 @@ def main():
for f in files:
if fnmatch.fnmatch(f, "*.bb"):
recipe = Recipe()
recipe.layer = layer
recipe.layerbranch = layerbranch
recipe.filename = f
recipe.filepath = os.path.relpath(root, layerdir)
update_recipe_file(config_data_copy, root, recipe, layerdir_start, repodir)
@ -410,19 +453,19 @@ def main():
machinename = check_machine_conf(fullpath)
if machinename:
machine = Machine()
machine.layer = layer
machine.layerbranch = layerbranch
machine.name = machinename
update_machine_conf_file(fullpath, machine)
machine.save()
# Save repo info
layer.vcs_last_rev = topcommit.hexsha
layer.vcs_last_commit = datetime.fromtimestamp(topcommit.committed_date)
layerbranch.vcs_last_rev = topcommit.hexsha
layerbranch.vcs_last_commit = datetime.fromtimestamp(topcommit.committed_date)
else:
logger.info("Layer %s is already up-to-date" % layer.name)
logger.info("Layer %s is already up-to-date for branch %s" % (layer.name, options.branch))
layer.vcs_last_fetch = datetime.now()
layer.save()
layerbranch.vcs_last_fetch = datetime.now()
layerbranch.save()
if options.dryrun:
transaction.rollback()

View File

@ -7,7 +7,7 @@
from django.conf.urls.defaults import *
from django.views.generic import DetailView, ListView
from layerindex.models import LayerItem, Recipe
from layerindex.views import LayerListView, RecipeSearchView, MachineSearchView, PlainTextListView, LayerDetailView, edit_layer_view, delete_layer_view, edit_layernote_view, delete_layernote_view
from layerindex.views import LayerListView, LayerReviewListView, RecipeSearchView, MachineSearchView, PlainTextListView, LayerDetailView, edit_layer_view, delete_layer_view, edit_layernote_view, delete_layernote_view, switch_branch_view
urlpatterns = patterns('',
url(r'^$',
@ -26,9 +26,7 @@ urlpatterns = patterns('',
template_name='layerindex/machines.html'),
name='machine_search'),
url(r'^review/$',
ListView.as_view(
queryset=LayerItem.objects.order_by('name').filter(status__in='N'),
context_object_name='layer_list',
LayerReviewListView.as_view(
template_name='layerindex/index.html'),
name='layer_list_review'),
url(r'^layer/(?P<slug>[-\w]+)/$',
@ -49,6 +47,8 @@ urlpatterns = patterns('',
template_name='layerindex/recipedetail.html'),
name='recipe'),
url(r'^layer/(?P<name>[-\w]+)/publish/$', 'layerindex.views.publish', name="publish"),
url(r'^branch/(?P<slug>[-\w]+)/$',
switch_branch_view, name="switch_branch"),
url(r'^raw/recipes.txt$',
PlainTextListView.as_view(
queryset=Recipe.objects.order_by('pn', 'layer'),

View File

@ -9,7 +9,7 @@ from django.http import HttpResponse, HttpResponseRedirect, HttpResponseForbidde
from django.core.urlresolvers import reverse
from django.core.exceptions import PermissionDenied
from django.template import RequestContext
from layerindex.models import LayerItem, LayerMaintainer, LayerDependency, LayerNote, Recipe, Machine
from layerindex.models import Branch, LayerItem, LayerMaintainer, LayerBranch, LayerDependency, LayerNote, Recipe, Machine
from datetime import datetime
from django.views.generic import DetailView, ListView
from layerindex.forms import EditLayerForm, LayerMaintainerFormSet, EditNoteForm
@ -79,39 +79,45 @@ def delete_layer_view(request, template_name, slug):
def edit_layer_view(request, template_name, slug=None):
if slug:
# Edit mode
branch = Branch.objects.filter(name=request.session.get('branch', 'master'))[:1].get()
layeritem = get_object_or_404(LayerItem, name=slug)
if not (request.user.is_authenticated() and (request.user.has_perm('layerindex.publish_layer') or layeritem.user_can_edit(request.user))):
raise PermissionDenied
layerbranch = get_object_or_404(LayerBranch, layer=layeritem, branch=branch)
deplistlayers = LayerItem.objects.exclude(id=layeritem.id).order_by('name')
else:
# Submit mode
branch = Branch.objects.filter(name='master')[:1].get()
layeritem = LayerItem()
layerbranch = LayerBranch(layer=layeritem, branch=branch)
deplistlayers = LayerItem.objects.all().order_by('name')
if request.method == 'POST':
form = EditLayerForm(request.user, request.POST, instance=layeritem)
maintainerformset = LayerMaintainerFormSet(request.POST, instance=layeritem)
form = EditLayerForm(request.user, layerbranch, request.POST, instance=layeritem)
maintainerformset = LayerMaintainerFormSet(request.POST, instance=layerbranch)
if form.is_valid() and maintainerformset.is_valid():
with transaction.commit_on_success():
form.save()
layerbranch.layer = layeritem
layerbranch.save()
maintainerformset.save()
if slug:
new_deps = form.cleaned_data['deps']
existing_deps = [deprec.dependency for deprec in layeritem.dependencies_set.all()]
existing_deps = [deprec.dependency for deprec in layerbranch.dependencies_set.all()]
for dep in new_deps:
if dep not in existing_deps:
deprec = LayerDependency()
deprec.layer = layeritem
deprec.layerbranch = layerbranch
deprec.dependency = dep
deprec.save()
for dep in existing_deps:
if dep not in new_deps:
layeritem.dependencies_set.filter(dependency=dep).delete()
layerbranch.dependencies_set.filter(dependency=dep).delete()
else:
# Save dependencies
for dep in form.cleaned_data['deps']:
deprec = LayerDependency()
deprec.layer = layeritem
deprec.layerbranch = layerbranch
deprec.dependency = dep
deprec.save()
# Send email
@ -133,8 +139,8 @@ def edit_layer_view(request, template_name, slug=None):
return HttpResponseRedirect(reverse('submit_layer_thanks'))
form.was_saved = True
else:
form = EditLayerForm(request.user, instance=layeritem)
maintainerformset = LayerMaintainerFormSet(instance=layeritem)
form = EditLayerForm(request.user, layerbranch, instance=layeritem)
maintainerformset = LayerMaintainerFormSet(instance=layerbranch)
return render(request, template_name, {
'form': form,
@ -145,6 +151,14 @@ def edit_layer_view(request, template_name, slug=None):
def submit_layer_thanks(request):
return render(request, 'layerindex/submitthanks.html')
def switch_branch_view(request, slug):
branch = get_object_or_404(Branch, name=slug)
request.session['branch'] = branch.name
return_url = request.META.get('HTTP_REFERER')
if not return_url:
return_url = reverse('layer_list')
return HttpResponseRedirect(return_url)
def about(request):
return render(request, 'layerindex/about.html')
@ -161,16 +175,20 @@ def _statuschange(request, name, newstatus):
return HttpResponseRedirect(w.get_absolute_url())
class LayerListView(ListView):
context_object_name = 'layer_list'
context_object_name = 'layerbranch_list'
def get_queryset(self):
return LayerItem.objects.filter(status='P').order_by('name')
return LayerBranch.objects.filter(branch__name=self.request.session.get('branch', 'master')).filter(layer__status='P').order_by('layer__name')
def get_context_data(self, **kwargs):
context = super(LayerListView, self).get_context_data(**kwargs)
context['layer_type_choices'] = LayerItem.LAYER_TYPE_CHOICES
return context
class LayerReviewListView(ListView):
def get_queryset(self):
return LayerBranch.objects.filter(branch__name=self.request.session.get('branch', 'master')).filter(layer__status='N').order_by('layer__name')
class LayerDetailView(DetailView):
model = LayerItem
slug_field = 'name'
@ -190,6 +208,7 @@ class LayerDetailView(DetailView):
context = super(LayerDetailView, self).get_context_data(**kwargs)
layer = context['layeritem']
context['useredit'] = layer.user_can_edit(self.user)
context['layerbranch'] = layer.get_layerbranch(self.request.session.get('branch', 'master'))
return context
class RecipeSearchView(ListView):
@ -198,11 +217,12 @@ class RecipeSearchView(ListView):
def get_queryset(self):
query_string = self.request.GET.get('q', '')
init_qs = Recipe.objects.filter(layerbranch__branch__name=self.request.session.get('branch', 'master'))
if query_string.strip():
entry_query = simplesearch.get_query(query_string, ['pn', 'summary', 'description', 'filename'])
return Recipe.objects.filter(entry_query).order_by('pn', 'layer')
return init_qs.filter(entry_query).order_by('pn', 'layerbranch__layer')
else:
return Recipe.objects.all().order_by('pn', 'layer')
return init_qs.order_by('pn', 'layerbranch__layer')
def get_context_data(self, **kwargs):
context = super(RecipeSearchView, self).get_context_data(**kwargs)
@ -215,11 +235,12 @@ class MachineSearchView(ListView):
def get_queryset(self):
query_string = self.request.GET.get('q', '')
init_qs = Machine.objects.filter(layerbranch__branch__name=self.request.session.get('branch', 'master'))
if query_string.strip():
entry_query = simplesearch.get_query(query_string, ['name', 'description'])
return Machine.objects.filter(entry_query).order_by('name', 'layer')
return init_qs.filter(entry_query).order_by('name', 'layerbranch__layer')
else:
return Machine.objects.all().order_by('name', 'layer')
return init_qs.order_by('name', 'layerbranch__layer')
def get_context_data(self, **kwargs):
context = super(MachineSearchView, self).get_context_data(**kwargs)

View File

@ -115,6 +115,7 @@ X_FRAME_OPTIONS = 'DENY'
from django.conf.global_settings import TEMPLATE_CONTEXT_PROCESSORS as TCP
TEMPLATE_CONTEXT_PROCESSORS = TCP + (
'django.core.context_processors.request',
'layerindex.context_processors.layerindex_context',
)
ROOT_URLCONF = 'urls'
@ -175,6 +176,12 @@ LOGIN_REDIRECT_URL = '/layerindex'
# Full path to directory where layers should be fetched into by the update script
LAYER_FETCH_DIR = ""
# Fetch URL of the BitBake repository for the update script
BITBAKE_REPO_URL = "git://git.openembedded.org/bitbake"
# Core layer to be used by the update script for basic BitBake configuration
CORE_LAYER_NAME = "openembedded-core"
# Settings for layer submission feature
SUBMIT_EMAIL_FROM = 'noreply@example.com'
SUBMIT_EMAIL_SUBJECT = 'OE Layerindex layer submission'