diff mbox series

[layerindex-web] layerindex: show a layer's branches in a table on the layer page

Message ID 20261114034111.76580-1-tim.orling@konsulko.com
State Under Review
Delegated to: Tim Orling
Headers show
Series [layerindex-web] layerindex: show a layer's branches in a table on the layer page | expand

Commit Message

Tim Orling Nov. 14, 2026, 3:41 a.m. UTC
For a layer like meta-qt6, where each branch tracks a different git
branch, the layer page only showed the branch being viewed. Add a
Branches tab listing every branch of the layer, with the git branch it
tracks (actual_branch), the date of its last commit, and whether it is
still updated.

[YOCTO #15380]

AI-Generated: Claude Opus 5.5
Signed-off-by: Tim Orling <tim.orling@konsulko.com>
---
This depends on "[PATCH v2] layerindex: show when a layer branch is no
longer updated" (bug 14781) for the updates column, and applies on top
of it and on master-next. Editing the branches, also asked for in the
bug, would follow separately.

On a test instance loaded with a copy of the layers.openembedded.org
database, meta-qt6's table shows each branch's git branch (master: dev,
scarthgap: lts-6.8, kirkstone: 6.5, ...) and that langdale is no longer
updated.

 layerindex/views.py              |  1 +
 templates/layerindex/detail.html | 23 ++++++++++++++++
 tests/test_branch_table.py       | 45 ++++++++++++++++++++++++++++++++
 3 files changed, 69 insertions(+)
 create mode 100644 tests/test_branch_table.py


base-commit: 08e58cdece30019a6f9e5d14af16ebe20afa6177
prerequisite-patch-id: d08807a0e95947278caa6b5f1c6c2e6e74dba0e5
diff mbox series

Patch

diff --git a/layerindex/views.py b/layerindex/views.py
index 85c82d6..a041226 100644
--- a/layerindex/views.py
+++ b/layerindex/views.py
@@ -493,6 +493,7 @@  class LayerDetailView(DetailView):
             context['appends'] = layerbranch.bbappend_set.order_by('filename')
             context['classes'] = layerbranch.bbclass_set.order_by('name')
             context['updates'] = LayerUpdate.objects.filter(layer=layerbranch.layer, branch=layerbranch.branch).order_by('-started')
+        context['layerbranches'] = LayerBranch.objects.filter(layer=layer, branch__hidden=False, branch__comparison=False).select_related('layer', 'branch').order_by('branch__sort_priority')
         context['url_branch'] = self.kwargs['branch']
         context['this_url_name'] = resolve(self.request.path_info).url_name
         if 'rrs' in settings.INSTALLED_APPS:
diff --git a/templates/layerindex/detail.html b/templates/layerindex/detail.html
index b14d030..7e79b3f 100644
--- a/templates/layerindex/detail.html
+++ b/templates/layerindex/detail.html
@@ -224,6 +224,7 @@ 
             {% if updates.count > 0 %}
                 <li><a href="#updates" data-toggle="tab">Updates</a></li>
             {% endif %}
+            <li><a href="#branches" data-toggle="tab">Branches</a></li>
         </ul>
 
         <div class="tab-content">
@@ -393,6 +394,28 @@ 
                     </table>
                 </div>
             {% endif %}
+            <div class="tab-pane" id="branches">
+                <table class="table table-bordered">
+                    <thead>
+                        <tr>
+                            <th>Branch</th>
+                            <th>Git branch</th>
+                            <th>Last commit</th>
+                            <th>Updates</th>
+                        </tr>
+                    </thead>
+                    <tbody>
+                        {% for lb in layerbranches %}
+                            <tr>
+                                <td>{% if lb.branch.name == url_branch %}<b>{{ lb.branch.name }}</b>{% else %}<a href="{% url 'layer_item' lb.branch.name layeritem.name %}">{{ lb.branch.name }}</a>{% endif %}</td>
+                                <td>{{ lb.get_checkout_branch }}</td>
+                                <td>{% if lb.vcs_last_commit %}{{ lb.vcs_last_commit|date:"Y-m-d" }}{% else %}<span class="text-muted">not indexed</span>{% endif %}</td>
+                                <td>{% with reason=lb.updates_disabled_reason %}{% if reason %}<span class="label label-default label-not-updated">not updated</span> <small class="text-muted">{{ reason }}</small>{% else %}updated{% endif %}{% endwith %}</td>
+                            </tr>
+                        {% endfor %}
+                    </tbody>
+                </table>
+            </div>
         </div>
  
 
diff --git a/tests/test_branch_table.py b/tests/test_branch_table.py
new file mode 100644
index 0000000..9d3b0c9
--- /dev/null
+++ b/tests/test_branch_table.py
@@ -0,0 +1,45 @@ 
+# layerindex-web - test the table of a layer's branches on the layer page
+#
+# Licensed under the MIT license, see COPYING.MIT for details
+#
+# SPDX-License-Identifier: MIT
+
+import re
+from datetime import datetime
+
+import pytest
+from django.urls import reverse
+
+from layerindex.models import Branch, LayerBranch, LayerItem
+
+
+@pytest.mark.django_db
+def test_branch_table(client):
+    # [YOCTO #15380] like meta-qt6, where each branch tracks a different git branch
+    master, _ = Branch.objects.get_or_create(name='master', defaults={'bitbake_branch': 'master'})
+    master.sort_priority = 1
+    master.save()
+    scarthgap = Branch.objects.create(name='scarthgap', bitbake_branch='2.8', sort_priority=2)
+    kirkstone = Branch.objects.create(name='kirkstone', bitbake_branch='2.0', sort_priority=3,
+                                      updates_enabled=False)
+    hidden = Branch.objects.create(name='hiddenbranch', bitbake_branch='master', hidden=True)
+    layer = LayerItem.objects.create(name='meta-qt', status='P', layer_type='S', summary='qt',
+                                     description='qt', vcs_url='git://example.com/meta-qt.git')
+    LayerBranch.objects.create(layer=layer, branch=master, actual_branch='dev',
+                               vcs_last_commit=datetime(2026, 11, 1))
+    LayerBranch.objects.create(layer=layer, branch=scarthgap, actual_branch='6.8')
+    LayerBranch.objects.create(layer=layer, branch=kirkstone, actual_branch='6.2')
+    LayerBranch.objects.create(layer=layer, branch=hidden)
+
+    page = client.get(reverse('layer_item', args=('master', 'meta-qt'))).content.decode()
+    table = page[page.index('id="branches"'):]
+    rows = [re.sub(r'<[^>]+>|\s+', ' ', r).split() for r in re.findall(r'<tr>(.*?)</tr>', table, re.S)[1:]]
+
+    assert [r[:2] for r in rows] == [['master', 'dev'], ['scarthgap', '6.8'], ['kirkstone', '6.2']]
+    assert rows[0][2:] == ['2026-11-01', 'updated']
+    assert rows[1][2:5] == ['not', 'indexed', 'updated']
+    assert 'Updates are disabled for the kirkstone branch' in table
+    assert 'hiddenbranch' not in table
+    # The current branch is not a link; the others are
+    assert reverse('layer_item', args=('scarthgap', 'meta-qt')) in table
+    assert reverse('layer_item', args=('master', 'meta-qt')) not in table