Skip to content

WIP: patchwork: enhance integration#253

Draft
derekbarbosa wants to merge 2 commits into
sashiko-dev:mainfrom
derekbarbosa:feature/patchwork
Draft

WIP: patchwork: enhance integration#253
derekbarbosa wants to merge 2 commits into
sashiko-dev:mainfrom
derekbarbosa:feature/patchwork

Conversation

@derekbarbosa

@derekbarbosa derekbarbosa commented Jun 9, 2026

Copy link
Copy Markdown
Collaborator

THIS PR IS A WORK IN PROGRESS

This PR adds patchwork integration to sashiko, allowing
subsystem maintainers to report review results as patchwork checks
either through direct API calls or email notifications.

Patchwork's permission model is coarse: an API token that can post
checks can also change patch states and perform maintainer operations. Some
subsystems (like linux-media) are not comfortable giving sashiko a write token
with that scope. While there are PRs open to change this behavior, they are
still pending, and we would ultimately prefer to give developers the choice to
opt-in to either-or-both, anyway.

This PR provides two modes so each subsystem can choose the integration that
fits their security posture:

  • API integration for subsystems that accept the token risk in exchange for
    direct, low-latency check delivery

  • Emails for subsystems that prefer sashiko to send a structured email that a
    local script (like pw_tools) parses and posts using the maintainer's own
    credentials

The following is an LLM-assisted summary of the changes in the proposed PR:

Config (email_policy.rs):

  • New patchwork.email field for email integration
  • SASHIKO_PATCHWORK_TOKEN env var (in lieu of .toml config)
  • URL normalization on load

Schema (schema.sql):

  • New patchwork_outbox table for retry-queued API check delivery

Runtime (patchwork.rs, worker/patchwork.rs, reviewer.rs):

  • PatchworkWorker polls the outbox, posts checks, retries on failure and
    reclaims ghost entries after 10 minutes
  • Reviewer inserts into the appropriate outbox(es) instead of fire-and-forget
    tokio::spawn
  • post_patchwork_check() refactored to return Result, accept a shared
    reqwest::Client, URL-encode message-IDs, and warn on ambiguous multi-patch
    results

Database (db.rs):

  • EmailOutboxRow.patch_id changed from i64 to Option to match the schema
    which allows NULL. Required for patchwork email notifications that use
    patch_id = NULL to avoid the dedup guard.

Patchwork API interaction

Two endpoints, executed sequentially by the PatchworkWorker: 1. GET
{api_url}/patches/?msgid={encoded_msgid}

  • resolve the email message-ID to a patchwork patch ID
  1. POST {api_url}/patches/{patch_id}/checks/
  • submit the check result (state, description, target_url, context)

Email mode skips both endpoints. It sends a parseable plain-text email that a
downstream tool converts into these same API calls using the maintainer's own
token.

Design tradeoffs

Separate patchwork_outbox table over extending email_outbox with extra columns.
Different semantics (retry with backoff vs one-shot), different columns, cleaner
separation.

Email mode reuses email_outbox with NULL patch_id over a separate table for
patchwork emails. Reuses the existing EmailWorker and SMTP infrastructure; no
new worker needed for email delivery.

Env var fills token gaps, does not overwrite over always overriding TOML.
Preserves per-subsystem token differentiation when maintainers configure
different tokens per patchwork instance.

Worker backoff blocks the loop over scheduled retry with future timestamps.
Simpler; expected volume is low; if patchwork is down, all entries to that
server fail anyway.

Unconditional worker startup over starting only when config has patchwork
enabled. Config is reloaded per-notification, so a startup-time check would miss
later enablement.

Known limitations

  • No SSRF protection on api_url -- pre-existing. Threat model assumes trusted
    config file authors. We add scheme validation (http/https only) but no host
    filtering.
  • Patchwork permissions scope unresolved -- upstream limitation. Email mode
    exists as the mitigation for subsystems that can't accept the broad token
    scope.
  • No integration test for PatchworkWorker -- requires mock HTTP server. Deferred
    to manual validation against a real patchwork instance. Unit tests cover all
    components individually.
  • Worker backoff blocks all entries -- acceptable at current volume. Would need
    a scheduling queue under high throughput.

Pre-existing issues fixed opportunistically

  1. EmailOutboxRow.patch_id type mismatch (i64 vs nullable schema)
  2. Message-ID not URL-encoded in patchwork API queries
  3. Multiple patches for same msgid silently used first without warning

fixes: #252

THIS PR IS A WORK IN PROGRESS

Add support for two patchwork check delivery modes configured
per-subsystem in email_policy.toml:

- API mode: direct REST API calls via a new database-backed
  patchwork_outbox table with retry-queuing (3 attempts,
  exponential backoff at 5s/30s/180s) and ghost sweep recovery.
  Replaces the previous fire-and-forget tokio::spawn approach.

- Email mode: structured plain-text notification emails queued
  through the existing email_outbox for downstream tools like
  pw_tools to parse and post checks.

Both modes can be enabled simultaneously per-subsystem.

Config changes:
- Add email field to PatchworkPolicy for email-based mode
- Add SASHIKO_PATCHWORK_TOKEN env var override for secure
  token injection without storing secrets in TOML on disk
- Add URL normalization on config load (trailing slash
  stripping, scheme validation with graceful degradation)

Schema changes:
- Add patchwork_outbox table with status index

Code changes:
- Add PatchworkWorker (modeled on EmailWorker) with retry loop
- Refactor post_patchwork_check() to return Result, accept a
  shared reqwest::Client, URL-encode message-IDs via
  Url::parse_with_params, and warn on multiple patch results
- Add compose_patchwork_email() for structured email format
- Add insert_patchwork_notification() using NULL patch_id to
  avoid the email_outbox dedup guard collision
- Fix EmailOutboxRow.patch_id from i64 to Option<i64> to
  match the schema which allows NULL (pre-existing mismatch)
- Rewire reviewer queue_notifications() to use outbox inserts
  instead of fire-and-forget spawns
- Start PatchworkWorker unconditionally in main.rs

Assisted-by: claude-opus-4-6 <noreply@anthropic.com>
Signed-off-by: derekbarbosa <derekasobrab@gmail.com>
Document both patchwork delivery modes (API and email) in the
configuration reference with example TOML blocks and the
structured email notification format spec.

- Expand patchwork section in docs/configuration.md with API
  mode, email mode, and email format specification
- Add SASHIKO_PATCHWORK_TOKEN to the environment variables table
- Add annotated patchwork examples to the example email_policy
  showing API-only, email-only, and dual-mode configurations

Assisted-by: claude-opus-4-6 <noreply@anthropic.com>
Signed-off-by: derekbarbosa <derekasobrab@gmail.com>
@derekbarbosa derekbarbosa requested a review from rgushchin June 9, 2026 04:14
@derekbarbosa derekbarbosa changed the title patchwork: enhance integration WIP: patchwork: enhance integration Jun 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Please add an e-mail prefix for linux-media profile

1 participant