feat(errors): typed exit code contract and migration of existing exits#1242
Conversation
Merge ProtectionsYour pull request matches the following merge protections and will not be merged until they are valid. 🟢 🤖 Continuous IntegrationWonderful, this rule succeeded.
🟢 👀 Review RequirementsWonderful, this rule succeeded.
🟢 Enforce conventional commitWonderful, this rule succeeded.Make sure that we follow https://www.conventionalcommits.org/en/v1.0.0/
🟢 🔎 ReviewsWonderful, this rule succeeded.
🟢 📕 PR descriptionWonderful, this rule succeeded.
|
There was a problem hiding this comment.
Pull request overview
This PR establishes a typed exit-code contract for the CLI (via an ExitCode IntEnum carried by a new MergifyError) and migrates multiple command error paths to use those typed exit codes, with documentation + tests to enforce the contract.
Changes:
- Introduces
utils.MergifyError(click.ClickException)with per-instanceExitCodeand a customshow()implementation. - Migrates config/ci CLI semantic error paths to raise
MergifyError(and usesclick.BadParameterfor argument errors) and normalizessys.exit(ExitCode.SUCCESS)usage. - Adds/extends tests and introduces
docs/exit-codes.mdas the documented exit-code contract (referenced from README).
Reviewed changes
Copilot reviewed 16 out of 16 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
mergify_cli/utils.py |
Adds MergifyError with typed exit_code and custom show() behavior. |
mergify_cli/config/cli.py |
Migrates config CLI errors to MergifyError / click.BadParameter with structured exit codes. |
mergify_cli/ci/cli.py |
Migrates CI CLI errors to MergifyError with structured exit codes. |
mergify_cli/stack/push.py |
Normalizes sys.exit(0) to sys.exit(ExitCode.SUCCESS). |
mergify_cli/stack/open.py |
Normalizes success exit to ExitCode.SUCCESS for user-cancel path. |
mergify_cli/stack/checkout.py |
Normalizes “no stacked PRs found” success exit to ExitCode.SUCCESS. |
mergify_cli/tests/test_utils.py |
Adds unit tests for MergifyError default/override exit code behavior. |
mergify_cli/tests/test_exit_codes.py |
Adds round-trip test ensuring every ExitCode survives via MergifyError. |
mergify_cli/tests/test_exit_code_contract.py |
Adds cross-command parametrized exit-code contract tests. |
mergify_cli/tests/test_cli.py |
Adds integration tests for Click standalone handling of MergifyError exit codes. |
mergify_cli/tests/config/test_validate.py |
Pins exit codes for config validate/simulate failure modes. |
mergify_cli/tests/ci/test_junit.py |
Pins junit-process error exits to GENERIC_ERROR to prevent regressions. |
mergify_cli/tests/ci/test_cli_exit_codes.py |
Adds CI command exit code pinning tests. |
mergify_cli/tests/ci/test_cli.py |
Updates existing CI tests to assert typed exit codes instead of 1. |
docs/exit-codes.md |
Adds the authoritative exit-code contract documentation. |
README.md |
Links to docs/exit-codes.md from the README exit-code section. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Establishes typed exit codes across the CLI as preparation for the
upcoming Python→Rust port, and migrates every semantic error path
to use them.
## Foundation
- `MergifyError(click.ClickException)`: CLI-level error with a typed
per-instance `exit_code` (ExitCode IntEnum). Click's standalone-mode
handler catches it automatically and exits with the typed code, so
`main()` needs no extra except clause.
- `show()` writes "error: <msg>" in red to stderr via `click.secho`,
matching the behavior of `click.ClickException`. Honors click's
`file` parameter when provided.
## Migrations
- `config/cli.py`: 7 `click.ClickException` raises -> `MergifyError`
(CONFIGURATION_ERROR / MERGIFY_API_ERROR / GENERIC_ERROR); the
schema-validation-errors path now uses `ctx.exit()` after printing
the per-error details (avoids a redundant "error: ..." line).
Invalid PR URL -> `click.BadParameter` (exit 2 - argument error).
- `ci/cli.py`: 5 `click.ClickException` raises -> `MergifyError`
(scopes / scopes_send -> CONFIGURATION_ERROR; queue_info outside
a merge queue context -> INVALID_STATE).
- `stack/{open,push,checkout}.py`: bare `sys.exit(0)` ->
`sys.exit(ExitCode.SUCCESS)` for uniformity.
- junit-process: pinned the existing GENERIC_ERROR exits against
regression (invalid XML, empty reports).
## Tests
- Round-trip: every ExitCode value round-trips through MergifyError.
- Integration: click's standalone-mode translates MergifyError to
the typed exit code via both CliRunner and the real `main()`.
- Parametrized cross-command contract walker pins the config and ci
failure modes; per-command tests cover stack and queue.
## UX impact
Semantic failures now exit with a typed code instead of bare 1.
Scripts relying on `exit == 1` for config or ci-scopes errors need
updating. `config simulate` with an invalid PR URL now exits 2
(click.BadParameter) rather than 1.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Change-Id: I4d526fbf31d854b4c4fd4bbf0bcebdbcf79d5368
96adb00 to
ce566f5
Compare
Revision history
|
|
This pull request is part of a Mergify stack:
|
Merge Queue Status
This pull request spent 5 minutes 36 seconds in the queue, including 5 minutes 19 seconds running CI. Required conditions to merge
|
Establishes typed exit codes across the CLI as preparation for the
upcoming Python→Rust port, and migrates every semantic error path
to use them.
Foundation
MergifyError(click.ClickException): CLI-level error with a typedper-instance
exit_code(ExitCode IntEnum). Click's standalone-modehandler catches it automatically and exits with the typed code, so
main()needs no extra except clause.show()writes "error: " in red to stderr viaclick.secho,matching the behavior of
click.ClickException. Honors click'sfileparameter when provided.Migrations
config/cli.py: 7click.ClickExceptionraises ->MergifyError(CONFIGURATION_ERROR / MERGIFY_API_ERROR / GENERIC_ERROR); the
schema-validation-errors path now uses
ctx.exit()after printingthe per-error details (avoids a redundant "error: ..." line).
Invalid PR URL ->
click.BadParameter(exit 2 - argument error).ci/cli.py: 5click.ClickExceptionraises ->MergifyError(scopes / scopes_send -> CONFIGURATION_ERROR; queue_info outside
a merge queue context -> INVALID_STATE).
stack/{open,push,checkout}.py: baresys.exit(0)->sys.exit(ExitCode.SUCCESS)for uniformity.regression (invalid XML, empty reports).
Tests
the typed exit code via both CliRunner and the real
main().failure modes; per-command tests cover stack and queue.
UX impact
Semantic failures now exit with a typed code instead of bare 1.
Scripts relying on
exit == 1for config or ci-scopes errors needupdating.
config simulatewith an invalid PR URL now exits 2(click.BadParameter) rather than 1.
Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com