Skip to content

Add --require-group for hook group intersections in prek run #2406

Description

@pygarap

Problem

Repeated --group options use union semantics: a hook is selected when it belongs to any requested group. There is currently no equivalent selector for requiring a hook to belong to every requested group, so users must define an additional combined group for each needed intersection.

Proposed behavior

Add a repeatable --require-group <GROUP> option to prek run. A hook would satisfy this filter only when its groups contains every value passed through --require-group.

The filters should compose independently of argument order:

(no --group values, or any --group value matches)
AND
(every --require-group value matches)
AND
(no --no-group value matches)

--no-group would continue to take precedence over inclusion filters.

Configuration

repos:
  - repo: https://github.com/astral-sh/ty-pre-commit
    rev: v0.0.63
    hooks:
      - id: ty
        groups: [lint, fast, local]

  - repo: https://github.com/astral-sh/ruff-pre-commit
    rev: v0.16.0
    hooks:
      - id: ruff-format
        groups: [format, fast, local]

  - repo: https://github.com/pre-commit/mirrors-mypy
    rev: v2.3.0
    hooks:
      - id: mypy
        groups: [lint, slow, ci]

  - repo: https://github.com/psf/black-pre-commit-mirror
    rev: 26.5.1
    hooks:
      - id: black
        groups: [format, slow, ci]

This configuration has two hooks in each functional group, two hooks in each performance group, fast hooks assigned to local, and slow hooks assigned to ci.

Usage

  1. Use multiple --require-group filters:
$ prek run --all-files --require-group lint --require-group fast

This runs only ty, which belongs to both required groups.

  1. Combine multiple --require-group and --group filters:
$ prek run --all-files --require-group format --require-group fast --group lint --group local

This runs only ruff-format. It belongs to every required group and matches the included local group. It does not need to match lint because repeated --group values use union semantics.

  1. Combine multiple --require-group and --no-group filters:
$ prek run --all-files --require-group lint --require-group slow --no-group format --no-group local

This runs only mypy. It belongs to every required group and belongs to neither excluded group.

  1. Combine multiple --require-group, --group, and --no-group filters:
$ prek run --all-files --require-group format --require-group slow --group lint --group ci --no-group fast --no-group local

This runs only black. It belongs to every required group, matches the included ci group, and belongs to neither excluded group.

Acceptance criteria

  • --require-group <GROUP> can be specified multiple times.
  • A hook matches only when it belongs to every specified required group.
  • --group, --require-group, and --no-group compose according to the selection model above.
  • Filter behavior is independent of argument order.
  • Using --require-group activates the same group selection mode as --group and --no-group.
  • An explicit --stage further restricts the matched hooks using the existing stage intersection behavior.
  • Hooks excluded by the resulting filters are not installed or executed.
  • A run fails with the existing explicit-selection error when no hook matches the complete intersection.
  • Existing behavior remains unchanged when --require-group is not provided.

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    ideaJust an idea

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions