This guide will show you how to:
- Add
rustfmt
andclippy
components tocargo
. - Add
cargo
check
,rustfmt
,clippy
, andtest
git pre-commit hooks to a rust project.
- A stable rust toolchain and
cargo
(e.g. using rustup).
Introduction
Having code checks, linting, formatting, and tests run automatically during CI runs is very advantageous. In particular, it helps enforce code standards and quality. From a rust perspective, having cargo
on hand to do this is very helpful. However, executing these locally during development is a manual process, which can sometimes cause issues - as humans we forget a lot! In those cases, code could be pushed to a CI runner which then fails because there were problems when running the linter, formatter, and/or tests.
Pre-commits are renowned in many programming languages (perhaps most notably the pre-commit
framework itself). Pre-commit hooks can help prevent these issues by executing checks automatically before committing.
This guide explores two options for adding pre-commit hooks within rust projects - see the Setting up pre-commit hooks section for more details. Both rely on cargo
’s additional components rustfmt
formatter and clippy
linter, so the following section starts by showing how these can be added to cargo
.
… they rely on users to install the pre-commit hooks locally, which presents another opportunity for us humans to make mistakes (by not installing before committing, for example). In this case pre-receive hooks maybe a better option. However, pre-receive hooks on GitHub seem to be an Enterprise only solution 😞.
Adding rustfmt
and clippy
rustfmt
and clippy
can be added as additional components to cargo
.
- Install
rustfmt
:
rustup component add rustfmt
- Install
clippy
:
rustup component add clippy
Setting up pre-commit hooks
This section provides two sets of instructions for adding pre-commit hooks within a rust project. Option 1 uses pre-commit
, which is a useful approach for those looking for an ‘off-the-shelf’ solution. Option 2 explores writing hooks directly, meaning no additional external dependencies are required.
They add the following hooks:
Hook | Description |
---|---|
cargo check |
Check source code and dependencies for compilation errors. |
cargo fmt |
Check source code for formatting inconsistencies. |
cargo clippy |
Run lints to check for common issues. |
cargo test |
Run whole test suite. |
The cargo test
hook is only available when using Option 2.
Option 1: Using pre-commit
The instructions in this section use pre-commit
and the currently supported rust pre-commit hooks. See here for other supported hooks.
Follow the latest
pre-commit
installation instructions.Within your git version controlled rust project root directory, create a
.pre-commit-config.yaml
file:
touch .pre-commit-config.yaml
- Open the file and add the following contents:
repos:
- repo: https://github.com/doublify/pre-commit-rust
rev: v1.0
hooks:
- id: cargo-check
args: ["--workspace"]
- id: fmt
args: ["--", "--check"]
- id: clippy
args: ["--", "-D", "warnings"]
Check and update rev
to the latest version before use. The above repo
url will provide the latest version number.
- Install the pre-commits:
pre-commit install
- Check the pre-commit hooks are working by calling:
pre-commit run --all-files
- Enjoy using rust with pre-commits 🥳
Option 2: Writing own hooks
The instruction in this section will work on MacOS and Linux, but not on Windows.
This approach works by adding a script that executes when attempting to commit. It runs the necessary cargo
compilation, formatting, and linting checks, then blocks the commit if an issue is detected at any stage.
The following instructions add the same check
, rustfmt
, and clippy
pre-commit checks as shown in the previous section. To demonstrate the flexibility of this approach, there is a version of the script where the test suite is also executed as part of the pre-commit checks (which is not currently achievable with the previous option). Adding test suite execution is purely a design choice, trading off between execution speed and risk of any changes causing test failures on the runners.
- Within your git version controlled rust project directory, create a
pre-commit
file in the.git/hooks/
directory:
touch .git/hooks/pre-commit
- Open the file, and add the following content:
#!/bin/sh
set -eu
# formatting variables
GREEN='\033[0;32m'
RED='\033[0;31m'
NC='\033[0m' # No Color
BOLD=$(tput bold)
NORM=$(tput sgr0)
echo "Running pre-commit checks..."
# cargo check hook
if ! cargo check --workspace
then
echo -e "cargo check: ......... ${RED}nok${NC}"
echo -e "${RED}Pre-commit: Issues detected when calling 'cargo check'."
exit 1
fi
echo -e "cargo check: ......... ${GREEN}ok${NC}"
# cargo rustfmt hook
if ! cargo fmt -- --check
then
echo -e "cargo rustfmt: ....... ${RED}nok${NC}"
echo -e "${RED}Pre-commit: Code style issues detected with rustfmt."
exit 1
fi
echo -e "cargo rustfmt: ....... ${GREEN}ok${NC}"
# cargo clippy hook
if ! cargo clippy --all-targets -- -D warnings
then
echo -e "cargo clippy: ........ ${RED}nok${NC}"
echo -e "${RED}Pre-commit: Issues detected by clippy."
exit 1
fi
echo -e "cargo clippy: ........ ${GREEN}ok${NC}"
# cargo test hook
if ! cargo test
then
echo -e "cargo test: .......... ${RED}nok${NC}"
echo -e "${RED}Pre-commit: Issues were detected when running the test suite."
exit 1
fi
echo -e "cargo test: .......... ${GREEN}ok${NC}"
echo -e "\n${GREEN}${BOLD}Success: ${NC}${NORM}All pre-commit checks passed ✅\n"
exit 0
#!/bin/sh
set -eu
# formatting variables
GREEN='\033[0;32m'
RED='\033[0;31m'
NC='\033[0m' # No Color
BOLD=$(tput bold)
NORM=$(tput sgr0)
echo "Running pre-commit checks..."
# cargo check hook
if ! cargo check --workspace
then
echo -e "cargo check: ......... ${RED}nok${NC}"
echo -e "${RED}Pre-commit: Issues detected when calling 'cargo check'."
exit 1
fi
echo -e "cargo check: ......... ${GREEN}ok${NC}"
# cargo rustfmt hook
if ! cargo fmt -- --check
then
echo -e "cargo rustfmt: ....... ${RED}nok${NC}"
echo -e "${RED}Pre-commit: Code style issues detected with rustfmt."
exit 1
fi
echo -e "cargo rustfmt: ....... ${GREEN}ok${NC}"
# cargo clippy hook
if ! cargo clippy --all-targets -- -D warnings
then
echo -e "cargo clippy: ........ ${RED}nok${NC}"
echo -e "${RED}Pre-commit: Issues detected by clippy."
exit 1
fi
echo -e "cargo clippy: ........ ${GREEN}ok${NC}"
echo -e "\n${GREEN}${BOLD}Success: ${NC}${NORM}All pre-commit checks passed ✅\n"
exit 0
The above code in step 2 is inspired by a solution posted in a deaddabe blog. This is built on here by adding additional hooks and CLI formatting.
- Make this file executable:
chmod +x .git/hooks/pre-commit
- Check the pre-commit hooks are working by calling:
bash .git/hooks/pre-commit
- Enjoy using rust with pre-commits 🥳