Git Hooks are shell scripts integrated to your git directory. They are triggered before: pre- or after: post- some actions, commit, push, checkout and run locally. The most useful function is pre-commit which can check your code syntax, lint to improve the code quality, or trigger any local test.

Hooks are mostly always the same, users share them on github and pre-commit tool automates hooks deployment.

$ pip install pre-commit

Deploy a hook

Create a test directory and git it.

$ mkdir git-commit-test && cd git-commit-test

$ git init
Initialized empty Git repository in /home/user/dev/git-commit-test/.git/

Choose hooks projects you want to use in pre-commit hooks list.
For this exemple, we will use yaml and shell checkers.

Pre-commit configuration file is .pre-commit-config.yaml, create it at the project root.

repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
  rev: v4.0.1
  hooks:
    - id: check-yaml
    - id: trailing-whitespace
- repo: https://github.com/shellcheck-py/shellcheck-py
  rev: v0.7.2.1
  hooks:
    - id: shellcheck

To init hook deployment, use install subcommand.

$ pre-commit install
pre-commit installed at .git/hooks/pre-commit

Test it

Let’s add a shell script to the project and a yaml file with some errors.

$ cat << EOF > test.sh
#!/bin/bash
var=1
echo $var
EOF

$ cat << EOF > test.yml
- key1
  key2:
EOF

Add those files to the index, pre-commit defaulty ignore files outside the index.

$ git add .

$ git status
On branch main

No commits yet

Changes to be committed:
  (use "git rm --cached <file>..." to unstage)
	new file:   .pre-commit-config.yaml
	new file:   test.sh
	new file:   test.yml

And just commit to fire your hooks !

$ git commit -m "added a great shell script and his yaml file"
Check Yaml...............................................................Failed
- hook id: check-yaml
- exit code: 1

mapping values are not allowed in this context
  in "test.yml", line 2, column 6

Trim Trailing Whitespace.................................................Failed
- hook id: trailing-whitespace
- exit code: 1
- files were modified by this hook

Fixing test.sh

shellcheck...............................................................Failed
- hook id: shellcheck
- exit code: 1

In test.sh line 2:
var=1
^-^ SC2034: var appears unused. Verify use (or export if used externally).

For more information:
  https://www.shellcheck.net/wiki/SC2034 -- var appears unused. Verify use (o...

As pre-commit hooks failed, the commit was not created.

$ git log
fatal: your current branch 'main' does not have any commits yet

Trailing whitespace hooks automatically fixed his errors.
If you fix your shell script and yaml file, your commit will be ok.

$ sed -i 's/$var/\"$var\"/' test.sh

$ sed -i 's/key1/key1:/' test.yml

$ git add test.*

$ git commit -m "first sane commit"
Check Yaml...............................................................Passed
Trim Trailing Whitespace.................................................Passed
shellcheck...............................................................Passed
[main (root-commit) c689536] first sane commit
 3 files changed, 15 insertions(+)
 create mode 100644 .pre-commit-config.yaml
 create mode 100644 test.sh
 create mode 100644 test.yml

Custom Ansible Hook

I wanted to be able to check ansible syntax before.

- repo: local
  hooks:
    - id: ansible-syntax-check
      name: Ansible syntax check
      entry: "ansible-playbook --syntax-check playbook.yml"
      pass_filenames: no
      types_or: [yaml, jinja]
      language: system

Create the playbook.

cat << EOF > playbook.yml
- hosts: test
  tasks:
    - name: copy test.sh
      copyy:
        src: ./test.sh
        dest: dir
EOF

Then try to commit it.

$ git add .
$ git commit -m "added an insane playbook"
[user@osz git-commit-test]$ git commit -m "added an insane playbook"
Check Yaml...............................................................Passed
Trim Trailing Whitespace.................................................Passed
shellcheck...........................................(no files to check)Skipped
Ansible syntax check.....................................................Failed
- hook id: ansible-syntax-check
- exit code: 4

[WARNING]: No inventory was parsed, only implicit localhost is available
[WARNING]: provided hosts list is empty, only localhost is available. Note that
the implicit localhost does not match 'all'
ERROR! couldn't resolve module/action 'copyy'. This often indicates a misspelling, missing collection, or incorrect module path.

The error appears to be in '/home/user/dev/git-commit-test/playbook.yml': line 3, column 7, but may
be elsewhere in the file depending on the exact syntax problem.

The offending line appears to be:

  tasks:
    - name: copy test.sh
      ^ here

As no shell script was in the index, shellcheck was skipped, but pre-commit detected our mispelled copy ansible module.