hooksmith 1.16.0

Trivial git hook management tool
Documentation
# Hooksmith

<pre align="center">
                ,"(                             .
               ////\                           /
              (//////--,,,,,_____            ,"
            _;"""----/////_______;,,        //
__________;"o,-------------......"""""`'-._/(
      ""'==._.__,;;;;"""           ____,.-.==
             "-.:______,...;---""/"   "    \(
                 '-._      `-._("           \\
                     '-._                     '._
</pre>

<h1 align="center">Git Hook Management Made Simple</h1>

<p align="center">
  <a href="https://crates.io/crates/hooksmith"><img src="https://img.shields.io/crates/v/hooksmith.svg" alt="Crates.io Version"></a>
  <a href="https://docs.rs/hooksmith"><img src="https://img.shields.io/docsrs/hooksmith/latest" alt="Documentation"></a>
  <a href="https://sonarcloud.io/summary/new_code?id=TomPlanche_hooksmith"><img src="https://sonarcloud.io/api/project_badges/measure?project=TomPlanche_hooksmith&metric=alert_status" alt="SonarCloud Status"></a>
  <a href="https://sonarcloud.io/summary/new_code?id=TomPlanche_hooksmith"><img src="https://sonarcloud.io/api/project_badges/measure?project=TomPlanche_hooksmith&metric=sqale_rating" alt="SonarCloud SQALE Rating"></a>
  <a href="https://sonarcloud.io/summary/new_code?id=TomPlanche_hooksmith"><img src="https://sonarcloud.io/api/project_badges/measure?project=TomPlanche_hooksmith&metric=security_rating" alt="SonarCloud Security Rating"></a>
  <a href="https://github.com/TomPlanche/hooksmith/blob/main/LICENSE"><img src="https://img.shields.io/crates/l/hooksmith" alt="License"></a>
  <a href="https://github.com/TomPlanche/hooksmith/actions/workflows/rust.yaml"><img src="https://github.com/TomPlanche/hooksmith/actions/workflows/rust.yaml/badge.svg" alt="Build Status"></a>
</p>

**Hooksmith** is a lightweight, easy-to-use tool that simplifies Git hook management. Define your hooks in a simple YAML file and let Hooksmith handle the rest.

## Table of Contents

- [Features]#features
- [Why Hooksmith?]#why-hooksmith
- [Installation]#installation
- [Quick Start]#quick-start
- [Usage]#usage
- [Performance Monitoring]#performance-monitoring
- [Path-based Blocks]#path-based-blocks
- [Command Reference]#command-reference
- [Contributing]#contributing
- [License]#license

## Features

- **Automatic Installation** - Set up hooks through your build scripts with `build.rs`
- **Local Testing** - Run hooks manually without triggering Git events
- **Performance Monitoring** - Track execution times for hooks and individual commands
- **Dry Run Mode** - Preview what would happen without making changes
- **Hook Validation** - Ensure your hooks comply with Git standards
- **Simple Configuration** - Define all your hooks in a clean YAML format
- **Step Progress** - See which hook and command is running at each step
- **Shell Completion** - Built-in Fish shell completions for improved productivity
- **Version Control** - Easily track hook changes with your repository
- **Error Handling** - Robust error handling with clear, actionable messages

## Why Hooksmith?

- **Minimal Dependencies** - Lightweight with only essential dependencies
- **Rust Powered** - Fast, reliable, and type-safe
- **Team Friendly** - Version control your hook configurations
- **Seamless Integration** - Works naturally with your Git workflow
- **Low Learning Curve** - Simple commands and clear documentation

## Installation

### Using Cargo

```bash
cargo install hooksmith
```

### As a Build Dependency

Add to your `Cargo.toml`:

```toml
[build-dependencies]
hooksmith = "1.16.0"
```

Create a `build.rs` file:

```rust
use std::path::Path;

fn main() {
    let config_path = Path::new("hooksmith.yaml");
    hooksmith::init(&config_path);
}
```

> **Note**: Hooksmith includes shell completions for Fish. After installation, they become available automatically.

### Dependencies

Hooksmith is built with minimal but powerful dependencies:
- `clap`: For robust command-line argument parsing
- `console` & `dialoguer`: For beautiful terminal interfaces
- `serde` & `serde_yaml`: For YAML configuration handling
- `thiserror`: For ergonomic error handling

## Quick Start

1. Create a `hooksmith.yaml` file in your project root:

```yaml
pre-commit:
  commands:
    - cargo fmt --all -- --check
    - cargo clippy -- --deny warnings

pre-push:
  commands:
    - cargo test
```

2. Install the hooks:

```bash
hooksmith install
```

That's it! Your Git hooks are now ready to use.

## Usage

### Configuration File

Hooksmith uses a YAML configuration file (default: `hooksmith.yaml`) to define your hooks:

```yaml
# Format and lint code before committing
pre-commit:
  commands:
    - cargo fmt --all -- --check
    - cargo clippy --workspace --all-features -- --deny warnings

# Run tests before pushing
pre-push:
  commands:
    - cargo test --all-features
    - cargo build --verbose

# Validate commit messages
commit-msg:
  commands:
    # Use custom script to validate commit messages
    - ./scripts/verify-commit-message.sh $1
```

#### Named Commands

You can optionally assign names to your commands for better readability and clearer output. This is especially useful for long or complex commands:

```yaml
pre-commit:
  commands:
    - cargo fmt --all -- --check
    - clippy-linter: cargo clippy --workspace --release --all-targets --all-features -- --deny warnings -D warnings -W clippy::correctness -W clippy::suspicious -W clippy::complexity -W clippy::perf -W clippy::style -W clippy::pedantic
    - typos

pre-push:
  commands:
    - cargo build -q
    - test-suite: cargo test -q
```

Benefits of named commands:
- **Better readability**: Long commands show their purpose clearly
- **Improved monitoring**: Performance reports display meaningful names instead of truncated commands
- **Enhanced dry-run output**: See at a glance what each command does

When you use named commands, both the dry-run output and performance monitoring will display the command name followed by the actual command in parentheses.

### Common Commands

```bash
# Install all hooks defined in configuration
hooksmith install

# Run a specific hook manually
hooksmith run pre-commit

# Run a hook with performance monitoring
hooksmith run pre-commit --profile

# Uninstall all hooks or a specific one
hooksmith uninstall
hooksmith uninstall pre-commit

# Compare installed hooks with configuration
hooksmith compare

# Validate hook configuration against Git standards
hooksmith validate
```

Add `--dry-run` to any command to preview changes without applying them:

```bash
hooksmith install --dry-run
```

## Performance Monitoring

Hooksmith includes built-in performance monitoring to help you optimize your hook execution times. Use the `--profile` flag with the `run` command to see detailed timing information:

```bash
hooksmith run pre-commit --profile
```

This will show you:
- Individual command execution times (showing command names when available)
- Total hook execution time
- Overall execution time when running multiple hooks

### Example Output

When running hooks, Hooksmith shows step-by-step progress:

```
running `pre-commit`, 1/1 steps:
  running `cargo fmt --all -- --check` 1/3
  running `clippy-linter` 2/3
  running `typos` 3/3
```

With `--profile`, a timing summary is appended:

```
running `pre-commit`, 1/1 steps:
  running `cargo fmt --all -- --check` 1/3
  running `clippy-linter` 2/3
  running `typos` 3/3

Hook execution summary:
  Hook 'pre-commit' (768ms)
    cargo fmt --all -- --check: 146ms
    clippy-linter: 394ms
    typos: 226ms
  Total: 768ms
```

### Use Cases

Performance monitoring is particularly useful for:
- **Identifying slow commands** in your hook chains
- **Optimizing CI/CD pipelines** by understanding bottlenecks
- **Debugging performance issues** in complex hook configurations
- **Tracking improvements** after optimizing your toolchain

## Path-based Blocks

Define commands that only run when files within specific paths have changed. This lets you scope expensive checks to the parts of the repository they affect.

### YAML structure

```yaml
pre-commit:
  # Global commands: always run
  commands:
    - cargo fmt --all -- --check

  # Path-based blocks: run only if matching files changed
  paths:
    src/:
      commands:
        - cargo clippy --workspace -- -D warnings

    crates/api/:
      working_directory: crates/api
      commands:
        - npm ci
        - npm test
```

### How it works

- **Matching**: A block runs when any changed file path starts with its key (simple prefix match). Use paths relative to the repo root; prefer a trailing slash (e.g., `src/`).
- **Supported hooks**: Change detection is implemented for `pre-commit` and `pre-push`.
  - `pre-commit`: uses `git diff --name-only --cached`.
  - `pre-push`: diffs `@{u}..HEAD` when upstream exists, otherwise falls back to `HEAD~1..HEAD`.
- **Order**: All matching path-based blocks run first, then global `commands` run.
- **Working directory**: Inside a path block, `working_directory` (optional) sets the directory for those commands only. Global commands run in the current directory.
- **No matches**: If no paths match, only global commands run. Omit `commands` if you want nothing to run in that case.
- **Multiple matches**: If a file matches several prefixes, all matching blocks run. The order between blocks is not guaranteed; the order of commands within a block is preserved.

## Command Reference

| Command | Description |
|---------|-------------|
| `install` | Install all hooks from configuration file |
| `run <hook>` | Run a specific hook manually |
| `run <hook> --profile` | Run a hook with performance timing information |
| `uninstall [hook]` | Uninstall all hooks or a specific one |
| `compare` | Compare installed hooks with configuration |
| `validate` | Validate hook configuration against Git standards |

### Global Options

| Option | Description |
|--------|-------------|
| `--config-path <PATH>` | Specify a custom configuration file path |
| `--dry-run` | Preview changes without applying them |
| `--verbose` | Show detailed output during execution |
| `--help` | Display help information |

### Run Command Options

| Option | Description |
|--------|-------------|
| `--interactive` or `-i` | Interactively select hooks to run |
| `--profile` or `-p` | Show performance timing for hook execution |

## Contributing

Contributions are welcome! Feel free to:

- Report bugs and suggest features
- Submit pull requests
- Improve documentation
- Share your use cases and feedback

## License

This project is dual-licensed under either:

- [Apache License 2.0]LICENSE-APACHE
- [MIT License]LICENSE-MIT

at your option.