/pre-commit-hooks

git pre-commit hooks that work with http://pre-commit.com/

Primary LanguageShellMIT LicenseMIT

Pre-commit git hooks

Git hooks to integrate with pre-commit.

Configure pre-commit

⚠️ These hooks now require Python3.

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:

        billy.bob@example.com
        jdoe@example.com

The associations include:

      2 Billy Bob <billy.bob@example.com>
      2 Bubba <billy.bob@example.com>

     13 John Doe <jdoe@example.com>
      4 jdoe <jdoe@example.com>

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 line
  • blank-at-eof, which looks for blank lines at the end of a file
  • space-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 the tabwidth option)
  • tab-in-indent, which looks for tabs in the indentation portion of a line
  • cr-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

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.

⚠️ The 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.

⚠️ The 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.