mbox series
Message ID20260604141826.2998337-1-robin@jarry.cc
DateThu, 04 Jun 2026 16:18:09 +0200
Headers
show
Return-Path: <robin@jarry.cc>
Received: from ringo (2a01cb00021ec0002e23edbec21b0e73.ipv6.abo.wanadoo.fr
 [IPv6:2a01:cb00:21e:c000:2e23:edbe:c21b:e73])
	by patches.jarry.cc (Postfix) with ESMTP id 3D35A1BC40B0
	for <pw@patches.jarry.cc>; Thu, 04 Jun 2026 16:18:29 +0200 (CEST)
From: Robin Jarry <robin@jarry.cc>
To: pw@patches.jarry.cc
Subject: [PATCH v3 00/16] Implement forge bidirectional sync
Date: Thu,  4 Jun 2026 16:18:09 +0200
Message-ID: <20260604141826.2998337-1-robin@jarry.cc>
X-Mailer: git-send-email 2.54.0
MIME-Version: 1.0
List-ID: <pw.jarry.cc>
Content-Transfer-Encoding: 8bit
Series
Implement forge bidirectional sync
github_prhttps://github.com/rjarry/patchwork/pull/4
github_branchpatchwork/implement-forge-bidirectional-sync-c

Message

Robin JarryJun. 4, 2026, 16:18. UTC
[v3,00/16] Implement forge bidirectional sync

Does it work or not?

Robin Jarry (16):
  parsemail: wrap parse_mail() in a single transaction
  parsemail: fix SeriesReference race with concurrent delivery
  series: add respin tracking for patch series versions
  series: add metadata key-value store
  forge: add per-project configuration model
  forge: add backend abstraction layer
  forge: add utilities for mailing-list sync
  forge: add git mirror utilities
  forge: sync github pull requests to mailing list
  forge: sync github comments and reviews to mailing list
  fixup! forge: sync github comments and reviews to mailing list
  forge: sync github check results to patchwork
  forge: sync patch series to github pull requests
  forge: forward mailing list comments to forge pull requests
  docs: add forge integration documentation
  deploy

 Makefile                                      | 147 +++++++
 docs/deployment/configuration.rst             |  37 ++
 docs/usage/forge.rst                          | 294 +++++++++++++
 docs/usage/index.rst                          |   1 +
 nginx.conf                                    |  62 +++
 parsemail.sh                                  |  14 +
 patchwork.service                             |  22 +
 patchwork/admin.py                            |  24 ++
 patchwork/api/series.py                       |  20 +-
 patchwork/forge/__init__.py                   | 273 ++++++++++++
 patchwork/forge/git.py                        | 289 +++++++++++++
 patchwork/forge/github/__init__.py            | 107 +++++
 patchwork/forge/github/api.py                 | 137 ++++++
 patchwork/forge/github/from_ml.py             | 133 ++++++
 patchwork/forge/github/to_ml.py               | 226 ++++++++++
 patchwork/forge/github/webhook.py             | 124 ++++++
 patchwork/forge/urls.py                       |  16 +
 patchwork/forge/util.py                       | 289 +++++++++++++
 patchwork/forge/views.py                      |  82 ++++
 patchwork/management/commands/parsemail.py    |   4 +-
 .../migrations/0049_series_respin_tracking.py |  33 ++
 patchwork/migrations/0050_series_metadata.py  |  41 ++
 patchwork/migrations/0051_forgeconfig.py      |  46 ++
 patchwork/models.py                           | 107 +++++
 patchwork/parser.py                           | 174 +++++++-
 patchwork/settings/base.py                    |  32 ++
 patchwork/settings/production.py              |  67 +++
 patchwork/templates/patchwork/submission.html |  25 ++
 patchwork/templatetags/utils.py               |   9 +
 patchwork/tests/api/test_series.py            |   2 +-
 patchwork/tests/forge/__init__.py             |   0
 patchwork/tests/forge/test_git.py             | 278 ++++++++++++
 patchwork/tests/forge/test_util.py            | 406 ++++++++++++++++++
 patchwork/tests/test_series.py                |  39 ++
 patchwork/urls.py                             |   9 +
 patchwork/views/cover.py                      |   3 +
 patchwork/views/patch.py                      |   2 +
 postfix/client_access                         |   2 +
 postfix/header_checks                         |   2 +
 postfix/main.cf                               |  53 +++
 postfix/master.cf                             |  28 ++
 postfix/recipient_bcc                         |   1 +
 postfix/transport                             |   2 +
 .../parsemail-race-fix-e5f6g7h8i9j0k1l2.yaml  |   5 +
 ...arsemail-transaction-d4e5f6g7h8i9j0k1.yaml |   6 +
 ...ries-respin-tracking-c3d4e5f6g7h8i9j0.yaml |  16 +
 uwsgi.ini                                     |  10 +
 47 files changed, 3677 insertions(+), 22 deletions(-)
 create mode 100644 Makefile
 create mode 100644 docs/usage/forge.rst
 create mode 100644 nginx.conf
 create mode 100755 parsemail.sh
 create mode 100644 patchwork.service
 create mode 100644 patchwork/forge/__init__.py
 create mode 100644 patchwork/forge/git.py
 create mode 100644 patchwork/forge/github/__init__.py
 create mode 100644 patchwork/forge/github/api.py
 create mode 100644 patchwork/forge/github/from_ml.py
 create mode 100644 patchwork/forge/github/to_ml.py
 create mode 100644 patchwork/forge/github/webhook.py
 create mode 100644 patchwork/forge/urls.py
 create mode 100644 patchwork/forge/util.py
 create mode 100644 patchwork/forge/views.py
 create mode 100644 patchwork/migrations/0049_series_respin_tracking.py
 create mode 100644 patchwork/migrations/0050_series_metadata.py
 create mode 100644 patchwork/migrations/0051_forgeconfig.py
 create mode 100644 patchwork/settings/production.py
 create mode 100644 patchwork/tests/forge/__init__.py
 create mode 100644 patchwork/tests/forge/test_git.py
 create mode 100644 patchwork/tests/forge/test_util.py
 create mode 100644 postfix/client_access
 create mode 100644 postfix/header_checks
 create mode 100644 postfix/main.cf
 create mode 100644 postfix/master.cf
 create mode 100644 postfix/recipient_bcc
 create mode 100644 postfix/transport
 create mode 100644 releasenotes/notes/parsemail-race-fix-e5f6g7h8i9j0k1l2.yaml
 create mode 100644 releasenotes/notes/parsemail-transaction-d4e5f6g7h8i9j0k1.yaml
 create mode 100644 releasenotes/notes/series-respin-tracking-c3d4e5f6g7h8i9j0.yaml
 create mode 100644 uwsgi.ini

Comments

pw@patches.jarry.ccJun. 4, 2026, 16:37. UTC
Review: commented

--- docs/usage/forge.rst
> @@ -0,0 +1,294 @@
> +Forge Integration
> +=================
> +
> +Patchwork can synchronize patch workflows with code forges such as GitHub.
> +When enabled, patch series submitted to a mailing list are automatically
> +converted into pull requests, and pull requests opened on a forge are converted
> +into patch series on the mailing list. Comments, reviews, and CI results flow
> +in both directions.
> +
> +.. contents::
> +   :local:
> +
> +Overview
> +--------
> +
> +The forge integration provides bidirectional synchronization:
> +
> +**Mailing list to forge:**
> +
> +- A completed patch series creates a pull request on the forge.
> +- Respin submissions (v2, v3, ...) force-push to the same branch and post an
> +  update comment on the existing pull request.
> +- Comments on patches or cover letters are forwarded to the pull request.
> +
> +**Forge to mailing list:**
> +
> +- A pull request opened on the forge generates a patch series email sent to the
> +  mailing list and ingested directly into the Patchwork database.
> +- Respin (force-push) events generate a new version of the patch series with
> +  proper version numbering and optional threading to the original.
> +- Comments and reviews on the pull request are forwarded as email replies to the
> +  series.

Ceci est très faux.
pw@patches.jarry.ccJun. 4, 2026, 16:38. UTC
Review: commented

Bof bof.

--- patchwork/forge/__init__.py
> @@ -0,0 +1,175 @@
> +# Patchwork - automated patch tracking system
> +# Copyright (C) 2026 Robin Jarry <robin@jarry.cc>
> +#
> +# SPDX-License-Identifier: GPL-2.0-or-later
> +
> +"""
> +Forge integration framework.
> +
> +Provides an abstraction layer for synchronizing patch workflows between
> +patchwork and code forges (GitHub, GitLab, etc.). Each forge platform is
> +implemented as a backend that registers itself at import time and is only
> +loaded when listed in settings.FORGE_BACKENDS.
> +
> +Backends are responsible for webhook signature verification and event parsing.
> +Sync logic (creating patches from PRs, forwarding comments) is implemented
> +per-backend but can reuse generic utilities from patchwork.forge.sync.
> +
> +Forge-specific dependencies remain entirely optional: a backend module is never
> +imported unless explicitly enabled in settings.

Foobar baz 1.

--- patchwork/forge/__init__.py
> @@ -0,0 +1,175 @@
> +# Patchwork - automated patch tracking system
> +# Copyright (C) 2026 Robin Jarry <robin@jarry.cc>
> +#
> +# SPDX-License-Identifier: GPL-2.0-or-later
> +
> +"""
> +Forge integration framework.
> +
> +Provides an abstraction layer for synchronizing patch workflows between
> +patchwork and code forges (GitHub, GitLab, etc.). Each forge platform is
> +implemented as a backend that registers itself at import time and is only
> +loaded when listed in settings.FORGE_BACKENDS.
> +
> +Backends are responsible for webhook signature verification and event parsing.
> +Sync logic (creating patches from PRs, forwarding comments) is implemented
> +per-backend but can reuse generic utilities from patchwork.forge.sync.
> +
> +Forge-specific dependencies remain entirely optional: a backend module is never
> +imported unless explicitly enabled in settings.
> +"""
> +
> +import importlib
> +import logging
> +from abc import ABC
> +from abc import abstractmethod
> +from dataclasses import dataclass
> +from dataclasses import field
> +
> +from django.conf import settings
> +
> +logger = logging.getLogger(__name__)
> +
> +
> +@dataclass
> +class ForgeUser:
> +    login: str = ''
> +    name: str = ''
> +    email: str = ''
> +
> +
> +@dataclass
> +class ReviewComment:
> +    path: str = ''
> +    diff_hunk: str = ''
> +    body: str = ''
> +
> +
> +@dataclass
> +class CheckRun:
> +    name: str = ''
> +    status: str = ''
> +    url: str = ''
> +    description: str = ''
> +
> +
> +@dataclass
> +class ForgeEvent:
> +    type: str = ''
> +    repo_key: str = ''
> +    pr_number: int = 0
> +    author: ForgeUser = field(default_factory=ForgeUser)
> +    body: str = ''
> +
> +    # review fields
> +    review_id: int = 0
> +    review_state: str = ''
> +
> +    # check fields
> +    check_suite_id: int = 0
> +    check_name: str = ''
> +    check_status: str = ''
> +    check_url: str = ''
> +    check_description: str = ''
> +
> +    # pull request fields
> +    pr_title: str = ''
> +    pr_body: str = ''
> +    pr_head: str = ''
> +    pr_base: str = ''
> +    pr_head_branch: str = ''
> +    pr_action: str = ''
> +    pr_before: str = ''
> +
> +
> +class ForgeBackend(ABC):
> +    """
> +    Base class for forge backend implementations.
> +
> +    Each backend handles webhook signature verification and event parsing for
> +    a specific forge platform (GitHub, GitLab, etc.). Backends register
> +    themselves at import time via register_backend().

Foo 2.

--- patchwork/forge/github/api.py
> @@ -94,3 +94,43 @@ def fetch_check_runs(gh, forge_config, check_suite_id):
>              )
>          )
>      return runs
> +
> +
> +def create_pr(gh, forge_config, title, body, head, base):
> +    """
> +    Create a pull request on GitHub. Return the PR number.
> +    """
> +    owner, repo = forge_config.repo.split('/', 1)
> +    result = gh_api_request(
> +        gh,
> +        forge_config,
> +        'POST',
> +        f'/repos/{owner}/{repo}/pulls',
> +        {'title': title, 'body': body, 'head': head, 'base': base},
> +    )
> +    return result['number']
> +
> +
> +def base_branch(gh, forge_config):
> +    """
> +    Return the default branch of the GitHub repository.
> +    """
> +    owner, repo = forge_config.repo.split('/', 1)
> +    result = gh_api_request(
> +        gh,
> +        forge_config,
> +        'GET',
> +        f'/repos/{owner}/{repo}',
> +    )

B llkjlsdjf lkjsdf