Workspace Mode¶
prek
supports a powerful workspace mode that allows you to manage multiple projects with their own pre-commit configurations within a single repository. This is particularly useful for monorepos or projects with complex directory structures.
Overview¶
A workspace is a directory structure that contains:
- A root
.pre-commit-config.yaml
file - Zero or more nested
.pre-commit-config.yaml
files in subdirectories
Each directory containing a .pre-commit-config.yaml
file is considered a project. Projects can be nested infinitely deep.
Discovery¶
When you run prek run
without the --config
option, prek
automatically discovers the workspace:
-
Find workspace root: Starting from the current working directory,
prek
walks up the directory tree until it finds a.pre-commit-config.yaml
file. This becomes the workspace root. -
Discover all projects: From the workspace root,
prek
recursively searches all subdirectories for additional.pre-commit-config.yaml
files. Each one becomes a separate project. -
Git repository boundary: The search stops at the git repository root (
.git
directory) to avoid including unrelated projects.
Note:
-
The workspace root is not necessarily the same as the git repository root, a workspace can exist within a subdirectory of a git repository.
-
The current working directory determines the workspace root discovery.
prek
starts searching from your current location and stops at the first.pre-commit-config.yaml
file found while traversing up the directory tree. Running from different directories may discover different workspace roots. Useprek -C <dir>
to change the working directory before execution. -
Directories beginning with a dot (e.g.
.hidden
) are ignored during project discovery.
Project Organization¶
Example Structure¶
my-monorepo/
├── .pre-commit-config.yaml # Workspace root config
├── .git/
├── docs/
│ └── .pre-commit-config.yaml # Nested project
├── src/
│ ├── .pre-commit-config.yaml # Nested project
│ └── backend/
│ └── .pre-commit-config.yaml # Deeply nested project
└── frontend/
└── .pre-commit-config.yaml # Nested project
In this example:
my-monorepo/
is the workspace rootdocs/
,src/
,src/backend/
, andfrontend/
are individual projects- Each project has its own
.pre-commit-config.yaml
file
Execution Model¶
File Collection¶
When running in workspace mode:
- Collect all files:
prek
collects all files within the workspace root directory - Apply global filters: Files are filtered based on include/exclude patterns from the workspace root config
- Distribute to projects: Each project receives a subset of files based on its location
Hook Execution¶
For each project:
- Scope to project directory: Hooks run within their project's root directory
- Filter files: Only files within the project's directory tree are passed to its hooks
- Independent execution: Each project's hooks run independently with their own environment
Execution Order¶
Projects are executed from deepest to shallowest:
src/backend/
(deepest)src/
docs/
frontend/
my-monorepo/
(root, last)
This ensures that more specific configurations (deeper projects) take precedence over general ones.
Note: Files in subprojects will be processed multiple times - once for each project in the hierarchy that contains them. For example, a file in src/backend/
will be checked by hooks in src/backend/
, then src/
, then the workspace root.
Example Output¶
When running prek run
on the example structure above, you might see output like this:
$ prek run
Running hooks for `src/backend`:
check python ast.........................................................Passed
check for merge conflicts................................................Passed
black....................................................................Passed
isort....................................................................Passed
Running hooks for `docs`:
Markdownlint.........................................(unimplemented yet)Skipped
Running hooks for `frontend`:
prettier.................................................................Passed
Running hooks for `src`:
isort....................................................................Passed
mypy.....................................................................Passed
check python ast.........................................................Passed
check docstring is first.................................................Passed
Running hooks for `.`:
fix end of files.........................................................Passed
check yaml...............................................................Passed
check for added large files..............................................Passed
trim trailing whitespace.................................................Passed
check for merge conflicts................................................Passed
Notice how:
- Files in
src/backend/
are processed by both thesrc/backend/
project and thesrc/
project - Each project runs in its own working directory
- The workspace root processes all files in the entire workspace
- Projects are executed from deepest to shallowest as described in the execution order
Command Line Usage¶
# Run from current directory, auto-discover workspace
prek run
# Run specific hook across all projects
prek run black
# Run from specific directory
cd src/backend && prek run
# Use -C option to change directory automatically
prek run -C src/backend
The -C <dir>
or --cd <dir>
option automatically changes to the specified directory before running, allowing you to target specific projects from any location in the workspace.
Note: When using prek install
, only the workspace root configuration's default_install_hook_types
will be honored. Nested project configurations are not considered during installation.
Project and Hook Selection¶
In workspace mode, you can selectively run hooks from specific projects or skip certain projects/hooks using flexible selector syntax.
Selector Syntax¶
The selector syntax has three different forms:
<hook-id>
: Matches all hooks with the given ID across all projects.<project-path>/
: Matches all hooks from the specified project and its subprojects.<project-path>:<hook-id>
: Matches only the specified hook from the specified project.
Selectors can be used to select specific hooks or projects, and combined with --skip
to exclude certain hooks or projects.
Note: <project-path>
can be a relative path, which is then resolved relative to the current working directory. Note that the trailing slash /
in a <project-path>
is important, if a selector does not contain a slash, it is interpreted as a hook ID.
Running Specific Hooks or Projects¶
# Run all hooks with a specific ID across all projects
prek run <hook-id>
# Run only hooks from a specific project
prek run <project-path>/
# Run only hooks with a specific ID from a specific project
prek run <project-path>:<hook-id>
Examples:
# Run all 'black' hooks across all projects
prek run black
# Run all hooks from the 'frontend' project
prek run frontend/
# Run only the 'lint' hook from the 'frontend' project
prek run frontend:lint
# Run the 'lint' from 'frontend' and 'black' from 'src/backend'
prek run frontend:lint src/backend:black
Skipping Projects or Hooks¶
You can skip specific projects or hooks using the --skip
option, with the same syntax as for selecting projects or hooks.
# Skip all hooks from a specific project
prek run --skip <project-path>/
# Skip specific hooks within a selected project
prek run <project-path>/ --skip <subproject-path>/
# Skip all hooks with a specific ID across all projects
prek run --skip <hook-id>
Examples:
# Run all hooks except those from the 'frontend' project
prek run --skip frontend/
# Run hooks from 'frontend' but skip 'frontend/docs'
prek run frontend/ --skip frontend/docs
# Run hooks from 'frontend' but skip 'frontend/docs' and 'frontend:lint'
prek run frontend/ --skip frontend/docs --skip frontend:lint
# Run all hooks except 'black' and 'markdownlint' hooks
prek run --skip black --skip markdownlint
Note: Selecting a project includes all its subprojects unless explicitly skipped. Skipping a project also skips all its subprojects.
Note: The PREK_SKIP
or SKIP
environment variable can be used as an alternative to --skip
. Multiple values should be comma-delimited:
# Skip 'frontend' and 'tests' projects
PREK_SKIP=frontend/,tests prek run
# Skip 'frontend/docs' project and 'src/backend:lint' hook
SKIP=frontend/docs,src/backend:lint prek run
Precedence rules for --skip
command line options and environment variables are: --skip
> PREK_SKIP
> SKIP
.
Advanced Examples¶
# Run 'lint' hooks from all projects except 'tests'
prek run lint --skip tests
# Run all hooks from 'src' and 'docs' but skip 'src/legacy'
prek run src/ docs/ --skip src/legacy
# Run 'format' hooks only from Python projects
prek run python:format
Single Config Mode¶
When you specify a configuration file using the -c
or --config
parameter, workspace mode is disabled and only the specified configuration file is used. This mode provides traditional pre-commit behavior similar to the original pre-commit tool.
In single config mode:
- No workspace discovery: Only the explicitly specified configuration file is used
- Single execution context: All hooks run from the git repository root directory
- Global file scope: All files in the git repository are passed to all hooks
- No project isolation: Hooks don't have access to project-specific working directories
Usage Examples¶
# Disable workspace mode, use specific config
prek run --config .pre-commit-config.yaml
# Use config from a subdirectory
prek run --config src/.pre-commit-config.yaml
# Short form using -c
prek run -c docs/.pre-commit-config.yaml
Key Differences: Workspace vs Single Config¶
Feature | Workspace Mode | Single Config Mode |
---|---|---|
Discovery | Auto-discovers all .pre-commit-config.yaml files | Uses single specified config file |
Working Directory | Uses workspace root | Uses git repository root |
File Scope | All files in workspace | All files in git repo |
Hook Scope | Project-specific file filtering | All files pass to all hooks |
Execution Context | Each project runs in its own directory | All hooks run from git root |
Configuration | Multiple configs | Single config file only |
Migration from Single Config¶
To migrate an existing single-config setup to workspace mode:
- Create workspace root: Move existing
.pre-commit-config.yaml
to repository root - Add project configs: Create
.pre-commit-config.yaml
in subdirectories as needed - Update file patterns: Adjust
files
/exclude
patterns to be project-relative - Test execution: Verify hooks run in correct directories with correct file sets
Workspace Cache¶
To improve performance in large monorepos, prek
introduces a workspace cache mechanism. The workspace cache stores the results of project discovery, so repeated runs are much faster.
- The cache is automatically used by default. You don't need to do anything for it to work.
- If you make changes to
.pre-commit-config.yaml
files, remove projects, or otherwise change the workspace structure,prek
will usually detect this and refresh the cache automatically. - If you add a new
.pre-commit-config.yaml
to your workspace,prek
may not detect it immediately, try running with--refresh
to ensure the cache is up to date.
This will clear and rebuild the workspace cache before running hooks.
Behavior Changes in Workspace Mode¶
When running in workspace mode, there are a few changes to the output format and behavior compared to single-config mode:
- Hook output is grouped by project, with a header indicating which project is currently running.
- Skipped hooks are not shown at all in the output, previously they were listed as "Skipped".
The workspace mode provides powerful organization capabilities while maintaining backward compatibility with existing single-config workflows.