Skip to content

Commit 5fce67b

Browse files
jdclaude
andauthored
chore: delete Python tree + simplify wheel/CI for pure-Rust binary (#1554)
End of the port. With every subcommand native, nothing in the wheel needs Python anymore — the install-time `mergify` binary covers the full CLI surface and clap handles unknown-subcommand suggestions natively. Drop the whole Python superstructure in one self-consistent change so CI doesn't briefly target a half-deleted tree. Deletions: - `mergify_cli/` — entire Python package (utils, cli, dym, exit_codes, github_types, stack/cli, __init__, __main__, __globals + the test suite). All functionality was already mirrored in the Rust crates; tests live alongside the Rust code they exercise (~600 tests across the workspace). - `crates/mergify-py-shim/` — the subprocess bridge that forwarded un-ported subcommands to Python. No un-ported subcommands left. - `compat-tests/` — `pytest` runner that diff'd Python vs. Rust output via `python -m mergify_cli`. No Python side to diff against. - `test_binary_build.py` — verified the Python-specific Windows UTF-8 re-exec in `cli.py::main`. The Rust binary handles UTF-8 natively, so the test target no longer exists. - `poe.toml` + `requirements-uv.txt` — Python toolchain pinning, irrelevant now. Modifications: - `pyproject.toml`: - Drop `dependencies` (httpx, click, rich, pydantic, …) and `[dependency-groups] dev` (mypy, ruff, pytest, …) — wheel has no Python runtime / no Python lint+test. - Drop `[tool.maturin]` `python-source` + `module-name` so the wheel doesn't try to bundle a package that's gone. - Drop `[tool.pytest.ini_options]`, `[tool.mypy]`, `[tool.ruff]` (long config block) — no Python to configure. - Keep `bindings = "bin"` + `manifest-path` so maturin still builds a binary-only wheel for PyPI. - Lower `requires-python` to `>=3.10` — only matters for the metadata-side `pip install` resolution, not the runtime. - `crates/mergify-cli/Cargo.toml` — drop `mergify-py-shim` dep. - `crates/mergify-cli/src/main.rs`: - Strip the shim infrastructure: `run_py_shim`, `inject_global_flags`, `prepend_one`, and `Dispatch::Shim` are gone. The `Dispatch` enum kept as a single-variant wrapper so the match in `main` stays exhaustive. - `dispatch_stack` no longer takes `debug` (was only used by the shim re-injection) and its `_` fallback exits via a synthetic `StackProbeCli` clap parse — that's what gives `stack pussh` clap's "did you mean `push`, `squash`?" formatting natively. - Clap parse failures at the binary's root now always exit via `clap::Error::exit()`, surfacing the same Levenshtein suggestions for unknown top-level subcommands (`staack` → "did you mean `stack`?"). - Tests for `shimmed_dispatch_*` deleted — the shim is gone and clap's exit-on-error path can't be unit-tested without subprocess plumbing. - `.github/workflows/ci.yaml` — drop `linters`, `compat-tests`, `test` jobs (Python lint/test). Keep `rust` (cargo) + `wheels` (build matrix) + a new `smoke-test-binary` job that `pip install`s each per-target wheel and runs `mergify --help` to catch a broken wheel entry on every supported OS. - `.github/workflows/func-tests-live.yaml` — replace `uv run --locked poe live-test` with `pip install --user pytest && python -m pytest -v func-tests/ -m live` so the live smoke runs without the poe/uv-deps superstructure. - `NATIVE_COMMANDS` — add `("stack", "push")` (was missing). End state: the wheel is one Rust binary + LICENSE; clap covers the full CLI; PyPI publishing pipeline unchanged. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
1 parent 0f91ee4 commit 5fce67b

43 files changed

Lines changed: 233 additions & 3472 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/ci.yaml

Lines changed: 34 additions & 122 deletions
Original file line numberDiff line numberDiff line change
@@ -8,56 +8,6 @@ on:
88
- devs/**
99

1010
jobs:
11-
linters:
12-
timeout-minutes: 5
13-
runs-on: ubuntu-24.04
14-
steps:
15-
- uses: actions/checkout@v6.0.2
16-
- uses: actions/setup-python@v6.2.0
17-
with:
18-
python-version: 3.14
19-
20-
# The maturin build-backend invokes cargo at sync time, so
21-
# every Python-using job needs the Rust toolchain on PATH.
22-
- name: Install Rust toolchain
23-
run: |
24-
rustup toolchain install stable --profile minimal
25-
rustup default stable
26-
- uses: Swatinem/rust-cache@v2
27-
28-
- uses: astral-sh/setup-uv@v8.1.0
29-
with:
30-
enable-cache: true
31-
version-file: requirements-uv.txt
32-
33-
- name: Linters
34-
shell: bash
35-
run: uv run --locked poe linters
36-
37-
compat-tests:
38-
timeout-minutes: 5
39-
runs-on: ubuntu-24.04
40-
steps:
41-
- uses: actions/checkout@v6.0.2
42-
- uses: actions/setup-python@v6.2.0
43-
with:
44-
python-version: 3.14
45-
46-
- name: Install Rust toolchain
47-
run: |
48-
rustup toolchain install stable --profile minimal
49-
rustup default stable
50-
- uses: Swatinem/rust-cache@v2
51-
52-
- uses: astral-sh/setup-uv@v8.1.0
53-
with:
54-
enable-cache: true
55-
version-file: requirements-uv.txt
56-
57-
- name: Compat tests
58-
shell: bash
59-
run: uv run --locked poe compat-test
60-
6111
rust:
6212
timeout-minutes: 10
6313
runs-on: ubuntu-24.04
@@ -84,101 +34,63 @@ jobs:
8434
- name: cargo build --release
8535
run: cargo build --release
8636

87-
test:
88-
# Cold-cache cargo builds on Windows can push past 15 minutes
89-
# (uv sync invokes the maturin build-backend, which compiles the
90-
# whole Rust workspace). Warm runs land in ~10 min; 25 gives the
91-
# cold path enough headroom to avoid spurious cancellations
92-
# without masking a genuinely runaway build.
93-
timeout-minutes: 25
37+
# Smoke build the full release wheel matrix on every PR so a
38+
# cross-compile or platform-specific maturin failure is caught
39+
# here, not on the next `release: published` event. Calls the
40+
# same reusable workflow the release uses, with version stamping
41+
# disabled (the placeholder "0.0.0" stays in pyproject.toml so
42+
# the wheel name doesn't pretend to be a release version).
43+
wheels:
44+
uses: ./.github/workflows/build-wheels.yml
45+
with:
46+
stamp-version: false
47+
48+
# Confirm the binary actually runs out of the freshly-built wheel
49+
# on each supported runtime. Lightweight smoke — `mergify --help`
50+
# exits 0 — covers the most common breakage class (a maturin
51+
# config change that ships a wheel with a broken or missing
52+
# binary entry, or a libc / runtime-link issue that only shows
53+
# up post-install). Builds the wheel locally with maturin
54+
# rather than downloading the `wheels`-job artifacts, since
55+
# `wheels` only uploads in release mode (PR smoke runs are
56+
# `stamp-version: false`).
57+
smoke-test-binary:
58+
timeout-minutes: 15
9459
strategy:
60+
fail-fast: false
9561
matrix:
9662
os: [ubuntu-24.04, windows-2025, macos-15]
97-
python: [3.13, 3.14]
9863
runs-on: ${{ matrix.os }}
9964
steps:
10065
- uses: actions/checkout@v6.0.2
10166
- uses: actions/setup-python@v6.2.0
10267
with:
103-
python-version: ${{ matrix.python }}
104-
68+
python-version: "3.13"
10569
- name: Install Rust toolchain
10670
run: |
10771
rustup toolchain install stable --profile minimal
10872
rustup default stable
10973
- uses: Swatinem/rust-cache@v2
11074
with:
111-
# The matrix runs the same target six times. Keying the
112-
# cache on (os, python) avoids cross-pollution while still
113-
# letting cargo reuse fetched crates within each shard.
114-
key: ${{ matrix.os }}-py${{ matrix.python }}
115-
116-
- uses: astral-sh/setup-uv@v8.1.0
117-
with:
118-
enable-cache: true
119-
version-file: requirements-uv.txt
120-
121-
- name: Tests
122-
shell: bash
123-
env:
124-
_MERGIFY_TEST_NEW_FLAKY_DETECTION: "true"
125-
PYTHONUTF8: "${{ matrix.os == 'windows-2025' && 1 || 0 }}"
126-
run: uv run --locked poe test
127-
128-
- name: Build
75+
key: smoke-${{ matrix.os }}
76+
- name: Build wheel
12977
shell: bash
130-
run: uv build
131-
132-
- name: Tests build
133-
shell: bash
134-
env:
135-
PYTHONUTF8: "${{ matrix.os == 'windows-2025' && 1 || 0 }}"
13678
run: |
137-
pip install dist/*.whl
138-
# We check the installed wheel produces a working binary, specially on Windows
139-
# to ensure the utf8 mode is enabled.
140-
python -c "import test_binary_build; test_binary_build.test_reexec_enables_utf8_and_prints_emoji();"
141-
142-
- name: Tests uv tool install
79+
pip install 'maturin~=1.7'
80+
maturin build --release --out dist
81+
- name: Install wheel
14382
shell: bash
144-
env:
145-
PYTHONUTF8: "${{ matrix.os == 'windows-2025' && 1 || 0 }}"
146-
# Regression guard for the Windows uv-trampoline discovery
147-
# path in `crates/mergify-py-shim`. `uv tool install` copies
148-
# a standalone trampoline `.exe` into the uv tool bin dir
149-
# (Windows requires privileges for symlinks, so unlike
150-
# pipx on Unix this is *not* a symlink back into the venv).
151-
# The shim has to walk to the uv tool dir via `uv tool dir`
152-
# to find the interpreter — exercise that on every PR so the
153-
# discovery path can't regress on a future shim rewrite.
154-
run: |
155-
uv tool install --force dist/*.whl
156-
bin_dir="$(uv tool dir --bin)"
157-
if [ "${RUNNER_OS}" = "Windows" ]; then
158-
"${bin_dir}/mergify.exe" --help > /dev/null
159-
else
160-
"${bin_dir}/mergify" --help > /dev/null
161-
fi
162-
163-
# Smoke build the full release wheel matrix on every PR so a
164-
# cross-compile or platform-specific maturin failure is caught
165-
# here, not on the next `release: published` event. Calls the
166-
# same reusable workflow the release uses, with version stamping
167-
# disabled (the placeholder "0.0.0" stays in pyproject.toml so
168-
# the wheel name doesn't pretend to be a release version).
169-
wheels:
170-
uses: ./.github/workflows/build-wheels.yml
171-
with:
172-
stamp-version: false
83+
run: pip install dist/*.whl
84+
- name: mergify --help
85+
shell: bash
86+
run: mergify --help
17387

17488
ci-gate:
17589
if: ${{ !cancelled() }}
17690
needs:
177-
- test
178-
- linters
179-
- compat-tests
18091
- rust
18192
- wheels
93+
- smoke-test-binary
18294
runs-on: ubuntu-latest
18395
steps:
18496
- name: Verify all jobs succeeded

.github/workflows/func-tests-live.yaml

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,11 @@ jobs:
2727
rustup default stable
2828
- uses: Swatinem/rust-cache@v2
2929

30-
- uses: astral-sh/setup-uv@v8.1.0
31-
with:
32-
enable-cache: true
33-
version-file: requirements-uv.txt
30+
- name: Build mergify binary
31+
run: cargo build --release
32+
33+
- name: Put built binary on PATH
34+
run: echo "${PWD}/target/release" >> "${GITHUB_PATH}"
3435

3536
- name: Live smoke tests
3637
shell: bash
@@ -47,4 +48,11 @@ jobs:
4748
# cause individual tests to skip rather than fail.
4849
LIVE_TEST_MERGIFY_TOKEN_CI: ${{ secrets.MERGIFY_CLI_LIVE_TEST_MERGIFY_TOKEN_CI }}
4950
LIVE_TEST_MERGIFY_TOKEN_ADMIN: ${{ secrets.MERGIFY_CLI_LIVE_TEST_MERGIFY_TOKEN_ADMIN }}
50-
run: uv run --locked poe live-test
51+
# Use `pip install pytest` over an ephemeral `uv tool run`
52+
# so the func-tests don't drag any package-management
53+
# tool into the supported-runtime contract. The wheel
54+
# itself has no Python deps; pytest only matters here as
55+
# the test runner.
56+
run: |
57+
pip install --user 'pytest>=8'
58+
python -m pytest -v func-tests/ -m live

Cargo.lock

Lines changed: 0 additions & 10 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

compat-tests/README.md

Lines changed: 0 additions & 44 deletions
This file was deleted.

compat-tests/cases/bad-subcommand/args

Lines changed: 0 additions & 1 deletion
This file was deleted.

compat-tests/cases/bad-subcommand/expected_exit

Lines changed: 0 additions & 1 deletion
This file was deleted.

compat-tests/cases/help/args

Lines changed: 0 additions & 1 deletion
This file was deleted.

compat-tests/cases/help/expected_exit

Lines changed: 0 additions & 1 deletion
This file was deleted.

compat-tests/cases/help/stdout_contains

Lines changed: 0 additions & 1 deletion
This file was deleted.

compat-tests/cases/stack-help/args

Lines changed: 0 additions & 1 deletion
This file was deleted.

0 commit comments

Comments
 (0)