Skip to content

feat(errors): typed exit code contract and migration of existing exits#1242

Merged
mergify[bot] merged 1 commit intomainfrom
devs/jd/worktree-rust-port/typed-exit-code-contract-migration-existing-exits--4d526fbf
Apr 20, 2026
Merged

feat(errors): typed exit code contract and migration of existing exits#1242
mergify[bot] merged 1 commit intomainfrom
devs/jd/worktree-rust-port/typed-exit-code-contract-migration-existing-exits--4d526fbf

Conversation

@jd
Copy link
Copy Markdown
Member

@jd jd commented Apr 20, 2026

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: " 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

Copilot AI review requested due to automatic review settings April 20, 2026 07:27
@mergify mergify Bot had a problem deploying to Mergify Merge Protections April 20, 2026 07:27 Failure
@mergify
Copy link
Copy Markdown
Contributor

mergify Bot commented Apr 20, 2026

Merge Protections

Your pull request matches the following merge protections and will not be merged until they are valid.

🟢 🤖 Continuous Integration

Wonderful, this rule succeeded.
  • all of:
    • check-success=ci-gate

🟢 👀 Review Requirements

Wonderful, this rule succeeded.
  • any of:
    • #approved-reviews-by>=2
    • author = dependabot[bot]
    • author = mergify-ci-bot
    • author = renovate[bot]

🟢 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)(?:\(.+\))?:

🟢 🔎 Reviews

Wonderful, this rule succeeded.
  • #changes-requested-reviews-by = 0
  • #review-requested = 0
  • #review-threads-unresolved = 0

🟢 📕 PR description

Wonderful, this rule succeeded.
  • body ~= (?ms:.{48,})

@mergify mergify Bot requested a review from a team April 20, 2026 07:32
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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-instance ExitCode and a custom show() implementation.
  • Migrates config/ci CLI semantic error paths to raise MergifyError (and uses click.BadParameter for argument errors) and normalizes sys.exit(ExitCode.SUCCESS) usage.
  • Adds/extends tests and introduces docs/exit-codes.md as 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.

Comment thread mergify_cli/utils.py Outdated
Comment thread mergify_cli/config/cli.py Outdated
@jd jd marked this pull request as ready for review April 20, 2026 07:36
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
@jd jd force-pushed the devs/jd/worktree-rust-port/typed-exit-code-contract-migration-existing-exits--4d526fbf branch from 96adb00 to ce566f5 Compare April 20, 2026 08:00
@jd
Copy link
Copy Markdown
Member Author

jd commented Apr 20, 2026

Revision history

# Type Changes Date
1 initial 96adb00 2026-04-20 08:00 UTC
2 content 96adb00 → ce566f5 2026-04-20 08:00 UTC

@mergify mergify Bot deployed to Mergify Merge Protections April 20, 2026 08:00 Active
@mergify mergify Bot requested a review from a team April 20, 2026 09:32
@jd
Copy link
Copy Markdown
Member Author

jd commented Apr 20, 2026

This pull request is part of a Mergify stack:

# Pull Request Link
1 feat(errors): typed exit code contract and migration of existing exits #1242 👈
2 refactor(stack): schema-lock stack list --json output #1243
3 test: formalize --json stdout/stderr discipline #1246

@mergify
Copy link
Copy Markdown
Contributor

mergify Bot commented Apr 20, 2026

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

mergify Bot added a commit that referenced this pull request Apr 20, 2026
@mergify mergify Bot added the queued label Apr 20, 2026
mergify Bot added a commit that referenced this pull request Apr 20, 2026
@mergify mergify Bot merged commit a616570 into main Apr 20, 2026
19 checks passed
@mergify mergify Bot deleted the devs/jd/worktree-rust-port/typed-exit-code-contract-migration-existing-exits--4d526fbf branch April 20, 2026 10:16
@mergify mergify Bot removed the queued label Apr 20, 2026
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.

4 participants