Skip to content

feat(rust): port queue pause and unpause to native Rust#1352

Open
jd wants to merge 1 commit intodevs/jd/worktree-rust-port/surface-clap-s-help-instead-erroring-native--2922aa2dfrom
devs/jd/worktree-rust-port/port-queue-pause-unpause-native-rust-phase-1-5--dba6fa38
Open

feat(rust): port queue pause and unpause to native Rust#1352
jd wants to merge 1 commit intodevs/jd/worktree-rust-port/surface-clap-s-help-instead-erroring-native--2922aa2dfrom
devs/jd/worktree-rust-port/port-queue-pause-unpause-native-rust-phase-1-5--dba6fa38

Conversation

@jd
Copy link
Copy Markdown
Member

@jd jd commented May 5, 2026

Two queue commands in one PR — both are idempotent one-shot API
calls that share the same auth + repository resolution. Pause
exercises the new PUT method; unpause exercises the new
DELETE-with-status-check method.

PUTs {"reason": "..."} to
/v1/repos/<repo>/merge-queue/pause, prints a confirmation line
with the reason and timestamp.

Safety rails match Python:

  • --yes-i-am-sure skips confirmation outright.
  • Interactive (TTY): prompts "Proceed? [y/N]". Anything other than
    y/yes aborts as a generic error.
  • Non-interactive without the flag: refuses with INVALID_STATE
    (exit 7), matching Python's
    raise SystemExit(ExitCode.INVALID_STATE).

--reason has a 255-char cap enforced by clap's value_parser
— bad input exits 2.

DELETEs the same path. On 404 the API is telling us the queue
wasn't paused, so the command prints "Queue is not currently
paused" and exits MERGIFY_API_ERROR (matches Python). On 2xx it
prints "Queue resumed." and exits 0.

Two new methods on mergify_core::HttpClient:

  • put<B, T>(path, body) -> Result<T, CliError> — mirror of
    post, different verb.
  • delete_if_exists(path) -> Result<DeleteOutcome, CliError>
    returns Deleted on 2xx, NotFound on 404, errors on any
    other non-success status. Lets commands like unpause give
    a friendlier 404 message without parsing error strings.

Auth resolution (token / api-url / repository) goes through the
shared mergify_core::auth module added earlier in the stack —
no per-crate copy of the resolvers in mergify-queue.

5 new unit tests in the queue crate:

  • parse_reason accepts short strings and rejects > 255 chars
  • run pauses and prints the API-returned reason + timestamp
  • run prints "Queue resumed" on 2xx
  • run errors with MERGIFY_API_ERROR on 404 carrying the
    "not currently paused" message

End-to-end smoke tested three paths:
queue pause --reason X -r owner/repo → exit 8 (missing token),
queue unpause -r owner/repo → exit 8 (missing token),
echo n | queue pause --reason X → exit 7 (non-TTY, no --sure).

The Python queue pause / queue unpause implementations and
their tests are removed in the same PR — the Rust binary now owns
both commands end-to-end. Binary: 8.4 MB → 8.5 MB. 56 Rust tests.

Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com

Depends-On: #1380

@jd
Copy link
Copy Markdown
Member Author

jd commented May 5, 2026

This pull request is part of a Mergify stack:

# Pull Request Link
1 fix(cli): surface clap's --help instead of erroring on native subcommands #1380
2 feat(rust): port queue pause and unpause to native Rust #1352 👈
3 feat(rust): port ci git-refs and ci queue-info to native Rust #1353
4 feat(rust): port queue status to native Rust #1359
5 test: derive native queue commands from the binary, not a hardcoded list #1366

@mergify mergify Bot had a problem deploying to Mergify Merge Protections May 5, 2026 09:39 Failure
@mergify
Copy link
Copy Markdown
Contributor

mergify Bot commented May 5, 2026

Merge Protections

Your pull request matches the following merge protections and will not be merged until they are valid.

🔴 ⛓️ Depends-On Requirements

Waiting for

This rule is failing.

Requirement based on the presence of Depends-On in the body of the pull request

🔴 👀 Review Requirements

Waiting for

  • #approved-reviews-by>=2
This rule is failing.
  • any of:
    • #approved-reviews-by>=2
    • author = dependabot[bot]
    • author = mergify-ci-bot
    • author = renovate[bot]

🔴 🔎 Reviews

Waiting for

  • #review-requested = 0
This rule is failing.
  • #review-requested = 0
  • #changes-requested-reviews-by = 0
  • #review-threads-unresolved = 0

🟢 🤖 Continuous Integration

Wonderful, this rule succeeded.
  • all of:
    • check-success=ci-gate

🟢 Enforce conventional commit

Wonderful, this rule succeeded.

Make sure that we follow https://www.conventionalcommits.org/en/v1.0.0/

  • title ~= ^(fix|feat|docs|style|refactor|perf|test|build|ci|chore|revert|ui)(?:\(.+\))?:

🟢 📕 PR description

Wonderful, this rule succeeded.
  • body ~= (?ms:.{48,})

@mergify mergify Bot requested a review from a team May 5, 2026 09:51
@jd jd force-pushed the devs/jd/worktree-rust-port/port-queue-pause-unpause-native-rust-phase-1-5--dba6fa38 branch from 253835c to 1ae9f28 Compare May 5, 2026 10:12
@jd jd force-pushed the devs/jd/worktree-rust-port/drop-port-status-toml-inventory-favor-port-delete--77522cb9 branch from 103418f to 9889d7e Compare May 5, 2026 10:12
@jd
Copy link
Copy Markdown
Member Author

jd commented May 5, 2026

Revision history

# Type Changes Reason Date
1 initial 253835c 2026-05-05 10:12 UTC
2 rebase 253835c → 1ae9f28 2026-05-05 10:12 UTC
3 rebase 1ae9f28 → 1670fa1 2026-05-05 10:19 UTC
4 rebase 1670fa1 → 8afcb9c 2026-05-05 11:16 UTC
5 content 8afcb9c → 2eeb6ff 2026-05-05 13:13 UTC
6 content 2eeb6ff → 7e85a94 2026-05-05 13:22 UTC
7 content 7e85a94 → 9478efd 2026-05-05 15:18 UTC
8 rebase 9478efd → 12bc871 2026-05-05 19:59 UTC
9 rebase 12bc871 → 8485cd1 2026-05-06 10:57 UTC
10 content 8485cd1 → 31c58ff 2026-05-06 13:00 UTC
11 content 31c58ff → 01e7be5 2026-05-07 13:40 UTC
12 content 01e7be5 → dd391d3 2026-05-07 15:17 UTC
13 content dd391d3 → 9d19f3e 2026-05-07 18:21 UTC

@mergify mergify Bot had a problem deploying to Mergify Merge Protections May 5, 2026 10:12 Failure
@jd jd force-pushed the devs/jd/worktree-rust-port/drop-port-status-toml-inventory-favor-port-delete--77522cb9 branch from 9889d7e to 549a14d Compare May 5, 2026 10:19
@jd jd force-pushed the devs/jd/worktree-rust-port/port-queue-pause-unpause-native-rust-phase-1-5--dba6fa38 branch from 1ae9f28 to 1670fa1 Compare May 5, 2026 10:19
@mergify mergify Bot had a problem deploying to Mergify Merge Protections May 5, 2026 10:19 Failure
@jd jd force-pushed the devs/jd/worktree-rust-port/port-queue-pause-unpause-native-rust-phase-1-5--dba6fa38 branch from 1670fa1 to 8afcb9c Compare May 5, 2026 11:16
@jd jd changed the base branch from devs/jd/worktree-rust-port/drop-port-status-toml-inventory-favor-port-delete--77522cb9 to devs/jd/worktree-rust-port/add-port-review-checklist-http-test-ux-parity--1fd9757e May 5, 2026 11:16
@mergify mergify Bot had a problem deploying to Mergify Merge Protections May 5, 2026 11:16 Failure
@jd jd force-pushed the devs/jd/worktree-rust-port/port-queue-pause-unpause-native-rust-phase-1-5--dba6fa38 branch from 8afcb9c to 2eeb6ff Compare May 5, 2026 13:13
@jd jd force-pushed the devs/jd/worktree-rust-port/add-port-review-checklist-http-test-ux-parity--1fd9757e branch from ed2b8b5 to 6b1d62e Compare May 5, 2026 13:13
@mergify mergify Bot had a problem deploying to Mergify Merge Protections May 5, 2026 13:13 Failure
@jd jd force-pushed the devs/jd/worktree-rust-port/port-queue-pause-unpause-native-rust-phase-1-5--dba6fa38 branch from 2eeb6ff to 7e85a94 Compare May 5, 2026 13:21
@mergify mergify Bot had a problem deploying to Mergify Merge Protections May 5, 2026 13:22 Failure
Comment thread crates/mergify-queue/src/auth.rs Outdated
Comment thread mergify_cli/tests/queue/test_skill.py Outdated
jd added a commit that referenced this pull request May 5, 2026
The Rust binary now serves ``mergify queue status`` natively. The
Python implementation (``mergify_cli/queue/cli.py:status`` plus the
batch/scope/topology helpers it depended on) is removed in the
same PR — the port-and-delete rule we adopted in #1322 keeps a
single live copy of every command.

## What ports

``mergify queue status [-r REPO] [-t TOKEN] [-u URL] [-b BRANCH]
[--json]``:

1. Resolves repository / token / API URL via the shared
   ``mergify_queue::auth`` resolver introduced in #1352.
2. Fetches ``GET /v1/repos/<repo>/merge-queue/status``, optionally
   with ``?branch=<branch>`` (URL-encoded via
   ``url::form_urlencoded::byte_serialize``).
3. With ``--json``: pretty-prints the raw response. The schema is
   Mergify's API contract, not this CLI's, so we deserialize into
   ``serde_json::Value`` and emit verbatim — unknown fields and
   future schema additions survive the round trip.
4. Without ``--json``: deserializes into a typed ``StatusView``
   that uses ``#[serde(default)] Option<…>`` for every field the
   Mergify API has historically treated as optional/nullable
   (matches the port checklist from #1357), then renders a
   header, an optional pause indicator, the batch tree (grouped
   by scope when there is more than one), and the waiting-PR
   list. Status icons (``● ◑ ◌ ✓ ✗ ◎ ⏳ ↻ ⏰ ❄``) and relative
   times (``5m ago`` / ``~1h``) match the Python implementation.

## Rendering departures

Python used Rich's ``Tree`` for batches. The Rust port emits
flat indented text instead — same data, simpler rendering. Both
are line-oriented and round-trip cleanly through pipes; the
fancy box-drawing was visual sugar that didn't survive piping
anyway.

## Tests (24 new, in mergify-queue::status)

- ``build_path`` covers no-branch, branch, and URL-encoding of a
  branch name with slashes + spaces (e.g. ``feature/foo bar``
  becomes ``feature%2Ffoo+bar`` in the query).
- ``relative_time`` covers seconds / minutes / hours / days,
  future prefix, and graceful empty-string return on a malformed
  timestamp (matches Python's "degrade rather than fail").
- ``topological_sort`` covers parents-before-children ordering
  and tolerance of ``parent_ids`` that reference missing batches.
- ``group_by_scope`` covers the ``[]`` → ``"default"`` fallback
  and multi-scope batches appearing under each scope they claim.
- ``status_icon`` covers known + unknown codes.
- End-to-end wiremock tests: empty queue, paused queue, batches +
  waiting PRs, multi-scope grouping, ``?branch=…`` query
  threading, JSON-passthrough preserving an ``extra_field``,
  and tolerance of a response that omits all optional fields.

## Wiring

- ``crates/mergify-queue/Cargo.toml``: adds ``chrono`` (relative
  time math), ``indexmap`` (scope groups in insertion order),
  promotes ``serde_json`` from dev to runtime (used for the
  ``serde_json::Value`` passthrough).
- ``crates/mergify-cli/src/main.rs``: registers the ``status``
  subcommand under ``QueueSubcommand``, threads the
  ``--branch``/``--json`` flags, dispatches to
  ``mergify_queue::status::run``. Adds ``status`` to
  ``looks_native``.
- ``mergify_cli/queue/api.py``: removes ``QueueStatusResponse``,
  ``QueueBatch``, ``QueuePause``, ``QueueChecksSummary``,
  ``QueueBatchStatus``, ``QueuePullRequest``,
  ``QueuePullRequestAuthor``, and ``get_queue_status`` — all
  now Rust-native. ``QueuePullResponse`` and friends stay for
  the still-shimmed ``queue show`` (next phase).
- ``mergify_cli/queue/cli.py``: removes the ``@queue.command
  status`` block and the helpers it owned (``STATUS_STYLES``,
  ``_status_text``, ``_batch_label``, ``_pr_label``,
  ``_topological_sort``, ``_group_batches_by_scope``,
  ``_print_batches``, ``_print_waiting_prs``).
  ``_relative_time`` stays — ``show`` still uses it.
- ``mergify_cli/tests/queue/test_cli.py``: deletes
  ``TestStatusCommand``, ``TestTopologicalSort``, and the
  ``_invoke_status`` helper. ``TestRelativeTime`` stays.

Workspace: 138 Rust tests green, 590 Python tests green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Change-Id: I8cebcd325f05173dfa41083da2ec6516a6ec3a3f
@jd jd force-pushed the devs/jd/worktree-rust-port/port-queue-pause-unpause-native-rust-phase-1-5--dba6fa38 branch from 7e85a94 to 9478efd Compare May 5, 2026 15:18
@jd jd changed the base branch from devs/jd/worktree-rust-port/add-port-review-checklist-http-test-ux-parity--1fd9757e to devs/jd/worktree-rust-port/add-mergify-core-auth-gh-git-config-fallbacks--3ae3542b May 5, 2026 15:18
@mergify
Copy link
Copy Markdown
Contributor

mergify Bot commented May 6, 2026

@jd this pull request is now in conflict 😩

@mergify mergify Bot added the conflict label May 6, 2026
Copilot AI review requested due to automatic review settings May 6, 2026 13:00
@jd jd force-pushed the devs/jd/worktree-rust-port/port-queue-pause-unpause-native-rust-phase-1-5--dba6fa38 branch from 8485cd1 to 31c58ff Compare May 6, 2026 13:00
@jd jd temporarily deployed to func-tests-live May 6, 2026 13:00 — with GitHub Actions Inactive
@jd jd temporarily deployed to func-tests-live May 6, 2026 13:00 — with GitHub Actions Inactive
@mergify mergify Bot had a problem deploying to Mergify Merge Protections May 6, 2026 13:01 Failure
@mergify mergify Bot removed the conflict label May 6, 2026
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Ports mergify queue pause / mergify queue unpause from the Python shim to native Rust, wiring them into the Rust mergify binary and removing the legacy Python implementations. This extends mergify-core’s HTTP client to support the required PUT and an idempotent DELETE-with-404-handling flow.

Changes:

  • Add a new mergify-queue Rust crate implementing queue pause and queue unpause (with unit tests) and dispatch these commands from crates/mergify-cli.
  • Extend mergify-core::HttpClient with put() and delete_if_exists() (returning Deleted vs NotFound).
  • Remove Python pause/unpause click commands + API helpers and delete the corresponding Python CLI tests; adjust skill validation to recognize Rust-native queue commands.

Reviewed changes

Copilot reviewed 12 out of 13 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
mergify_cli/tests/queue/test_skill.py Allows skill validation to treat pause/unpause as Rust-native queue commands.
mergify_cli/tests/queue/test_cli.py Removes Python-level tests for pause/unpause now that the implementation moved to Rust.
mergify_cli/queue/cli.py Deletes the Python click implementations of queue pause/queue unpause.
mergify_cli/queue/api.py Removes Python API helper functions/types for pausing/unpausing the queue.
crates/mergify-queue/src/pause.rs New Rust implementation of queue pause, including confirmation flow and tests.
crates/mergify-queue/src/unpause.rs New Rust implementation of queue unpause, including 404 “not paused” behavior and tests.
crates/mergify-queue/src/lib.rs Exposes the new queue subcommand modules.
crates/mergify-queue/Cargo.toml Introduces the new mergify-queue crate and its dependencies.
crates/mergify-core/src/lib.rs Re-exports DeleteOutcome from the HTTP module.
crates/mergify-core/src/http.rs Adds put() and delete_if_exists() plus the supporting status-only execution path.
crates/mergify-cli/src/main.rs Adds clap parsing + native dispatch for queue pause and queue unpause.
crates/mergify-cli/Cargo.toml Adds the new mergify-queue crate as a dependency.
Cargo.lock Updates the lockfile for the new crate/dependency graph changes.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread crates/mergify-queue/src/pause.rs Outdated
Comment thread crates/mergify-core/src/http.rs
Comment thread crates/mergify-queue/Cargo.toml Outdated
@jd jd force-pushed the devs/jd/worktree-rust-port/port-queue-pause-unpause-native-rust-phase-1-5--dba6fa38 branch from 31c58ff to 01e7be5 Compare May 7, 2026 13:40
@jd jd temporarily deployed to func-tests-live May 7, 2026 13:40 — with GitHub Actions Inactive
@jd jd temporarily deployed to func-tests-live May 7, 2026 13:40 — with GitHub Actions Inactive
@mergify mergify Bot had a problem deploying to Mergify Merge Protections May 7, 2026 13:40 Failure
@jd jd changed the base branch from main to devs/jd/worktree-rust-port/surface-clap-s-help-instead-erroring-native--2922aa2d May 7, 2026 15:17
@jd jd force-pushed the devs/jd/worktree-rust-port/port-queue-pause-unpause-native-rust-phase-1-5--dba6fa38 branch from 01e7be5 to dd391d3 Compare May 7, 2026 15:17
@jd jd temporarily deployed to func-tests-live May 7, 2026 15:17 — with GitHub Actions Inactive
@jd jd temporarily deployed to func-tests-live May 7, 2026 15:17 — with GitHub Actions Inactive
@mergify mergify Bot had a problem deploying to Mergify Merge Protections May 7, 2026 15:17 Failure
@jd jd marked this pull request as ready for review May 7, 2026 15:54
Two queue commands in one PR — both are idempotent one-shot API
calls that share the same auth + repository resolution. Pause
exercises the new PUT method; unpause exercises the new
DELETE-with-status-check method.

PUTs ``{"reason": "..."}`` to
``/v1/repos/<repo>/merge-queue/pause``, prints a confirmation line
with the reason and timestamp.

Safety rails match Python:

- ``--yes-i-am-sure`` skips confirmation outright.
- Interactive (TTY): prompts "Proceed? [y/N]". Anything other than
  ``y``/``yes`` aborts as a generic error.
- Non-interactive without the flag: refuses with INVALID_STATE
  (exit 7), matching Python's
  ``raise SystemExit(ExitCode.INVALID_STATE)``.

``--reason`` has a 255-char cap enforced by clap's ``value_parser``
— bad input exits 2.

DELETEs the same path. On 404 the API is telling us the queue
wasn't paused, so the command prints "Queue is not currently
paused" and exits MERGIFY_API_ERROR (matches Python). On 2xx it
prints "Queue resumed." and exits 0.

Two new methods on ``mergify_core::HttpClient``:

- ``put<B, T>(path, body) -> Result<T, CliError>`` — mirror of
  ``post``, different verb.
- ``delete_if_exists(path) -> Result<DeleteOutcome, CliError>`` —
  returns ``Deleted`` on 2xx, ``NotFound`` on 404, errors on any
  other non-success status. Lets commands like ``unpause`` give
  a friendlier 404 message without parsing error strings.

Auth resolution (token / api-url / repository) goes through the
shared ``mergify_core::auth`` module added earlier in the stack —
no per-crate copy of the resolvers in ``mergify-queue``.

5 new unit tests in the queue crate:

- ``parse_reason`` accepts short strings and rejects > 255 chars
- ``run`` pauses and prints the API-returned reason + timestamp
- ``run`` prints "Queue resumed" on 2xx
- ``run`` errors with MERGIFY_API_ERROR on 404 carrying the
  "not currently paused" message

End-to-end smoke tested three paths:
``queue pause --reason X -r owner/repo`` → exit 8 (missing token),
``queue unpause -r owner/repo`` → exit 8 (missing token),
``echo n | queue pause --reason X`` → exit 7 (non-TTY, no --sure).

The Python ``queue pause`` / ``queue unpause`` implementations and
their tests are removed in the same PR — the Rust binary now owns
both commands end-to-end. Binary: 8.4 MB → 8.5 MB. 56 Rust tests.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Change-Id: Idba6fa38caf403fd5f4184cda462b5f7c1eb3ebf
jd added a commit that referenced this pull request May 7, 2026
Reviewer feedback (#1352): hardcoding the set of Rust-native queue
subcommands in ``mergify_cli/tests/queue/test_skill.py`` made the
skill-reference validation drift-prone — a port PR that forgot to
update ``NATIVE_QUEUE_COMMANDS`` would silently pass even though
the skill was referencing a command the binary couldn't handle.

Two changes make the binary the single source of truth:

1. ``crates/mergify-cli/src/main.rs`` factors the
   ``(group, subcommand)`` pairs into a top-level
   ``NATIVE_COMMANDS`` const. ``looks_native`` iterates that
   const instead of a `match` arm. A new hidden flag
   ``--list-native-commands`` (intercepted before clap or the
   shim) prints one ``<group> <subcommand>`` pair per line and
   exits ``0``.

2. ``test_skill.py`` queries the installed ``mergify`` binary via
   ``subprocess.run([…, "--list-native-commands"])`` to discover
   the native set, replacing the ``NATIVE_QUEUE_COMMANDS``
   frozenset. The test skips cleanly when the binary isn't on
   ``PATH`` (rare; ``uv run pytest`` installs it first).

The result: the next port PR adds an entry to ``NATIVE_COMMANDS``
in main.rs as part of its normal wiring, and ``test_skill.py``
picks it up automatically. No parallel list to maintain.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

Change-Id: I74502fe8affcc58f26eaaa9d058668eb36fec83b
@jd jd force-pushed the devs/jd/worktree-rust-port/port-queue-pause-unpause-native-rust-phase-1-5--dba6fa38 branch from dd391d3 to 9d19f3e Compare May 7, 2026 18:21
@jd jd had a problem deploying to func-tests-live May 7, 2026 18:22 — with GitHub Actions Failure
@jd jd had a problem deploying to func-tests-live May 7, 2026 18:22 — with GitHub Actions Failure
@mergify mergify Bot had a problem deploying to Mergify Merge Protections May 7, 2026 18:22 Failure
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants