Skip to content

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

Merged
mergify[bot] merged 1 commit into
mainfrom
devs/jd/worktree-rust-port/port-queue-pause-unpause-native-rust-phase-1-5--dba6fa38
May 11, 2026
Merged

feat(rust): port queue pause and unpause to native Rust#1352
mergify[bot] merged 1 commit into
mainfrom
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

@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 feat(rust): port queue pause and unpause to native Rust #1352 👈
2 test(ci): add live smoke test for ci git-refs #1404
3 feat(rust): port ci git-refs to native Rust #1405
4 test(ci): add live smoke test for ci queue-info #1406
5 feat(rust): port ci queue-info to native Rust #1407
6 test(queue): add live smoke test for queue status #1409
7 feat(rust): port queue status to native Rust #1359
8 test: derive native queue commands from the binary, not a hardcoded list #1366
9 test(queue): add live smoke test for queue show #1408
10 feat(rust): port queue show to native Rust #1399

@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.

🟢 🤖 Continuous Integration

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

🟢 👀 Review Requirements

Wonderful, this rule succeeded.
  • any of:
    • #approved-reviews-by>=2
    • author = dependabot[bot]
    • author = mergify-ci-bot
    • author = renovate[bot]

🟢 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)(?:\(.+\))?:

🟢 🔎 Reviews

Wonderful, this rule succeeded.
  • #changes-requested-reviews-by = 0
  • #review-requested = 0
  • #review-threads-unresolved = 0

🟢 📕 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
14 rebase 9d19f3e → dc54105 2026-05-11 07:13 UTC
15 rebase dc54105 → b010249 2026-05-11 11:46 UTC
16 content b010249 → b425f9d 2026-05-11 14:08 UTC
17 rebase b425f9d → 43c5e17 2026-05-11 14:59 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
@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
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
@jd jd force-pushed the devs/jd/worktree-rust-port/surface-clap-s-help-instead-erroring-native--2922aa2d branch from 544e9b2 to cbcac36 Compare May 11, 2026 07:13
@jd jd force-pushed the devs/jd/worktree-rust-port/port-queue-pause-unpause-native-rust-phase-1-5--dba6fa38 branch from 9d19f3e to dc54105 Compare May 11, 2026 07:13
@jd jd had a problem deploying to func-tests-live May 11, 2026 07:13 — with GitHub Actions Failure
@jd jd had a problem deploying to func-tests-live May 11, 2026 07:13 — with GitHub Actions Failure
@jd jd had a problem deploying to func-tests-live May 11, 2026 07:13 — with GitHub Actions Failure
@mergify mergify Bot had a problem deploying to Mergify Merge Protections May 11, 2026 07:14 Failure
Base automatically changed from devs/jd/worktree-rust-port/surface-clap-s-help-instead-erroring-native--2922aa2d to main May 11, 2026 10:04
@mergify
Copy link
Copy Markdown
Contributor

mergify Bot commented May 11, 2026

@jd this pull request is now in conflict 😩

JulianMaurin
JulianMaurin previously approved these changes May 11, 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>
Change-Id: Idba6fa38caf403fd5f4184cda462b5f7c1eb3ebf
@mergify
Copy link
Copy Markdown
Contributor

mergify Bot commented May 11, 2026

Merge Queue Status

This pull request spent 16 minutes 42 seconds in the queue, including 16 minutes 19 seconds running CI.

Required conditions to merge

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.

4 participants