guardy 0.2.4

Fast, secure git hooks in Rust with secret scanning and protected file synchronization
Documentation
# Guardy Configuration Example
# This file showcases all available configuration options
# Place this file as .guardy.toml in your project root
# General configuration
[general]
verbose = 0 # 0=warn, 1=info, 2=debug, 3=trace
quiet = false # Suppress non-error output
debug = false # Enable debug mode
color = true # Enable colored output
interactive = true # Enable interactive prompts
# Scanner configuration for secret detection
[scanner]
mode = "auto" # auto, single, parallel
max_threads = 0 # 0 = auto-detect
max_file_size_mb = 10 # Skip files larger than this
thread_percentage = 70 # % of CPU cores to use
stack_size_mb = 15 # Stack size for worker threads
channel_buffer_multiplier = 8
result_chunk_size = 500
min_files_for_parallel = 100
# File processing options
include_binary = false
follow_symlinks = false
enable_entropy_analysis = true
entropy_threshold = 0.00001
ignore_test_code = true
# Display options
show = false # Show detected secrets (dangerous!)
sensitive = false # Show sensitive info in output
# Paths and patterns to ignore
ignore_paths = [
  "target/",
  "node_modules/",
  ".git/",
  "dist/",
  "build/",
  "vendor/",
  ".venv/",
  "__pycache__/",
]
ignore_patterns = [
  "(?i)password\\s*[:=]\\s*['\"][^'\"]*['\"]",
  "(?i)secret\\s*[:=]\\s*['\"][^'\"]*['\"]",
]
custom_patterns = [
  # Add your custom regex patterns here
  # "MY_CUSTOM_[A-Z0-9]{32}"
]
# Git hooks configuration
[hooks]
skip_all = false # Emergency override to skip all hooks
parallel_execution = true # Run hooks in parallel by default
# Individual hook configurations
[hooks.pre-commit]
skip = false
parallel = true
# Note: This is YAML-style format shown in TOML for demonstration
# For actual TOML configs, use the commands.<name> format below
# For YAML configs (.guardy.yml), you can use the multi-line format directly
[hooks.pre-commit.commands.scan-secrets]
builtin = "scan_secrets"
[hooks.pre-commit.commands.rust-format]
run = "cargo fmt --all -- --check"
glob = ["*.rs"]
stage_fixed = true
continue_on_error = false
[hooks.pre-commit.commands.rust-lint]
run = "cargo clippy --all-targets --all-features -- -D warnings"
glob = ["*.rs"]
continue_on_error = false
[hooks.pre-commit.commands.python-format]
run = "black {staged_files}"
glob = ["*.py"]
exclude = ["venv/**", ".venv/**"]
stage_fixed = true
skip = ["merge", "rebase"]
[hooks.pre-commit.commands.python-lint]
run = "ruff check {staged_files}"
glob = ["*.py"]
exclude = ["venv/**", ".venv/**"]
skip = ["merge", "rebase"]
[hooks.pre-commit.commands.js-format]
run = "prettier --write {staged_files}"
glob = ["*.js", "*.jsx", "*.ts", "*.tsx", "*.json"]
stage_fixed = true
skip = ["merge", "rebase"]
[hooks.pre-commit.commands.config-format]
run = "dprint fmt {staged_files} --allow-no-files"
glob = ["*.toml", "*.yml", "*.yaml", "*.md"]
stage_fixed = true
continue_on_error = true
[hooks.pre-commit.commands.check-large-files]
run = "guardy check-size --max-size=5MB {staged_files}"
all_files = false
continue_on_error = false
[hooks.commit-msg]
skip = false
parallel = false
[hooks.commit-msg.commands.conventional-commit]
run = """
  if ! grep -qE '^(feat|fix|docs|style|refactor|test|chore|perf|ci|build|revert)(\\(.+\\))?: .{1,50}' "{1}"; then
    echo "✗ Commit message must follow conventional commits format:"
    echo "   type(scope): description"
    echo ""
    echo "Types: feat, fix, docs, style, refactor, test, chore, perf, ci, build, revert"
    echo "Example: feat(scanner): add support for custom patterns"
    exit 1
  fi
"""
skip = ["merge", "rebase"]
[hooks.commit-msg.commands.commit-length]
run = """
  first_line=$(head -n1 "{1}")
  if [ ${#first_line} -gt 72 ]; then
    echo "✗ Commit message first line should be ≤ 72 characters (currently ${#first_line})"
    exit 1
  fi
"""
[hooks.pre-push]
skip = false
parallel = true
[hooks.pre-push.commands.check-clean]
run = """
  if output="$(git status --porcelain)" && [ -n "$output" ]; then
    echo "✗ Repository has uncommitted changes:"
    echo "$output"
    echo ""
    echo "Please commit or stash changes before pushing."
    exit 1
  fi
"""
[hooks.pre-push.commands.rust-test]
run = "cargo check --all"
glob = ["*.rs"]
continue_on_error = false
[hooks.pre-push.commands.check-outdated]
run = "cargo outdated --exit-code 1"
glob = ["Cargo.toml"]
continue_on_error = true
[hooks.pre-push.commands.security-audit]
run = "cargo audit"
glob = ["Cargo.lock"]
continue_on_error = false
[hooks.post-checkout]
skip = false
parallel = false
[hooks.post-checkout.commands.update-deps]
run = """
  if [ -f "Cargo.toml" ]; then
    echo "Updating Rust dependencies..."
    cargo fetch
  fi
  if [ -f "package.json" ]; then
    echo "Updating Node dependencies..."
    if [ -f "pnpm-lock.yaml" ]; then
      pnpm install --frozen-lockfile
    elif [ -f "yarn.lock" ]; then
      yarn install --frozen-lockfile
    else
      npm ci
    fi
  fi
"""
continue_on_error = true
[hooks.post-checkout.commands.check-git-crypt]
run = """
  if [ -f ".git-crypt/.gitattributes" ]; then
    if ! git-crypt status | grep -q "not locked"; then
      echo "⚠️  Repository appears to be git-crypt locked"
      echo "Run 'git-crypt unlock' to decrypt files"
    fi
  fi
"""
continue_on_error = true
# Security configuration (used by scan_secrets builtin)
[hooks.security]
patterns = [
  "sk-[a-zA-Z0-9]{48}", # OpenAI API keys
  "ghp_[a-zA-Z0-9]{36}", # GitHub personal access tokens
  "ghs_[a-zA-Z0-9]{36}", # GitHub server tokens
  "glpat-[a-zA-Z0-9-]{20,}", # GitLab personal access tokens
  "ey[a-zA-Z0-9]{20,}\\.[a-zA-Z0-9]+", # JWT tokens
  "AIza[0-9A-Za-z\\-_]{35}", # Google API keys
  "AKIA[0-9A-Z]{16}", # AWS access keys
  "[a-zA-Z0-9+/]{40,}={0,2}", # Generic base64 secrets (high entropy)
  "-----BEGIN (RSA |EC )?PRIVATE KEY-----", # Private keys
  "xox[baprs]-[0-9]{10,13}-[a-zA-Z0-9]{24,32}", # Slack tokens
]
exclude_files = [
  "*.lock",
  "*.log",
  ".husky/*",
  "test/fixtures/*",
  "**/*.test.*",
  "**/*.spec.*",
]
# Branch protection
[hooks.branch_protection]
protected_branches = ["main", "master", "develop", "release/*"]
allow_direct_commits = false
require_pull_request = true
# Git-crypt configuration
[hooks.git_crypt]
enabled = true
required_files = [
  ".env",
  "secrets/*",
  "*.key",
  "*.pem",
]
auto_lock_on_commit = true
# Code formatting configuration
[hooks.formatting]
enabled = true
command = "nx format:write --uncommitted"
auto_fix = false
check_only_in_ci = true
# Package manager configuration
[hooks.package_manager]
preferred = "pnpm"
auto_install = true
lockfile_only = true # Only install from lockfile (ci mode)
# Sync configuration for file synchronization
[sync]
auto_update = false
interactive = true
show_diff = true
[[sync.repos]]
name = "shared-configs"
repo = "https://github.com/myorg/shared-configs"
version = "v1.0.0"
source_path = "configs/"
dest_path = "./"
include = [
  ".editorconfig",
  ".prettierrc",
  "dprint.json",
  ".rustfmt.toml",
]
exclude = []
protected = true
[[sync.repos]]
name = "ci-workflows"
repo = "https://github.com/myorg/ci-templates"
version = "main"
source_path = "github/"
dest_path = ".github/"
include = [
  "workflows/*.yml",
  "actions/*",
]
exclude = [
  "workflows/experimental-*",
]
protected = false
[sync.protection]
auto_protect_synced = true
block_modifications = true
warn_on_conflict = true
# MCP (Model Context Protocol) configuration
[mcp]
enabled = false
port = 8080
host = "127.0.0.1"
tools = [
  "git-status",
  "hook-run",
  "security-scan",
  "config-validate",
]
# External tools configuration
[external_tools]
git_crypt = "git-crypt"
nx = "nx"
pnpm = "pnpm"
cargo = "cargo"
python = "python3"
node = "node"
# Status command display options
[status]
verbose = false
show_config = false
format = "text" # text, json, yaml
show_hooks = true
show_sync = true
show_security = true
# Config command options
[config]
format = "toml" # toml, json, yaml
show_defaults = false
auto_validate = true
show_source = false # Show where each value comes from (cli, env, file, default)