From 288c1df19e687896923c183ecea2a96cb6f3b4fb Mon Sep 17 00:00:00 2001 From: Dhruv Vaishnav Date: Tue, 11 Aug 2026 18:20:31 +0530 Subject: [PATCH] fix: integrate git hooks with pre-commit Copying commit-msg and pre-push directly into .git/hooks can replace hooks that contributors already use. Configure both stages through pre-commit and install its dispatcher from the existing task. Pre-commit preserves prior hooks as legacy hooks, runs them alongside Gitlance, and keeps repeated installation idempotent. Reuse the commit-msg hook and the --not-on-remotes mode added by #12 and #13. Pre-commit passes the prepared message file to the existing hook. For pre-push, use a small adapter to select the pushed ref from PRE_COMMIT_TO_REF or PRE_COMMIT_LOCAL_BRANCH. This validates a branch that is not checked out and skips non-branch refs without changing the Gitlance CLI. Document the local setup and uninstall workflow. Closes: #9 Signed-off-by: Dhruv Vaishnav --- .pre-commit-config.yaml | 21 +++++++++++++++++++++ CONTRIBUTING.md | 15 +++++++++++++++ Taskfile.yml | 17 ++++++++--------- githooks/pre_commit_pre_push.py | 24 ++++++++++++++++++++++++ 4 files changed, 68 insertions(+), 9 deletions(-) create mode 100644 .pre-commit-config.yaml create mode 100644 githooks/pre_commit_pre_push.py diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..f59457a --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,21 @@ +# SPDX-FileCopyrightText: Canonical Ltd. +# +# SPDX-License-Identifier: Apache-2.0 + +default_install_hook_types: [commit-msg, pre-push] + +repos: + - repo: local + hooks: + - id: gitlance-commit-msg + name: Validate commit message with gitlance + entry: bash githooks/commit-msg + language: system + stages: [commit-msg] + - id: gitlance-pre-push + name: Validate commits with gitlance + entry: python githooks/pre_commit_pre_push.py + language: python + stages: [pre-push] + always_run: true + pass_filenames: false diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9deb64c..a48cfa7 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -17,6 +17,21 @@ Contributions are welcome! Please follow these guidelines. 5. Add `Signed-off-by` trailer to commits 6. Open a pull request +### Local commit validation + +Gitlance can validate commit messages and pushed commits locally. Install +[pre-commit](https://pre-commit.com/#install), install the local Gitlance +binary, and register both hooks: + +```console +cargo install --path . --locked +task install-githooks +``` + +The installation is idempotent. If a `commit-msg` or `pre-push` hook already +exists, pre-commit preserves it as a legacy hook and runs it alongside +Gitlance. Running `pre-commit uninstall` restores the previous hooks. + ## Commit Requirements - Use conventional format: `feat:`, `fix:`, `docs:`, etc. diff --git a/Taskfile.yml b/Taskfile.yml index 0da335a..addd074 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -21,15 +21,14 @@ tasks: install-githooks: desc: "Install git hooks for commit validation" - vars: - HOOKS_DIR: - sh: git rev-parse --git-path hooks - cmds: - - cp githooks/pre-push "{{.HOOKS_DIR}}/" - - chmod +x "{{.HOOKS_DIR}}/pre-push" - - cp githooks/commit-msg "{{.HOOKS_DIR}}/" - - chmod +x "{{.HOOKS_DIR}}/commit-msg" - - echo "✓ Git hooks installed to {{.HOOKS_DIR}}" + preconditions: + - sh: pre-commit --version + msg: "pre-commit is required: https://pre-commit.com/#install" + - sh: gitlance --help + msg: "gitlance must be installed and available in PATH: cargo install --path . --locked" + cmds: + - pre-commit install + - echo "✓ Git hooks installed with pre-commit" setup: desc: "Setup Rust toolchain and development environment" diff --git a/githooks/pre_commit_pre_push.py b/githooks/pre_commit_pre_push.py new file mode 100644 index 0000000..7bd04fe --- /dev/null +++ b/githooks/pre_commit_pre_push.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python3 + +# SPDX-FileCopyrightText: Canonical Ltd. +# +# SPDX-License-Identifier: Apache-2.0 + +import os +import subprocess +import sys + + +def main() -> int: + local_branch = os.environ.get("PRE_COMMIT_LOCAL_BRANCH", "HEAD") + if local_branch != "HEAD" and not local_branch.startswith("refs/heads/"): + return 0 + + head = os.environ.get("PRE_COMMIT_TO_REF") or local_branch + return subprocess.call( + ["gitlance", "--head", head, "--not-on-remotes"], + ) + + +if __name__ == "__main__": + sys.exit(main())