feat: add shared git hooks with setup script
Git hooks were only in .git/hooks/ which isn't tracked by git. Created a shared githooks/ directory so all contributors get the pre-commit hook that enforces SDLC requirements. Changes: - githooks/pre-commit: Pre-commit hook enforcing SDLC (lint, tests, docs) - scripts/setup-githooks.sh: Script to configure git core.hooksPath - README.md: Added first-time setup instructions - AGENTS.md: Updated startup steps and project structure Users/agents run ./scripts/setup-githooks.sh after cloning to enable hooks. Reference: docs/SDLC.md 💘 Generated with Crush Assisted-by: GLM-5 via Crush <crush@charm.land>
This commit is contained in:
@@ -119,6 +119,8 @@ A pre-commit hook automatically enforces SDLC requirements:
|
|||||||
└── security-model.md
|
└── security-model.md
|
||||||
|
|
||||||
src/ # Source scripts
|
src/ # Source scripts
|
||||||
|
scripts/ # Utility scripts (setup-githooks.sh)
|
||||||
|
githooks/ # Shared git hooks (pre-commit)
|
||||||
config/ # Configuration files
|
config/ # Configuration files
|
||||||
├── includes.installer/ # Installer configs (preseed.cfg)
|
├── includes.installer/ # Installer configs (preseed.cfg)
|
||||||
├── hooks/live/ # Live system hooks
|
├── hooks/live/ # Live system hooks
|
||||||
@@ -135,6 +137,9 @@ output/ # Build artifacts
|
|||||||
|
|
||||||
### 1. Start Up
|
### 1. Start Up
|
||||||
```bash
|
```bash
|
||||||
|
# Configure git hooks (if not already done)
|
||||||
|
./scripts/setup-githooks.sh
|
||||||
|
|
||||||
# Check current state
|
# Check current state
|
||||||
ls -lh output/
|
ls -lh output/
|
||||||
git log --oneline -10
|
git log --oneline -10
|
||||||
|
|||||||
@@ -94,6 +94,12 @@ tail -f /tmp/knel-iso-build.log
|
|||||||
ls -lh output/
|
ls -lh output/
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### First-Time Setup (After Cloning)
|
||||||
|
```bash
|
||||||
|
# Configure git hooks (required for SDLC enforcement)
|
||||||
|
./scripts/setup-githooks.sh
|
||||||
|
```
|
||||||
|
|
||||||
### SDLC Workflow (MANDATORY)
|
### SDLC Workflow (MANDATORY)
|
||||||
```bash
|
```bash
|
||||||
# After ANY changes:
|
# After ANY changes:
|
||||||
|
|||||||
186
githooks/pre-commit
Executable file
186
githooks/pre-commit
Executable file
@@ -0,0 +1,186 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
#
|
||||||
|
# KNEL-Football Secure OS - Pre-Commit Hook
|
||||||
|
# Enforces SDLC.md requirements automatically
|
||||||
|
#
|
||||||
|
# This hook runs BEFORE every commit and ensures:
|
||||||
|
# 1. All tests pass
|
||||||
|
# 2. Zero lint warnings
|
||||||
|
# 3. Tests exist for modified code
|
||||||
|
# 4. Documentation is updated for changes
|
||||||
|
#
|
||||||
|
# Reference: docs/SDLC.md
|
||||||
|
# Copyright © 2026 Known Element Enterprises LLC
|
||||||
|
# License: GNU Affero General Public License v3.0 only
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
|
echo -e "${YELLOW}╔══════════════════════════════════════════════════════════════╗${NC}"
|
||||||
|
echo -e "${YELLOW}║ SDLC ENFORCEMENT - Pre-Commit Check ║${NC}"
|
||||||
|
echo -e "${YELLOW}╚══════════════════════════════════════════════════════════════╝${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Track if any check fails
|
||||||
|
FAILED=0
|
||||||
|
|
||||||
|
# Get list of staged files
|
||||||
|
STAGED_FILES=$(git diff --cached --name-only --diff-filter=ACM)
|
||||||
|
STAGED_SHELL_FILES=$(git diff --cached --name-only --diff-filter=ACM | grep -E '\.(sh|bash)$' || true)
|
||||||
|
|
||||||
|
# Skip checks if only documentation changes
|
||||||
|
ONLY_DOCS=1
|
||||||
|
for file in $STAGED_FILES; do
|
||||||
|
if [[ ! "$file" =~ ^docs/ && ! "$file" =~ \.md$ && ! "$file" =~ ^LICENSE ]]; then
|
||||||
|
ONLY_DOCS=0
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ "$ONLY_DOCS" == "1" ]]; then
|
||||||
|
echo -e "${YELLOW}Only documentation changes detected - skipping code checks${NC}"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# CHECK 1: Lint (ShellCheck) - Zero warnings required
|
||||||
|
# =============================================================================
|
||||||
|
echo -e "${YELLOW}[1/4] Running lint checks (shellcheck)...${NC}"
|
||||||
|
|
||||||
|
if [[ -n "$STAGED_SHELL_FILES" ]]; then
|
||||||
|
LINT_OUTPUT=$(./run.sh lint 2>&1) || {
|
||||||
|
echo -e "${RED}✗ LINT FAILED${NC}"
|
||||||
|
echo "$LINT_OUTPUT"
|
||||||
|
echo ""
|
||||||
|
echo -e "${RED}SDLC VIOLATION: Zero lint warnings required${NC}"
|
||||||
|
echo -e "${RED}Reference: docs/SDLC.md - Code Quality Standards${NC}"
|
||||||
|
FAILED=1
|
||||||
|
}
|
||||||
|
|
||||||
|
if [[ $FAILED -eq 0 ]]; then
|
||||||
|
echo -e "${GREEN}✓ Lint passed${NC}"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo -e "${GREEN}✓ No shell files to lint${NC}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# CHECK 2: Unit Tests - All must pass
|
||||||
|
# =============================================================================
|
||||||
|
echo -e "${YELLOW}[2/4] Running unit tests...${NC}"
|
||||||
|
|
||||||
|
TEST_OUTPUT=$(./run.sh test:unit 2>&1) || {
|
||||||
|
echo -e "${RED}✗ UNIT TESTS FAILED${NC}"
|
||||||
|
echo "$TEST_OUTPUT"
|
||||||
|
echo ""
|
||||||
|
echo -e "${RED}SDLC VIOLATION: All tests must pass before commit${NC}"
|
||||||
|
echo -e "${RED}Reference: docs/SDLC.md - TDD Workflow${NC}"
|
||||||
|
FAILED=1
|
||||||
|
}
|
||||||
|
|
||||||
|
if [[ $FAILED -eq 0 ]]; then
|
||||||
|
echo -e "${GREEN}✓ Unit tests passed${NC}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# CHECK 3: Test Coverage - Tests must exist for modified code
|
||||||
|
# =============================================================================
|
||||||
|
echo -e "${YELLOW}[3/4] Checking test coverage for modified files...${NC}"
|
||||||
|
|
||||||
|
MISSING_TESTS=""
|
||||||
|
|
||||||
|
for file in $STAGED_FILES; do
|
||||||
|
# Check if this is a source file that needs tests
|
||||||
|
if [[ "$file" =~ ^src/.*\.sh$ ]]; then
|
||||||
|
basename=$(basename "$file" .sh)
|
||||||
|
test_file="tests/unit/${basename}_test.bats"
|
||||||
|
|
||||||
|
if [[ ! -f "$test_file" ]]; then
|
||||||
|
MISSING_TESTS="$MISSING_TESTS\n - $file -> expected: $test_file"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if this is a config hook that needs tests
|
||||||
|
if [[ "$file" =~ ^config/hooks/.*\.sh$ ]]; then
|
||||||
|
hookname=$(basename "$file" .sh)
|
||||||
|
# Hooks are tested via integration tests
|
||||||
|
if [[ ! -f "tests/integration/config_test.bats" ]]; then
|
||||||
|
MISSING_TESTS="$MISSING_TESTS\n - $file -> integration tests missing"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ -n "$MISSING_TESTS" ]]; then
|
||||||
|
echo -e "${RED}✗ MISSING TEST COVERAGE${NC}"
|
||||||
|
echo -e "The following files lack corresponding tests:"
|
||||||
|
echo -e "$MISSING_TESTS"
|
||||||
|
echo ""
|
||||||
|
echo -e "${RED}SDLC VIOLATION: TDD requires tests for all code${NC}"
|
||||||
|
echo -e "${RED}Reference: docs/SDLC.md - Test-Driven Development${NC}"
|
||||||
|
FAILED=1
|
||||||
|
else
|
||||||
|
echo -e "${GREEN}✓ All modified files have tests${NC}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# CHECK 4: Documentation Sync - PRD updated for new features
|
||||||
|
# =============================================================================
|
||||||
|
echo -e "${YELLOW}[4/4] Checking documentation synchronization...${NC}"
|
||||||
|
|
||||||
|
# Check for new function definitions in staged shell files
|
||||||
|
NEW_FUNCTIONS=""
|
||||||
|
for file in $STAGED_SHELL_FILES; do
|
||||||
|
# Extract function names from staged changes
|
||||||
|
FUNCTIONS=$(git diff --cached "$file" | grep -E '^\+.*\(\)\s*\{' | sed 's/^\+//;s/().*//;s/\s//g' || true)
|
||||||
|
if [[ -n "$FUNCTIONS" ]]; then
|
||||||
|
NEW_FUNCTIONS="$NEW_FUNCTIONS\n $file: $(echo "$FUNCTIONS" | tr '\n' ' ')"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# If new functions added, check if PRD, docs, or JOURNAL were updated
|
||||||
|
if [[ -n "$NEW_FUNCTIONS" ]]; then
|
||||||
|
DOCS_UPDATED=$(echo "$STAGED_FILES" | grep -E '^(docs/|PRD\.md|JOURNAL\.md)' || true)
|
||||||
|
|
||||||
|
if [[ -z "$DOCS_UPDATED" ]]; then
|
||||||
|
echo -e "${YELLOW}⚠ New functions detected without documentation updates:${NC}"
|
||||||
|
echo -e "$NEW_FUNCTIONS"
|
||||||
|
echo -e "${YELLOW}Note: Consider updating PRD.md, docs/, or JOURNAL.md${NC}"
|
||||||
|
# This is a warning, not a hard failure
|
||||||
|
else
|
||||||
|
echo -e "${GREEN}✓ Documentation appears to be updated${NC}"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo -e "${GREEN}✓ No new functions to document${NC}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# Final Result
|
||||||
|
# =============================================================================
|
||||||
|
echo ""
|
||||||
|
echo -e "${YELLOW}╔══════════════════════════════════════════════════════════════╗${NC}"
|
||||||
|
|
||||||
|
if [[ $FAILED -eq 1 ]]; then
|
||||||
|
echo -e "${YELLOW}║ COMMIT BLOCKED ║${NC}"
|
||||||
|
echo -e "${YELLOW}╚══════════════════════════════════════════════════════════════╝${NC}"
|
||||||
|
echo ""
|
||||||
|
echo -e "${RED}SDLC requirements not met. Please fix the above issues.${NC}"
|
||||||
|
echo ""
|
||||||
|
echo -e "${YELLOW}Quick fix commands:${NC}"
|
||||||
|
echo " ./run.sh lint # Fix lint warnings"
|
||||||
|
echo " ./run.sh test:unit # Run unit tests"
|
||||||
|
echo " ./run.sh test # Run all tests"
|
||||||
|
echo ""
|
||||||
|
echo -e "${YELLOW}Reference: docs/SDLC.md${NC}"
|
||||||
|
exit 1
|
||||||
|
else
|
||||||
|
echo -e "${YELLOW}║ ALL CHECKS PASSED ║${NC}"
|
||||||
|
echo -e "${YELLOW}╚══════════════════════════════════════════════════════════════╝${NC}"
|
||||||
|
echo ""
|
||||||
|
echo -e "${GREEN}✓ SDLC requirements verified${NC}"
|
||||||
|
echo -e "${GREEN}✓ Commit allowed${NC}"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
43
scripts/setup-githooks.sh
Executable file
43
scripts/setup-githooks.sh
Executable file
@@ -0,0 +1,43 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
#
|
||||||
|
# KNEL-Football Secure OS - Git Hooks Setup
|
||||||
|
# Configures git to use the shared hooks from the githooks/ directory
|
||||||
|
#
|
||||||
|
# Run this once after cloning the repository:
|
||||||
|
# ./scripts/setup-githooks.sh
|
||||||
|
#
|
||||||
|
# Copyright (c) 2026 Known Element Enterprises LLC
|
||||||
|
# License: GNU Affero General Public License v3.0 only
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||||
|
HOOKS_DIR="$REPO_ROOT/githooks"
|
||||||
|
|
||||||
|
echo "Setting up git hooks..."
|
||||||
|
echo "Repository: $REPO_ROOT"
|
||||||
|
echo "Hooks directory: $HOOKS_DIR"
|
||||||
|
|
||||||
|
# Verify hooks directory exists
|
||||||
|
if [[ ! -d "$HOOKS_DIR" ]]; then
|
||||||
|
echo "ERROR: githooks/ directory not found"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Make all hooks executable
|
||||||
|
chmod +x "$HOOKS_DIR"/*
|
||||||
|
|
||||||
|
# Configure git to use the shared hooks directory
|
||||||
|
git -C "$REPO_ROOT" config core.hooksPath githooks/
|
||||||
|
|
||||||
|
# Verify configuration
|
||||||
|
CONFIGURED_PATH=$(git -C "$REPO_ROOT" config --get core.hooksPath)
|
||||||
|
echo ""
|
||||||
|
echo "Git hooks configured successfully!"
|
||||||
|
echo " core.hooksPath = $CONFIGURED_PATH"
|
||||||
|
echo ""
|
||||||
|
echo "Available hooks:"
|
||||||
|
ls -1 "$HOOKS_DIR"
|
||||||
|
echo ""
|
||||||
|
echo "Hooks are now active for this repository."
|
||||||
Reference in New Issue
Block a user