Skip to content

Commit 6a96e78

Browse files
committed
Make CFbot add a history item when patch needs rebase
This is needed to send notifications to a patch author that it subscribed. We don't send updates to other subscribers to the patch, as this information is generally only useful for the author themselves. By using the "history item" infrastructure it's also displayed on the patch page in the "History" table. That seems quite useful too, because it makes it clear how long the patch has needed a rebase.
1 parent 8e33e1e commit 6a96e78

File tree

4 files changed

+105
-24
lines changed

4 files changed

+105
-24
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# Generated by Django 4.2.17 on 2024-12-25 11:17
2+
3+
from django.conf import settings
4+
from django.db import migrations, models
5+
import django.db.models.deletion
6+
7+
8+
class Migration(migrations.Migration):
9+
10+
dependencies = [
11+
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
12+
("commitfest", "0006_cfbot_integration"),
13+
]
14+
15+
operations = [
16+
migrations.AddField(
17+
model_name="cfbotbranch",
18+
name="needs_rebase_since",
19+
field=models.DateTimeField(blank=True, null=True),
20+
),
21+
migrations.AddField(
22+
model_name="patchhistory",
23+
name="by_cfbot",
24+
field=models.BooleanField(default=False),
25+
),
26+
migrations.AlterField(
27+
model_name="patchhistory",
28+
name="by",
29+
field=models.ForeignKey(
30+
blank=True,
31+
null=True,
32+
on_delete=django.db.models.deletion.CASCADE,
33+
to=settings.AUTH_USER_MODEL,
34+
),
35+
),
36+
migrations.AddConstraint(
37+
model_name="patchhistory",
38+
constraint=models.CheckConstraint(
39+
check=models.Q(
40+
models.Q(("by_cfbot", True), ("by__isnull", True)),
41+
models.Q(("by_cfbot", False), ("by__isnull", False)),
42+
_connector="OR",
43+
),
44+
name="check_by",
45+
),
46+
),
47+
]

pgcommitfest/commitfest/models.py

+38-22
Original file line numberDiff line numberDiff line change
@@ -224,46 +224,61 @@ class Meta:
224224
class PatchHistory(models.Model):
225225
patch = models.ForeignKey(Patch, blank=False, null=False, on_delete=models.CASCADE)
226226
date = models.DateTimeField(blank=False, null=False, auto_now_add=True, db_index=True)
227-
by = models.ForeignKey(User, blank=False, null=False, on_delete=models.CASCADE)
227+
by = models.ForeignKey(User, blank=True, null=True, on_delete=models.CASCADE)
228+
by_cfbot = models.BooleanField(null=False, blank=False, default=False)
228229
what = models.CharField(max_length=500, null=False, blank=False)
229230

230231
@property
231232
def by_string(self):
233+
if (self.by_cfbot):
234+
return "CFbot"
235+
232236
return "%s %s (%s)" % (self.by.first_name, self.by.last_name, self.by.username)
233237

234238
def __str__(self):
235239
return "%s - %s" % (self.patch.name, self.date)
236240

237241
class Meta:
238242
ordering = ('-date', )
243+
constraints = [
244+
models.CheckConstraint(
245+
check=(
246+
models.Q(by_cfbot=True) & models.Q(by__isnull=True)
247+
) | (
248+
models.Q(by_cfbot=False) & models.Q(by__isnull=False)
249+
),
250+
name='check_by',
251+
),
252+
]
239253

240254
def save_and_notify(self, prevcommitter=None,
241-
prevreviewers=None, prevauthors=None):
255+
prevreviewers=None, prevauthors=None, authors_only=False):
242256
# Save this model, and then trigger notifications if there are any. There are
243257
# many different things that can trigger notifications, so try them all.
244258
self.save()
245259

246260
recipients = []
247-
recipients.extend(self.patch.subscribers.all())
248-
249-
# Current or previous committer wants all notifications
250-
try:
251-
if self.patch.committer and self.patch.committer.user.userprofile.notify_all_committer:
252-
recipients.append(self.patch.committer.user)
253-
except UserProfile.DoesNotExist:
254-
pass
255-
256-
try:
257-
if prevcommitter and prevcommitter.user.userprofile.notify_all_committer:
258-
recipients.append(prevcommitter.user)
259-
except UserProfile.DoesNotExist:
260-
pass
261-
262-
# Current or previous reviewers wants all notifications
263-
recipients.extend(self.patch.reviewers.filter(userprofile__notify_all_reviewer=True))
264-
if prevreviewers:
265-
# prevreviewers is a list
266-
recipients.extend(User.objects.filter(id__in=[p.id for p in prevreviewers], userprofile__notify_all_reviewer=True))
261+
if not authors_only:
262+
recipients.extend(self.patch.subscribers.all())
263+
264+
# Current or previous committer wants all notifications
265+
try:
266+
if self.patch.committer and self.patch.committer.user.userprofile.notify_all_committer:
267+
recipients.append(self.patch.committer.user)
268+
except UserProfile.DoesNotExist:
269+
pass
270+
271+
try:
272+
if prevcommitter and prevcommitter.user.userprofile.notify_all_committer:
273+
recipients.append(prevcommitter.user)
274+
except UserProfile.DoesNotExist:
275+
pass
276+
277+
# Current or previous reviewers wants all notifications
278+
recipients.extend(self.patch.reviewers.filter(userprofile__notify_all_reviewer=True))
279+
if prevreviewers:
280+
# prevreviewers is a list
281+
recipients.extend(User.objects.filter(id__in=[p.id for p in prevreviewers], userprofile__notify_all_reviewer=True))
267282

268283
# Current or previous authors wants all notifications
269284
recipients.extend(self.patch.authors.filter(userprofile__notify_all_author=True))
@@ -358,6 +373,7 @@ class CfbotBranch(models.Model):
358373
apply_url = models.TextField(null=False)
359374
# Actually a postgres enum column
360375
status = models.TextField(choices=STATUS_CHOICES, null=False)
376+
needs_rebase_since = models.DateTimeField(null=True, blank=True)
361377
created = models.DateTimeField(auto_now_add=True)
362378
modified = models.DateTimeField(auto_now=True)
363379

pgcommitfest/commitfest/templates/mail/patch_notify.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ have received updates in the PostgreSQL commitfest app:
55
{{p.patch.name}}
66
https://commitfest.postgresql.org/{{p.patch.patchoncommitfest_set.all.0.commitfest.id}}/{{p.patch.id}}/
77
{%for h in p.entries%}
8-
* {{h.what}} ({{h.by}}){%endfor%}
8+
* {{h.what}} by {{h.by_string()}}{%endfor%}
99

1010

1111
{%endfor%}

pgcommitfest/commitfest/views.py

+19-1
Original file line numberDiff line numberDiff line change
@@ -823,7 +823,7 @@ def cfbot_ingest(message):
823823
branch_id = branch_status["branch_id"]
824824

825825
try:
826-
Patch.objects.get(pk=patch_id)
826+
patch = Patch.objects.get(pk=patch_id)
827827
except Patch.DoesNotExist:
828828
# If the patch doesn't exist, there's nothing to do. This should never
829829
# happen in production, but on the test system it's possible because
@@ -910,6 +910,24 @@ def cfbot_ingest(message):
910910
# per patch.
911911
cursor.execute("DELETE FROM commitfest_cfbottask WHERE patch_id=%s AND branch_id != %s", (patch_id, branch_id))
912912

913+
# We change the needs_rebase field using a separate UPDATE because this way
914+
# we can find out what the previous state of the field was (sadly INSERT ON
915+
# CONFLICT does not allow us to return that). We need to know the previous
916+
# state so we can skip sending notifications if the needs_rebase status did
917+
# not change.
918+
needs_rebase = branch_status['commit_id'] is None
919+
if bool(branch_in_db.needs_rebase_since) is not needs_rebase:
920+
if needs_rebase:
921+
branch_in_db.needs_rebase_since = datetime.now()
922+
else:
923+
branch_in_db.needs_rebase_since = None
924+
branch_in_db.save()
925+
926+
if needs_rebase:
927+
PatchHistory(patch=patch, by=None, by_cfbot=True, what='Patch needs rebase').save_and_notify(authors_only=True)
928+
else:
929+
PatchHistory(patch=patch, by=None, by_cfbot=True, what='Patch does not need rebase anymore').save_and_notify(authors_only=True)
930+
913931

914932
@csrf_exempt
915933
def cfbot_notify(request):

0 commit comments

Comments
 (0)