Skip to content

Commit 54413f0

Browse files
authored
Merge branch 'main' into pgauth_smallfix
2 parents 6ae52f1 + 238b4fe commit 54413f0

27 files changed

Lines changed: 1075 additions & 176 deletions

.github/workflows/ci.yaml

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,37 @@ jobs:
3333

3434
- name: Run djhtml
3535
run: djhtml pgcommitfest/*/templates/*.html pgcommitfest/*/templates/*.inc --tabwidth=1 --check
36+
37+
test:
38+
runs-on: ubuntu-24.04
39+
name: "Django Tests"
40+
41+
services:
42+
postgres:
43+
image: postgres:18
44+
env:
45+
POSTGRES_USER: postgres
46+
POSTGRES_PASSWORD: postgres
47+
options: >-
48+
--health-cmd pg_isready
49+
--health-interval 10s
50+
--health-timeout 5s
51+
--health-retries 5
52+
ports:
53+
- 5432:5432
54+
55+
steps:
56+
- name: Checkout code
57+
uses: actions/checkout@v4
58+
59+
- name: Set up Python with uv
60+
uses: astral-sh/setup-uv@v4
61+
62+
- name: Create CI settings
63+
run: cp pgcommitfest/local_settings_example.py pgcommitfest/local_settings.py
64+
65+
- name: Install dependencies
66+
run: uv sync --extra dev
67+
68+
- name: Run tests
69+
run: uv run pytest --create-db

.github/workflows/deploy.yaml

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,17 @@ on:
55
branches:
66
- main
77
- prod
8+
tags:
9+
- 'v*'
810

911
jobs:
1012
deployment:
1113
runs-on: ubuntu-latest
12-
environment: ${{ github.ref_name }}
14+
environment: ${{ startsWith(github.ref, 'refs/tags/') && 'prod' || github.ref_name }}
1315
steps:
1416
- name: Trigger deploy
1517
run: |
16-
curl --fail-with-body --silent --show-error -X POST ${{ secrets.HOOKURL }} -H "X-Key: ${{ secrets.HOOKSECRET }}"
18+
curl --fail-with-body --silent --show-error -X POST ${{ secrets.HOOKURL }} \
19+
-H "X-Key: ${{ secrets.HOOKSECRET }}" \
20+
-H "Content-Type: application/x-www-form-urlencoded" \
21+
-d "commit=${{ github.sha }}"

media/commitfest/css/commitfest.css

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,12 @@
44

55
body {
66
--bs-body-font-size: 14px;
7-
--bs-body-font-family: -apple-system,"Segoe UI",Roboto,"Helvetica Neue","Noto Sans","Liberation Sans",Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";
8-
--bs-font-sans-serif: -apple-system,"Segoe UI",Roboto,"Helvetica Neue","Noto Sans","Liberation Sans",Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";
7+
--bs-body-font-family: -apple-system, "Segoe UI", Roboto, "Helvetica Neue",
8+
"Noto Sans", "Liberation Sans", Arial, sans-serif, "Apple Color Emoji",
9+
"Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
10+
--bs-font-sans-serif: -apple-system, "Segoe UI", Roboto, "Helvetica Neue",
11+
"Noto Sans", "Liberation Sans", Arial, sans-serif, "Apple Color Emoji",
12+
"Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
913
}
1014

1115
/* Bootstrap 5 inline delete/close buttons */
Lines changed: 5 additions & 0 deletions
Loading

media/commitfest/js/commitfest.js

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ function addAnnotation(threadid) {
180180
$("#annotateThreadList").find("option").remove();
181181
$("#annotateMessage").val("");
182182
$("#annotateMsgId").val("");
183-
const modal = new bootstrap.Modal(document.getElementById('annotateModal'));
183+
const modal = new bootstrap.Modal(document.getElementById("annotateModal"));
184184
modal.show();
185185
$("#annotateThreadList").focus();
186186
updateAnnotationMessages(threadid);
@@ -248,7 +248,7 @@ function deleteAnnotation(annid) {
248248
}
249249

250250
function flagCommitted(committer) {
251-
const modal = new bootstrap.Modal(document.getElementById('commitModal'));
251+
const modal = new bootstrap.Modal(document.getElementById("commitModal"));
252252
modal.show();
253253
$("#committerSelect")[0].selectize.setValue(committer);
254254
$("#doCommitButton").unbind("click");
@@ -307,7 +307,9 @@ function togglePatchFilterButton(buttonId, collapseId) {
307307
* Upstream user search dialog
308308
*/
309309
function search_and_store_user() {
310-
const modal = new bootstrap.Modal(document.getElementById('searchUserModal'));
310+
const modal = new bootstrap.Modal(
311+
document.getElementById("searchUserModal"),
312+
);
311313
$("#doSelectUserButton").unbind("click");
312314
$("#doSelectUserButton").click(() => {
313315
if (!$("#searchUserList").val()) {

pgcommitfest/commitfest/admin.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,16 @@ class PatchAdmin(admin.ModelAdmin):
3232
list_display = ("name",)
3333

3434

35+
class MailThreadAdmin(admin.ModelAdmin):
36+
list_display = (
37+
"messageid",
38+
"subject",
39+
"firstmessage",
40+
"latestmsgid",
41+
"latestmessage",
42+
)
43+
44+
3545
class MailThreadAttachmentAdmin(admin.ModelAdmin):
3646
list_display = (
3747
"date",
@@ -68,5 +78,5 @@ class TagAdmin(admin.ModelAdmin):
6878
admin.site.register(CfbotBranch)
6979
admin.site.register(CfbotTask)
7080

71-
admin.site.register(MailThread)
81+
admin.site.register(MailThread, MailThreadAdmin)
7282
admin.site.register(MailThreadAttachment, MailThreadAttachmentAdmin)

pgcommitfest/commitfest/ajax.py

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -114,17 +114,23 @@ def refresh_single_thread(thread):
114114
)
115115
if thread.latestmsgid != r[-1]["msgid"]:
116116
# There is now a newer mail in the thread!
117+
parse_and_add_attachments(r, thread)
118+
# Potentially update the last mail date - if there wasn't already a mail on each patch
119+
# from a *different* thread that had an earlier date.
120+
new_latestmessage = r[-1]["date"]
121+
for p in thread.patches.filter(lastmail__lt=new_latestmessage):
122+
p.lastmail = new_latestmessage
123+
p.save()
124+
125+
# Finally, we update the thread entry itself. We should only do that at
126+
# the end, in case any of the previous steps fail. Then we'll retry on
127+
# the next run. We could also do this in a transaction, but that would
128+
# mean we lock a bunch of rows for potentially a long time.
117129
thread.latestmsgid = r[-1]["msgid"]
118130
thread.latestmessage = r[-1]["date"]
119131
thread.latestauthor = r[-1]["from"]
120132
thread.latestsubject = r[-1]["subj"]
121133
thread.save()
122-
parse_and_add_attachments(r, thread)
123-
# Potentially update the last mail date - if there wasn't already a mail on each patch
124-
# from a *different* thread that had an earlier date.
125-
for p in thread.patches.filter(lastmail__lt=thread.latestmessage):
126-
p.lastmail = thread.latestmessage
127-
p.save()
128134

129135

130136
@transaction.atomic

pgcommitfest/commitfest/fixtures/commitfest_data.json

Lines changed: 66 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1106,19 +1106,19 @@
11061106
"fields": {
11071107
"branch_id": 345,
11081108
"branch_name": "cf/2",
1109-
"commit_id": null,
1109+
"commit_id": "def456",
11101110
"apply_url": "http://cfbot.cputube.org/patch_4573.log",
1111-
"status": "failed",
1112-
"needs_rebase_since": "2025-03-01T22:30:42",
1113-
"failing_since": "2025-02-01T22:30:42",
1111+
"status": "finished",
1112+
"needs_rebase_since": null,
1113+
"failing_since": null,
11141114
"created": "2025-01-26T22:11:09.961",
11151115
"modified": "2025-03-01T22:59:14.717",
1116-
"version": "",
1117-
"patch_count": null,
1118-
"first_additions": null,
1119-
"first_deletions": null,
1120-
"all_additions": null,
1121-
"all_deletions": null
1116+
"version": "v2",
1117+
"patch_count": 3,
1118+
"first_additions": 50,
1119+
"first_deletions": 10,
1120+
"all_additions": 120,
1121+
"all_deletions": 35
11221122
}
11231123
},
11241124
{
@@ -1295,5 +1295,61 @@
12951295
"created": "2025-07-03T06:29:25.086",
12961296
"modified": "2025-07-03T06:29:25.086"
12971297
}
1298+
},
1299+
{
1300+
"model": "commitfest.cfbottask",
1301+
"pk": 9,
1302+
"fields": {
1303+
"task_id": "12347",
1304+
"task_name": "FormattingCheck",
1305+
"patch": 1,
1306+
"branch_id": 123,
1307+
"position": 3,
1308+
"status": "FAILED",
1309+
"created": "2025-01-26T22:08:00.000",
1310+
"modified": "2025-01-26T22:08:30.000"
1311+
}
1312+
},
1313+
{
1314+
"model": "commitfest.cfbottask",
1315+
"pk": 10,
1316+
"fields": {
1317+
"task_id": "34501",
1318+
"task_name": "Linux build",
1319+
"patch": 2,
1320+
"branch_id": 345,
1321+
"position": 1,
1322+
"status": "COMPLETED",
1323+
"created": "2025-01-26T22:11:30.000",
1324+
"modified": "2025-01-26T22:12:15.000"
1325+
}
1326+
},
1327+
{
1328+
"model": "commitfest.cfbottask",
1329+
"pk": 11,
1330+
"fields": {
1331+
"task_id": "34502",
1332+
"task_name": "MacOS Build",
1333+
"patch": 2,
1334+
"branch_id": 345,
1335+
"position": 2,
1336+
"status": "COMPLETED",
1337+
"created": "2025-01-26T22:11:35.000",
1338+
"modified": "2025-01-26T22:13:20.000"
1339+
}
1340+
},
1341+
{
1342+
"model": "commitfest.cfbottask",
1343+
"pk": 12,
1344+
"fields": {
1345+
"task_id": "34503",
1346+
"task_name": "FormattingCheck",
1347+
"patch": 2,
1348+
"branch_id": 345,
1349+
"position": 3,
1350+
"status": "COMPLETED",
1351+
"created": "2025-01-26T22:11:40.000",
1352+
"modified": "2025-01-26T22:12:00.000"
1353+
}
12981354
}
12991355
]

pgcommitfest/commitfest/forms.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,17 @@ class CommitFestFilterForm(forms.Form):
2424
reviewer = forms.ChoiceField(required=False, label="Reviewer (type to search)")
2525
sortkey = forms.IntegerField(required=False)
2626

27-
def __init__(self, data, *args, **kwargs):
27+
def __init__(self, data, commitfest=None, *args, **kwargs):
2828
super(CommitFestFilterForm, self).__init__(data, *args, **kwargs)
29+
self.commitfest = commitfest
30+
31+
# Update selectize_fields with cf parameter if commitfest is provided
32+
if commitfest:
33+
self.selectize_fields = {
34+
"author": f"/lookups/user?cf={commitfest.id}",
35+
"reviewer": f"/lookups/user?cf={commitfest.id}",
36+
"tag": None,
37+
}
2938

3039
self.fields["sortkey"].widget = forms.HiddenInput()
3140

pgcommitfest/commitfest/lookups.py

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,54 @@
1-
from django.contrib.auth.decorators import login_required
21
from django.contrib.auth.models import User
2+
from django.db import connection
33
from django.db.models import Q
4-
from django.http import Http404, HttpResponse
4+
from django.http import Http404, HttpResponse, HttpResponseForbidden
55

66
import json
77

88

9-
@login_required
109
def userlookup(request):
1110
query = request.GET.get("query", None)
11+
cf = request.GET.get("cf", None)
12+
1213
if not query:
1314
raise Http404()
1415

16+
# Start with base filters for active users matching the query
1517
users = User.objects.filter(
1618
Q(is_active=True),
1719
Q(username__icontains=query)
1820
| Q(first_name__icontains=query)
1921
| Q(last_name__icontains=query),
2022
)
2123

24+
# If no commitfest filter is provided, require login
25+
if not cf:
26+
if not request.user.is_authenticated:
27+
return HttpResponseForbidden(
28+
"Login required when not filtering by commitfest"
29+
)
30+
else:
31+
# Filter users to only those who have participated in the specified commitfest.
32+
with connection.cursor() as cursor:
33+
cursor.execute(
34+
"""
35+
SELECT cpa.user_id FROM commitfest_patch_authors cpa
36+
INNER JOIN commitfest_patchoncommitfest poc ON poc.patch_id = cpa.patch_id
37+
WHERE poc.commitfest_id = %(cf)s
38+
UNION
39+
SELECT cpr.user_id FROM commitfest_patch_reviewers cpr
40+
INNER JOIN commitfest_patchoncommitfest poc ON poc.patch_id = cpr.patch_id
41+
WHERE poc.commitfest_id = %(cf)s
42+
UNION
43+
SELECT p.committer_id FROM commitfest_patch p
44+
INNER JOIN commitfest_patchoncommitfest poc ON poc.patch_id = p.id
45+
WHERE poc.commitfest_id = %(cf)s AND p.committer_id IS NOT NULL
46+
""",
47+
{"cf": cf},
48+
)
49+
user_ids = [row[0] for row in cursor.fetchall()]
50+
users = users.filter(id__in=user_ids)
51+
2252
return HttpResponse(
2353
json.dumps(
2454
{

0 commit comments

Comments
 (0)