Skip to content

feat(rust): port ci git-refs and ci queue-info to native Rust#1353

Closed
jd wants to merge 3 commits into
devs/jd/worktree-rust-port/port-queue-pause-unpause-native-rust-phase-1-5--dba6fa38from
devs/jd/worktree-rust-port/port-ci-git-refs-ci-queue-info-native-rust-phase--8d3f96e6
Closed

feat(rust): port ci git-refs and ci queue-info to native Rust#1353
jd wants to merge 3 commits into
devs/jd/worktree-rust-port/port-queue-pause-unpause-native-rust-phase-1-5--dba6fa38from
devs/jd/worktree-rust-port/port-ci-git-refs-ci-queue-info-native-rust-phase--8d3f96e6

Conversation

@jd
Copy link
Copy Markdown
Member

@jd jd commented May 5, 2026

Adds github_event (GitHub Actions event payload deserialization) and
queue_metadata (MQ YAML fenced-block extraction) shared modules to the
mergify-ci crate, and ports two commands on top of them:

  • ci queue-info — prints MQ batch metadata as pretty JSON; errors
    INVALID_STATE (exit 7) outside an MQ context. Appends to
    $GITHUB_OUTPUT with the same ghadelimiter_<uuid> heredoc the
    Python version uses.

  • ci git-refs — detects base/head refs from Buildkite env, GitHub
    event payload, refs/notes/mergify/<branch> git notes, MQ PR body,
    or falls back to HEAD^..HEAD. Supports text/shell/json
    output formats, writes base/head to $GITHUB_OUTPUT, and calls
    buildkite-agent meta-data set when BUILDKITE=true.

The notes reader is injected as a trait-object callback so unit tests
can exercise the note-driven detection path without touching a real
git repository; the production path shells out via
real_notes_reader.

The Python implementations of ci git-refs and ci queue-info are
removed in the same PR — the Rust binary now owns both commands
end-to-end. The looks-native heuristic in main.rs learns the two
new subcommand names so argv errors surface as clap exit 2 instead
of silently falling through to the Python shim.

Adds serde_yaml_ng (YAML parser) and uuid (ghadelimiter) deps to
the mergify-ci crate. Binary grows ~100KB to 8.6MB.

19 new Rust tests (8 event/metadata, 3 queue-info, 12 git-refs + 2
format round-trips merged in). Full workspace: 79 tests green,
compat harness 4/4.

Depends-On: #1352

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

🔴 ⛓️ 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/port-ci-git-refs-ci-queue-info-native-rust-phase--8d3f96e6 branch from 0ca3459 to 9d6db8e 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 0ca3459 2026-05-05 10:12 UTC
2 rebase 0ca3459 → 9d6db8e 2026-05-05 10:12 UTC
3 rebase 9d6db8e → a2714b7 2026-05-05 10:19 UTC
4 rebase a2714b7 → 742d92c 2026-05-05 11:16 UTC
5 rebase 742d92c → 839ea5a 2026-05-05 13:13 UTC
6 rebase 839ea5a → 4ddd589 2026-05-05 13:22 UTC
7 rebase 4ddd589 → 3df5705 2026-05-05 15:18 UTC
8 content 3df5705 → 0349b1d 2026-05-05 19:59 UTC
9 rebase 0349b1d → c3fc948 2026-05-06 10:57 UTC
10 content c3fc948 → 6421363 2026-05-06 13:00 UTC
11 rebase 6421363 → ece765d 2026-05-07 13:40 UTC
12 rebase ece765d → cc96b9a 2026-05-07 15:17 UTC
13 rebase cc96b9a → 45c9d93 2026-05-07 18:21 UTC
14 rebase 45c9d93 → 8f81284 2026-05-11 07:13 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/port-ci-git-refs-ci-queue-info-native-rust-phase--8d3f96e6 branch from 9d6db8e to a2714b7 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-ci-git-refs-ci-queue-info-native-rust-phase--8d3f96e6 branch from a2714b7 to 742d92c Compare May 5, 2026 11:16
@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
@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-ci-git-refs-ci-queue-info-native-rust-phase--8d3f96e6 branch from 742d92c to 839ea5a 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-ci-git-refs-ci-queue-info-native-rust-phase--8d3f96e6 branch from 839ea5a to 4ddd589 Compare May 5, 2026 13:21
@mergify mergify Bot had a problem deploying to Mergify Merge Protections May 5, 2026 13:22 Failure
@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 force-pushed the devs/jd/worktree-rust-port/port-ci-git-refs-ci-queue-info-native-rust-phase--8d3f96e6 branch from 4ddd589 to 3df5705 Compare May 5, 2026 15:18
@mergify mergify Bot had a problem deploying to Mergify Merge Protections May 5, 2026 15:18 Failure
@jd jd changed the title feat(rust): port ci git-refs and ci queue-info to native Rust (Phase 1.6) feat(rust): port ci git-refs and ci queue-info to native Rust May 5, 2026
@mergify mergify Bot had a problem deploying to Mergify Merge Protections May 6, 2026 10:57 Failure
mergify Bot pushed a commit that referenced this pull request May 6, 2026
#1357)

The 2026.5.5.x release shipped a ``ci scopes-send`` regression
(fixed in #1356): the Rust port deserialized the response body via
``let _: serde_json::Value = client.post(...)`` while the Python
original ignored the body, and the Mergify endpoint returns an
empty body on success. ``Value`` still requires valid JSON, so the
Rust version surfaced ``parse response JSON: error decoding
response body`` to every user once it shipped. Test mocks used
``set_body_json(json!({}))`` instead of an empty body, so the bug
hid through unit tests too.

The fix is structural — split the HTTP client so callers pick
``post`` (parses) vs ``post_no_response`` (drops) up front — but
the bug class is broader than this one command. Capturing the
lessons in AGENTS.md so the next porter (human or assistant) walks
through them before opening a PR.

Three categories of pitfall, all observed during the port so far:

1. **HTTP response handling** — match the Python original. If
   Python ignores the response, Rust must use ``post_no_response``
   etc.; if Python parses, optional/nullable fields must be
   ``Option<T>`` + ``#[serde(default)]``.

2. **Test fixtures** — mirror the real server. ``ResponseTemplate``
   bodies must match what the endpoint actually returns; an empty
   body is not the same as ``json!({})``.

3. **UX parity with click** — char-counted validators use
   ``chars().count()`` (not ``len()``); confirmation prompts go to
   stderr; resolve all required state before prompting.

Audit of existing/in-flight ports against the checklist:

- ``config validate``, ``config simulate`` (main): match
- ``ci scopes-send`` (main): regressed, fix in #1356
- ``queue pause`` / ``unpause`` (#1352): match
- ``ci git-refs`` / ``queue-info`` (#1353): no HTTP, no risk

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@jd jd force-pushed the devs/jd/worktree-rust-port/port-ci-git-refs-ci-queue-info-native-rust-phase--8d3f96e6 branch from c3fc948 to 6421363 Compare May 6, 2026 13:00
@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
@jd jd force-pushed the devs/jd/worktree-rust-port/port-ci-git-refs-ci-queue-info-native-rust-phase--8d3f96e6 branch from 6421363 to ece765d 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 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 force-pushed the devs/jd/worktree-rust-port/port-ci-git-refs-ci-queue-info-native-rust-phase--8d3f96e6 branch from ece765d to cc96b9a 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 force-pushed the devs/jd/worktree-rust-port/port-ci-git-refs-ci-queue-info-native-rust-phase--8d3f96e6 branch from cc96b9a to 45c9d93 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
@mergify mergify Bot had a problem deploying to Mergify Merge Protections May 7, 2026 18:22 Failure
jd and others added 3 commits May 11, 2026 09:13
…ands

Reported by user: ``mergify queue status --help`` rejected the
flag with ``error: unexpected argument '--help' found``. Root
cause: ``CliRoot`` had ``disable_help_flag = true``, so clap's
auto-generated ``--help`` machinery wasn't wired up — and our
``detect_native`` then surfaced the resulting parser error
(because ``looks_native`` matched) instead of falling through.

Three changes:

1. Drop ``disable_help_flag = true`` from ``CliRoot``. clap now
   auto-recognizes ``-h`` / ``--help`` at every level and
   generates its standard help blurb listing flags + subcommands.

2. ``detect_native`` catches the special help-error kinds
   (``DisplayHelp`` and
   ``DisplayHelpOnMissingArgumentOrSubcommand``) explicitly and
   calls ``err.exit()`` regardless of the ``looks_native``
   heuristic. ``err.exit()`` prints help to stdout and calls
   ``process::exit(0)``. The previous flow also called
   ``err.exit()`` on parser errors when ``looks_native``, but it
   only fired when the argv contained a (group, subcommand) pair
   — root-level ``mergify --help`` had no such pair so it would
   have leaked to the Python shim.

3. Honor ``MERGIFY_CLI_TESTING_UTF8_MODE`` from the Rust binary.
   ``test_binary_build.py`` runs ``mergify --help`` against the
   wheel-installed binary to verify UTF-8 emoji output works
   (particularly on Windows). The marker used to be printed by
   ``mergify_cli/cli.py::main``, but now that ``--help`` is
   handled natively the Python path no longer fires. Mirror the
   marker from the Rust binary: the binary is UTF-8 native on
   every platform (no ``os.execv`` re-exec needed), so report
   ``utf8_mode=1`` on Windows (matching the post-re-exec value
   the test expects) and ``utf8_mode=0`` elsewhere.

Verified locally:

- ``mergify --help`` lists the three native top-level groups
  (``config`` / ``ci`` / ``queue``).
- ``mergify queue --help`` lists the native queue subcommands
  (``pause`` / ``unpause`` / ``status``).
- ``mergify queue status --help`` shows ``--branch`` / ``--json``
  / ``--token`` / ``--api-url`` / ``--repository`` with their
  doc-strings.
- ``MERGIFY_CLI_TESTING_UTF8_MODE=1 mergify --help`` prints
  ``utf8_mode=0`` and ``✅`` before the help text.

Known follow-up: ``mergify queue --help`` doesn't list ``show``
because it's still shimmed (no clap variant). Fixable by
registering opaque clap stubs for shimmed commands; deferred to
the PR that ports ``queue show``.

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

Change-Id: I2922aa2d83af6c99cb551f2a95bf5a4959832375
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
Adds `github_event` (GitHub Actions event payload deserialization) and
`queue_metadata` (MQ YAML fenced-block extraction) shared modules to the
mergify-ci crate, and ports two commands on top of them:

- `ci queue-info` — prints MQ batch metadata as pretty JSON; errors
  INVALID_STATE (exit 7) outside an MQ context. Appends to
  `$GITHUB_OUTPUT` with the same `ghadelimiter_<uuid>` heredoc the
  Python version uses.

- `ci git-refs` — detects base/head refs from Buildkite env, GitHub
  event payload, `refs/notes/mergify/<branch>` git notes, MQ PR body,
  or falls back to `HEAD^..HEAD`. Supports `text`/`shell`/`json`
  output formats, writes `base`/`head` to `$GITHUB_OUTPUT`, and calls
  `buildkite-agent meta-data set` when `BUILDKITE=true`.

The notes reader is injected as a trait-object callback so unit tests
can exercise the note-driven detection path without touching a real
git repository; the production path shells out via
`real_notes_reader`.

The Python implementations of `ci git-refs` and `ci queue-info` are
removed in the same PR — the Rust binary now owns both commands
end-to-end. The looks-native heuristic in `main.rs` learns the two
new subcommand names so argv errors surface as clap exit 2 instead
of silently falling through to the Python shim.

Adds `serde_yaml_ng` (YAML parser) and `uuid` (ghadelimiter) deps to
the mergify-ci crate. Binary grows ~100KB to 8.6MB.

19 new Rust tests (8 event/metadata, 3 queue-info, 12 git-refs + 2
format round-trips merged in). Full workspace: 79 tests green,
compat harness 4/4.

Change-Id: I8d3f96e6cb4eb51e6cd195951b3e622cee7efdd4
@jd jd force-pushed the devs/jd/worktree-rust-port/port-ci-git-refs-ci-queue-info-native-rust-phase--8d3f96e6 branch from 45c9d93 to 8f81284 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
@mergify mergify Bot had a problem deploying to Mergify Merge Protections May 11, 2026 07:14 Failure
@jd jd marked this pull request as ready for review May 11, 2026 07:58
@jd jd force-pushed the devs/jd/worktree-rust-port/port-queue-pause-unpause-native-rust-phase-1-5--dba6fa38 branch from dc54105 to b010249 Compare May 11, 2026 11:46
@jd jd closed this May 11, 2026
@jd jd deleted the devs/jd/worktree-rust-port/port-ci-git-refs-ci-queue-info-native-rust-phase--8d3f96e6 branch May 11, 2026 11:46
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.

1 participant