Metadata-Version: 2.4
Name: pytest-mergify
Version: 2026.10.6
Requires-Dist: pytest>=6.2.5
Requires-Dist: pluggy>=1.2
Requires-Dist: pytest-timeout>=2.4.0
License-File: LICENSE
License-File: THIRD_PARTY_LICENSES
Summary: Pytest plugin for Mergify
Author-email: Julien Danjou <jd@mergify.com>
License-Expression: LicenseRef-Mergify-Proprietary
Requires-Python: >=3.10
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
Project-URL: Homepage, https://docs.mergify.com/test-engine/test-frameworks/pytest/
Project-URL: Issues, https://github.com/Mergifyio/mergify-cli/issues

# pytest-mergify

Pytest plugin for [Mergify Test Engine](https://docs.mergify.com/test-engine/).

More information at https://mergify.com

## Features

- **Test tracing** — Sends OpenTelemetry traces for every test to Mergify's API
- **Flaky test detection** — Intelligently reruns tests to detect flakiness with budget constraints
- **Test quarantine** — Quarantines failing tests so they don't block CI
- **Test selection** — Runs only the previously-failing tests when Mergify's merge queue reruns a job

## Installation

Install the package alongside `pytest` (>= 6.0.0):

```bash
pip install pytest-mergify
```

The plugin is auto-discovered by pytest — no manual registration required.

## Configuration

Set the `MERGIFY_TOKEN` environment variable with your Mergify API token.

The plugin activates automatically when running in CI (detected via the `CI` environment variable). To enable outside CI, set `MERGIFY_CI_ENABLE=true`.

### Environment Variables

| Variable | Description | Default |
|---|---|---|
| `MERGIFY_TOKEN` | Mergify API authentication token | (required) |
| `MERGIFY_API_URL` | Mergify API endpoint | `https://api.mergify.com` |
| `MERGIFY_CI_ENABLE` | Force-enable outside CI | `false` |
| `MERGIFY_CI_DEBUG` | Print spans to console instead of uploading | `false` |
| `MERGIFY_TRACEPARENT` | W3C distributed trace context | — |
| `MERGIFY_TEST_JOB_NAME` | Mergify test job name | — |
| `MERGIFY_TEST_SELECTION_ENABLE` | Opt this job into test selection (see below) | `false` |

The names these replace, `PYTEST_MERGIFY_ENABLE` and `PYTEST_MERGIFY_DEBUG`,
are deprecated: they still work when the new name is not set, and the plugin
says which one to switch to at the end of the session.

For detailed documentation, see the [official guide](https://docs.mergify.com/test-engine/test-frameworks/pytest/).

### Test selection

When Mergify's merge queue reruns a job — a retry, or a step of a batch
bisection — only the tests that failed on the previous attempt are
informative. The plugin asks Mergify whether the current run is such a rerun
and, if so, runs only those tests; the rest are reported as deselected.

Two other answers exist. Mergify may say the previous attempt of this job
already ran every one of these tests and they all passed: the run then executes
no test and exits green, and still reports itself so the attempt is visible.
Or Mergify may stop the run outright. That happens when several runs of this
job report under the same name and run the same tests: Mergify cannot tell
which one the current run repeats, and it will not guess which tests to skip.
The run then **fails**, showing Mergify's explanation of what it saw — usually
asking you to give each of those runs its own `MERGIFY_TEST_JOB_NAME`.

**This is off until you turn it on, per job.** Installing the plugin is not
enough: a feature that decides not to run tests starts only where you wrote
that it should. Set `MERGIFY_TEST_SELECTION_ENABLE=true` on the job you want
reduced:

```yaml
      - name: Run tests
        run: pytest
        env:
          MERGIFY_TOKEN: ${{ secrets.MERGIFY_TOKEN }}
          MERGIFY_TEST_SELECTION_ENABLE: "true"
```

A job that does not set it never queries the endpoint and always runs the full
suite; everything else the plugin does — test tracing, flaky detection,
quarantine — is unaffected either way. Anything that is not a recognised yes
(unset, empty, `false`, or a value the plugin cannot parse) means no.

Past that there is nothing to configure: the plugin uses the token and job
identity it already has, and Mergify decides. Every remaining situation — a
normal run, a rerun Mergify has no previous results for, an unreachable API, an
answer from a newer Mergify this plugin does not understand — runs the full
suite, so the feature never costs coverage. It is also enabled per organization
on Mergify's side, so a job that opts in stays inactive until your organization
is opted in too.

#### Bisection steps: failures first

When the merge queue splits a failed batch, a test that passed with every pull
request of the batch can fail with only some of them, so a bisection step is
never reduced: it runs its full suite. Mergify may instead tell it to run first
the tests this job failed in the run the step was split from. They run before
every other test; if one of them fails again, the step is red whatever the rest
does, so the run stops there (exit code 1, the remaining tests not run) and the
queue can split again without waiting for the suite. If they all pass, the rest
of the suite runs as usual. No test is ever skipped, so a green step still
comes from a full suite. A test a retry rescued, or a quarantined failure, does
not stop the run.

Under pytest-xdist this works with the `load` (the default with `-n`) and
`worksteal` schedulers; with `loadgroup`, tests marked `xdist_group` still run
before them. With `loadscope` and `loadfile`, xdist
schedules whole modules or classes by size, so those tests do not run first:
the run still stops once they have all run and one failed again. Workers on a
remote gateway (`--tx ssh=…`) run to the end.

An opted-in job also tells Mergify what it concluded when its session ends —
which tests failed, how many ran — in one small request sent before the test
results are uploaded. That is what the next rerun of the job is answered from,
seconds after this one finished, without waiting for the results to be
processed. If that request fails, the run's own result is untouched: the
terminal says so, and the next rerun simply runs the full suite.

## Development

### Prerequisites

- Python >= 3.10
- [uv](https://docs.astral.sh/uv/)

### Setup

```bash
uv sync
```

### Running Tests

```bash
uv run poe test
```

`uv run poe build` builds the wheel into `dist/`; when one is there,
`poe test` runs against it rather than the project's own install, which is
how CI builds the extension once for every Python version. Every uv-driven
build of the extension uses Cargo's dev profile.

### Linting

```bash
uv run poe lint
```

## License

Proprietary. Use is governed by the
[Mergify Terms of Service](https://mergify.com/tos).

