[pre-commit] Add codespell-log commit-msg hook

Now that we're using codespell to check spelling in gdb files, can we use
codespell to bring this spelling warning:
...
$ echo usuable | codespell -
1: usuable
	usuable ==> usable
...
to:
...
$ git commit -a -m "Usuable stuff"
...
?

First, let's look at a straightforward commit-msg hook implementation:
...
    - id: codespell
      name: codespell-commit-msg
      verbose: true
      always_run: true
      stages: [commit-msg]
...
installed using:
...
$ pre-commit install -t commit-msg
...

When trying the commit, we get:
...
$ echo "/* bla */" >> gdb/gdb.c
$ git commit -a -m "Usuable stuff"
black................................................(no files to check)Skipped
flake8...............................................(no files to check)Skipped
isort................................................(no files to check)Skipped
codespell............................................(no files to check)Skipped
check-include-guards.................................(no files to check)Skipped
black................................................(no files to check)Skipped
flake8...............................................(no files to check)Skipped
codespell............................................(no files to check)Skipped
codespell-commit-msg.....................................................Failed
- hook id: codespell
- duration: 0.06s
- exit code: 65

.git/COMMIT_EDITMSG:1: Usuable ==> Usable

check-include-guards.................................(no files to check)Skipped
$
...

The commit was aborted, but the commit message is still there:
...
$ cat .git/COMMIT_EDITMSG
Usuable stuff
...

We can retry and edit the commit message to clean up the typo:
...
$ git commit -e -F .git/COMMIT_EDITMSG -a
...
but it's a bit cumbersome.

Furthermore, say we fix a typo and want to document this in the commit log, and
do:
...
$ git commit -m "Fixed typo: useable -> usable" -a
...

This commit cannot succeed, unless we add a codespell ignore tag, which feels
like taking it too far.

Both these problems can be addressed by setting things up in such a way that
the commit always succeeds, and codespell output is shown as a hint.

Ideally, we'd tell to pre-commit to implement this using some setting, but
there doesn't seem to be one.

So we use some indirection.  Instead of using native codespell, use a local
hook that calls a script gdb/contrib/codespell-log.sh, which calls pre-commit,
which calls codespell.

Using this approach, we get:
...
$ echo "/* bla */" >> gdb/gdb.c
$ git commit -a -m "Usuable stuff"
black................................................(no files to check)Skipped
flake8...............................................(no files to check)Skipped
isort................................................(no files to check)Skipped
codespell............................................(no files to check)Skipped
check-include-guards.................................(no files to check)Skipped
black................................................(no files to check)Skipped
flake8...............................................(no files to check)Skipped
codespell............................................(no files to check)Skipped
check-include-guards.................................(no files to check)Skipped
codespell-log............................................................Passed
- hook id: codespell-log
- duration: 0.18s

codespell-log-internal...................................................Failed
- hook id: codespell
- exit code: 65

.git/COMMIT_EDITMSG:1: Usuable ==> Usable

[codespell/codespell-log-2 d081bd25a40] Usuable stuff
 1 file changed, 1 insertion(+)
$
...

This is obviously convoluted, but it works.  Perhaps we can propose a
pre-commit improvement (always_pass) and simplify this eventually.

Checked new script codespell-log.sh with shell-check.

Approved-By: Simon Marchi <simon.marchi@efficios.com>
This commit is contained in:
Tom de Vries
2025-04-25 19:22:36 +02:00
parent a1537331ab
commit 129228c8b0
2 changed files with 103 additions and 0 deletions

View File

@@ -38,6 +38,7 @@
# See https://pre-commit.com/hooks.html for more hooks
minimum_pre_commit_version: 3.2.0
default_install_hook_types: [pre-commit, commit-msg]
repos:
- repo: https://github.com/psf/black-pre-commit-mirror
rev: 25.1.0
@@ -80,3 +81,10 @@ repos:
# All gdb header files, but not headers in the test suite.
files: '^(gdb(support|server)?)/.*\.h$'
exclude: '.*/testsuite/.*'
- id: codespell-log
name: codespell-log
language: script
entry: gdb/contrib/codespell-log.sh
verbose: true
always_run: true
stages: [commit-msg]

95
gdb/contrib/codespell-log.sh Executable file
View File

@@ -0,0 +1,95 @@
#!/usr/bin/env bash
# Copyright (C) 2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# Script to be used as pre-commit commit-msg hook to spell-check the commit
# log using codespell.
#
# Using codespell directly as a pre-commit commit-msg hook has the drawback
# that:
# - if codespell fails, the commit fails
# - if the commit log mentions a typo correction, it'll require a
# codespell:ignore annotation.
#
# This script works around these problems by treating codespell output as a
# hint, and ignoring codespell exit status.
#
# Implementation note: rather than using codespell directly, this script uses
# pre-commit to call codespell, because it allows us to control the codespell
# version that is used.
# Exit on error.
set -e
# Initialize temporary file names.
cfg=""
output=""
cleanup()
{
for f in "$cfg" "$output"; do
if [ "$f" != "" ]; then
rm -f "$f"
fi
done
}
# Schedule cleanup.
trap cleanup EXIT
# Create temporary files.
cfg=$(mktemp)
output=$(mktemp)
gen_cfg ()
{
cat > "$1" <<EOF
repos:
- repo: https://github.com/codespell-project/codespell
rev: v2.4.1
hooks:
- id: codespell
name: codespell-log-internal
stages: [manual]
args: [--config, gdb/contrib/setup.cfg]
EOF
}
# Generate pre-commit configuration file.
gen_cfg "$cfg"
# Setup pre-commit command to run.
cmd=(pre-commit \
run \
-c "$cfg" \
codespell \
--hook-stage manual \
--files "$@")
# Run pre-commit command.
if "${cmd[@]}" \
> "$output" \
2>&1; then
# Command succeeded quietly, we're done.
exit 0
fi
# Command failed quietly, now show the output.
#
# Simply doing "cat $output" doesn't produce colored output, so we just
# run the command again, that should be fast enough.
#
# Ignore codespell exit status.
"${cmd[@]}" || true