Robin Jarry [Mon, 8 Jun 2026 11:59:43 +0000 (13:59 +0200)]
parsemail: wrap parse_mail() in a single transaction
Wrap the entire parse_mail() call in transaction.atomic() so that all
database writes from email parsing run inside a single transaction.
The existing transaction.atomic() blocks inside parse_mail() become
savepoints within this outer transaction. The series deduplication
retry logic continues to work since savepoint rollbacks are scoped
to their own savepoint.
This also ensures that any on_commit() callbacks registered by signal
handlers only fire after the full email has been parsed and all
patch/series associations are committed.
Signed-off-by: Robin Jarry <robin@jarry.cc> Signed-off-by: Stephen Finucane <stephen@that.guru> Reviewed-by: Stephen Finucane <stephen@that.guru>
[stephenfin: Slight tweaks to the release notes]
(cherry picked from commit 644cef7cd369abd39eba9523f8ecbc16d2ea4206)
Robin Jarry [Mon, 8 Jun 2026 12:00:55 +0000 (14:00 +0200)]
parsemail: fix SeriesReference race with concurrent delivery
When multiple parsemail processes run in parallel (e.g. postfix
delivering several messages from the same series at once), two
processes can try to create a SeriesReference for the same msgid
simultaneously. The second one fails with an IntegrityError:
This can result in incomplete series that never reach the
"received_all" state because the failed parsemail invocation
prevents one of the patches from being recorded.
The existing get/create pattern has a classic TOCTOU race: the get
succeeds (no reference found), but by the time create runs, another
process has already inserted the row. Replace both the try/get/
except/create block and the bare create call with get_or_create
which handles the race atomically at the database level.
Signed-off-by: Robin Jarry <robin@jarry.cc> Reviwed-by: Stephen Finucane <stephen@that.guru>
(cherry picked from commit 3558907533a1a420ccceec6935635e7c2245eb69)
notifications.py: don't crash if notifications recipient is invalid
On Django 3:3.2.19, if recipient is not filled or is invalid, cron
job fails with:
Traceback (most recent call last):
File "/usr/local/patchwork/./manage.py", line 17, in <module>
execute_from_command_line(sys.argv)
File "/usr/lib/python3/dist-packages/django/core/management/__init__.py", line 419, in execute_from_command_line
utility.execute()
File "/usr/lib/python3/dist-packages/django/core/management/__init__.py", line 413, in execute
self.fetch_command(subcommand).run_from_argv(self.argv)
File "/usr/lib/python3/dist-packages/django/core/management/base.py", line 354, in run_from_argv
self.execute(*args, **cmd_options)
File "/usr/lib/python3/dist-packages/django/core/management/base.py", line 398, in execute
output = self.handle(*args, **options)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/patchwork/patchwork/management/commands/cron.py", line 19, in handle
errors = send_notifications()
^^^^^^^^^^^^^^^^^^^^
File "/usr/local/patchwork/patchwork/notifications.py", line 84, in send_notifications
message.send()
File "/usr/lib/python3/dist-packages/django/core/mail/message.py", line 284, in send
return self.get_connection(fail_silently).send_messages([self])
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3/dist-packages/django/core/mail/backends/smtp.py", line 109, in send_messages
sent = self._send(message)
^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3/dist-packages/django/core/mail/backends/smtp.py", line 122, in _send
recipients = [sanitize_address(addr, encoding) for addr in email_message.recipients()]
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3/dist-packages/django/core/mail/backends/smtp.py", line 122, in <listcomp>
recipients = [sanitize_address(addr, encoding) for addr in email_message.recipients()]
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3/dist-packages/django/core/mail/message.py", line 99, in sanitize_address
address_parts = nm + localpart + domain
~~~^~~~~~~~~~~
TypeError: can only concatenate str (not "NoneType") to str
As there's no point trying to send a notification to an invalid
e-mail, just drops it.
Signed-off-by: Mauro Carvalho Chehab <mchehab@kernel.org> Co-authored-by: Stephen Finucane <stephen@that.guru>
[stephenfin: Updated to fix linter issues]
(cherry picked from commit 1139b9ae0e09e589073c506d4209dea865621eb1)
Robin Jarry [Wed, 5 Oct 2022 14:24:05 +0000 (16:24 +0200)]
css: make diff colors more accessible
The colors used to display patch diffs are confusing. The context color
is very similar to the added line color and the contrast between added
and removed lines is very low.
Originally, the choice of purple/blue (instead of the more common
red/green palette) may have been made with colorblindness accessibility
in mind. However, after inspecting the current colors with
colorblindness "simulators", I found that the low contrast was
consistent no matter what vision deficiency (if any) you might have.
Update the colors to use a more common red/green palette. Add background
colors to increase contrast for colorblind people. Use less confusing
colors for context and diff hunks. Use normal line height to prevent
background colors from overlapping. Use a different color for email
quotes (blue) to avoid confusion with added lines.
I have made a compilation of the current and updated color palette
previews for normal vision and all common color deficiencies. I also
included the same diff as seen from Github interface for reference.
Thomas Monjalon [Fri, 16 Aug 2024 07:20:28 +0000 (09:20 +0200)]
templates/submission: Fix alignment of commit message
Preformatted content must not be indented
because any space is kept in the output,
making the content wrongly indented.
When aligning message headers to the left,
the new HTML code has been indented
including some preformatted content indented with two spaces.
The fix is to remove the indent of the content.
Signed-off-by: Thomas Monjalon <thomas@monjalon.net> Fixes: fe34ab2ffad3 ("patch-detail: left align message headers") Reviewed-by: Robin Jarry <robin@jarry.cc> Reviewed-by: Stephen Finucane <stephen@that.guru>
(cherry picked from commit 13bbc0d94ea035c149f8acd042d2998778b9efd9)
build(deps): update sqlparse requirement from ~=0.5.0 to ~=0.5.1
Updates the requirements on [sqlparse](https://github.com/andialbrecht/sqlparse) to permit the latest version.
- [Changelog](https://github.com/andialbrecht/sqlparse/blob/master/CHANGELOG)
- [Commits](https://github.com/andialbrecht/sqlparse/compare/0.5.0...0.5.1)
Updates the requirements on [django-debug-toolbar](https://github.com/jazzband/django-debug-toolbar) to permit the latest version.
- [Release notes](https://github.com/jazzband/django-debug-toolbar/releases)
- [Changelog](https://github.com/jazzband/django-debug-toolbar/blob/main/docs/changes.rst)
- [Commits](https://github.com/jazzband/django-debug-toolbar/compare/4.4...4.4.6)
build(deps): update mysqlclient requirement from ~=2.2.0 to ~=2.2.4
Updates the requirements on [mysqlclient](https://github.com/PyMySQL/mysqlclient) to permit the latest version.
- [Release notes](https://github.com/PyMySQL/mysqlclient/releases)
- [Changelog](https://github.com/PyMySQL/mysqlclient/blob/main/HISTORY.rst)
- [Commits](https://github.com/PyMySQL/mysqlclient/compare/v2.2.0...v2.2.4)
build(deps): update openapi-core requirement from ~=0.19.0 to ~=0.19.2
Updates the requirements on [openapi-core](https://github.com/python-openapi/openapi-core) to permit the latest version.
- [Release notes](https://github.com/python-openapi/openapi-core/releases)
- [Commits](https://github.com/python-openapi/openapi-core/compare/0.19.0...0.19.2)
build(deps): update psycopg2-binary requirement from ~=2.9.0 to ~=2.9.9
Updates the requirements on [psycopg2-binary](https://github.com/psycopg/psycopg2) to permit the latest version.
- [Changelog](https://github.com/psycopg/psycopg2/blob/master/NEWS)
- [Commits](https://github.com/psycopg/psycopg2/compare/2.9.6...2.9.9)
build(deps): update django requirement from ~=5.0.0 to ~=5.0.7
Updates the requirements on [django](https://github.com/django/django) to permit the latest version.
- [Commits](https://github.com/django/django/compare/5.0...5.0.7)
build(deps): update psycopg2 requirement from ~=2.9.0 to ~=2.9.9
Updates the requirements on [psycopg2](https://github.com/psycopg/psycopg2) to permit the latest version.
- [Changelog](https://github.com/psycopg/psycopg2/blob/master/NEWS)
- [Commits](https://github.com/psycopg/psycopg2/compare/2.9.6...2.9.9)
Ali Alnubani [Tue, 1 Aug 2023 08:15:35 +0000 (11:15 +0300)]
lib: add missing permissions for patchwork_covercomment_id_seq
Grants UPDATE and SELECT privileges on patchwork_covercomment_id_seq table
for user 'nobody' to fix the following exception when cover comments
are being parsed:
django.db.utils.ProgrammingError: permission denied for sequence
patchwork_covercomment_id_seq
Ali Alnubani [Mon, 31 Jul 2023 10:54:21 +0000 (13:54 +0300)]
lib: add missing permissions for patchwork_cover_id_seq
Grants UPDATE and SELECT privileges on patchwork_cover_id_seq table
for user 'nobody' to fix the following exception when cover letters
are being parsed:
django.db.utils.ProgrammingError: permission denied for sequence
patchwork_cover_id_seq
In commit 2653fdbb2, we started encoding slashes in message IDs
presented to users. This allowed us to support message IDs containing
slashes, which are allowed per the RFC and have been seen in the wild.
However, we continue to store the original unencoded message IDs in the
database and unfortunately we neglected to reverse the decoding in the
functions for downloading patch diffs and mboxes. Address this now.
Signed-off-by: Stephen Finucane <stephen@that.guru> Fixes: 2653fdbb2 ("urls: Encode slashes in message IDs") Closes: #518 Cc: Siddhesh Poyarekar <siddhesh@gotplt.org> Cc: DJ Delorie <dj@delorie.com>