Source code for governance.hooks
"""
Script
------
hooks.py
Path
----
python/hillstar/governance/hooks.py
Purpose
-------
Git hook management: install, remove, and verify pre-commit hooks that
enforce Hillstar workflow execution before allowing commits.
Inputs
------
- project_dir: path to the git repository root
Outputs
-------
- .git/hooks/pre-commit script that calls `hillstar enforce check`
Assumptions
-----------
- Git repository exists at project_dir
- hillstar CLI is on PATH
Parameters
----------
- project_dir: str
Failure Modes
-------------
- .git/hooks/ does not exist: not a git repo
- pre-commit hook already exists: prompts before overwriting
Author: Julen Gamboa <julen.gamboa.ds@gmail.com>
Created
-------
2026-02-08
Last Edited
-----------
2026-02-08
"""
from __future__ import annotations
import os
import stat
PRE_COMMIT_TEMPLATE = """\
#!/usr/bin/env bash
# Hillstar governance pre-commit hook
# Auto-installed by: hillstar enforce install
# DO NOT EDIT — managed by Hillstar governance module
set -euo pipefail
# Check if development mode is active
DEV_MODE_FLAG=""
if [[ "${HILLSTAR_DEV_MODE:-0}" == "1" ]]; then
DEV_MODE_FLAG="--dev"
fi
# Allow bypass with env var
if [[ "${HILLSTAR_FORCE_COMMIT:-0}" == "1" ]]; then
echo "[hillstar] Force commit override active. Skipping governance check."
exit 0
fi
# Check if hillstar is available
if ! command -v hillstar &> /dev/null; then
echo "[hillstar] WARNING: hillstar not found on PATH, skipping governance check."
exit 0
fi
# Run governance check (with --dev flag if HILLSTAR_DEV_MODE=1)
echo "[hillstar] Checking workflow execution compliance..."
if hillstar enforce check $DEV_MODE_FLAG; then
echo "[hillstar] Governance check passed."
exit 0
else
echo ""
echo "[hillstar] Commit blocked: no recent Hillstar workflow execution found."
echo "[hillstar] Run: hillstar execute <workflow.json>"
echo "[hillstar] Or use development mode: HILLSTAR_DEV_MODE=1 git commit ..."
exit 1
fi
"""
[docs]
class HookManager:
"""Manage git hooks for Hillstar governance enforcement."""
[docs]
def __init__(self, project_dir: str = "."):
self.project_dir = os.path.abspath(project_dir)
self._hooks_dir = os.path.join(self.project_dir, ".git", "hooks")
self._hook_path = os.path.join(self._hooks_dir, "pre-commit")
[docs]
def is_git_repo(self) -> bool:
"""Check if project_dir is a git repository."""
return os.path.isdir(os.path.join(self.project_dir, ".git"))
[docs]
def is_installed(self) -> bool:
"""Check if the Hillstar pre-commit hook is installed."""
if not os.path.exists(self._hook_path):
return False
with open(self._hook_path, encoding="utf-8") as f:
return "Hillstar governance" in f.read()
[docs]
def install(self, force: bool = False) -> tuple[bool, str]:
"""
Install the pre-commit hook.
Args:
force: Overwrite existing hook without prompting.
Returns:
(success, message)
"""
if not self.is_git_repo():
return False, f"Not a git repository: {self.project_dir}"
if not os.path.isdir(self._hooks_dir):
os.makedirs(self._hooks_dir, exist_ok=True)
if os.path.exists(self._hook_path) and not force:
with open(self._hook_path, encoding="utf-8") as f:
existing = f.read()
if "Hillstar governance" in existing:
return True, "Hook already installed"
return False, (
f"A pre-commit hook already exists at {self._hook_path}. "
"Use --force to overwrite."
)
with open(self._hook_path, "w", encoding="utf-8") as f:
f.write(PRE_COMMIT_TEMPLATE)
# Make executable
current = os.stat(self._hook_path).st_mode
os.chmod(self._hook_path, current | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)
return True, f"Pre-commit hook installed at {self._hook_path}"
[docs]
def uninstall(self) -> tuple[bool, str]:
"""Remove the Hillstar pre-commit hook."""
if not os.path.exists(self._hook_path):
return True, "No hook to remove"
with open(self._hook_path, encoding="utf-8") as f:
content = f.read()
if "Hillstar governance" not in content:
return False, "Existing hook was not installed by Hillstar — not removing"
os.remove(self._hook_path)
return True, f"Pre-commit hook removed from {self._hook_path}"
[docs]
def status(self) -> dict:
"""Return hook installation status."""
return {
"is_git_repo": self.is_git_repo(),
"hook_path": self._hook_path,
"is_installed": self.is_installed(),
}