Metadata-Version: 2.4
Name: pytest-mergify
Version: 2026.10.8
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 first the tests a merge-queue bisection step failed in the step above, and stops as soon as one fails again

## 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_FAIL_FIRST_ENABLE` | Set to `false` to never run first what a merge-queue bisection step of this job failed before | `true` |

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/).

### Bisection steps: failures first

When Mergify's merge queue splits a failed batch, each bisection step only
needs to tell whether it is red. The plugin asks Mergify whether the current
run is such a step and, if so, runs first the tests this job failed in the run
the step was split from (Fail First). 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.

**It is on by default**: there is nothing to configure, the plugin uses the
token and job identity it already has, and Mergify decides. A job that must
always run its suite in its usual order turns it off:

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

Such a job never queries the endpoint; everything else the plugin does — test
tracing, flaky detection, quarantine — is unaffected either way. Only a
recognised no (`false`, `no`, `off`, `0`) turns it off.
`MERGIFY_TEST_SELECTION_ENABLE`, its former name, is still read while the new
one is unset or empty, and the summary at the end of the run says to switch.

It only ever asks in a build of a merge-queue batch, the only builds a
bisection step can be: on GitHub Actions, the event of the draft pull request
the queue opened; on any other build, the queue's git note on the branch, one
`git ls-remote` against `origin` that runs while the tests are being collected.
Every other build asks nothing and sends nothing. Of a batch's builds, only
those below a failed batch, and not retrying their own checks, ask: the
others cannot be a bisection step, and send their results only when the
batch holds two pull requests or more, so a bisection step may read them, or
when they failed. A build where git cannot answer (no `origin`, credentials
the test step does not hold) asks, as it would without this.

Every other situation — a run that is not a bisection step, an unreachable API,
an answer from a newer Mergify this plugin does not understand — runs the full
suite in its usual order. It is also enabled per organization on Mergify's
side: until yours has it, every run is served the full suite, says nothing
about it, and still sends
its results, which show what Fail First would do there.

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 bisection step 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 step simply runs its full suite
in its usual order.

## 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).

