Using Container Images with prek and pre-commit
Table of Contents
The prek tool manages Git hooks, and enables you to run the same hooks at any time, not just when you commit changes. It downloads the tools and runtimes for the hooks as needed. This means that it can provide a cross-platform way to install and run a complete set of tools for formatting and checking code.
For security and consistency, use container images to provide the tools for these hooks. This ensures that the contributors to a project run the same container images, and that CI systems also use those same images. For better supply chain security, you can specify images that you distribute through your own container registries.
The prek tool supersedes pre-commit. It can use the same hooks as
pre-commit, and works with existingpre-commitproject configurations.
Installing Prek #
Running docker_image or docker hooks requires a container runtime such as Podman, Docker or Apple Container. By default,
prekautomatically detects the container runtime.
To install prek on a development system, use the packages from the npm or
Python registries. If you use a package management tool like
npm, pipx, or uv, you can
specify which version of prek it installs:
npm install -g @j178/prek@0.4.10
pipx install prek==0.4.10
The prek project releases container images, so that you can run the same
hooks on your CI system, using the same version of prek that you run on development systems.
You can also install prek with Homebrew. Homebrew always installs the latest version of prek:
brew install prek
Adding Prek to a Project #
By default, prek uses the configuration file prek.toml. Both prek and pre-commit can use a
.pre-commit-config.yaml file. Consider using the YAML format, because the YAML maintenance tools that the hook manager
runs for other products will then run on the configuration.
Create the hooks configuration in a file called .pre-commit-config.yaml file. Save this file in the root directory of
your project:
---
minimum_prek_version: "0.4.0"
repos:
- repo: builtin
hooks:
- id: check-added-large-files
- id: check-case-conflict
- id: check-json
- id: check-merge-conflict
- id: check-symlinks
- id: check-toml
- id: check-vcs-permalinks
- id: check-xml
- id: check-yaml
- id: destroyed-symlinks
- id: end-of-file-fixer
- id: fix-byte-order-marker
- id: mixed-line-ending
- id: trailing-whitespace
This configuration enables built-in to prek hooks. You can then add
docker_image hooks to run other tools with container images.
To activate the configuration, run prek install. This adds the hooks to the Git configuration for your copy of the
project, so that the tools automatically run on the staged changes each time that you commit.
cd my-project
prek install
Adding Hooks #
To add a hook that uses a container image, you can either specify a remote hook configuration that uses containers, or define the hook directly in the configuration file. Many Open Source projects provide remote hook configurations for the tools that they produce.
Once you create a configuration file with remote Git hooks, run
prek update --freeze. This replaces the versionrefsof any remote hooks with hashes, which protects you from supply-chain attacks against the remote hook repositories.
If possible, avoid using remote hook configurations altogether. Instead, create hooks in your prek configuration
files. This enables you to have full control over the configuration of the hook and which container image it runs.
For example, you may want to use a hook to detect secrets in code, and decide to use gitleaks for this. The Gitleaks project provides three hooks in the remote hook configuration that it publishes:
- id: gitleaks
name: Detect hardcoded secrets
description: Detect hardcoded secrets using Gitleaks
entry: gitleaks git --pre-commit --redact --staged --verbose
language: golang
pass_filenames: false
- id: gitleaks-docker
name: Detect hardcoded secrets
description: Detect hardcoded secrets using Gitleaks
entry: zricethezav/gitleaks git --pre-commit --redact --staged --verbose
language: docker_image
pass_filenames: false
- id: gitleaks-system
name: Detect hardcoded secrets
description: Detect hardcoded secrets using Gitleaks
entry: gitleaks git --pre-commit --redact --staged --verbose
language: system
You could call the docker_image hook as a remote hook, but it is safer to copy the hook into your own configuration
file. You can then specify the exact container image that your project hook uses. Here is an example of a complete
configuration file:
---
minimum_prek_version: "0.4.0"
repos:
- repo: builtin
hooks:
- id: check-added-large-files
- id: check-case-conflict
- id: check-illegal-windows-names
- id: check-json
- id: check-merge-conflict
- id: check-symlinks
- id: check-toml
- id: check-vcs-permalinks
- id: check-xml
- id: check-yaml
- id: destroyed-symlinks
- id: end-of-file-fixer
- id: fix-byte-order-marker
- id: mixed-line-ending
- id: trailing-whitespace
- repo: local
hooks:
- id: gitleaks-docker
name: Detect hardcoded secrets
description: Detect hardcoded secrets using Gitleaks
entry: zricethezav/gitleaks@sha256:c00b6bd0aeb3071cbcb79009cb16a60dd9e0a7c60e2be9ab65d25e6bc8abbb7f git --pre-commit --redact --staged --verbose
language: docker_image
pass_filenames: false
Here, we use the SHA index digest to specify the version of the image, rather than latest or a version tag. If an
attacker gains access to the container registry they might change the image tags to point to images that contain
malware. You can see the SHA digests for a container image in the Web interface of the container registry that publishes
it. For example, the Gitleaks project publishes images on Docker Hub, and
this page lists the tags.
To avoid issues with different systems using different CPU architectures, use multi-architecture container images where possible. If you cannot, container images may run with CPU emulation. Docker Desktop automatically emulates the required CPU architecture as needed, but other runtimes like Podman require extra configuration to run images with CPU emulation.
If a project does not provide a docker_image hook, you can copy the configuration for a hook that they publish, and
adapt it. To convert a hook configuration to use a container image, set the language to docker_image and update the
entry to start with required container image, like this:
entry: zricethezav/gitleaks@sha256:c00b6bd0aeb3071cbcb79009cb16a60dd9e0a7c60e2be9ab65d25e6bc8abbb7f git --pre-commit --redact --staged --verbose
language: docker_image
You can replace the container image that a hook uses at any time. This example uses the Docker Hardened Image for gitleaks, which is a multi-architecture image:
- repo: local
hooks:
- id: gitleaks-docker
name: Detect hardcoded secrets
description: Detect hardcoded secrets using Gitleaks
entry: dhi.io/gitleaks:8@sha256:3ffd6debb567b711bb6cd741caf084d76904ece99f7fe88d906cc57501b53469 git --pre-commit --redact --staged --verbose
language: docker_image
pass_filenames: false
Using Hooks #
The hooks automatically run on the staged changes each time that you commit. To run a tool without commiting a change,
use prek run. If you add the option --all-files it will check the current files in the project, not just staged
changes.
For example, to run the gitleaks-docker hook on the project, use this command:
prek run gitleaks-docker --all-files
To run every hook on the project, use this command:
prek run --all-files