toaster: cummulative 26062017 patch
cover toaster: cummulative 26062017 patch
From: David Reyna <[email protected]>
*** NOTE for MERGE: New Files: ***
create mode 100644 bitbake/lib/toaster/orm/migrations/0016_clone_progress.py create mode 100644 bitbake/lib/toaster/orm/migrations/0017_distro_clone.py create mode 100644 bitbake/lib/toaster/toastergui/templates/distro_btn.html
I have been advised that: "If the patch creates any NEW files, the integration scripts that pull it into bitbake will fail. So, if new files are created as part of this patch set, you need to explicitly point that out in the body of the email for the patch set"
- David
=================
The following changes since commit b8358bc61435ec996ecd6931fb6d6d41bb4f710b:
bitbake: bitbake-selftest: add bb.tests.event to bitbake-selftest (2017-06-23 14:14:17 +0100)
are available in the git repository at:
git://git.yoctoproject.org/poky-contrib dreyna/submit/dreyna/toaster/cummulative_062017_patch
http://git.yoctoproject.org/cgit.cgi/poky-contrib/log/?h=dreyna/submit/dreyna/toaster/cummulative_062017_patch
David Reyna (5):
toaster: address Django-1.10 API deprecations
toaster: git clone progress bar
toaster: Add distro selection support
toaster: large package set breaks sqlite query
toaster: test 'commit' first in get_vcs_reference
bin/toaster | 10 ++- lib/toaster/bldcollector/urls.py | 10 ++- lib/toaster/bldcontrol/localhostbecontroller.py | 12 +++ .../management/commands/checksettings.py | 6 +- .../bldcontrol/management/commands/runbuilds.py | 6 +- lib/toaster/orm/management/commands/lsupdates.py | 27 ++++++- lib/toaster/orm/migrations/0016_clone_progress.py | 24 ++++++ lib/toaster/orm/migrations/0017_distro_clone.py | 25 ++++++ lib/toaster/orm/models.py | 50 +++++++++++- lib/toaster/toastergui/api.py | 13 +++- lib/toaster/toastergui/static/js/mrbsection.js | 15 ++++ lib/toaster/toastergui/static/js/projectpage.js | 73 +++++++++++++++++ lib/toaster/toastergui/tables.py | 91 ++++++++++++++++++++++ lib/toaster/toastergui/templates/base.html | 1 + .../toastergui/templates/baseprojectpage.html | 1 + lib/toaster/toastergui/templates/distro_btn.html | 20 +++++ lib/toaster/toastergui/templates/mrb_section.html | 29 ++++++- lib/toaster/toastergui/templates/project.html | 16 ++++ lib/toaster/toastergui/typeaheads.py | 30 +++++++ lib/toaster/toastergui/urls.py | 69 +++++++++------- lib/toaster/toastergui/views.py | 13 +++- lib/toaster/toastergui/widgets.py | 4 + .../toastermain/management/commands/buildslist.py | 6 +- lib/toaster/toastermain/settings.py | 70 ++++++++++------- lib/toaster/toastermain/urls.py | 8 +- toaster-requirements.txt | 2 +- 26 files changed, 539 insertions(+), 92 deletions(-) create mode 100644 bitbake/lib/toaster/orm/migrations/0016_clone_progress.py create mode 100644 bitbake/lib/toaster/orm/migrations/0017_distro_clone.py create mode 100644 bitbake/lib/toaster/toastergui/templates/distro_btn.html
Signature · 1 line
1.9.1
PATCH 1/5 toaster: address Django-1.10 API deprecations
From: David Reyna <[email protected]>
There are four main API deprecations in Django-1.10:
(a) String view arguments to url() must be replaced by the explicit class reference
(b) New TEMPLATES stucture in settings.py consolidates TEMPLATE_DIRS, TEMPLATE_CONTEXT_PROCESSORS, TEMPLATE_LOADERS, TEMPLATE_STRING_IF_INVALID, and TEMPLATE_DEBUG
(c) patterns() wrapper in url() is removed, with urlpatterns now a simple list
(d) NoArgsCommand in commands() must be replace by BaseCommand, and handle_noargs() changed to handle()
Also, the Django version checker must be updated to accept two digit sub-version numbers (e.g. "1.8" < "1.10")
[YOCTO #11684]
--- bin/toaster | 10 +++- lib/toaster/bldcollector/urls.py | 10 ++-- .../management/commands/checksettings.py | 6 +- .../bldcontrol/management/commands/runbuilds.py | 6 +- lib/toaster/orm/management/commands/lsupdates.py | 8 +-- lib/toaster/toastergui/urls.py | 61 +++++++++---------- .../toastermain/management/commands/buildslist.py | 6 +- lib/toaster/toastermain/settings.py | 70 +++++++++++++--------- lib/toaster/toastermain/urls.py | 8 ++- toaster-requirements.txt | 2 +- 10 files changed, 105 insertions(+), 82 deletions(-)
bin/toaster +8 -2
@@ -116,8 +116,14 @@ verify_prereq() { # Verify Django version reqfile=$(python3 -c "import os; print(os.path.realpath('$BBBASEDIR/toaster-requirements.txt'))") exp='s/Django\([><=]\+\)\([^,]\+\),\([><=]\+\)\(.\+\)/' - exp=$exp'import sys,django;version=django.get_version().split(".");' - exp=$exp'sys.exit(not (version \1 "\2".split(".") and version \3 "\4".split(".")))/p' + # expand version parts to 2 digits to support 1.10.x > 1.8 + # (note:helper functions hard to insert in-line) + exp=$exp'import sys,django;' + exp=$exp'version=["%02d" % int(n) for n in django.get_version().split(".")];' + exp=$exp'vmin=["%02d" % int(n) for n in "\2".split(".")];' + exp=$exp'vmax=["%02d" % int(n) for n in "\4".split(".")];' + exp=$exp'sys.exit(not (version \1 vmin and version \3 vmax))' + exp=$exp'/p' if ! sed -n "$exp" $reqfile | python3 - ; then req=`grep ^Django $reqfile` echo "This program needs $req"
lib/toaster/bldcollector/urls.py +6 -4
@@ -1,7 +1,7 @@ # # BitBake Toaster Implementation # -# Copyright (C) 2014 Intel Corporation +# Copyright (C) 2014-2017 Intel Corporation # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 as
@@ -19,7 +19,9 @@ from django.conf.urls import patterns, include, url -urlpatterns = patterns('bldcollector.views', +import bldcollector.views + +urlpatterns = [ # landing point for pushing a bitbake_eventlog.json file to this toaster instace - url(r'^eventfile$', 'eventfile', name='eventfile'), - ) + url(r'^eventfile$', bldcollector.views.eventfile, name='eventfile'), +]
lib/toaster/bldcontrol/management/commands/checksettings.py +3 -3
@@ -1,4 +1,4 @@ -from django.core.management.base import NoArgsCommand, CommandError +from django.core.management.base import BaseCommand, CommandError from django.db import transaction from django.core.management import call_command
@@ -18,7 +18,7 @@ def DN(path): return os.path.dirname(path) -class Command(NoArgsCommand): +class Command(BaseCommand): args = "" help = "Verifies that the configured settings are valid and usable, or prompts the user to fix the settings."
@@ -152,7 +152,7 @@ class Command(NoArgsCommand): - def handle_noargs(self, **options): + def handle(self, **options): retval = 0 retval += self._verify_build_environment() retval += self._verify_default_settings()
lib/toaster/bldcontrol/management/commands/runbuilds.py +3 -3
@@ -1,4 +1,4 @@ -from django.core.management.base import NoArgsCommand +from django.core.management.base import BaseCommand from django.db import transaction from django.db.models import Q
@@ -16,7 +16,7 @@ import os logger = logging.getLogger("toaster") -class Command(NoArgsCommand): +class Command(BaseCommand): args = "" help = "Schedules and executes build requests as possible. "\ "Does not return (interrupt with Ctrl-C)"
@@ -168,7 +168,7 @@ class Command(NoArgsCommand): except Exception as e: logger.warn("runbuilds: schedule exception %s" % str(e)) - def handle_noargs(self, **options): + def handle(self, **options): pidfile_path = os.path.join(os.environ.get("BUILDDIR", "."), ".runbuilds.pid")
lib/toaster/orm/management/commands/lsupdates.py +4 -4
@@ -4,7 +4,7 @@ # # BitBake Toaster Implementation # -# Copyright (C) 2016 Intel Corporation +# Copyright (C) 2016-2017 Intel Corporation # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 as
@@ -19,7 +19,7 @@ # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -from django.core.management.base import NoArgsCommand +from django.core.management.base import BaseCommand from orm.models import LayerSource, Layer, Release, Layer_Version from orm.models import LayerVersionDependency, Machine, Recipe
@@ -56,7 +56,7 @@ class Spinner(threading.Thread): self.signal = False -class Command(NoArgsCommand): +class Command(BaseCommand): args = "" help = "Updates locally cached information from a layerindex server"
@@ -307,5 +307,5 @@ class Command(NoArgsCommand): os.system('setterm -cursor on') - def handle_noargs(self, **options): + def handle(self, **options): self.update()
lib/toaster/toastergui/urls.py +31 -30
158 lines not on this page · raw message
lib/toaster/toastermain/management/commands/buildslist.py +3 -3
@@ -1,13 +1,13 @@ -from django.core.management.base import NoArgsCommand, CommandError +from django.core.management.base import BaseCommand, CommandError from orm.models import Build import os -class Command(NoArgsCommand): +class Command(BaseCommand): args = "" help = "Lists current builds" - def handle_noargs(self,**options): + def handle(self,**options): for b in Build.objects.all(): print("%d: %s %s %s" % (b.pk, b.machine, b.distro, ",".join([x.target for x in b.target_set.all()])))
lib/toaster/toastermain/settings.py +41 -29
92 lines not on this page · raw message
lib/toaster/toastermain/urls.py +5 -3
@@ -23,6 +23,8 @@ from django.conf.urls import patterns, include, url from django.views.generic import RedirectView from django.views.decorators.cache import never_cache +import bldcollector.views + import logging logger = logging.getLogger("toaster")
@@ -31,7 +33,7 @@ logger = logging.getLogger("toaster") from django.contrib import admin admin.autodiscover() -urlpatterns = patterns('', +urlpatterns = [ # Examples: # url(r'^toaster/', include('toaster.foo.urls')),
@@ -42,11 +44,11 @@ urlpatterns = patterns('', # This is here to maintain backward compatibility and will be deprecated # in the future. - url(r'^orm/eventfile$', 'bldcollector.views.eventfile'), + url(r'^orm/eventfile$', bldcollector.views.eventfile), # if no application is selected, we have the magic toastergui app here url(r'^$', never_cache(RedirectView.as_view(url='/toastergui/', permanent=True))), -) +] import toastermain.settings
toaster-requirements.txt +1 -1
@@ -1,3 +1,3 @@ -Django>1.8,<1.9 +Django>1.8,<1.9.9 beautifulsoup4>=4.4.0 pytz
Signature · 1 line
1.9.1
PATCH 2/5 toaster: git clone progress bar
In reply to David ReynaFrom: David Reyna <[email protected]>
If a project has a lot of additional layers, the build may appear to hang while those layers are checked out. This patch adds a clone progress bar that is visible before the parsing progress appears.
[YOCTO #9916]
--- lib/toaster/bldcontrol/localhostbecontroller.py | 12 ++++++++++ lib/toaster/orm/migrations/0016_clone_progress.py | 24 +++++++++++++++++++ lib/toaster/orm/models.py | 15 ++++++++++++ lib/toaster/toastergui/static/js/mrbsection.js | 15 ++++++++++++ lib/toaster/toastergui/templates/mrb_section.html | 29 ++++++++++++++++++++++- lib/toaster/toastergui/widgets.py | 4 ++++ 6 files changed, 98 insertions(+), 1 deletion(-) create mode 100644 bitbake/lib/toaster/orm/migrations/0016_clone_progress.py
lib/toaster/bldcontrol/localhostbecontroller.py +12 -0
@@ -85,6 +85,11 @@ class LocalhostBEController(BuildEnvironmentController): return local_checkout_path + def setCloneStatus(self,bitbake,status,total,current): + bitbake.req.build.repos_cloned=current + bitbake.req.build.repos_to_clone=total + bitbake.req.build.save() + def setLayers(self, bitbake, layers, targets): """ a word of attention: by convention, the first layer for any build will be poky! """
@@ -147,7 +152,13 @@ class LocalhostBEController(BuildEnvironmentController): logger.info("Using pre-checked out source for layer %s", cached_layers) # 3. checkout the repositories + clone_count=0 + clone_total=len(gitrepos.keys()) + self.setCloneStatus(bitbake,'Started',clone_total,clone_count) for giturl, commit in gitrepos.keys(): + self.setCloneStatus(bitbake,'progress',clone_total,clone_count) + clone_count += 1 + localdirname = os.path.join(self.be.sourcedir, self.getGitCloneDirectory(giturl, commit)) logger.debug("localhostbecontroller: giturl %s:%s checking out in current directory %s" % (giturl, commit, localdirname))
@@ -198,6 +209,7 @@ class LocalhostBEController(BuildEnvironmentController): if name != "bitbake": layerlist.append(localdirpath.rstrip("/")) + self.setCloneStatus(bitbake,'complete',clone_total,clone_count) logger.debug("localhostbecontroller: current layer list %s " % pformat(layerlist)) # 5. create custom layer and add custom recipes to it
lib/toaster/orm/migrations/0016_clone_progress.py new file +24 -0
@@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations, models + +class Migration(migrations.Migration): + + dependencies = [ + ('orm', '0015_layer_local_source_dir'), + ] + + operations = [ + migrations.AddField( + model_name='build', + name='repos_cloned', + field=models.IntegerField(default=0), + ), + migrations.AddField( + model_name='build', + name='repos_to_clone', + field=models.IntegerField(default=1), + ), + ] +
lib/toaster/orm/models.py +15 -0
@@ -437,6 +437,12 @@ class Build(models.Model): # number of recipes parsed so far for this build recipes_parsed = models.IntegerField(default=0) + # number of repos to clone for this build + repos_to_clone = models.IntegerField(default=1) + + # number of repos cloned so far for this build + repos_cloned = models.IntegerField(default=0) + @staticmethod def get_recent(project=None): """
@@ -667,6 +673,13 @@ class Build(models.Model): else: return False + def is_cloning(self): + """ + True if the build is still cloning repos + """ + return self.outcome == Build.IN_PROGRESS and \ + self.repos_cloned < self.repos_to_clone + def is_parsing(self): """ True if the build is still parsing recipes
@@ -698,6 +711,8 @@ class Build(models.Model): return 'Cancelling'; elif self.is_queued(): return 'Queued' + elif self.is_cloning(): + return 'Cloning' elif self.is_parsing(): return 'Parsing' elif self.is_starting():
lib/toaster/toastergui/static/js/mrbsection.js +15 -0
@@ -61,6 +61,12 @@ function mrbSectionInit(ctx){ return (cached.recipes_parsed_percentage !== build.recipes_parsed_percentage); } + // returns true if the number of repos cloned/to clone changed + function cloneProgressChanged(build) { + var cached = getCached(build); + return (cached.repos_cloned_percentage !== build.repos_cloned_percentage); + } + function refreshMostRecentBuilds(){ libtoaster.getMostRecentBuilds( libtoaster.ctx.mostRecentBuildsUrl,
@@ -100,6 +106,15 @@ function mrbSectionInit(ctx){ container.html(html); } + else if (cloneProgressChanged(build)) { + // update the clone progress text + selector = '#repos-cloned-percentage-' + build.id; + $(selector).html(build.repos_cloned_percentage); + + // update the recipe progress bar + selector = '#repos-cloned-percentage-bar-' + build.id; + $(selector).width(build.repos_cloned_percentage + '%'); + } else if (tasksProgressChanged(build)) { // update the task progress text selector = '#build-pc-done-' + build.id;
lib/toaster/toastergui/templates/mrb_section.html +28 -1
@@ -64,7 +64,9 @@ </div> <div data-build-state="<%:state%>"> - <%if state == 'Parsing'%> + <%if state == 'Cloning'%> + <%include tmpl='#cloning-repos-build-template'/%> + <%else state == 'Parsing'%> <%include tmpl='#parsing-recipes-build-template'/%> <%else state == 'Queued'%> <%include tmpl='#queued-build-template'/%>
@@ -98,6 +100,31 @@ </div> </script> +<!-- cloning repos build --> +<script id="cloning-repos-build-template" type="text/x-jsrender"> + <!-- progress bar and parse completion percentage --> + <div data-role="build-status" class="col-md-4 col-md-offset-1 progress-info"> + <!-- progress bar --> + <div class="progress"> + <div id="repos-cloned-percentage-bar-<%:id%>" + style="width: <%:repos_cloned_percentage%>%;" + class="progress-bar"> + </div> + </div> + </div> + + <div class="col-md-4 progress-info"> + <!-- parse completion percentage --> + <span class="glyphicon glyphicon-question-sign get-help get-help-blue" + title="Toaster is cloning the repos required for your build"> + </span> + + Cloning <span id="repos-cloned-percentage-<%:id%>"><%:repos_cloned_percentage%></span>% complete + + <%include tmpl='#cancel-template'/%> + </div> +</script> + <!-- parsing recipes build --> <script id="parsing-recipes-build-template" type="text/x-jsrender"> <!-- progress bar and parse completion percentage -->
lib/toaster/toastergui/widgets.py +4 -0
@@ -511,6 +511,10 @@ class MostRecentBuildsView(View): int((build_obj.recipes_parsed / build_obj.recipes_to_parse) * 100) + build['repos_cloned_percentage'] = \ + int((build_obj.repos_cloned / + build_obj.repos_to_clone) * 100) + tasks_complete_percentage = 0 if build_obj.outcome in (Build.SUCCEEDED, Build.FAILED): tasks_complete_percentage = 100
Signature · 1 line
1.9.1
PATCH 3/5 toaster: Add distro selection support
In reply to David ReynaFrom: David Reyna <[email protected]>
Add the ability to select a distro in the project page, based on values from the Layer Index. Add a distro selection page with the add layer feature, based on the add machine page.
[YOCTO #10632]
--- lib/toaster/orm/management/commands/lsupdates.py | 19 +++++ lib/toaster/orm/migrations/0017_distro_clone.py | 25 ++++++ lib/toaster/orm/models.py | 31 ++++++++ lib/toaster/toastergui/api.py | 13 +++- lib/toaster/toastergui/static/js/projectpage.js | 73 +++++++++++++++++ lib/toaster/toastergui/tables.py | 91 ++++++++++++++++++++++ lib/toaster/toastergui/templates/base.html | 1 + .../toastergui/templates/baseprojectpage.html | 1 + lib/toaster/toastergui/templates/distro_btn.html | 20 +++++ lib/toaster/toastergui/templates/project.html | 16 ++++ lib/toaster/toastergui/typeaheads.py | 30 +++++++ lib/toaster/toastergui/urls.py | 8 ++ 12 files changed, 325 insertions(+), 3 deletions(-) create mode 100644 bitbake/lib/toaster/orm/migrations/0017_distro_clone.py create mode 100644 bitbake/lib/toaster/toastergui/templates/distro_btn.html
lib/toaster/orm/management/commands/lsupdates.py +19 -0
@@ -23,6 +23,7 @@ from django.core.management.base import BaseCommand from orm.models import LayerSource, Layer, Release, Layer_Version from orm.models import LayerVersionDependency, Machine, Recipe +from orm.models import Distro import os import sys
@@ -249,6 +250,24 @@ class Command(BaseCommand): depends_on=lvd) self.mini_progress("Layer version dependencies", i, total) + # update Distros + logger.info("Fetching distro information") + distros_info = _get_json_response( + apilinks['distros'] + "?filter=layerbranch__branch__name:%s" % + "OR".join(whitelist_branch_names)) + + total = len(distros_info) + for i, di in enumerate(distros_info): + distro, created = Distro.objects.get_or_create( + name=di['name'], + layer_version=Layer_Version.objects.get( + pk=li_layer_branch_id_to_toaster_lv_id[di['layerbranch']])) + distro.up_date = di['updated'] + distro.name = di['name'] + distro.description = di['description'] + distro.save() + self.mini_progress("distros", i, total) + # update machines logger.info("Fetching machine information") machines_info = _get_json_response(
lib/toaster/orm/migrations/0017_distro_clone.py new file +25 -0
@@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations, models + +class Migration(migrations.Migration): + + dependencies = [ + ('orm', '0016_clone_progress'), + ] + + operations = [ + migrations.CreateModel( + name='Distro', + fields=[ + ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), + ('up_id', models.IntegerField(default=None, null=True)), + ('up_date', models.DateTimeField(default=None, null=True)), + ('name', models.CharField(max_length=255)), + ('description', models.CharField(max_length=255)), + ('layer_version', models.ForeignKey(to='orm.Layer_Version')), + ], + ), + ] +
lib/toaster/orm/models.py +31 -0
@@ -321,6 +321,22 @@ class Project(models.Model): return queryset + def get_available_distros(self): + """ Returns QuerySet of all Distros which are provided by the + Layers currently added to the Project """ + queryset = Distro.objects.filter( + layer_version__in=self.get_project_layer_versions()) + + return queryset + + def get_all_compatible_distros(self): + """ Returns QuerySet of all the compatible Wind River distros available to the + project including ones from Layers not currently added """ + queryset = Distro.objects.filter( + layer_version__in=self.get_all_compatible_layer_versions()) + + return queryset + def get_available_recipes(self): """ Returns QuerySet of all the recipes that are provided by layers added to this project """
@@ -1795,6 +1811,21 @@ def signal_runbuilds(): except FileNotFoundError: logger.info("Stopping existing runbuilds: no current process found") +class Distro(models.Model): + search_allowed_fields = ["name", "description", "layer_version__layer__name"] + up_date = models.DateTimeField(null = True, default = None) + + layer_version = models.ForeignKey('Layer_Version') + name = models.CharField(max_length=255) + description = models.CharField(max_length=255) + + def get_vcs_distro_file_link_url(self): + path = self.name+'.conf' + return self.layer_version.get_vcs_file_link_url(path) + + def __unicode__(self): + return "Distro " + self.name + "(" + self.description + ")" + django.db.models.signals.post_save.connect(invalidate_cache) django.db.models.signals.post_delete.connect(invalidate_cache) django.db.models.signals.m2m_changed.connect(invalidate_cache)
lib/toaster/toastergui/api.py +10 -3
@@ -874,6 +874,12 @@ class XhrProject(View): machinevar.value = request.POST['machineName'] machinevar.save() + # Distro name change + if 'distroName' in request.POST: + distrovar = prj.projectvariable_set.get(name="DISTRO") + distrovar.value = request.POST['distroName'] + distrovar.save() + return JsonResponse({"error": "ok"}) def get(self, request, *args, **kwargs):
@@ -960,10 +966,11 @@ class XhrProject(View): except ProjectVariable.DoesNotExist: data["machine"] = None try: - data["distro"] = project.projectvariable_set.get( - name="DISTRO").value + data["distro"] = {"name": + project.projectvariable_set.get( + name="DISTRO").value} except ProjectVariable.DoesNotExist: - data["distro"] = "-- not set yet" + data["distro"] = None data['error'] = "ok"
lib/toaster/toastergui/static/js/projectpage.js +73 -0
97 lines not on this page · raw message
lib/toaster/toastergui/tables.py +91 -0
100 lines not on this page · raw message
lib/toaster/toastergui/templates/base.html +1 -0
@@ -49,6 +49,7 @@ recipesTypeAheadUrl: {% url 'xhr_recipestypeahead' project.id as paturl%}{{paturl|json}}, layersTypeAheadUrl: {% url 'xhr_layerstypeahead' project.id as paturl%}{{paturl|json}}, machinesTypeAheadUrl: {% url 'xhr_machinestypeahead' project.id as paturl%}{{paturl|json}}, + distrosTypeAheadUrl: {% url 'xhr_distrostypeahead' project.id as paturl%}{{paturl|json}}, projectBuildsUrl: {% url 'projectbuilds' project.id as pburl %}{{pburl|json}}, xhrCustomRecipeUrl : "{% url 'xhr_customrecipe' %}", projectId : {{project.id}},
lib/toaster/toastergui/templates/baseprojectpage.html +1 -0
@@ -32,6 +32,7 @@ $(document).ready(function(){ <li><a href="{% url 'projectsoftwarerecipes' project.id %}">Software recipes</a></li> <li><a href="{% url 'projectmachines' project.id %}">Machines</a></li> <li><a href="{% url 'projectlayers' project.id %}">Layers</a></li> + <li><a href="{% url 'projectdistros' project.id %}">Distros</a></li> <li class="nav-header">Extra configuration</li> <li><a href="{% url 'projectconf' project.id %}">BitBake variables</a></li>
lib/toaster/toastergui/templates/distro_btn.html new file +20 -0
@@ -0,0 +1,20 @@ +<a href="{% url 'project' extra.pid %}?setDistro={{data.name}}" class="btn btn-default btn-block layer-exists-{{data.layer_version.id}}" + {% if data.layer_version.pk not in extra.current_layers %} + style="display:none;" + {% endif %}> + Set distro</a> +<a class="btn btn-default btn-block layerbtn layer-add-{{data.layer_version.id}}" data-layer='{ + "id": {{data.layer_version.id}}, + "name": "{{data.layer_version.layer.name}}", + "xhrLayerUrl": "{% url "xhr_layer" extra.pid data.pk %}", + "layerdetailurl": "{%url 'layerdetails' extra.pid data.layer_version.id %}" + }' data-directive="add" + {% if data.layer_version.pk in extra.current_layers %} + style="display:none;" + {% endif %} +> + <span class="glyphicon glyphicon-plus"></span> + Add layer + <span class="glyphicon glyphicon-question-sign get-help" title="To select this distro, you must first add the {{data.layer_version.layer.name}} layer to your project"></i> +</a> +
lib/toaster/toastergui/templates/project.html +16 -0
@@ -77,6 +77,22 @@ </form> </div> + <div class="well well-transparent" id="distro-section"> + <h3>Distro</h3> + + <p class="lead"><span id="project-distro-name"></span> <span class="glyphicon glyphicon-edit" id="change-distro-toggle"></span></p> + + <form id="select-distro-form" style="display:none;" class="form-inline"> + <span class="help-block">Distro suggestions come from the Layer Index</a></span> + <div class="form-group"> + <input class="form-control" id="distro-change-input" autocomplete="off" value="" data-provide="typeahead" data-minlength="1" data-autocomplete="off" type="text"> + </div> + <button id="distro-change-btn" class="btn btn-default" type="button">Save</button> + <a href="#" id="cancel-distro-change" class="btn btn-link">Cancel</a> + <p class="form-link"><a href="{% url 'projectdistros' project.id %}">View compatible distros</a></p> + </form> + </div> + <div class="well well-transparent"> <h3>Most built recipes</h3>
lib/toaster/toastergui/typeaheads.py +30 -0
@@ -100,6 +100,36 @@ class MachinesTypeAhead(ToasterTypeAhead): return results +class DistrosTypeAhead(ToasterTypeAhead): + """ Typeahead for all the distros available in the current project's + configuration """ + def __init__(self): + super(DistrosTypeAhead, self).__init__() + + def apply_search(self, search_term, prj, request): + distros = prj.get_available_distros() + distros = distros.order_by("name") + + primary_results = distros.filter(name__istartswith=search_term) + secondary_results = distros.filter(name__icontains=search_term).exclude(pk__in=primary_results) + tertiary_results = distros.filter(layer_version__layer__name__icontains=search_term).exclude(pk__in=primary_results).exclude(pk__in=secondary_results) + + results = [] + + for distro in list(primary_results) + list(secondary_results) + list(tertiary_results): + + detail = "[ %s ]" % (distro.layer_version.layer.name) + needed_fields = { + 'id' : distro.pk, + 'name' : distro.name, + 'detail' : detail, + } + + results.append(needed_fields) + + return results + + class RecipesTypeAhead(ToasterTypeAhead): """ Typeahead for all the recipes available in the current project's configuration """
lib/toaster/toastergui/urls.py +8 -0
@@ -158,6 +158,11 @@ urlpatterns = [ name=tables.LayerMachinesTable.__name__.lower()), + url(r'^project/(?P<pid>\d+)/distros/$', + tables.DistrosTable.as_view(template_name="generic-toastertable-page.html"), + name="projectdistros"), + + url(r'^project/(?P<pid>\d+)/customrecipe/(?P<custrecipeid>\d+)/selectpackages/$', tables.SelectPackagesTable.as_view(), name="recipeselectpackages"),
@@ -187,6 +192,9 @@ urlpatterns = [ typeaheads.GitRevisionTypeAhead.as_view(), name='xhr_gitrevtypeahead'), + url(r'^xhr_typeahead/(?P<pid>\d+)/distros$', + typeaheads.DistrosTypeAhead.as_view(), name='xhr_distrostypeahead'), + url(r'^xhr_testreleasechange/(?P<pid>\d+)$', views.xhr_testreleasechange, name='xhr_testreleasechange'), url(r'^xhr_configvaredit/(?P<pid>\d+)$', views.xhr_configvaredit,
Signature · 1 line
1.9.1
PATCH 4/5 toaster: large package set breaks sqlite query
In reply to David ReynaFrom: David Reyna <[email protected]>
If you build a project with a large package set, you will get a crash in "views.py" when the dashboard attempts to fetch the package set to calculate the package count and size. This is a sqlite limitation, and it fails with as few as 1220 packages.
[YOCTO #11717]
--- lib/toaster/toastergui/views.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-)
lib/toaster/toastergui/views.py +9 -4
@@ -457,10 +457,15 @@ def builddashboard( request, build_id ): npkg = 0 pkgsz = 0 package = None - for package in Package.objects.filter(id__in = [x.package_id for x in t.target_installed_package_set.all()]): - pkgsz = pkgsz + package.size - if package.installed_name: - npkg = npkg + 1 + # Chunk the query to avoid "too many SQL variables" error + package_set = t.target_installed_package_set.all() + package_set_len = len(package_set) + for ps_start in range(0,package_set_len,500): + ps_stop = min(ps_start+500,package_set_len) + for package in Package.objects.filter(id__in = [x.package_id for x in package_set[ps_start:ps_stop]]): + pkgsz = pkgsz + package.size + if package.installed_name: + npkg = npkg + 1 elem['npkg'] = npkg elem['pkgsz'] = pkgsz ti = Target_Image_File.objects.filter(target_id = t.id)
Signature · 1 line
1.9.1
PATCH 5/5 toaster: test 'commit' first in get_vcs_reference
In reply to David ReynaFrom: David Reyna <[email protected]>
The 'commit' value should be tested and used first when resolving the ref for a layer, since that is an explicit override in each layer index 'layeritem' record. If should take precedence over more-global 'branch' and 'release' values, instead of being last.
[YOCTO #11515]
--- lib/toaster/orm/models.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
lib/toaster/orm/models.py +2 -2
@@ -1516,12 +1516,12 @@ class Layer_Version(models.Model): return self._handle_url_path(self.layer.vcs_web_tree_base_url, '') def get_vcs_reference(self): + if self.commit is not None and len(self.commit) > 0: + return self.commit if self.branch is not None and len(self.branch) > 0: return self.branch if self.release is not None: return self.release.name - if self.commit is not None and len(self.commit) > 0: - return self.commit return 'N/A' def get_detailspage_url(self, project_id=None):
Signature · 1 line
1.9.1