Git Hooks That Enforce Ansible Quality

Configuring Git hooks to enforce Ansible best practices gives infrastructure teams feedback before changes reach a shared branch. Catching malformed YAML, inconsistent task names or a missing FQCN while the author has context makes fixes quicker and keeps review focused on design.

The approach below uses pre-commit to check staged files. It suits the mixed macOS and Linux workstations common across Australian IT teams, and the same configuration can run in CI as a consistent baseline.

Why local checks matter for Ansible

Git invokes hooks at defined points in the local workflow. A pre-commit hook runs after files are staged, so it inspects exactly what the next commit will contain. That precision matters when a whitespace fix sits beside a task file with a broken variable name.

Keep the local boundary sensible. Linting, YAML parsing and lightweight policy checks belong here; syntax validation with collections, Molecule scenarios and deployment tests usually belong in CI. Hooks should catch predictable mistakes in seconds, not duplicate a test platform.

Choose checks that match your risk

Start with a small, fast baseline:

A heavier tier can be reserved for CI:

The split keeps routine work quick. Agree on non-negotiable rules, then tune exclusions for encrypted vault files, templates and older technical debt. Useful output keeps the hook in use; noise encourages bypass.

Install a practical pre-commit workflow

Install the tools in isolated environments. On macOS and Linux, pipx keeps executables on PATH without changing the system Python:

python3 -m pipx install pre-commit
python3 -m pipx install ansible-lint
python3 -m pipx install yamllint

Create .pre-commit-config.yaml. Local hooks keep the setup independent of a public hook repository:

repos:
  - repo: local
    hooks:
      - id: ansible-lint
        name: Ansible lint
        entry: ansible-lint
        language: system
        types: [yaml]
        files: ^(playbooks|roles|inventory|group_vars|host_vars)/.*\.ya?ml$
      - id: yamllint
        name: YAML style
        entry: yamllint --strict -c .yamllint.yml
        language: system
        types: [yaml]
        files: ^(playbooks|roles|inventory|group_vars|host_vars)/.*\.ya?ml$

Adjust the path expression to match the repository and add .yamllint.yml before enabling style checks.

Install the hook and establish a baseline:

pre-commit install
pre-commit run --all-files

pre-commit install creates the local hook in each clone. The first all-files run may expose existing problems, while normal commits check only staged, matching files.

Keep rules consistent across repositories

Keep .pre-commit-config.yaml, .yamllint.yml and any Ansible lint profile in version control. CI can then run pre-commit run --all-files with the same rules. Avoid a separate CI lint list; drift soon produces a local pass followed by a pipeline failure.

The generated .git/hooks directory is not cloned. Add a bootstrap step that installs pinned tools and runs pre-commit install for every workstation. If you use a custom core.hooksPath, include it in onboarding and confirm execute permission.

Ansible often manages VMware inventory and guest operations alongside ordinary compute. Where that applies, the site's free PowerCLI tools can supply reusable commands, while the hook protects the YAML and inventory definitions that feed those systems.

Fit checks into Australian delivery teams

Australian teams are often spread across Sydney, Melbourne, Brisbane and Perth, spanning the mainland's three work time zones. A quick hook gives an engineer in Perth the same deterministic result as one in Sydney, without waiting for a colleague to finish a review.

Keep feedback in the familiar Australian register: “Fair dinkum, that role has three lint errors” or “No worries, I'll sort it before arvo” is clear and friendly. The hook should state the rule and file, not shame the author for a simple mistake.

Australian banking, telecom and government programmes often require formal change evidence and audit trails. Environments around ANZ, CBA, Telstra and Optus show why stable lint results matter: a commit may feed a controlled change window. For background on the practitioner behind this site, see Jonathan Frappier's profile.

Troubleshoot failures without lowering standards

If the hook does nothing, run pre-commit install in that clone and check git config --get core.hooksPath. Git does not copy .git/hooks when a repository is cloned, so a fresh workstation can otherwise bypass every local control.

For a missing command, run pipx list and confirm the tool's executable directory is on PATH. pipx ensurepath can repair a common setup issue, after which the shell must be reopened. Pin tested tool versions in the bootstrap process to avoid surprise rule changes.

False positives usually come from checking files that are not Ansible content, encrypted vault files or repository-wide policy applied to a staged subset. Narrow the files expression, add precise exclude patterns and keep genuine exceptions documented. Use SKIP=ansible-lint only for a known, time-bound bypass; CI still runs the full check.

Make the first clean commit count

Roll out the hook in one repository, resolve a manageable baseline and make it a required local check. Australian delivery calendars often slow around Christmas and Easter, so installing the control before a handover or release freeze avoids ad hoc lint cleanup during an already busy period.

Create the configuration today, run pre-commit install, and make the next commit pass every local check. A clean run then becomes a reliable signal for reviewers, release managers and the engineer waiting in another time zone.