Skip to content

Commit 2fe6f73

Browse files
committed
CFbot integration
This adds our own custom replication between the CFbot and the commitfest app. We currently keep the last CFbot runs in our database, in an attempt to keep the queries easy and efficient. The CFbot results are now shown on the overview page of each commitfest, as well as on the page for each specific patch.
1 parent 41ec695 commit 2fe6f73

14 files changed

+354
-2
lines changed

media/commitfest/css/commitfest.css

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,3 +65,7 @@ div.form-group div.controls input.threadpick-input {
6565
#annotateMessageBody.loading * {
6666
display: none;
6767
}
68+
69+
.cfbot-summary img {
70+
margin-top: -3px;
71+
}
Lines changed: 4 additions & 0 deletions
Loading

media/commitfest/new_failure.svg

Lines changed: 5 additions & 0 deletions
Loading

media/commitfest/new_success.svg

Lines changed: 4 additions & 0 deletions
Loading

media/commitfest/old_failure.svg

Lines changed: 5 additions & 0 deletions
Loading

media/commitfest/old_success.svg

Lines changed: 4 additions & 0 deletions
Loading

media/commitfest/running.svg

Lines changed: 4 additions & 0 deletions
Loading

media/commitfest/waiting_to_start.svg

Lines changed: 3 additions & 0 deletions
Loading
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
# Generated by Django 4.2.17 on 2024-12-21 14:15
2+
3+
from django.db import migrations, models
4+
import django.db.models.deletion
5+
6+
7+
class Migration(migrations.Migration):
8+
9+
dependencies = [
10+
('commitfest', '0005_history_dateindex'),
11+
]
12+
13+
operations = [
14+
migrations.CreateModel(
15+
name='CfbotBranch',
16+
fields=[
17+
('patch', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, primary_key=True, related_name='cfbot_branch', serialize=False, to='commitfest.patch')),
18+
('branch_id', models.IntegerField()),
19+
('branch_name', models.TextField()),
20+
('commit_id', models.TextField(blank=True, null=True)),
21+
('apply_url', models.TextField()),
22+
('status', models.TextField(choices=[('testing', 'Testing'), ('finished', 'Finished'), ('failed', 'Failed'), ('timeout', 'Timeout')])),
23+
('created', models.DateTimeField(auto_now_add=True)),
24+
('modified', models.DateTimeField(auto_now=True)),
25+
],
26+
),
27+
migrations.CreateModel(
28+
name='CfbotTask',
29+
fields=[
30+
('id', models.BigAutoField(primary_key=True, serialize=False)),
31+
('task_id', models.TextField(unique=True)),
32+
('task_name', models.TextField()),
33+
('branch_id', models.IntegerField()),
34+
('position', models.IntegerField()),
35+
('status', models.TextField(choices=[('CREATED', 'Created'), ('NEEDS_APPROVAL', 'Needs Approval'), ('TRIGGERED', 'Triggered'), ('EXECUTING', 'Executing'), ('FAILED', 'Failed'), ('COMPLETED', 'Completed'), ('SCHEDULED', 'Scheduled'), ('ABORTED', 'Aborted'), ('ERRORED', 'Errored')])),
36+
('created', models.DateTimeField(auto_now_add=True)),
37+
('modified', models.DateTimeField(auto_now=True)),
38+
('patch', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='cfbot_tasks', to='commitfest.patch')),
39+
],
40+
),
41+
migrations.RunSQL(
42+
"""
43+
CREATE TYPE cfbotbranch_status AS ENUM (
44+
'testing',
45+
'finished',
46+
'failed',
47+
'timeout'
48+
);
49+
"""
50+
),
51+
migrations.RunSQL(
52+
"""
53+
CREATE TYPE cfbottask_status AS ENUM (
54+
'CREATED',
55+
'NEEDS_APPROVAL',
56+
'TRIGGERED',
57+
'EXECUTING',
58+
'FAILED',
59+
'COMPLETED',
60+
'SCHEDULED',
61+
'ABORTED',
62+
'ERRORED'
63+
);
64+
"""
65+
),
66+
migrations.RunSQL(
67+
"""
68+
ALTER TABLE commitfest_cfbotbranch
69+
ALTER COLUMN status TYPE cfbotbranch_status
70+
USING status::cfbotbranch_status;
71+
"""
72+
),
73+
migrations.RunSQL(
74+
"""
75+
ALTER TABLE commitfest_cfbottask
76+
ALTER COLUMN status TYPE cfbottask_status
77+
USING status::cfbottask_status;
78+
"""
79+
),
80+
]

pgcommitfest/commitfest/models.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,3 +341,54 @@ class PatchStatus(models.Model):
341341
class PendingNotification(models.Model):
342342
history = models.ForeignKey(PatchHistory, blank=False, null=False, on_delete=models.CASCADE)
343343
user = models.ForeignKey(User, blank=False, null=False, on_delete=models.CASCADE)
344+
345+
346+
class CfbotBranch(models.Model):
347+
STATUS_CHOICES = [
348+
('testing', 'Testing'),
349+
('finished', 'Finished'),
350+
('failed', 'Failed'),
351+
('timeout', 'Timeout'),
352+
]
353+
354+
patch = models.OneToOneField(Patch, on_delete=models.CASCADE, related_name="cfbot_branch", primary_key=True)
355+
branch_id = models.IntegerField(null=False)
356+
branch_name = models.TextField(null=False)
357+
commit_id = models.TextField(null=True, blank=True)
358+
apply_url = models.TextField(null=False)
359+
# Actually a postgres enum column
360+
status = models.TextField(choices=STATUS_CHOICES, null=False)
361+
created = models.DateTimeField(auto_now_add=True)
362+
modified = models.DateTimeField(auto_now=True)
363+
364+
365+
class CfbotTask(models.Model):
366+
STATUS_CHOICES = [
367+
('CREATED', 'Created'),
368+
('NEEDS_APPROVAL', 'Needs Approval'),
369+
('TRIGGERED', 'Triggered'),
370+
('EXECUTING', 'Executing'),
371+
('FAILED', 'Failed'),
372+
('COMPLETED', 'Completed'),
373+
('SCHEDULED', 'Scheduled'),
374+
('ABORTED', 'Aborted'),
375+
('ERRORED', 'Errored'),
376+
]
377+
378+
# This id is only used by Django. Using text type for primary keys, has
379+
# historically caused problems.
380+
id = models.BigAutoField(primary_key=True)
381+
# This is the id used by the external CI system. Currently with CirrusCI
382+
# this is an integer, and thus we could probably store it as such. But
383+
# given that we might need to change CI providers at some point, and that
384+
# CI provider might use e.g. UUIDs, we prefer to consider the format of the
385+
# ID opaque and store it as text.
386+
task_id = models.TextField(unique=True)
387+
task_name = models.TextField(null=False)
388+
patch = models.ForeignKey(Patch, on_delete=models.CASCADE, related_name="cfbot_tasks")
389+
branch_id = models.IntegerField(null=False)
390+
position = models.IntegerField(null=False)
391+
# Actually a postgres enum column
392+
status = models.TextField(choices=STATUS_CHOICES, null=False)
393+
created = models.DateTimeField(auto_now_add=True)
394+
modified = models.DateTimeField(auto_now=True)

pgcommitfest/commitfest/templates/commitfest.html

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ <h3>{{p.is_open|yesno:"Active patches,Closed patches"}}</h3>
6363
<th>{%if p.is_open%}<a href="#" style="color:#333333;" onclick="return sortpatches(0);">Patch</a>{%if sortkey == 0%}<div style="float:right;"><i class="glyphicon glyphicon-arrow-down"></i></div>{%endif%}{%endif%}</th>
6464
<th>Status</th>
6565
<th>Ver</th>
66+
<th>CI status</th>
6667
<th>Author</th>
6768
<th>Reviewers</th>
6869
<th>Committer</th>
@@ -86,6 +87,31 @@ <h3>{{p.is_open|yesno:"Active patches,Closed patches"}}</h3>
8687
<td><a href="{{p.id}}/">{{p.name}}</a></td>
8788
<td><span class="label label-{{p.status|patchstatuslabel}}">{{p.status|patchstatusstring}}</span></td>
8889
<td>{%if p.targetversion%}<span class="label label-default">{{p.targetversion}}</span>{%endif%}</td>
90+
<td>
91+
{%if p.cfbot_results %}
92+
<a href="https://cirrus-ci.com/github/postgresql-cfbot/postgresql/cf%2F{{p.id}}"
93+
class="cfbot-summary"
94+
{%if p.cfbot_results.failed_task_names %}
95+
title="Failed: {{p.cfbot_results.failed_task_names}}"
96+
{%endif%}
97+
>
98+
{%if p.cfbot_results.needs_rebase %}
99+
<span class="label label-warning">Needs rebase!</span>
100+
{%else%}
101+
{%if p.cfbot_results.failed > 0 %}
102+
<img src="/media/commitfest/new_failure.svg"/>
103+
{%elif p.cfbot_results.running > 0 %}
104+
<img src="/media/commitfest/running.svg"/>
105+
{%elif p.cfbot_results.completed > 0 %}
106+
<img src="/media/commitfest/new_success.svg"/>
107+
{%endif%}
108+
{{p.cfbot_results.completed}}/{{p.cfbot_results.total}}
109+
{%endif%}
110+
</a>
111+
{% else %}
112+
<span class="label label-default">Not processed</span>
113+
{%endif%}
114+
</td>
89115
<td>{{p.author_names|default:''}}</td>
90116
<td>{{p.reviewer_names|default:''}}</td>
91117
<td>{{p.committer|default:''}}</td>

pgcommitfest/commitfest/templates/patch.html

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,32 @@
1212
<th>Title</th>
1313
<td>{{patch.name}}</td>
1414
</tr>
15+
<tr>
16+
<th>CI (CFBot)</th>
17+
<td>
18+
{%if not cfbot_branch %}
19+
<span class="label label-default">Not processed</span></a>
20+
{%elif not cfbot_branch.commit_id %}
21+
<a href="https://cirrus-ci.com/github/postgresql-cfbot/postgresql/cf%2F{{patch.id}}">
22+
<span class="label label-warning">Needs rebase!</span></a>
23+
{%else%}
24+
<a href="https://cirrus-ci.com/github/postgresql-cfbot/postgresql/cf%2F{{patch.id}}">
25+
<span class="label label-default">Summary</span></a>
26+
{%for c in cfbot_tasks %}
27+
{%if c.status == 'COMPLETED'%}
28+
<a href="https://cirrusci.com/task/{{c.id}}" title="{{c.task_name}}: {{c.status}}"><img src="/media/commitfest/new_success.svg"/></a>
29+
{%elif c.status == 'CREATED' %}
30+
<a href="https://cirrusci.com/task/{{c.id}}" title="{{c.task_name}}: {{c.status}}"><img src="/media/commitfest/waiting_to_start.svg"/></a>
31+
{%elif c.status == 'EXECUTING' %}
32+
<a href="https://cirrusci.com/task/{{c.id}}" title="{{c.task_name}}: {{c.status}}"><img src="/media/commitfest/running.svg"/></a>
33+
{%else %}
34+
<a href="https://cirrusci.com/task/{{c.id}}" title="{{c.task_name}}: {{c.status}}"><img src="/media/commitfest/new_failure.svg"/></a>
35+
{%endif%}
36+
{%endfor%}
37+
{%endif%}
38+
</a>
39+
</td>
40+
</tr>
1541
<tr>
1642
<th>Topic</th>
1743
<td>{{patch.topic}}</td>

0 commit comments

Comments
 (0)