Pre-commit git hooks
Git hooks to integrate with pre-commit.
Configure pre-commit
Add to .pre-commit-config.yaml
in your git repo:
- repo: https://github.com/jumanjihouse/pre-commit-hooks
rev: master # or specific git tag
hooks:
- id: bundler-audit
- id: check-mailmap
- id: fasterer
- id: forbid-binary
- id: forbid-space-in-indent
- id: git-check # Configure in .gitattributes
- id: git-dirty # Configure in .gitignore
- id: markdownlint # Configure in .mdlrc
- id: reek
- id: require-ascii
- id: rubocop
- id: script-must-have-extension
- id: script-must-not-have-extension
- id: shellcheck
- id: shfmt
Two ways to invoke pre-commit
If you want to invoke the checks as a git pre-commit hook, run:
pre-commit install
If you want to run the checks on-demand (outside of git hooks), run:
pre-commit run --all-files --verbose
The test harness of this git repo uses the second approach to run the checks on-demand.
Available hooks
bundler-audit
What it does
- Checks for vulnerable versions of gems in
Gemfile.lock
. - Checks for insecure gem sources (
http://
). - Allows ignoring certain advisories that have been manually worked around.
- Prints advisory information.
More info
See https://github.com/rubysec/bundler-audit for details.
check-mailmap
What it does
Detect botched name/email translations in git history.
git shortlog -sn
is useful to summarize contributors.
However, it gets muddy when an email address is associated with multiple names.
Reasons include:
- the author's full name was messed up
- not always written the same way
- the author has multiple email addresses
More info
Sample output for good condition:
$ pre-commit run check-mailmap --all-files --verbose
[check-mailmap] Detect if an email address needs to be added to mailmap.......................Passed
Sample output for bad condition:
$ pre-commit run check-mailmap --all-files --verbose
[check-mailmap] Detect if an email address needs to be added to mailmap.......................Failed
hookid: check-mailmap
The following email addresses are associated with more than one name:
[email protected]
[email protected]
The associations include:
2 Billy Bob <[email protected]>
2 Bubba <[email protected]>
13 John Doe <[email protected]>
4 jdoe <[email protected]>
fasterer
What it does
Suggest ways to improve speed of Ruby code.
More info
fasterer
suggests speed improvements that you can check in detail at the
fast-ruby repo.
Note: You should not follow the suggestions blindly.
forbid-binary
What it does
Prevent binary files from being committed.
More info
Fail if a file appears to be a binary filetype.
Override with an exclude
regular expression,
such as the example here.
forbid-space-in-indent
What it does
Prevent files with spaces within indentation from being committed.
More info
Fail if a file contains spaces within indentation.
Override with an exclude
regular expression,
such as the example here.
git-check
What it does
Check both committed and uncommitted files for git conflict markers and
whitespace errors according to core.whitespace
and conflict-marker-size
configuration in a git repo.
More info
This hook uses git
itself to perform the checks.
The git-scm book describes
here
that there are six core.whitespace
checks.
Enabled by default:
blank-at-eol
, which looks for spaces at the end of a lineblank-at-eof
, which looks for blank lines at the end of a filespace-before-tab
, which looks for spaces before tabs at the beginning of a line
Disabled by default:
indent-with-non-tab
, which looks for lines that begin with spaces instead of tabs (and is controlled by thetabwidth
option)tab-in-indent
, which looks for tabs in the indentation portion of a linecr-at-eol
, which looks for carriage returns at the end of a line
Custom configuration (overrides)
The git documentation describes here how to configure the various checks.
The recommended place to persist the configuration is the .gitattributes
file,
described here.
It provides fine control over configuration per file path for both
core.whitespace
and conflict-marker-size
.
Real-world examples of .gitattributes
file to configure overrides per path:
git-dirty
What it does
During the pre-commit stage, do nothing.
Otherwise, detect whether the git tree contains modified, staged, or untracked files.
More info
This is useful to run near the end of a CI process to see if a build step has modified the git tree in unexpected ways.
Custom configuration (overrides)
The recommended place to persist the configuration is the .gitignore
file,
described here.
markdownlint
What it does
Check markdown files and flag style issues.
More info
markdownlint is a ruby tool that examines markdown files against various style rules.
Custom configuration (overrides)
Provide .mdlrc
in the top-level of your project git repo.
For an annotated example of overrides, see in this project:
protect-first-parent
What it does
Helps to ensure the first-parent sequence of commits on the default branch is a true record of commits.
This protection is probably best done as a pre-receive hook. However, central git repos like GitHub, GitLab, and so forth do not allow users to configure server-side hooks.
This client-side hook fills the gap to help prevent foxtrot merges.
More info
- https://en.it1352.com/article/b9ff488428bd49d39f338d421bd1b8f9.html
- https://bit-booster.blogspot.com/2016/02/no-foxtrots-allowed.html
- https://devblog.nestoria.com/post/98892582763/maintaining-a-consistent-linear-history-for-git
- https://dev.to/etcwilde/merge-trees-visualizing-git-repositories
- https://pdfs.semanticscholar.org/a0e2/e630fc7b5bcf9e86c424a2551d0b76aec53a.pdf
reek
What it does
Detect code smells in Ruby code.
More info
Reek is a tool that examines Ruby classes, modules and methods and reports any Code Smells it finds.
For an excellent introduction to Code Smells and Reek check out this blog post or that one. There is also this talk from RubyConfBY (there is also a slide deck if you prefer that).
Note: Do not follow the suggestions blindly.
This hook uses the identify
library of pre-commit to identify ruby scripts.
If the file is a ruby script, then run reek against the file.
Custom configuration (overrides)
The recommended place to persist the configuration is the .reek
file,
described here.
You can also create in-line comments in the source code for individual overrides.
require-ascii
What it does
Requires that text files have ascii-encoding, including the extended ascii set. This is useful to detect files that have unicode characters.
Custom configuration (overrides)
Use the built-in overrides from the pre-commit framework.
rubocop
What it does
RuboCop is a Ruby static code analyzer. Out of the box it enforces many of the guidelines outlined in the community Ruby Style Guide.
More info
This hook uses the identify
library of pre-commit to identify ruby scripts.
If the file is a ruby script, then run rubocop against the file.
Additionally, run rubocop-rspec against rspec files.
Custom configuration (overrides)
Most aspects of rubocop behavior can be tweaked via various configuration options.
Rubocop-performance is documented here.
Rubocop-rspec is documented here.
script-must-have-extension
What it does
The Google shell style guide states:
Libraries must have a
.sh
extension and should not be executable.
This hook checks for conformance.
Default
Filter on files that are both shell
and non-executable
.
types: [shell, non-executable]
Custom configuration (overrides)
Suppose your local style guide is the opposite of the default.
In other words, you require executable scripts to end with .sh
.
Put this in your .pre-commit-config.yaml
:
- repo: https://github.com/jumanjihouse/pre-commit-hooks
rev: <version>
hooks:
- id: script-must-have-extension
name: Local policy is to use .sh extension for shell scripts
types: [shell, executable]
Note the use of "name" to override the hook's default name and provide context for the override.
script-must-not-have-extension
What it does
The Google shell style guide states:
Executables should have no extension (strongly preferred)
This hook checks for conformance.
Default
Filter on files that are both shell
and executable
.
types: [shell, executable]
Custom configuration (overrides)
You can use this hook to forbid filename extensions on other types of files.
Put something like this in your .pre-commit-config.yaml
:
- repo: https://github.com/jumanjihouse/pre-commit-hooks
rev: <version>
hooks:
- id: script-must-not-have-extension
name: Local policy is to exclude extension from all shell files
types: [shell]
- id: script-must-not-have-extension
name: Executable Ruby scripts must not have a file extension
types: [ruby, executable]
Note the use of "name" to override the hook's default name and provide context for the override.
shellcheck
What it does
Run shellcheck against scripts.
More info
This hook uses the identify
library of pre-commit to identify shell scripts.
If the file is a shell script, then run shellcheck against the file.
By default, this hooks passes -e SC1091
to shellcheck.
Override locally with the args
parameter in .pre-commit-config.yaml
.
shellcheck
hook requires
shellcheck.
shfmt
What it does
Run shfmt -w
against scripts with args.
More info
This hook uses the identify
library of pre-commit to identify shell scripts.
If the file is a shell script, then run shfmt against the file.
Override locally with .editorconfig
.
shfmt
hook requires a recent version of
shfmt.
Contributing
Please see CONTRIBUTING.md.
Testing
Please see TESTING.md.
License
The code in this repo is licensed under the MIT License.