diff --git a/.gitignore b/.gitignore index a91d488..59f52dd 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ docs/_build .vscode/ .idea/ +.worktrees/ diff --git a/hooks.toml b/.peter-hook.toml similarity index 100% rename from hooks.toml rename to .peter-hook.toml diff --git a/CHANGELOG.md b/CHANGELOG.md index 38cdede..cfa9b04 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,39 @@ All notable changes to this project will be documented in this file. +## [5.0.0] - 2025-12-10 + +### BREAKING CHANGES + +- **Configuration file renamed from `hooks.toml` to `.peter-hook.toml`** + - Peter-hook now searches for `.peter-hook.toml` instead of `hooks.toml` + - If `hooks.toml` files are detected, peter-hook will error and refuse to run + - Migration: Rename all `hooks.toml` files to `.peter-hook.toml` + - Commands affected: All commands except `version` and `license` + +**Migration guide:** + +For single configuration: +```bash +mv hooks.toml .peter-hook.toml +``` + +For monorepos with multiple configurations: +```bash +# Find all deprecated files +find . -name "hooks.toml" -type f + +# Rename each one +cd backend && mv hooks.toml .peter-hook.toml +cd ../frontend && mv hooks.toml .peter-hook.toml +``` + +**Why this change:** +- Follows dotfile conventions for tool configuration +- Reduces visual clutter in repository root +- Makes the file more discoverable as tool-specific config +- Aligns with modern CLI tool practices + ## [Unreleased] ### Added diff --git a/CLAUDE.md b/CLAUDE.md index 6220bde..57c0891 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -92,7 +92,7 @@ just bump-version patch # Bump version only (no release) - **CLI Interface** (`src/cli/mod.rs`): Command-line interface ### Key Features -- **Hierarchical Configuration**: Nearest `hooks.toml` file wins +- **Hierarchical Configuration**: Nearest `.peter-hook.toml` file wins - **Safe Parallel Execution**: Repository-modifying hooks run sequentially, read-only hooks run in parallel - **Hook Groups**: Combine individual hooks with execution strategies - **Cross-platform**: Rust implementation supporting macOS, Linux, Windows @@ -333,7 +333,7 @@ Partial stderr: WARNING: Test database cleanup incomplete ### Multi-Config Group Execution Behavior -When multiple config groups are involved (different `hooks.toml` files for different changed files), peter-hook executes them sequentially with fail-fast semantics: +When multiple config groups are involved (different `.peter-hook.toml` files for different changed files), peter-hook executes them sequentially with fail-fast semantics: **Execution Order:** 1. Groups are processed in the order they are resolved (typically by file path) @@ -344,9 +344,9 @@ When multiple config groups are involved (different `hooks.toml` files for diffe **Example Scenario:** ``` Changed files: - - backend/api.rs → Config Group A (backend/hooks.toml) - - frontend/app.tsx → Config Group B (frontend/hooks.toml) - - docs/README.md → Config Group C (docs/hooks.toml) + - backend/api.rs → Config Group A (backend/.peter-hook.toml) + - frontend/app.tsx → Config Group B (frontend/.peter-hook.toml) + - docs/README.md → Config Group C (docs/.peter-hook.toml) Execution flow: Group A (backend): Run hooks → SUCCESS ✓ @@ -375,9 +375,9 @@ Template variables use `{VARIABLE_NAME}` syntax and can be used in: - `workdir` field (working directory paths) **Available template variables:** -- `{HOOK_DIR}` - Directory containing the hooks.toml file +- `{HOOK_DIR}` - Directory containing the .peter-hook.toml file - `{REPO_ROOT}` - Git repository root directory -- `{PROJECT_NAME}` - Name of the directory containing hooks.toml +- `{PROJECT_NAME}` - Name of the directory containing .peter-hook.toml - `{HOME_DIR}` - User's home directory (from $HOME) - `{PATH}` - Current PATH environment variable - `{WORKING_DIR}` - Current working directory diff --git a/Cargo.lock b/Cargo.lock index e420a19..538acec 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -377,7 +377,7 @@ version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fde0e0ec90c9dfb3b4b1a0891a7dcd0e2bffde2f7efed5fe7c9bb00e5bfb915e" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -624,7 +624,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -797,6 +797,19 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" +[[package]] +name = "globset" +version = "0.4.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52dfc19153a48bde0cbd630453615c8151bce3a5adfac7a0aebfbf0a1e1f57e3" +dependencies = [ + "aho-corasick", + "bstr", + "log", + "regex-automata", + "regex-syntax", +] + [[package]] name = "h2" version = "0.4.12" @@ -1070,6 +1083,22 @@ dependencies = [ "icu_properties", ] +[[package]] +name = "ignore" +version = "0.4.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3d782a365a015e0f5c04902246139249abf769125006fbe7649e2ee88169b4a" +dependencies = [ + "crossbeam-deque", + "globset", + "log", + "memchr", + "regex-automata", + "same-file", + "walkdir", + "winapi-util", +] + [[package]] name = "indexmap" version = "2.12.0" @@ -1117,7 +1146,7 @@ checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9" dependencies = [ "hermit-abi 0.5.2", "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -1379,7 +1408,7 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "peter-hook" -version = "4.0.3" +version = "5.0.0" dependencies = [ "anyhow", "assert_cmd", @@ -1390,6 +1419,7 @@ dependencies = [ "dirs", "git2", "glob", + "ignore", "indicatif", "mockito", "once_cell", @@ -1400,6 +1430,7 @@ dependencies = [ "tempfile", "toml 0.9.8", "wait-timeout", + "walkdir", "workhelix-cli-common", ] @@ -1542,7 +1573,7 @@ dependencies = [ "once_cell", "socket2", "tracing", - "windows-sys 0.52.0", + "windows-sys 0.60.2", ] [[package]] @@ -1738,7 +1769,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -1800,6 +1831,15 @@ version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + [[package]] name = "scc" version = "2.4.0" @@ -2139,7 +2179,7 @@ dependencies = [ "getrandom 0.3.4", "once_cell", "rustix", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -2515,6 +2555,16 @@ dependencies = [ "libc", ] +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + [[package]] name = "want" version = "0.3.1" @@ -2662,7 +2712,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 75e1c19..9160499 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "peter-hook" -version = "4.0.3" +version = "5.0.0" edition = "2024" rust-version = "1.86.0" authors = ["Peter Hook Contributors"] @@ -27,6 +27,8 @@ serde = { version = "1.0", features = ["derive"] } workhelix-cli-common = "0.4.1" cargo-edit = "0.13.7" wait-timeout = "0.2" +walkdir = "2" +ignore = "0.4" diff --git a/README.md b/README.md index 47d50a0..4602ce8 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ Peter Hook enables different paths within a monorepo to have their own custom gi ## Key Features -- **🏗️ Per-File Hierarchical Resolution**: Each changed file finds its nearest `hooks.toml`, enabling true monorepo patterns with path-specific validation +- **🏗️ Per-File Hierarchical Resolution**: Each changed file finds its nearest `.peter-hook.toml`, enabling true monorepo patterns with path-specific validation - **⚡ Safe Parallel Execution**: Automatic parallelization of compatible hooks for 2-3x speed improvement - **🔗 Hook Composition**: Combine individual hooks into reusable groups with dependency management - **🛡️ Repository Safety**: File-modifying hooks never run simultaneously, preventing race conditions @@ -88,7 +88,7 @@ install -m 0755 target/release/peter-hook ~/.local/bin/ ### Basic Usage -1. **Create a configuration file** (`hooks.toml`): +1. **Create a configuration file** (`.peter-hook.toml`): ```toml # Individual hooks @@ -197,7 +197,7 @@ parallel = true # Use execution = "parallel" instead Share and reuse hooks/groups across files, with local overrides. ```toml -# hooks.toml (project) +# .peter-hook.toml (project) imports = ["../hooks.lib.toml", ".hooks/common.toml"] [groups.pre-commit] @@ -230,12 +230,12 @@ Peter Hook supports powerful template variables in commands, working directories #### Built-in Variables ```toml -{HOOK_DIR} # Directory containing the hooks.toml file +{HOOK_DIR} # Directory containing the .peter-hook.toml file {WORKING_DIR} # Current working directory when hook runs {REPO_ROOT} # Git repository root directory {HOOK_DIR_REL} # Relative path from repo root to hook directory {WORKING_DIR_REL} # Relative path from repo root to working directory -{PROJECT_NAME} # Name of the directory containing hooks.toml +{PROJECT_NAME} # Name of the directory containing .peter-hook.toml {HOME_DIR} # User's home directory {IS_WORKTREE} # "true" or "false" - whether running in a worktree {WORKTREE_NAME} # Name of current worktree (only available in worktrees) @@ -345,7 +345,7 @@ Git worktrees in this repository: Per-worktree configuration allows different branches to have different validation requirements: ```toml -# Main repository hooks.toml - Strict validation +# Main repository .peter-hook.toml - Strict validation [hooks.full-test-suite] command = "cargo test --all --release" modifies_repository = false @@ -362,7 +362,7 @@ execution = "parallel" ``` ```toml -# Feature branch worktree hooks.toml - Fast iteration +# Feature branch worktree .peter-hook.toml - Fast iteration [hooks.quick-test] command = "cargo test --lib" modifies_repository = false @@ -661,7 +661,7 @@ Peter Hook implements **true per-file hierarchical resolution** where each chang When you run a git hook (e.g., `pre-commit`), Peter Hook: 1. **Detects all changed files** from git (staged, working directory, or push range) -2. **For each changed file**, walks up from that file's directory to find the nearest `hooks.toml` +2. **For each changed file**, walks up from that file's directory to find the nearest `.peter-hook.toml` 3. **Checks if that config defines the requested event** (e.g., `pre-commit`) 4. **Falls back to parent configs** if the event isn't defined locally 5. **Groups files** that share the same configuration @@ -672,23 +672,23 @@ When you run a git hook (e.g., `pre-commit`), Peter Hook: ``` /monorepo/ ├── .git -├── hooks.toml # Defines: pre-push +├── .peter-hook.toml # Defines: pre-push ├── backend/ -│ ├── hooks.toml # Defines: pre-commit +│ ├── .peter-hook.toml # Defines: pre-commit │ └── api/ -│ ├── hooks.toml # Defines: pre-push +│ ├── .peter-hook.toml # Defines: pre-push │ └── server.rs # File A └── frontend/ └── app.js # File B ``` **Scenario: Modify `backend/api/server.rs` (File A)** -- **pre-commit hook**: Uses `/monorepo/backend/hooks.toml` (walks up, finds first config with pre-commit) -- **pre-push hook**: Uses `/monorepo/backend/api/hooks.toml` (nearest config defines it) +- **pre-commit hook**: Uses `/monorepo/backend/.peter-hook.toml` (walks up, finds first config with pre-commit) +- **pre-push hook**: Uses `/monorepo/backend/api/.peter-hook.toml` (nearest config defines it) **Scenario: Modify `frontend/app.js` (File B)** -- **pre-commit hook**: Uses `/monorepo/hooks.toml` (no frontend/hooks.toml, falls back to root) -- **pre-push hook**: Uses `/monorepo/hooks.toml` (defined at root) +- **pre-commit hook**: Uses `/monorepo/.peter-hook.toml` (no frontend/.peter-hook.toml, falls back to root) +- **pre-push hook**: Uses `/monorepo/.peter-hook.toml` (defined at root) **Scenario: Modify both files simultaneously** - Peter Hook executes hooks from **both** configs in the same commit: @@ -702,17 +702,17 @@ If a config doesn't define the requested event, Peter Hook automatically searche ``` /monorepo/ -├── hooks.toml # Defines: pre-commit, pre-push +├── .peter-hook.toml # Defines: pre-commit, pre-push └── microservices/ - ├── hooks.toml # Defines: pre-commit only + ├── .peter-hook.toml # Defines: pre-commit only └── auth/ └── src/ └── lib.rs ``` When `microservices/auth/src/lib.rs` is modified: -- **pre-commit**: Uses `/monorepo/microservices/hooks.toml` (found locally) -- **pre-push**: Uses `/monorepo/hooks.toml` (falls back to parent, `microservices/hooks.toml` doesn't define it) +- **pre-commit**: Uses `/monorepo/microservices/.peter-hook.toml` (found locally) +- **pre-push**: Uses `/monorepo/.peter-hook.toml` (falls back to parent, `microservices/.peter-hook.toml` doesn't define it) ### Benefits @@ -740,25 +740,25 @@ When `microservices/auth/src/lib.rs` is modified: ### Real-World Example ```toml -# /monorepo/hooks.toml - Repository-wide safety net +# /monorepo/.peter-hook.toml - Repository-wide safety net [groups.pre-push] includes = ["security-scan", "secret-detection"] execution = "parallel" description = "Security checks for all code" -# /monorepo/backend/hooks.toml - Backend quality standards +# /monorepo/backend/.peter-hook.toml - Backend quality standards [groups.pre-commit] includes = ["rust-format", "rust-clippy", "rust-test"] execution = "parallel" description = "Rust validation pipeline" -# /monorepo/frontend/hooks.toml - Frontend quality standards +# /monorepo/frontend/.peter-hook.toml - Frontend quality standards [groups.pre-commit] includes = ["prettier", "eslint", "jest"] execution = "parallel" description = "JavaScript validation pipeline" -# /monorepo/shared/hooks.toml - Library quality standards +# /monorepo/shared/.peter-hook.toml - Library quality standards [groups.pre-commit] includes = ["format", "lint", "test", "doc-check", "api-compat"] execution = "sequential" diff --git a/VERSION b/VERSION index aa31e71..28cbf7c 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -4.0.3 \ No newline at end of file +5.0.0 \ No newline at end of file diff --git a/docs/architecture.rst b/docs/architecture.rst index 42c9e4f..7d81a0a 100644 --- a/docs/architecture.rst +++ b/docs/architecture.rst @@ -4,7 +4,7 @@ Architecture Core Components --------------- -- ``src/config/parser.rs``: Parse and validate ``hooks.toml``; supports string/array commands and execution strategies +- ``src/config/parser.rs``: Parse and validate ``.peter-hook.toml``; supports string/array commands and execution strategies - ``src/hooks/resolver.rs``: Find nearest config; resolve groups; apply file targeting - ``src/hooks/dependencies.rs``: Topological sort and phase planning - ``src/hooks/executor.rs``: Execute hooks with sequential/parallel/force-parallel strategies; enforce safety @@ -16,7 +16,7 @@ Core Components Execution Model --------------- -- Resolve hooks for the requested event from the nearest ``hooks.toml`` +- Resolve hooks for the requested event from the nearest ``.peter-hook.toml`` - Compute changed files and filter hooks by ``files`` patterns (use ``--all-files`` to skip filtering) - If dependencies exist, build an execution plan with parallel phases - Execute hooks according to their ``execution_type`` (per-file, in-place, other) diff --git a/docs/cli.rst b/docs/cli.rst index d9001f3..73f971c 100644 --- a/docs/cli.rst +++ b/docs/cli.rst @@ -53,7 +53,7 @@ Options: validate ^^^^^^^^ -Parse and validate the nearest ``hooks.toml``. Prints discovered hooks and groups. +Parse and validate the nearest ``.peter-hook.toml``. Prints discovered hooks and groups. Options: @@ -121,7 +121,7 @@ Positional: The generated scripts now include dynamic completions for key verbs: - ``run`` suggests only git hook events that peter-hook knows how to install. -- ``lint`` suggests hook and group names discovered from the nearest ``hooks.toml`` (placeholder groups are skipped). +- ``lint`` suggests hook and group names discovered from the nearest ``.peter-hook.toml`` (placeholder groups are skipped). .. note:: diff --git a/docs/configuration.rst b/docs/configuration.rst index 1ca393a..c98ee75 100644 --- a/docs/configuration.rst +++ b/docs/configuration.rst @@ -1,7 +1,7 @@ Configuration ============= -Peter Hook reads configuration from the nearest ``hooks.toml`` file to the current working directory. Child directories override parent configurations: the nearest file wins. +Peter Hook reads configuration from the nearest ``.peter-hook.toml`` file to the current working directory. Child directories override parent configurations: the nearest file wins. Hook Definition --------------- @@ -74,7 +74,7 @@ The ``execution_type`` field controls how changed files are passed to hook comma Working Directory Control -------------------------- -By default, hooks run in the directory containing their ``hooks.toml`` file. Use ``run_at_root = true`` to override this and run at the repository root instead: +By default, hooks run in the directory containing their ``.peter-hook.toml`` file. Use ``run_at_root = true`` to override this and run at the repository root instead: .. code-block:: toml @@ -89,11 +89,11 @@ Hook Groups Imports (Hook Libraries) ------------------------ -You can split reusable hooks/groups into separate TOML files and import them into your project ``hooks.toml``. Use ``peter-hook validate --trace-imports`` to inspect how imports were resolved, any overrides, cycles that were skipped, and unused imports. Add ``--json`` to emit machine-readable diagnostics. +You can split reusable hooks/groups into separate TOML files and import them into your project ``.peter-hook.toml``. Use ``peter-hook validate --trace-imports`` to inspect how imports were resolved, any overrides, cycles that were skipped, and unused imports. Add ``--json`` to emit machine-readable diagnostics. .. code-block:: toml - # hooks.toml + # .peter-hook.toml imports = ["../hooks.lib.toml", ".hooks/common.toml"] [groups.pre-commit] diff --git a/docs/examples.rst b/docs/examples.rst index 5558b9f..f85d82c 100644 --- a/docs/examples.rst +++ b/docs/examples.rst @@ -4,20 +4,20 @@ Examples File Targeting -------------- -.. literalinclude:: ../examples/file-targeting.toml +.. literalinclude:: ../examples/.peter-hook-file-targeting.toml :language: toml - :caption: examples/file-targeting.toml + :caption: examples/.peter-hook-file-targeting.toml Parallel Hooks -------------- -.. literalinclude:: ../examples/parallel-hooks.toml +.. literalinclude:: ../examples/.peter-hook-parallel.toml :language: toml - :caption: examples/parallel-hooks.toml + :caption: examples/.peter-hook-parallel.toml Advanced Features ----------------- -.. literalinclude:: ../examples/advanced-features.toml +.. literalinclude:: ../examples/.peter-hook-advanced.toml :language: toml - :caption: examples/advanced-features.toml + :caption: examples/.peter-hook-advanced.toml diff --git a/docs/file-list.md b/docs/file-list.md index 0861686..a4fba72 100644 --- a/docs/file-list.md +++ b/docs/file-list.md @@ -136,7 +136,7 @@ Early validation prevents surprises at runtime. Users get immediate feedback dur ## Usage Example ```toml -# hooks.toml +# .peter-hook.toml [hooks.pytest] command = "pytest" description = "Run Python tests only when Python files change" diff --git a/docs/global_config.rst b/docs/global_config.rst index c4fab12..d4c145e 100644 --- a/docs/global_config.rst +++ b/docs/global_config.rst @@ -48,8 +48,8 @@ With this setting enabled, you can create shared hooks in ``~/.local/peter-hook/ .. code-block:: toml - # Any repository's hooks.toml - imports = ["/home/user/.local/peter-hook/common-hooks.toml"] + # Any repository's .peter-hook.toml + imports = ["/home/user/.local/peter-hook/common-.peter-hook.toml"] [hooks.format] command = "cargo fmt" @@ -117,11 +117,11 @@ When ``allow_local = true``, you can create a personal hook library in ``~/.loca .. code-block:: text ~/.local/peter-hook/ - ├── rust-hooks.toml # Rust-specific hooks - ├── python-hooks.toml # Python-specific hooks - └── security-hooks.toml # Security scanning hooks + ├── rust-.peter-hook.toml # Rust-specific hooks + ├── python-.peter-hook.toml # Python-specific hooks + └── security-.peter-hook.toml # Security scanning hooks -**Example shared hook library** (``~/.local/peter-hook/rust-hooks.toml``): +**Example shared hook library** (``~/.local/peter-hook/rust-.peter-hook.toml``): .. code-block:: toml @@ -147,8 +147,8 @@ When ``allow_local = true``, you can create a personal hook library in ``~/.loca .. code-block:: toml - # hooks.toml in any Rust project - imports = ["/home/user/.local/peter-hook/rust-hooks.toml"] + # .peter-hook.toml in any Rust project + imports = ["/home/user/.local/peter-hook/rust-.peter-hook.toml"] [groups.pre-commit] includes = ["rust-fmt", "rust-clippy", "rust-test"] diff --git a/docs/overview.rst b/docs/overview.rst index 6000dba..a23ed7a 100644 --- a/docs/overview.rst +++ b/docs/overview.rst @@ -6,7 +6,7 @@ Peter Hook enables different paths within a monorepo to have their own custom gi Key Features ------------ -- Hierarchical configuration: nearest ``hooks.toml`` file wins +- Hierarchical configuration: nearest ``.peter-hook.toml`` file wins - Safe parallel execution: repository-modifying hooks never run concurrently - Hook composition: reusable groups with execution strategies - File targeting: run hooks only when matching files change diff --git a/docs/plans/2025-12-10-config-rename-design.md b/docs/plans/2025-12-10-config-rename-design.md new file mode 100644 index 0000000..13efecd --- /dev/null +++ b/docs/plans/2025-12-10-config-rename-design.md @@ -0,0 +1,399 @@ +# Configuration File Rename: hooks.toml → .peter-hook.toml + +**Date:** 2025-12-10 +**Version:** 5.0.0 (Breaking Change) +**Status:** Approved + +## Overview + +Rename the configuration file from `hooks.toml` to `.peter-hook.toml` as a breaking change in version 5.0.0. + +### Goal + +- Update configuration file discovery to use `.peter-hook.toml` instead of `hooks.toml` +- Provide clear, actionable error messages when deprecated `hooks.toml` files are detected +- Hard-fail immediately when deprecated files exist (no grace period) + +### Rationale + +The new name `.peter-hook.toml`: +- Follows dotfile conventions for tool configuration +- Reduces visual clutter in repository root +- Makes the file more discoverable as tool-specific config +- Aligns with modern CLI tool practices (e.g., `.prettierrc`, `.eslintrc`) + +## Implementation Strategy + +### 1. Core Changes + +**Update `find_config_file()` in `src/hooks/resolver.rs`:** + +```rust +pub fn find_config_file(&self) -> Result> { + let mut current = self.current_dir.as_path(); + + loop { + let config_path = current.join(".peter-hook.toml"); // Changed from "hooks.toml" + if config_path.exists() { + return Ok(Some(config_path)); + } + + // Walk up to parent directory + match current.parent() { + Some(parent) => current = parent, + None => return Ok(None), + } + } +} +``` + +### 2. Deprecation Detection + +**Add deprecation checker in `src/main.rs`:** + +Create a new function that walks the repository tree looking for `hooks.toml` files: + +```rust +fn check_for_deprecated_config_files() -> Result<()> { + let repo = GitRepository::find_from_dir(&env::current_dir()?)?; + let mut deprecated_files = Vec::new(); + + // Walk repository from root, respecting .gitignore + // Collect all paths ending in "hooks.toml" + + if !deprecated_files.is_empty() { + eprintln!("Error: hooks.toml is no longer supported. Rename to .peter-hook.toml\n"); + eprintln!("Found deprecated files:"); + for file in &deprecated_files { + eprintln!(" - {}", file.display()); + } + eprintln!("\nRun in each directory: mv hooks.toml .peter-hook.toml"); + std::process::exit(1); + } + + Ok(()) +} +``` + +**Hook into main() early:** + +Call this check right after parsing CLI args for all commands EXCEPT: +- `version` - Should always work +- `license` - Should always work + +All other commands (`run`, `install`, `validate`, `lint`, `list`, `config`, etc.) should fail if deprecated files exist. + +### 3. Error Message Behavior + +**For single deprecated file:** +``` +Error: hooks.toml is no longer supported. Rename to .peter-hook.toml + +Found deprecated files: + - ./hooks.toml + +Run in each directory: mv hooks.toml .peter-hook.toml +``` + +**For multiple deprecated files (monorepo):** +``` +Error: hooks.toml is no longer supported. Rename to .peter-hook.toml + +Found deprecated files: + - ./hooks.toml + - ./backend/hooks.toml + - ./frontend/hooks.toml + +Run in each directory: mv hooks.toml .peter-hook.toml +``` + +## Test Updates + +### Categories of Changes + +**1. Test fixtures and setup code:** +```rust +// Before: +std::fs::write(&temp_dir.join("hooks.toml"), config_content)?; + +// After: +std::fs::write(&temp_dir.join(".peter-hook.toml"), config_content)?; +``` + +**2. Test assertions checking file paths:** +```rust +// Before: +assert!(temp_dir.join("hooks.toml").exists()); + +// After: +assert!(temp_dir.join(".peter-hook.toml").exists()); +``` + +**3. Error message assertions:** +```rust +// Before: +assert!(output.contains("hooks.toml")); + +// After: +assert!(output.contains(".peter-hook.toml")); +``` + +### New Deprecation Tests + +Add tests in `tests/deprecation_tests.rs`: + +1. **Test single deprecated file detection:** + - Create `hooks.toml` in temp repo + - Run any command (except `version`/`license`) + - Assert exit code 1 + - Assert error message contains file path and fix command + +2. **Test multiple deprecated files:** + - Create `hooks.toml` in multiple subdirectories + - Run command + - Assert all files are listed in error message + +3. **Test version/license commands still work:** + - Create `hooks.toml` in temp repo + - Run `peter-hook version` + - Assert success (exit 0) + - Run `peter-hook license` + - Assert success (exit 0) + +4. **Test new config name works:** + - Create `.peter-hook.toml` in temp repo + - Run various commands + - Assert all work correctly + +### Bulk Updates + +**Approach:** Use find/replace with manual verification: + +```bash +# Find all occurrences +rg "hooks\.toml" --files-with-matches + +# Replace in source files +rg "hooks\.toml" -l | xargs sed -i '' 's/hooks\.toml/.peter-hook.toml/g' +``` + +Then manually review changes to ensure correctness, especially in: +- Comments and documentation strings +- Error messages +- File path constructions + +**Validation:** Run full test suite after bulk changes to catch any issues. + +## Documentation Updates + +### User-Facing Documentation + +1. **README.md** + - Update Quick Start examples + - Update all code blocks showing config file + +2. **docs/quickstart.rst** + - Change config file name in examples + +3. **docs/configuration.rst** + - Update file name references + - Update example paths + +4. **docs/architecture.rst** + - Update technical references + +5. **docs/examples.rst** + - Update all example code blocks + +6. **CLAUDE.md** (both root and project-specific) + - Update agent instructions with new file name + +### Example Files + +**Rename example files:** +- `examples/file-targeting.toml` → Keep as-is (example, not discovered config) +- `examples/parallel-hooks.toml` → Keep as-is (example, not discovered config) +- `examples/advanced-features.toml` → Keep as-is (example, not discovered config) +- `examples/hooks-with-imports.toml` → Keep as-is (example, not discovered config) + +**Keep as-is:** +- `examples/hooks.lib.toml` - This is a library file for imports, not a main config + +### Project's Own Config + +Rename the project's own configuration file: +```bash +mv /Users/jfb/Projects/rust/peter-hook/hooks.toml /Users/jfb/Projects/rust/peter-hook/.peter-hook.toml +``` + +### CHANGELOG.md + +Add entry for version 5.0.0: + +```markdown +## [5.0.0] - 2025-12-10 + +### BREAKING CHANGES + +- **Configuration file renamed from `hooks.toml` to `.peter-hook.toml`** + - Peter-hook now searches for `.peter-hook.toml` instead of `hooks.toml` + - If `hooks.toml` files are detected, peter-hook will error and refuse to run + - Migration: Rename all `hooks.toml` files to `.peter-hook.toml` + - Commands affected: All commands except `version` and `license` + +**Migration guide:** + +For single configuration: +```bash +mv hooks.toml .peter-hook.toml +``` + +For monorepos with multiple configurations: +```bash +# Find all deprecated files +find . -name "hooks.toml" -type f + +# Rename each one +cd backend && mv hooks.toml .peter-hook.toml +cd ../frontend && mv hooks.toml .peter-hook.toml +``` + +**Why this change:** +- Follows dotfile conventions for tool configuration +- Reduces visual clutter in repository root +- Makes the file more discoverable as tool-specific config +- Aligns with modern CLI tool practices +``` + +## Implementation Order + +1. **Add deprecation detection** (new code) + - Implement `check_for_deprecated_config_files()` in `src/main.rs` + - Hook into command routing + - Test manually with `.peter-hook.toml` present + +2. **Update core resolver** (modify existing) + - Change `find_config_file()` in `src/hooks/resolver.rs` + - Update constant/string literal from `".peter-hook.toml"` to `".peter-hook.toml"` + +3. **Update tests** (bulk changes) + - Run find/replace across test files + - Add new deprecation tests + - Run full test suite and fix failures + +4. **Update documentation** (after code works) + - Update all doc files with new config name + - Update examples and code blocks + +5. **Rename example files** (after docs) + - Rename all example `.toml` files (except library files) + - Update any cross-references + +6. **Rename project's own config** (dogfooding) + - `mv .peter-hook.toml .peter-hook.toml` + - Test that peter-hook still works for development + +7. **Update CHANGELOG and bump version** (final step) + - Add 5.0.0 entry to CHANGELOG.md + - Use `just release major` to bump to 5.0.0 + - Create release + +## Risks and Mitigations + +### Risk 1: Missing References + +**Risk:** Some `.peter-hook.toml` references may be missed in bulk updates. + +**Mitigation:** +- Use comprehensive grep/ripgrep search after changes +- Run full test suite (all 425+ occurrences should be covered by tests) +- Manual testing with both old and new config names +- Search for literal string `".peter-hook.toml"` in codebase after changes + +### Risk 2: Breaking User Workflows + +**Risk:** Users' existing workflows will break immediately on upgrade. + +**Mitigation:** +- Clear, actionable error messages with exact fix commands +- Major version bump (5.0.0) signals breaking change +- Comprehensive migration guide in CHANGELOG +- Release notes with prominent migration instructions +- Consider GitHub release announcement with migration guide + +### Risk 3: Import Paths in Library Files + +**Risk:** Library files imported by projects may have wrong references. + +**Mitigation:** +- Library files like `hooks.lib.toml` keep their names (they're imported, not discovered) +- Only the main config file that peter-hook searches for needs `.peter-hook.toml` name +- Imported files can have any name +- Document this clearly in migration guide + +### Risk 4: CI/CD Pipeline Breakage + +**Risk:** Users' CI pipelines may break if they reference `.peter-hook.toml` in scripts. + +**Mitigation:** +- Major version bump signals need for review before upgrading +- Error message clearly shows what changed +- Users can pin to 4.x versions if not ready to migrate +- Document migration in release notes + +## Testing Validation Checklist + +Before release, verify: + +- [ ] All existing tests pass with new config name +- [ ] New deprecation tests verify error behavior +- [ ] Manual testing: `hooks.toml` triggers error with correct message +- [ ] Manual testing: `.peter-hook.toml` works correctly +- [ ] Manual testing: Multiple deprecated files all listed in error +- [ ] Manual testing: `version` and `license` commands work even with `hooks.toml` +- [ ] Manual testing: All other commands fail with `hooks.toml` +- [ ] Grep/ripgrep search finds no remaining `hooks.toml` references in code (except deprecation checker) +- [ ] Documentation examples all use `.peter-hook.toml` +- [ ] Example files renamed and working +- [ ] Project's own config renamed and working + +## Files Requiring Changes + +**Source code (8 files):** +- `src/hooks/resolver.rs` - Update `find_config_file()` +- `src/main.rs` - Add deprecation check +- `src/git/installer.rs` - Update comments/references +- `src/hooks/executor.rs` - Update comments +- `src/hooks/hierarchical.rs` - Update references +- `src/doctor.rs` - Update references +- `src/config/parser.rs` - Update comments +- `src/config/global.rs` - Update comments + +**Test files (~50 files):** +- All files in `tests/` directory +- Add new `tests/deprecation_tests.rs` + +**Documentation (10 files):** +- `README.md` +- `CLAUDE.md` (root and project) +- `docs/*.rst` (all docs) +- `CHANGELOG.md` + +**Examples (5 files):** +- All files in `examples/` directory (rename and update) + +**Project config:** +- `hooks.toml` → `.peter-hook.toml` + +**Total:** ~75 files requiring updates + +## Success Criteria + +1. ✅ Peter-hook searches for `.peter-hook.toml` instead of `hooks.toml` +2. ✅ Presence of `hooks.toml` causes immediate error with helpful message +3. ✅ Error message lists ALL deprecated files in repository +4. ✅ Error message provides exact fix command +5. ✅ All tests pass with new configuration name +6. ✅ All documentation updated +7. ✅ Version bumped to 5.0.0 +8. ✅ CHANGELOG has comprehensive migration guide diff --git a/docs/plans/2025-12-10-config-rename-implementation.md b/docs/plans/2025-12-10-config-rename-implementation.md new file mode 100644 index 0000000..492f96b --- /dev/null +++ b/docs/plans/2025-12-10-config-rename-implementation.md @@ -0,0 +1,861 @@ +# Configuration File Rename Implementation Plan + +> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. + +**Goal:** Rename configuration file from `.peter-hook.toml` to `.peter-hook.toml` with deprecation detection + +**Architecture:** Update config file discovery to search for `.peter-hook.toml`, add early validation that detects and reports all deprecated `.peter-hook.toml` files in repository, fail immediately with helpful error message listing all files and fix commands. + +**Tech Stack:** Rust 1.86.0, walkdir crate for filesystem traversal, ignore crate for .gitignore respect + +--- + +## Task 1: Add deprecation detection function + +**Files:** +- Modify: `src/main.rs` (add new function around line 100) +- No tests yet (will add in Task 3) + +**Step 1: Add walkdir and ignore crate dependencies** + +Add to `Cargo.toml` dependencies section (if not already present): + +```toml +walkdir = "2" +ignore = "0.4" +``` + +Expected: These crates are likely already dependencies. Check first with `grep walkdir Cargo.toml`. + +**Step 2: Import required modules in src/main.rs** + +Add these imports at the top of `src/main.rs`: + +```rust +use walkdir::WalkDir; +use ignore::WalkBuilder; +``` + +**Step 3: Write deprecation detection function** + +Add this function in `src/main.rs` after the imports and before `main()`: + +```rust +/// Check for deprecated .peter-hook.toml files in the repository +/// +/// Walks the repository tree respecting .gitignore and collects all +/// .peter-hook.toml files. If any are found, prints error message and exits. +fn check_for_deprecated_config_files() -> Result<()> { + use std::env; + + // Try to find repository root + let current_dir = env::current_dir().context("Failed to get current directory")?; + + let repo = match GitRepository::find_from_dir(¤t_dir) { + Ok(repo) => repo, + Err(_) => { + // Not in a git repository, skip check + return Ok(()); + } + }; + + let repo_root = repo.git_dir().parent() + .context("Failed to get repository root")?; + + // Walk repository respecting .gitignore + let mut deprecated_files = Vec::new(); + + for entry in WalkBuilder::new(repo_root) + .hidden(false) // Include hidden directories + .git_ignore(true) // Respect .gitignore + .build() + { + let entry = entry.context("Failed to read directory entry")?; + + if entry.file_type().map_or(false, |ft| ft.is_file()) { + if let Some(file_name) = entry.path().file_name() { + if file_name == ".peter-hook.toml" { + // Store relative path from repo root + let relative_path = entry.path() + .strip_prefix(repo_root) + .unwrap_or(entry.path()); + deprecated_files.push(relative_path.to_path_buf()); + } + } + } + } + + if !deprecated_files.is_empty() { + eprintln!("Error: .peter-hook.toml is no longer supported. Rename to .peter-hook.toml\n"); + eprintln!("Found deprecated files:"); + for file in &deprecated_files { + eprintln!(" - {}", file.display()); + } + eprintln!("\nRun in each directory: mv .peter-hook.toml .peter-hook.toml"); + std::process::exit(1); + } + + Ok(()) +} +``` + +**Step 4: Hook deprecation check into main()** + +Find the `main()` function in `src/main.rs` and add the check early, right after CLI parsing but before command dispatch. + +Find this code pattern (around line 150-200): + +```rust +#[tokio::main] +async fn main() -> Result<()> { + let cli = Cli::parse(); + + // Debug/trace setup code... +``` + +Add the deprecation check after CLI parsing and before the match statement on `cli.command`: + +```rust +#[tokio::main] +async fn main() -> Result<()> { + let cli = Cli::parse(); + + // Debug/trace setup code... + + // Check for deprecated config files (skip for version/license commands) + match &cli.command { + Commands::Version | Commands::License => { + // Skip deprecation check for these commands + } + _ => { + // Run deprecation check for all other commands + check_for_deprecated_config_files()?; + } + } + + // Rest of main() continues... + match cli.command { +``` + +**Step 5: Verify it compiles** + +Run: `rustup run 1.86.0 cargo build` + +Expected: SUCCESS (no errors) + +**Step 6: Commit deprecation detection** + +```bash +git add Cargo.toml src/main.rs +git commit -m "feat: add deprecation detection for .peter-hook.toml files" +``` + +--- + +## Task 2: Update core resolver to use .peter-hook.toml + +**Files:** +- Modify: `src/hooks/resolver.rs:76-82` + +**Step 1: Update find_config_file() to search for .peter-hook.toml** + +In `src/hooks/resolver.rs`, find the `find_config_file()` method (around line 76): + +```rust +pub fn find_config_file(&self) -> Result> { + let mut current = self.current_dir.as_path(); + + loop { + let config_path = current.join(".peter-hook.toml"); + if config_path.exists() { + return Ok(Some(config_path)); + } +``` + +Change to: + +```rust +pub fn find_config_file(&self) -> Result> { + let mut current = self.current_dir.as_path(); + + loop { + let config_path = current.join(".peter-hook.toml"); + if config_path.exists() { + return Ok(Some(config_path)); + } +``` + +**Step 2: Verify it compiles** + +Run: `rustup run 1.86.0 cargo build` + +Expected: SUCCESS + +**Step 3: Commit resolver update** + +```bash +git add src/hooks/resolver.rs +git commit -m "feat: update resolver to search for .peter-hook.toml" +``` + +--- + +## Task 3: Add deprecation tests + +**Files:** +- Create: `tests/deprecation_tests.rs` + +**Step 1: Create deprecation test file** + +Create `tests/deprecation_tests.rs`: + +```rust +use assert_cmd::Command; +use std::fs; +use tempfile::TempDir; + +/// Helper to create a test repository with a .peter-hook.toml file +fn create_repo_with_deprecated_config() -> (TempDir, std::path::PathBuf) { + let temp_dir = TempDir::new().unwrap(); + + // Initialize git repo + std::process::Command::new("git") + .args(["init"]) + .current_dir(temp_dir.path()) + .output() + .expect("Failed to init git repo"); + + // Create deprecated .peter-hook.toml + let config_path = temp_dir.path().join(".peter-hook.toml"); + fs::write(&config_path, "[hooks.test]\ncommand = \"echo test\"\nmodifies_repository = false\n") + .unwrap(); + + (temp_dir, config_path) +} + +#[test] +fn test_deprecation_error_on_single_file() { + let (temp_dir, _) = create_repo_with_deprecated_config(); + + // Try to run any command (except version/license) + let mut cmd = Command::cargo_bin("peter-hook").unwrap(); + let output = cmd + .arg("validate") + .current_dir(temp_dir.path()) + .output() + .unwrap(); + + // Should exit with error + assert!(!output.status.success()); + + let stderr = String::from_utf8_lossy(&output.stderr); + + // Check error message contains key information + assert!(stderr.contains(".peter-hook.toml is no longer supported")); + assert!(stderr.contains(".peter-hook.toml")); + assert!(stderr.contains(".peter-hook.toml")); // Should list the file + assert!(stderr.contains("mv .peter-hook.toml .peter-hook.toml")); // Should show fix +} + +#[test] +fn test_deprecation_error_lists_multiple_files() { + let temp_dir = TempDir::new().unwrap(); + + // Initialize git repo + std::process::Command::new("git") + .args(["init"]) + .current_dir(temp_dir.path()) + .output() + .expect("Failed to init git repo"); + + // Create multiple deprecated .peter-hook.toml files + fs::write( + temp_dir.path().join(".peter-hook.toml"), + "[hooks.test]\ncommand = \"echo test\"\nmodifies_repository = false\n" + ).unwrap(); + + fs::create_dir_all(temp_dir.path().join("backend")).unwrap(); + fs::write( + temp_dir.path().join("backend/.peter-hook.toml"), + "[hooks.test]\ncommand = \"echo test\"\nmodifies_repository = false\n" + ).unwrap(); + + fs::create_dir_all(temp_dir.path().join("frontend")).unwrap(); + fs::write( + temp_dir.path().join("frontend/.peter-hook.toml"), + "[hooks.test]\ncommand = \"echo test\"\nmodifies_repository = false\n" + ).unwrap(); + + // Try to run validate + let mut cmd = Command::cargo_bin("peter-hook").unwrap(); + let output = cmd + .arg("validate") + .current_dir(temp_dir.path()) + .output() + .unwrap(); + + assert!(!output.status.success()); + + let stderr = String::from_utf8_lossy(&output.stderr); + + // Should list all three files + assert!(stderr.contains(".peter-hook.toml")); + assert!(stderr.contains("backend/.peter-hook.toml") || stderr.contains("backend\\.peter-hook.toml")); + assert!(stderr.contains("frontend/.peter-hook.toml") || stderr.contains("frontend\\.peter-hook.toml")); +} + +#[test] +fn test_version_command_bypasses_deprecation_check() { + let (temp_dir, _) = create_repo_with_deprecated_config(); + + // Version command should work even with deprecated config + let mut cmd = Command::cargo_bin("peter-hook").unwrap(); + let output = cmd + .arg("version") + .current_dir(temp_dir.path()) + .output() + .unwrap(); + + // Should succeed + assert!(output.status.success()); + + let stdout = String::from_utf8_lossy(&output.stdout); + assert!(stdout.contains("peter-hook")); // Should show version +} + +#[test] +fn test_license_command_bypasses_deprecation_check() { + let (temp_dir, _) = create_repo_with_deprecated_config(); + + // License command should work even with deprecated config + let mut cmd = Command::cargo_bin("peter-hook").unwrap(); + let output = cmd + .arg("license") + .current_dir(temp_dir.path()) + .output() + .unwrap(); + + // Should succeed + assert!(output.status.success()); + + let stdout = String::from_utf8_lossy(&output.stdout); + assert!(stdout.contains("MIT") || stdout.contains("Apache")); // Should show license +} + +#[test] +fn test_new_config_name_works() { + let temp_dir = TempDir::new().unwrap(); + + // Initialize git repo + std::process::Command::new("git") + .args(["init"]) + .current_dir(temp_dir.path()) + .output() + .expect("Failed to init git repo"); + + // Create NEW config with correct name + fs::write( + temp_dir.path().join(".peter-hook.toml"), + "[hooks.test]\ncommand = \"echo test\"\nmodifies_repository = false\n" + ).unwrap(); + + // Validate should work + let mut cmd = Command::cargo_bin("peter-hook").unwrap(); + let output = cmd + .arg("validate") + .current_dir(temp_dir.path()) + .output() + .unwrap(); + + // Should succeed or show "Configuration is valid" + let stderr = String::from_utf8_lossy(&output.stderr); + let stdout = String::from_utf8_lossy(&output.stdout); + + // Should NOT contain deprecation error + assert!(!stderr.contains(".peter-hook.toml is no longer supported")); + assert!(!stdout.contains(".peter-hook.toml is no longer supported")); +} +``` + +**Step 2: Run deprecation tests** + +Run: `rustup run 1.86.0 cargo test --test deprecation_tests` + +Expected: All tests should PASS + +**Step 3: Commit deprecation tests** + +```bash +git add tests/deprecation_tests.rs +git commit -m "test: add deprecation detection tests" +``` + +--- + +## Task 4: Bulk update test files + +**Files:** +- Modify: All files in `tests/` directory (~50 files) + +**Step 1: Find all test files with .peter-hook.toml references** + +Run: `grep -r "hooks\.toml" tests/ --files-with-matches` + +Expected: List of ~30-40 test files + +**Step 2: Bulk replace .peter-hook.toml with .peter-hook.toml in tests** + +Run in worktree root: + +```bash +find tests/ -name "*.rs" -type f -exec sed -i '' 's/hooks\.toml/.peter-hook.toml/g' {} + +``` + +Note: On Linux, use `sed -i` without the empty string argument. + +**Step 3: Verify changes look correct** + +Run: `git diff tests/ | head -100` + +Expected: See changes like: +- `".peter-hook.toml"` → `".peter-hook.toml"` +- `.join(".peter-hook.toml")` → `.join(".peter-hook.toml")` +- Test strings updated + +**Step 4: Run all tests to verify** + +Run: `rustup run 1.86.0 cargo test --all` + +Expected: All tests PASS (may take 2-3 minutes) + +If tests fail, review failures and fix individually. Common issues: +- Hardcoded paths that need manual update +- Test assertions checking for specific strings +- File existence checks + +**Step 5: Commit test updates** + +```bash +git add tests/ +git commit -m "test: update all tests to use .peter-hook.toml" +``` + +--- + +## Task 5: Update source code comments and strings + +**Files:** +- Modify: `src/git/installer.rs` (comments around line 286, 299) +- Modify: `src/main.rs` (error messages around line 352, 819) + +**Step 1: Update installer comments** + +In `src/git/installer.rs`, find the git hook script template comments (around lines 286-299): + +Change: +```rust +# Edit your .peter-hook.toml configuration instead +``` + +To: +```rust +# Edit your .peter-hook.toml configuration instead +``` + +**Step 2: Update error message in main.rs** + +In `src/main.rs`, find the "No .peter-hook.toml file found" message (around line 819): + +Change: +```rust +println!("No .peter-hook.toml file found in current directory or parent directories"); +``` + +To: +```rust +println!("No .peter-hook.toml file found in current directory or parent directories"); +``` + +**Step 3: Update hint message in main.rs** + +In `src/main.rs`, find the hint about checking configuration (around line 352): + +Change: +```rust +println!("💡 \x1b[36mTip:\x1b[0m Check your \x1b[33m.peter-hook.toml\x1b[0m configuration"); +``` + +To: +```rust +println!("💡 \x1b[36mTip:\x1b[0m Check your \x1b[33m.peter-hook.toml\x1b[0m configuration"); +``` + +**Step 4: Search for any remaining .peter-hook.toml in source** + +Run: `grep -r "hooks\.toml" src/ --color` + +Expected: Should only find references in the deprecation checker function we wrote + +**Step 5: Verify compilation** + +Run: `rustup run 1.86.0 cargo build` + +Expected: SUCCESS + +**Step 6: Commit source code updates** + +```bash +git add src/ +git commit -m "refactor: update comments and error messages to reference .peter-hook.toml" +``` + +--- + +## Task 6: Update documentation files + +**Files:** +- Modify: `README.md` +- Modify: `CLAUDE.md` +- Modify: `docs/configuration.rst` +- Modify: `docs/quickstart.rst` +- Modify: `docs/architecture.rst` +- Modify: `docs/examples.rst` +- Modify: `docs/global_config.rst` +- Modify: `docs/cli.rst` +- Modify: `docs/overview.rst` +- Modify: `docs/templating.rst` + +**Step 1: Bulk replace in documentation** + +Run in worktree root: + +```bash +find docs/ README.md CLAUDE.md -type f \( -name "*.md" -o -name "*.rst" \) -exec sed -i '' 's/hooks\.toml/.peter-hook.toml/g' {} + +``` + +**Step 2: Verify changes** + +Run: `git diff docs/ README.md CLAUDE.md | head -200` + +Expected: See documentation examples updated with new filename + +**Step 3: Check for any remaining references** + +Run: `grep -r "hooks\.toml" docs/ README.md CLAUDE.md` + +Expected: No results (all should be updated) + +**Step 4: Manually review README.md** + +The README is user-facing and critical. Open `README.md` and verify: +- Quick Start section uses `.peter-hook.toml` +- All code examples use correct filename +- Installation instructions make sense + +**Step 5: Commit documentation updates** + +```bash +git add docs/ README.md CLAUDE.md +git commit -m "docs: update all documentation to reference .peter-hook.toml" +``` + +--- + +## Task 7: Rename and update example files + +**Files:** +- Rename: `examples/file-targeting.toml` → `examples/.peter-hook-file-targeting.toml` +- Rename: `examples/parallel-.peter-hook.toml` → `examples/.peter-hook-parallel.toml` +- Rename: `examples/advanced-features.toml` → `examples/.peter-hook-advanced.toml` +- Rename: `examples/hooks-with-imports.toml` → `examples/.peter-hook-with-imports.toml` +- Keep: `examples/hooks.lib.toml` (it's a library file, not main config) + +**Step 1: Rename example files** + +Run these commands: + +```bash +git mv examples/file-targeting.toml examples/.peter-hook-file-targeting.toml +git mv examples/parallel-.peter-hook.toml examples/.peter-hook-parallel.toml +git mv examples/advanced-features.toml examples/.peter-hook-advanced.toml +git mv examples/hooks-with-imports.toml examples/.peter-hook-with-imports.toml +``` + +**Step 2: Update content of example files** + +Update any internal references to `.peter-hook.toml` in the example files: + +Run: `sed -i '' 's/hooks\.toml/.peter-hook.toml/g' examples/.peter-hook-*.toml` + +**Step 3: Update docs/examples.rst** + +In `docs/examples.rst`, update any references to the renamed example files. + +**Step 4: Verify examples** + +Run: `ls -la examples/` + +Expected: Should see `.peter-hook-*.toml` files and `hooks.lib.toml` + +**Step 5: Commit example file renames** + +```bash +git add examples/ +git commit -m "refactor: rename example files to use .peter-hook-*.toml naming" +``` + +--- + +## Task 8: Rename project's own configuration file + +**Files:** +- Rename: `.peter-hook.toml` → `.peter-hook.toml` + +**Step 1: Rename the project's own config file** + +Run in worktree root: + +```bash +git mv .peter-hook.toml .peter-hook.toml +``` + +**Step 2: Verify the project still works** + +Run: `rustup run 1.86.0 cargo run -- validate` + +Expected: Should show "✓ Configuration is valid" + +**Step 3: Test hook installation still works** + +Run: `rustup run 1.86.0 cargo run -- install --force` + +Expected: Should install hooks successfully + +**Step 4: Commit config rename** + +```bash +git add .peter-hook.toml +git commit -m "refactor: rename project config to .peter-hook.toml" +``` + +--- + +## Task 9: Update CHANGELOG and bump version + +**Files:** +- Modify: `CHANGELOG.md` +- Modify: `VERSION` +- Modify: `Cargo.toml` + +**Step 1: Add CHANGELOG entry** + +Add this entry at the top of `CHANGELOG.md` (after the header): + +```markdown +## [5.0.0] - 2025-12-10 + +### BREAKING CHANGES + +- **Configuration file renamed from `.peter-hook.toml` to `.peter-hook.toml`** + - Peter-hook now searches for `.peter-hook.toml` instead of `.peter-hook.toml` + - If `.peter-hook.toml` files are detected, peter-hook will error and refuse to run + - Migration: Rename all `.peter-hook.toml` files to `.peter-hook.toml` + - Commands affected: All commands except `version` and `license` + +**Migration guide:** + +For single configuration: +```bash +mv .peter-hook.toml .peter-hook.toml +``` + +For monorepos with multiple configurations: +```bash +# Find all deprecated files +find . -name ".peter-hook.toml" -type f + +# Rename each one +cd backend && mv .peter-hook.toml .peter-hook.toml +cd ../frontend && mv .peter-hook.toml .peter-hook.toml +``` + +**Why this change:** +- Follows dotfile conventions for tool configuration +- Reduces visual clutter in repository root +- Makes the file more discoverable as tool-specific config +- Aligns with modern CLI tool practices +``` + +**Step 2: Use versioneer to bump to 5.0.0** + +Run: `versioneer major` + +Expected: Updates VERSION and Cargo.toml to 5.0.0 + +**Step 3: Verify version synchronization** + +Run: `cat VERSION && grep '^version' Cargo.toml | head -1` + +Expected: Both should show 5.0.0 + +**Step 4: Commit version bump** + +```bash +git add CHANGELOG.md VERSION Cargo.toml +git commit -m "chore: bump version to 5.0.0" +``` + +--- + +## Task 10: Final verification + +**Files:** +- None (verification only) + +**Step 1: Run full test suite** + +Run: `rustup run 1.86.0 cargo test --all` + +Expected: All tests PASS + +**Step 2: Search for any remaining .peter-hook.toml references** + +Run: `grep -r "hooks\.toml" --exclude-dir=target --exclude-dir=.git` + +Expected: Should only find: +- In deprecation checker code (src/main.rs) +- In deprecation test file (tests/deprecation_tests.rs) +- Possibly in CHANGELOG.md (explaining the change) + +**Step 3: Build release binary** + +Run: `rustup run 1.86.0 cargo build --release` + +Expected: SUCCESS + +**Step 4: Manual smoke test with deprecated config** + +```bash +# Create temp directory with old config +mkdir -p /tmp/test-peter-hook +cd /tmp/test-peter-hook +git init +echo '[hooks.test]\ncommand = "echo test"\nmodifies_repository = false' > .peter-hook.toml + +# Try to run peter-hook +/path/to/worktree/target/release/peter-hook validate +``` + +Expected: Should show error message about deprecated config + +**Step 5: Manual smoke test with new config** + +```bash +# Rename to new config +mv .peter-hook.toml .peter-hook.toml + +# Try to run peter-hook +/path/to/worktree/target/release/peter-hook validate +``` + +Expected: Should show "✓ Configuration is valid" + +**Step 6: Review git log** + +Run: `git log --oneline` + +Expected: Should see clean commit history with all tasks completed + +--- + +## Task 11: Prepare for merge + +**Files:** +- None (git operations only) + +**Step 1: Push branch to remote** + +Run: `git push -u origin feature/config-rename-5.0.0` + +Expected: Branch pushed successfully + +**Step 2: Switch back to main worktree** + +Run: `cd /Users/jfb/Projects/rust/peter-hook` + +**Step 3: Create pull request** + +Use GitHub CLI or web interface: + +```bash +gh pr create \ + --title "feat!: rename configuration file from .peter-hook.toml to .peter-hook.toml" \ + --body "$(cat <<'EOF' +## Summary + +Breaking change: Renames configuration file from `.peter-hook.toml` to `.peter-hook.toml`. + +## Changes + +- Config discovery now searches for `.peter-hook.toml` +- Deprecation detection: errors if `.peter-hook.toml` files exist +- Error message lists all deprecated files with fix instructions +- Updated all tests, docs, and examples +- Version bumped to 5.0.0 + +## Migration + +For users upgrading from 4.x to 5.0.0: + +```bash +# Single config +mv .peter-hook.toml .peter-hook.toml + +# Monorepo with multiple configs +find . -name ".peter-hook.toml" -exec sh -c 'mv "$1" "$(dirname "$1")/.peter-hook.toml"' _ {} \; +``` + +## Testing + +- ✅ All existing tests updated and passing +- ✅ New deprecation tests added +- ✅ Manual testing with both old and new configs +- ✅ Verified error messages are helpful + +## Breaking Change Notice + +This is a major version bump (5.0.0) due to the breaking change in configuration file naming. + +Closes #XXX (if there's an issue) +EOF +)" +``` + +**Step 4: Request review (if team workflow)** + +Add reviewers or wait for CI to pass. + +--- + +## Success Criteria + +- [ ] All tests pass with new configuration name +- [ ] Deprecation detection works and lists all files +- [ ] Error messages are clear and actionable +- [ ] `version` and `license` commands bypass check +- [ ] All documentation updated +- [ ] All examples updated +- [ ] Project's own config renamed +- [ ] Version bumped to 5.0.0 +- [ ] CHANGELOG has migration guide +- [ ] No remaining `.peter-hook.toml` references except in deprecation code + +--- + +## Notes + +- This is a breaking change requiring major version bump +- Users will need to rename their config files manually +- Error message provides exact commands to fix +- Library files (like `hooks.lib.toml`) keep their names +- Only main config files need to be renamed diff --git a/docs/quickstart.rst b/docs/quickstart.rst index 1a902a8..a432c5f 100644 --- a/docs/quickstart.rst +++ b/docs/quickstart.rst @@ -1,7 +1,7 @@ Quickstart ========== -1. Create ``hooks.toml`` in your repo (or subdirectory): +1. Create ``.peter-hook.toml`` in your repo (or subdirectory): .. code-block:: toml diff --git a/docs/security-audit-results.md b/docs/security-audit-results.md index be1312f..54d1e4d 100644 --- a/docs/security-audit-results.md +++ b/docs/security-audit-results.md @@ -20,7 +20,7 @@ The template expansion system in peter-hook has been thoroughly audited for comm Peter-hook uses a **whitelist-only** approach for template variables: **Allowed Variables**: -- `{HOOK_DIR}` - Directory containing hooks.toml +- `{HOOK_DIR}` - Directory containing .peter-hook.toml - `{REPO_ROOT}` - Git repository root - `{HOME_DIR}` - User home directory ($HOME) - `{PATH}` - Current PATH for extending @@ -377,7 +377,7 @@ Peter-hook protects against: ### Out of Scope Peter-hook does NOT protect against: -- Malicious hooks.toml files (user controls config) +- Malicious .peter-hook.toml files (user controls config) - Compromised repositories (user controls repo) - OS-level vulnerabilities (relies on system security) - Network-based attacks (no network functionality) @@ -421,7 +421,7 @@ Peter-hook does NOT protect against: ### For Users 1. **Trust Your Repository**: Only run peter-hook in repositories you trust -2. **Review Hooks**: Inspect hooks.toml files before running hooks +2. **Review Hooks**: Inspect .peter-hook.toml files before running hooks 3. **Use `{CHANGED_FILES_FILE}`**: For maximum safety with filenames 4. **Keep Updated**: Install security updates promptly diff --git a/docs/stress-test-results.md b/docs/stress-test-results.md index 4b14be1..ddc400e 100644 --- a/docs/stress-test-results.md +++ b/docs/stress-test-results.md @@ -18,7 +18,7 @@ All tests pass with excellent performance characteristics: ### 1. Deep Hierarchy Resolution (10 Levels) **Test**: `test_deep_hierarchy_10_levels` -- **Setup**: 10-level nested directory structure, each with hooks.toml +- **Setup**: 10-level nested directory structure, each with .peter-hook.toml - **Behavior**: Nearest configuration wins (hierarchical resolution) - **Result**: ~520ms - **Limit**: 2 seconds diff --git a/docs/templating.rst b/docs/templating.rst index a974b63..3977934 100644 --- a/docs/templating.rst +++ b/docs/templating.rst @@ -6,12 +6,12 @@ Template variables are available in ``command``, ``workdir``, and ``env`` fields Built-in Variables ------------------ -- ``{HOOK_DIR}``: Directory containing the ``hooks.toml`` +- ``{HOOK_DIR}``: Directory containing the ``.peter-hook.toml`` - ``{WORKING_DIR}``: Working directory when the hook runs - ``{REPO_ROOT}``: Git repository root - ``{HOOK_DIR_REL}``: ``HOOK_DIR`` relative to repo root - ``{WORKING_DIR_REL}``: ``WORKING_DIR`` relative to repo root -- ``{PROJECT_NAME}``: Name of directory containing ``hooks.toml`` +- ``{PROJECT_NAME}``: Name of directory containing ``.peter-hook.toml`` - ``{HOME_DIR}``: User home directory - ``{PATH}``: Current PATH environment variable (useful for extending PATH) - ``{IS_WORKTREE}``: "true" or "false" - whether running in a worktree diff --git a/docs/test-plan.md b/docs/test-plan.md index 22542ec..454c778 100644 --- a/docs/test-plan.md +++ b/docs/test-plan.md @@ -168,7 +168,7 @@ timeout_seconds = 600 # Override default: 10 minutes 1. **Deep Hierarchy (10 levels)** - 520ms (limit: 2s) ✅ - 10-level nested directory structure - - Each level has its own hooks.toml + - Each level has its own .peter-hook.toml - Hierarchical resolution performance 2. **Large File Set (1000 files)** - 35ms execution (limit: 5s) ✅ diff --git a/examples/advanced-features.toml b/examples/.peter-hook-advanced.toml similarity index 100% rename from examples/advanced-features.toml rename to examples/.peter-hook-advanced.toml diff --git a/examples/file-targeting.toml b/examples/.peter-hook-file-targeting.toml similarity index 100% rename from examples/file-targeting.toml rename to examples/.peter-hook-file-targeting.toml diff --git a/examples/parallel-hooks.toml b/examples/.peter-hook-parallel.toml similarity index 100% rename from examples/parallel-hooks.toml rename to examples/.peter-hook-parallel.toml diff --git a/examples/hooks-with-imports.toml b/examples/.peter-hook-with-imports.toml similarity index 87% rename from examples/hooks-with-imports.toml rename to examples/.peter-hook-with-imports.toml index 91d8abb..6eb6be5 100644 --- a/examples/hooks-with-imports.toml +++ b/examples/.peter-hook-with-imports.toml @@ -1,4 +1,4 @@ -# Example project hooks.toml using an imported hooks library +# Example project .peter-hook.toml using an imported hooks library # Import the reusable library in this directory imports = ["./hooks.lib.toml"] diff --git a/examples/hooks.lib.toml b/examples/hooks.lib.toml index 661b9a7..658f0a6 100644 --- a/examples/hooks.lib.toml +++ b/examples/hooks.lib.toml @@ -1,4 +1,4 @@ -# Example Hooks Library (import into your project's hooks.toml) +# Example Hooks Library (import into your project's .peter-hook.toml) # ===== RUST BACKEND ===== [hooks.rust-format] diff --git a/src/config/global.rs b/src/config/global.rs index aa4e958..d645f20 100644 --- a/src/config/global.rs +++ b/src/config/global.rs @@ -266,7 +266,7 @@ mod tests { assert!(config.is_absolute_path_allowed(&test_file).unwrap()); - let outside_file = home_dir.join("other-dir").join("hooks.toml"); + let outside_file = home_dir.join("other-dir").join(".peter-hook.toml"); if let Some(parent) = outside_file.parent() { if let Err(err) = fs::create_dir_all(parent) { if err.kind() == std::io::ErrorKind::PermissionDenied { diff --git a/src/config/parser.rs b/src/config/parser.rs index a7869ef..ca1fd1e 100644 --- a/src/config/parser.rs +++ b/src/config/parser.rs @@ -11,7 +11,7 @@ use std::{ use crate::config::GlobalConfig; -/// Represents a hook configuration file (hooks.toml) +/// Represents a hook configuration file (.peter-hook.toml) #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] pub struct HookConfig { /// Individual hook definitions @@ -146,7 +146,7 @@ impl HookGroup { } impl HookConfig { - /// Parse a hooks.toml file from the given path + /// Parse a .peter-hook.toml file from the given path /// /// # Errors /// @@ -156,7 +156,7 @@ impl HookConfig { Self::from_file_internal(path.as_ref(), &mut visited, None) } - /// Parse a hooks.toml file and collect import diagnostics + /// Parse a .peter-hook.toml file and collect import diagnostics /// /// # Errors /// @@ -403,7 +403,7 @@ impl HookConfig { }) } - /// Parse a hooks.toml configuration from a string + /// Parse a .peter-hook.toml configuration from a string /// /// # Errors /// @@ -683,7 +683,7 @@ modifies_repository = true // Simulate git repo root std::fs::create_dir_all(dir.join(".git")).unwrap(); let lib = dir.join("hooks.lib.toml"); - let base = dir.join("hooks.toml"); + let base = dir.join(".peter-hook.toml"); fs::write( &lib, @@ -768,7 +768,7 @@ includes = ["common", "lint", "test"] let td = TempDir::new().unwrap(); let dir = td.path(); std::fs::create_dir_all(dir.join(".git")).unwrap(); - let base = dir.join("hooks.toml"); + let base = dir.join(".peter-hook.toml"); fs::write(&base, "imports = [\"/etc/passwd\"]\n").unwrap(); let err = HookConfig::from_file(&base).unwrap_err(); // Now we expect it to be rejected because it's not in home directory/allowlist @@ -786,8 +786,8 @@ includes = ["common", "lint", "test"] // file outside repo let outside = outer_dir.join("evil.toml"); fs::write(&outside, "[hooks.bad]\ncommand=\"echo bad\"\n").unwrap(); - // hooks.toml at repo root trying to import ../evil.toml - let base = outer_dir.join("repo/hooks.toml"); + // .peter-hook.toml at repo root trying to import ../evil.toml + let base = outer_dir.join("repo/.peter-hook.toml"); fs::write(&base, "imports = [\"../evil.toml\"]\n").unwrap(); let err = HookConfig::from_file(&base).unwrap_err(); assert!(format!("{err:#}").contains("outside repository root")); @@ -1120,7 +1120,7 @@ command = "echo 'default behavior'" fs::create_dir_all(&repo_root).unwrap(); fs::create_dir_all(repo_root.join(".git")).unwrap(); - let hooks_file = repo_root.join("hooks.toml"); + let hooks_file = repo_root.join(".peter-hook.toml"); // Try to import absolute path not in allowlist let toml_content = format!( @@ -1156,7 +1156,7 @@ command = "echo test" fs::create_dir_all(&repo_root).unwrap(); fs::create_dir_all(repo_root.join(".git")).unwrap(); - let hooks_file = repo_root.join("hooks.toml"); + let hooks_file = repo_root.join(".peter-hook.toml"); // Test with a peter-hook directory path (this will likely fail in CI/tests // but demonstrates the intended usage) @@ -1227,7 +1227,7 @@ command = "rm -rf /" fs::create_dir_all(&repo_root).unwrap(); fs::create_dir_all(repo_root.join(".git")).unwrap(); - let hooks_file = repo_root.join("hooks.toml"); + let hooks_file = repo_root.join(".peter-hook.toml"); let toml_content = format!( r#" imports = ["{}"] @@ -1276,7 +1276,7 @@ command = "echo shared" .unwrap(); // Create main config with relative import - let hooks_file = repo_root.join("hooks.toml"); + let hooks_file = repo_root.join(".peter-hook.toml"); fs::write( &hooks_file, r#" @@ -1342,7 +1342,7 @@ includes = ["lint"] let td = TempDir::new().unwrap(); let dir = td.path(); std::fs::create_dir_all(dir.join(".git")).unwrap(); - let config_file = dir.join("hooks.toml"); + let config_file = dir.join(".peter-hook.toml"); fs::write( &config_file, diff --git a/src/doctor.rs b/src/doctor.rs index 519c713..18c0c28 100644 --- a/src/doctor.rs +++ b/src/doctor.rs @@ -110,7 +110,7 @@ fn check_configuration() -> Vec { if hook_names.is_empty() { checks.push(DoctorCheck::fail( "Hook configuration", - "No hooks or groups defined in hooks.toml", + "No hooks or groups defined in .peter-hook.toml", )); } else { checks.push(DoctorCheck::pass(format!( @@ -130,7 +130,7 @@ fn check_configuration() -> Vec { Ok(None) => { checks.push(DoctorCheck::fail( "Configuration file", - "No hooks.toml found - create one to configure peter-hook", + "No .peter-hook.toml found - create one to configure peter-hook", )); } Err(e) => { diff --git a/src/git/installer.rs b/src/git/installer.rs index ca99f97..71c2d81 100644 --- a/src/git/installer.rs +++ b/src/git/installer.rs @@ -283,7 +283,7 @@ impl GitHookInstaller { r#"#!/bin/sh # Generated by peter-hook # Do not edit this file directly - it will be overwritten -# Edit your hooks.toml configuration instead +# Edit your .peter-hook.toml configuration instead exec "{}" run {} "$@" "#, @@ -296,7 +296,7 @@ exec "{}" run {} "$@" r#"#!/bin/sh # Generated by peter-hook # Do not edit this file directly - it will be overwritten -# Edit your hooks.toml configuration instead +# Edit your .peter-hook.toml configuration instead exec "{}" run {} "#, @@ -543,7 +543,7 @@ mod tests { let hooks_dir = git_dir.join("hooks"); std::fs::create_dir_all(&hooks_dir).unwrap(); - let config_path = temp_dir.join("hooks.toml"); + let config_path = temp_dir.join(".peter-hook.toml"); std::fs::write(&config_path, config_content).unwrap(); let repo = GitRepository::find_from_dir(temp_dir).unwrap(); diff --git a/src/hooks/executor.rs b/src/hooks/executor.rs index 5873cef..71974c6 100644 --- a/src/hooks/executor.rs +++ b/src/hooks/executor.rs @@ -1647,7 +1647,7 @@ mod tests { run_at_root: true, timeout_seconds: 300, }, - source_file: config_dir.join("hooks.toml"), + source_file: config_dir.join(".peter-hook.toml"), working_directory: config_dir.clone(), }; @@ -1667,7 +1667,7 @@ mod tests { timeout_seconds: 300, run_at_root: false, }, - source_file: config_dir.join("hooks.toml"), + source_file: config_dir.join(".peter-hook.toml"), working_directory: config_dir.clone(), }; diff --git a/src/hooks/hierarchical.rs b/src/hooks/hierarchical.rs index a188d50..a5ab295 100644 --- a/src/hooks/hierarchical.rs +++ b/src/hooks/hierarchical.rs @@ -1,7 +1,7 @@ //! Hierarchical hook resolution for monorepos //! //! This module implements per-file hook resolution where each changed file -//! finds its nearest hooks.toml and uses that configuration. This enables +//! finds its nearest .peter-hook.toml and uses that configuration. This enables //! monorepo-style setups where different subdirectories have different quality //! gates. @@ -28,9 +28,9 @@ pub struct ConfigGroup { pub resolved_hooks: ResolvedHooks, } -/// Find all hooks.toml files for a given file path from nearest to root +/// Find all .peter-hook.toml files for a given file path from nearest to root /// -/// Walks up from the file's directory to collect all hooks.toml files. +/// Walks up from the file's directory to collect all .peter-hook.toml files. /// Stops at the repository root. /// /// # Arguments @@ -60,7 +60,7 @@ fn find_all_configs_for_file(file_path: &Path, repo_root: &Path) -> Vec }; loop { - let config_path = current.join("hooks.toml"); + let config_path = current.join(".peter-hook.toml"); if config_path.exists() { configs.push(config_path); } @@ -282,7 +282,7 @@ fn merge_configs_for_event(config_paths: &[PathBuf], event: &str) -> Result PathBuf { - let config_path = dir.join("hooks.toml"); + let config_path = dir.join(".peter-hook.toml"); std::fs::write(&config_path, content).unwrap(); config_path } @@ -641,7 +641,7 @@ mod tests { let resolver = HookResolver::new(&nested); let config_path = resolver.find_config_file().unwrap().unwrap(); - assert_eq!(config_path, root.join("projects/hooks.toml")); + assert_eq!(config_path, root.join("projects/.peter-hook.toml")); } #[test] diff --git a/src/main.rs b/src/main.rs index 9b466c8..2b06fae 100644 --- a/src/main.rs +++ b/src/main.rs @@ -17,6 +17,59 @@ use std::{ io::{self, IsTerminal, Write}, process, }; +use ignore::WalkBuilder; + +/// Check for deprecated hooks.toml files in the repository +/// +/// Walks the repository tree respecting .gitignore and collects all +/// hooks.toml files. If any are found, prints error message and exits. +fn check_for_deprecated_config_files() -> Result<()> { + // Try to find repository root + let current_dir = env::current_dir().context("Failed to get current directory")?; + + let Ok(repo) = GitRepository::find_from_dir(¤t_dir) else { + // Not in a git repository, skip check + return Ok(()); + }; + + let repo_root = repo.git_dir.parent() + .context("Failed to get repository root")?; + + // Walk repository respecting .gitignore + let mut deprecated_files = Vec::new(); + + for entry in WalkBuilder::new(repo_root) + .hidden(false) // Include hidden directories + .git_ignore(true) // Respect .gitignore + .build() + { + let entry = entry.context("Failed to read directory entry")?; + + if entry.file_type().is_some_and(|ft| ft.is_file()) { + if let Some(file_name) = entry.path().file_name() { + if file_name == "hooks.toml" { + // Store relative path from repo root + let relative_path = entry.path() + .strip_prefix(repo_root) + .unwrap_or_else(|_| entry.path()); + deprecated_files.push(relative_path.to_path_buf()); + } + } + } + } + + if !deprecated_files.is_empty() { + eprintln!("Error: hooks.toml is no longer supported. Rename to .peter-hook.toml\n"); + eprintln!("Found deprecated files:"); + for file in &deprecated_files { + eprintln!(" - {}", file.display()); + } + eprintln!("\nRun in each directory: mv hooks.toml .peter-hook.toml"); + std::process::exit(1); + } + + Ok(()) +} fn main() { if let Err(e) = run() { @@ -38,6 +91,17 @@ fn run() -> Result<()> { debug::enable_trace(); } + // Check for deprecated config files (skip for version/license commands) + match &cli.command { + Commands::Version | Commands::License => { + // Skip deprecation check for these commands + } + _ => { + // Run deprecation check for all other commands + check_for_deprecated_config_files()?; + } + } + match cli.command { Commands::Install { force, @@ -349,7 +413,7 @@ fn run_hooks(event: &str, _git_args: &[String], all_files: bool, dry_run: bool) // No config groups found if io::stdout().is_terminal() { println!("❌ \x1b[33mNo hooks configured for event:\x1b[0m \x1b[1m{event}\x1b[0m"); - println!("💡 \x1b[36mTip:\x1b[0m Check your \x1b[33mhooks.toml\x1b[0m configuration"); + println!("💡 \x1b[36mTip:\x1b[0m Check your \x1b[33m.peter-hook.toml\x1b[0m configuration"); } else { println!("No hooks found for event: {event}"); } @@ -816,7 +880,7 @@ fn validate_config(trace_imports: bool, json: bool) -> Result<()> { } } None => { - println!("No hooks.toml file found in current directory or parent directories"); + println!("No .peter-hook.toml file found in current directory or parent directories"); } } diff --git a/tests/cli_integration_tests.rs b/tests/cli_integration_tests.rs index 0b3e704..eda2732 100644 --- a/tests/cli_integration_tests.rs +++ b/tests/cli_integration_tests.rs @@ -123,7 +123,7 @@ placeholder = true includes = [] "#; - fs::write(temp_dir.path().join("hooks.toml"), config).unwrap(); + fs::write(temp_dir.path().join(".peter-hook.toml"), config).unwrap(); let output = Command::new(bin_path()) .current_dir(temp_dir.path()) @@ -155,7 +155,7 @@ fn test_doctor_command() { fn test_validate_no_config() { let temp_dir = TempDir::new().unwrap(); - // Initialize git repo but no hooks.toml + // Initialize git repo but no .peter-hook.toml Git2Repository::init(temp_dir.path()).unwrap(); let output = Command::new(bin_path()) @@ -175,13 +175,13 @@ fn test_validate_with_valid_config() { // Initialize git repo Git2Repository::init(temp_dir.path()).unwrap(); - // Create valid hooks.toml + // Create valid .peter-hook.toml let config = r#" [hooks.test] command = "echo test" modifies_repository = false "#; - fs::write(temp_dir.path().join("hooks.toml"), config).unwrap(); + fs::write(temp_dir.path().join(".peter-hook.toml"), config).unwrap(); let output = Command::new(bin_path()) .current_dir(temp_dir.path()) @@ -199,12 +199,12 @@ fn test_validate_with_invalid_config() { // Initialize git repo Git2Repository::init(temp_dir.path()).unwrap(); - // Create invalid hooks.toml + // Create invalid .peter-hook.toml let config = r" [hooks.broken] # Missing required fields "; - fs::write(temp_dir.path().join("hooks.toml"), config).unwrap(); + fs::write(temp_dir.path().join(".peter-hook.toml"), config).unwrap(); let output = Command::new(bin_path()) .current_dir(temp_dir.path()) @@ -239,7 +239,7 @@ fn test_list_with_config() { // Initialize git repo Git2Repository::init(temp_dir.path()).unwrap(); - // Create hooks.toml + // Create .peter-hook.toml let config = r#" [hooks.test1] command = "echo test1" @@ -249,7 +249,7 @@ modifies_repository = false command = "echo test2" modifies_repository = true "#; - fs::write(temp_dir.path().join("hooks.toml"), config).unwrap(); + fs::write(temp_dir.path().join(".peter-hook.toml"), config).unwrap(); let output = Command::new(bin_path()) .current_dir(temp_dir.path()) @@ -298,7 +298,7 @@ fn test_install_in_git_repo() { command = "echo test" modifies_repository = false "#; - fs::write(temp_dir.path().join("hooks.toml"), config).unwrap(); + fs::write(temp_dir.path().join(".peter-hook.toml"), config).unwrap(); let output = Command::new(bin_path()) .current_dir(temp_dir.path()) @@ -367,7 +367,7 @@ fn test_lint_mode_not_in_git_repo() { command = "echo test" modifies_repository = false "#; - fs::write(temp_dir.path().join("hooks.toml"), config).unwrap(); + fs::write(temp_dir.path().join(".peter-hook.toml"), config).unwrap(); let output = Command::new(bin_path()) .current_dir(temp_dir.path()) diff --git a/tests/deprecation_tests.rs b/tests/deprecation_tests.rs new file mode 100644 index 0000000..aafadfa --- /dev/null +++ b/tests/deprecation_tests.rs @@ -0,0 +1,162 @@ +#![allow(clippy::all, clippy::pedantic, clippy::nursery)] +//! Tests for deprecated hooks.toml configuration file detection + +use git2::Repository as Git2Repository; +use std::{fs, process::Command}; +use tempfile::TempDir; + +fn bin_path() -> std::path::PathBuf { + assert_cmd::cargo::cargo_bin("peter-hook") +} + +/// Helper to create a test repository with a hooks.toml file (deprecated) +fn create_repo_with_deprecated_config() -> (TempDir, std::path::PathBuf) { + let temp_dir = TempDir::new().unwrap(); + + // Initialize git repo + Git2Repository::init(temp_dir.path()).unwrap(); + + // Create deprecated hooks.toml + let config_path = temp_dir.path().join("hooks.toml"); + fs::write( + &config_path, + "[hooks.test]\ncommand = \"echo test\"\nmodifies_repository = false\n", + ) + .unwrap(); + + (temp_dir, config_path) +} + +#[test] +fn test_deprecation_error_on_single_file() { + let (temp_dir, _) = create_repo_with_deprecated_config(); + + // Try to run any command (except version/license) + let output = Command::new(bin_path()) + .arg("validate") + .current_dir(temp_dir.path()) + .output() + .unwrap(); + + // Should exit with error + assert!(!output.status.success()); + + let stderr = String::from_utf8_lossy(&output.stderr); + + // Check error message contains key information + assert!(stderr.contains("hooks.toml is no longer supported")); + assert!(stderr.contains("hooks.toml")); + assert!(stderr.contains("hooks.toml")); // Should list the file + assert!(stderr.contains("mv hooks.toml .peter-hook.toml")); // Should show fix +} + +#[test] +fn test_deprecation_error_lists_multiple_files() { + let temp_dir = TempDir::new().unwrap(); + + // Initialize git repo + Git2Repository::init(temp_dir.path()).unwrap(); + + // Create multiple deprecated hooks.toml files + fs::write( + temp_dir.path().join("hooks.toml"), + "[hooks.test]\ncommand = \"echo test\"\nmodifies_repository = false\n", + ) + .unwrap(); + + fs::create_dir_all(temp_dir.path().join("backend")).unwrap(); + fs::write( + temp_dir.path().join("backend/hooks.toml"), + "[hooks.test]\ncommand = \"echo test\"\nmodifies_repository = false\n", + ) + .unwrap(); + + fs::create_dir_all(temp_dir.path().join("frontend")).unwrap(); + fs::write( + temp_dir.path().join("frontend/hooks.toml"), + "[hooks.test]\ncommand = \"echo test\"\nmodifies_repository = false\n", + ) + .unwrap(); + + // Try to run validate + let output = Command::new(bin_path()) + .arg("validate") + .current_dir(temp_dir.path()) + .output() + .unwrap(); + + assert!(!output.status.success()); + + let stderr = String::from_utf8_lossy(&output.stderr); + + // Should list all three files + assert!(stderr.contains("hooks.toml")); + assert!(stderr.contains("backend/hooks.toml") || stderr.contains("backend\\hooks.toml")); + assert!(stderr.contains("frontend/hooks.toml") || stderr.contains("frontend\\hooks.toml")); +} + +#[test] +fn test_version_command_bypasses_deprecation_check() { + let (temp_dir, _) = create_repo_with_deprecated_config(); + + // Version command should work even with deprecated config + let output = Command::new(bin_path()) + .arg("version") + .current_dir(temp_dir.path()) + .output() + .unwrap(); + + // Should succeed + assert!(output.status.success()); + + let stdout = String::from_utf8_lossy(&output.stdout); + assert!(stdout.contains("peter-hook")); // Should show version +} + +#[test] +fn test_license_command_bypasses_deprecation_check() { + let (temp_dir, _) = create_repo_with_deprecated_config(); + + // License command should work even with deprecated config + let output = Command::new(bin_path()) + .arg("license") + .current_dir(temp_dir.path()) + .output() + .unwrap(); + + // Should succeed + assert!(output.status.success()); + + let stdout = String::from_utf8_lossy(&output.stdout); + assert!(stdout.contains("MIT") || stdout.contains("Apache")); // Should show license +} + +#[test] +fn test_new_config_name_works() { + let temp_dir = TempDir::new().unwrap(); + + // Initialize git repo + Git2Repository::init(temp_dir.path()).unwrap(); + + // Create NEW config with correct name + fs::write( + temp_dir.path().join(".peter-hook.toml"), + "[hooks.test]\ncommand = \"echo test\"\nmodifies_repository = false\n", + ) + .unwrap(); + + // Validate should work + let output = Command::new(bin_path()) + .arg("validate") + .current_dir(temp_dir.path()) + .output() + .unwrap(); + + // Should succeed or show "Configuration is valid" + let stderr = String::from_utf8_lossy(&output.stderr); + let stdout = String::from_utf8_lossy(&output.stdout); + + // Should NOT contain deprecation error + assert!(!stderr.contains("hooks.toml is no longer supported")); + assert!(!stdout.contains("hooks.toml is no longer supported")); +} diff --git a/tests/executor_comprehensive_tests.rs b/tests/executor_comprehensive_tests.rs index 3492f99..e376381 100644 --- a/tests/executor_comprehensive_tests.rs +++ b/tests/executor_comprehensive_tests.rs @@ -24,7 +24,7 @@ fn test_execute_single_hook_success() { Git2Repository::init(temp_dir.path()).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.test] command = "echo success" @@ -47,7 +47,7 @@ fn test_execute_single_hook_failure() { Git2Repository::init(temp_dir.path()).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.failing] command = "false" @@ -71,7 +71,7 @@ fn test_execute_multiple_hooks_sequential() { Git2Repository::init(temp_dir.path()).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.first] command = "echo first" @@ -106,7 +106,7 @@ fn test_execute_multiple_hooks_parallel() { Git2Repository::init(temp_dir.path()).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.first] command = "echo first" @@ -137,7 +137,7 @@ fn test_execute_force_parallel() { Git2Repository::init(temp_dir.path()).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.modifier] command = "echo mod" @@ -168,7 +168,7 @@ fn test_execute_with_dependencies() { Git2Repository::init(temp_dir.path()).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.first] command = "echo first" @@ -196,7 +196,7 @@ fn test_execute_with_env_vars() { Git2Repository::init(temp_dir.path()).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.with-env] command = "echo $TEST_VAR" @@ -224,7 +224,7 @@ fn test_execute_with_run_at_root() { fs::create_dir(&subdir).unwrap(); fs::write( - subdir.join("hooks.toml"), + subdir.join(".peter-hook.toml"), r#" [hooks.root-hook] command = "pwd" @@ -248,7 +248,7 @@ fn test_execute_array_command() { Git2Repository::init(temp_dir.path()).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.array-cmd] command = ["echo", "test"] @@ -271,7 +271,7 @@ fn test_execute_in_place_execution_type() { Git2Repository::init(temp_dir.path()).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.in-place] command = "echo test" @@ -296,7 +296,7 @@ fn test_execute_other_execution_type() { Git2Repository::init(temp_dir.path()).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.other-type] command = "echo {CHANGED_FILES}" @@ -320,7 +320,7 @@ fn test_execute_captures_output() { Git2Repository::init(temp_dir.path()).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.output] command = "echo test_output" @@ -343,7 +343,7 @@ fn test_execute_nonexistent_command() { Git2Repository::init(temp_dir.path()).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.nonexistent] command = "nonexistent_command_xyz_123" @@ -370,7 +370,7 @@ fn test_execute_with_file_patterns() { fs::write(temp_dir.path().join("test.rs"), "fn main() {}").unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.rust-check] command = "echo checking" @@ -394,7 +394,7 @@ fn test_execute_with_run_always() { Git2Repository::init(temp_dir.path()).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.always] command = "echo always" @@ -418,7 +418,7 @@ fn test_execute_parallel_safe_hooks() { Git2Repository::init(temp_dir.path()).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.test1] command = "echo test1" @@ -449,7 +449,7 @@ fn test_execute_mixed_modifiers() { Git2Repository::init(temp_dir.path()).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.readonly] command = "echo readonly" @@ -483,7 +483,7 @@ fn test_execute_hook_with_custom_workdir() { fs::create_dir(&custom_dir).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.custom-wd] command = "pwd" @@ -514,7 +514,7 @@ fn test_execute_all_execution_strategies() { for (name, strategy) in strategies { fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), format!( r#" [hooks.test] @@ -544,7 +544,7 @@ fn test_execute_hook_with_template_variables() { Git2Repository::init(temp_dir.path()).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.template] command = "echo {HOOK_DIR}" @@ -567,7 +567,7 @@ fn test_execute_returns_results_map() { Git2Repository::init(temp_dir.path()).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.test] command = "echo test" diff --git a/tests/failure_recovery_tests.rs b/tests/failure_recovery_tests.rs index c02de21..ca10b9f 100644 --- a/tests/failure_recovery_tests.rs +++ b/tests/failure_recovery_tests.rs @@ -77,7 +77,7 @@ includes = ["success-1", "failure", "success-2"] description = "Mixed success/failure" execution_strategy = "parallel" "#; - fs::write(repo_path.join("hooks.toml"), config).unwrap(); + fs::write(repo_path.join(".peter-hook.toml"), config).unwrap(); // Create and stage a file fs::write(repo_path.join("test.txt"), "content").unwrap(); @@ -146,7 +146,7 @@ includes = ["parallel-fail", "parallel-success", "sequential-continues"] description = "Fail in parallel phase" execution_strategy = "parallel" "#; - fs::write(repo_path.join("hooks.toml"), config).unwrap(); + fs::write(repo_path.join(".peter-hook.toml"), config).unwrap(); // Create and stage a file fs::write(repo_path.join("test.txt"), "content").unwrap(); @@ -211,7 +211,7 @@ includes = ["seq-1-fail", "seq-2-continues", "seq-3-continues"] description = "Sequential failure" execution_strategy = "sequential" "#; - fs::write(repo_path.join("hooks.toml"), config).unwrap(); + fs::write(repo_path.join(".peter-hook.toml"), config).unwrap(); // Create and stage a file fs::write(repo_path.join("test.txt"), "content").unwrap(); @@ -271,7 +271,7 @@ includes = ["timeout-hook", "continues-after-timeout"] description = "Timeout failure" execution_strategy = "sequential" "#; - fs::write(repo_path.join("hooks.toml"), config).unwrap(); + fs::write(repo_path.join(".peter-hook.toml"), config).unwrap(); // Create and stage a file fs::write(repo_path.join("test.txt"), "content").unwrap(); @@ -323,7 +323,7 @@ timeout_seconds = 5 includes = ["my-custom-validation-hook"] description = "Validation hooks" "#; - fs::write(repo_path.join("hooks.toml"), config).unwrap(); + fs::write(repo_path.join(".peter-hook.toml"), config).unwrap(); // Create and stage a file fs::write(repo_path.join("test.txt"), "content").unwrap(); @@ -375,7 +375,7 @@ timeout_seconds = 5 includes = ["nonexistent-tool"] description = "Nonexistent command" "#; - fs::write(repo_path.join("hooks.toml"), config).unwrap(); + fs::write(repo_path.join(".peter-hook.toml"), config).unwrap(); // Create and stage a file fs::write(repo_path.join("test.txt"), "content").unwrap(); @@ -444,7 +444,7 @@ includes = ["pass-1", "pass-2", "fail", "pass-3", "pass-4"] description = "Partial failure" execution_strategy = "parallel" "#; - fs::write(repo_path.join("hooks.toml"), config).unwrap(); + fs::write(repo_path.join(".peter-hook.toml"), config).unwrap(); // Create and stage a file fs::write(repo_path.join("test.txt"), "content").unwrap(); @@ -484,7 +484,7 @@ timeout_seconds = 5 includes = ["exit-2"] description = "Non-standard exit code" "#; - fs::write(repo_path.join("hooks.toml"), config).unwrap(); + fs::write(repo_path.join(".peter-hook.toml"), config).unwrap(); // Create and stage a file fs::write(repo_path.join("test.txt"), "content").unwrap(); @@ -527,7 +527,7 @@ timeout_seconds = 5 includes = ["parent-fail", "child-depends-on-parent"] description = "Dependency chain" "#; - fs::write(repo_path.join("hooks.toml"), config).unwrap(); + fs::write(repo_path.join(".peter-hook.toml"), config).unwrap(); // Create and stage a file fs::write(repo_path.join("test.txt"), "content").unwrap(); @@ -591,7 +591,7 @@ includes = ["fail-1", "fail-2", "fail-3"] description = "Multiple failures" execution_strategy = "parallel" "#; - fs::write(repo_path.join("hooks.toml"), config).unwrap(); + fs::write(repo_path.join(".peter-hook.toml"), config).unwrap(); // Create and stage a file fs::write(repo_path.join("test.txt"), "content").unwrap(); @@ -643,7 +643,7 @@ timeout_seconds = 5 includes = ["would-fail"] description = "Would fail in real run" "#; - fs::write(repo_path.join("hooks.toml"), config).unwrap(); + fs::write(repo_path.join(".peter-hook.toml"), config).unwrap(); // Create and stage a file fs::write(repo_path.join("test.txt"), "content").unwrap(); diff --git a/tests/hierarchical_comprehensive_tests.rs b/tests/hierarchical_comprehensive_tests.rs index 743ddd1..4e5a6e4 100644 --- a/tests/hierarchical_comprehensive_tests.rs +++ b/tests/hierarchical_comprehensive_tests.rs @@ -15,7 +15,7 @@ fn test_hierarchical_simple_config() { Git2Repository::init(temp_dir.path()).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.pre-commit] command = "echo test" @@ -50,7 +50,7 @@ fn test_hierarchical_nested_configs() { // Root config fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.pre-commit] command = "echo root" @@ -63,7 +63,7 @@ modifies_repository = false let subdir = temp_dir.path().join("sub"); fs::create_dir(&subdir).unwrap(); fs::write( - subdir.join("hooks.toml"), + subdir.join(".peter-hook.toml"), r#" [hooks.pre-commit] command = "echo nested" @@ -125,7 +125,7 @@ fn test_hierarchical_with_working_directory_mode() { Git2Repository::init(temp_dir.path()).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.test] command = "echo test" @@ -163,7 +163,7 @@ fn test_hierarchical_with_staged_mode() { Git2Repository::init(temp_dir.path()).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.pre-commit] command = "echo test" @@ -198,7 +198,7 @@ fn test_hierarchical_three_level_nesting() { // Level 1 (root) fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.test] command = "echo root" @@ -211,7 +211,7 @@ modifies_repository = false let l2 = temp_dir.path().join("level2"); fs::create_dir(&l2).unwrap(); fs::write( - l2.join("hooks.toml"), + l2.join(".peter-hook.toml"), r#" [hooks.test] command = "echo level2" @@ -224,7 +224,7 @@ modifies_repository = false let l3 = l2.join("level3"); fs::create_dir(&l3).unwrap(); fs::write( - l3.join("hooks.toml"), + l3.join(".peter-hook.toml"), r#" [hooks.test] command = "echo level3" @@ -258,7 +258,7 @@ fn test_hierarchical_with_groups() { Git2Repository::init(temp_dir.path()).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.a] command = "echo a" @@ -299,7 +299,7 @@ fn test_hierarchical_nonexistent_event() { Git2Repository::init(temp_dir.path()).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.pre-commit] command = "echo test" @@ -334,7 +334,7 @@ fn test_hierarchical_in_worktree() { Git2Repository::init(temp_dir.path()).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.test] command = "echo test" @@ -374,7 +374,7 @@ fn test_hierarchical_multiple_configs_same_level() { fs::create_dir(&sub2).unwrap(); fs::write( - sub1.join("hooks.toml"), + sub1.join(".peter-hook.toml"), r#" [hooks.test] command = "echo sub1" @@ -384,7 +384,7 @@ modifies_repository = false .unwrap(); fs::write( - sub2.join("hooks.toml"), + sub2.join(".peter-hook.toml"), r#" [hooks.test] command = "echo sub2" diff --git a/tests/installer_comprehensive_tests.rs b/tests/installer_comprehensive_tests.rs index 413539a..f5ea48d 100644 --- a/tests/installer_comprehensive_tests.rs +++ b/tests/installer_comprehensive_tests.rs @@ -98,7 +98,7 @@ fn test_install_all_hooks() { // Create config fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.pre-commit] command = "echo test" @@ -155,7 +155,7 @@ fn test_install_creates_hook_directory() { Git2Repository::init(temp_dir.path()).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.pre-commit] command = "echo test" @@ -264,7 +264,7 @@ fn test_install_then_uninstall() { Git2Repository::init(temp_dir.path()).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.test] command = "echo test" diff --git a/tests/main_comprehensive_final_tests.rs b/tests/main_comprehensive_final_tests.rs index 690544b..53f234f 100644 --- a/tests/main_comprehensive_final_tests.rs +++ b/tests/main_comprehensive_final_tests.rs @@ -57,7 +57,7 @@ fn test_run_deep_hierarchy() { { let level_path = temp_dir.path().join(level); fs::write( - level_path.join("hooks.toml"), + level_path.join(".peter-hook.toml"), format!( r#" [hooks.level-{i}] @@ -109,7 +109,7 @@ fn test_install_backs_up_existing_hooks() { } fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.pre-commit] command = "echo new hook" @@ -141,7 +141,7 @@ fn test_uninstall_restores_backup() { // Create hooks and install fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.pre-commit] command = "echo test" @@ -175,7 +175,7 @@ fn test_run_multiple_config_groups() { // Root config fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.root-check] command = "echo root" @@ -189,7 +189,7 @@ files = ["*.txt"] let sub1 = temp_dir.path().join("sub1"); fs::create_dir(&sub1).unwrap(); fs::write( - sub1.join("hooks.toml"), + sub1.join(".peter-hook.toml"), r#" [hooks.sub-check] command = "echo sub1" @@ -272,7 +272,7 @@ fn test_validate_nested_imports() { fs::create_dir(&lib2).unwrap(); fs::write( - lib2.join("hooks.toml"), + lib2.join(".peter-hook.toml"), r#" [hooks.base] command = "echo base" @@ -282,9 +282,9 @@ modifies_repository = false .unwrap(); fs::write( - lib1.join("hooks.toml"), + lib1.join(".peter-hook.toml"), r#" -imports = ["../lib2/hooks.toml"] +imports = ["../lib2/.peter-hook.toml"] [hooks.mid] command = "echo mid" @@ -294,9 +294,9 @@ modifies_repository = false .unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" -imports = ["lib1/hooks.toml"] +imports = ["lib1/.peter-hook.toml"] [hooks.main] command = "echo main" @@ -330,7 +330,7 @@ fn test_lint_many_files() { } fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.check-all] command = "echo checking" diff --git a/tests/main_exhaustive_tests.rs b/tests/main_exhaustive_tests.rs index c92ad77..d2e6b7b 100644 --- a/tests/main_exhaustive_tests.rs +++ b/tests/main_exhaustive_tests.rs @@ -28,7 +28,7 @@ fn test_run_no_changed_files() { .unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.pre-commit] command = "echo test" @@ -61,7 +61,7 @@ fn test_run_single_changed_file() { index.write().unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.check] command = "echo checking" @@ -104,7 +104,7 @@ fn test_run_five_changed_files() { index.write().unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.check] command = "echo checking" @@ -147,7 +147,7 @@ fn test_run_six_changed_files() { index.write().unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.check] command = "echo checking" @@ -179,7 +179,7 @@ fn test_run_single_hook() { index.write().unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.single] command = "echo single" @@ -210,7 +210,7 @@ fn test_run_two_to_three_hooks() { index.write().unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.hook1] command = "echo 1" @@ -252,7 +252,7 @@ fn test_run_four_to_six_hooks() { index.write().unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.h1] command = "echo 1" @@ -315,7 +315,7 @@ includes = ["hook1", "hook2", "hook3", "hook4", "hook5", "hook6", "hook7", "hook "#, ); - fs::write(temp_dir.path().join("hooks.toml"), config).unwrap(); + fs::write(temp_dir.path().join(".peter-hook.toml"), config).unwrap(); let output = Command::new(bin_path()) .current_dir(temp_dir.path()) @@ -334,7 +334,7 @@ fn test_run_hook_with_run_always_flag() { Git2Repository::init(temp_dir.path()).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.always-hook] command = "echo always" @@ -366,7 +366,7 @@ fn test_run_hook_no_patterns() { index.write().unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.no-patterns] command = "echo no patterns" @@ -391,7 +391,7 @@ fn test_lint_no_matching_files() { let temp_dir = TempDir::new().unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.rust-only] command = "echo rust" @@ -420,7 +420,7 @@ fn test_lint_multi_hook_group() { fs::write(temp_dir.path().join("test.txt"), "content").unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.check1] command = "echo check1" @@ -460,7 +460,7 @@ fn test_validate_shows_merges() { fs::create_dir(&base).unwrap(); fs::write( - base.join("hooks.toml"), + base.join(".peter-hook.toml"), r#" [hooks.base-hook] command = "echo base" @@ -471,9 +471,9 @@ files = ["*.txt"] .unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" -imports = ["base/hooks.toml"] +imports = ["base/.peter-hook.toml"] [hooks.base-hook] command = "echo overridden" @@ -505,7 +505,7 @@ fn test_run_hook_success_exit_code() { index.write().unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.success] command = "true" @@ -537,7 +537,7 @@ fn test_run_hook_failure_exit_code() { index.write().unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.failure] command = "exit 1" @@ -597,7 +597,7 @@ fn test_lint_file_count_display() { } fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.rust] command = "echo checking rust" @@ -629,7 +629,7 @@ fn test_run_mixed_execution_types() { index.write().unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.per-file] command = "echo" @@ -665,7 +665,7 @@ fn test_validate_with_warnings() { Git2Repository::init(temp_dir.path()).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.test] command = "echo test" @@ -695,7 +695,7 @@ fn test_run_hook_with_output_and_errors() { index.write().unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.verbose] command = "echo stdout && echo stderr >&2" diff --git a/tests/main_install_advanced_tests.rs b/tests/main_install_advanced_tests.rs index 0286fac..82f1962 100644 --- a/tests/main_install_advanced_tests.rs +++ b/tests/main_install_advanced_tests.rs @@ -31,7 +31,7 @@ fn test_install_with_existing_unmanaged_hooks_force() { // Create config fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.pre-commit] command = "echo new" @@ -74,7 +74,7 @@ fn test_install_with_existing_unmanaged_hooks_no_force() { // Create config fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.pre-commit] command = "echo new" @@ -109,7 +109,7 @@ fn test_install_with_all_worktree_strategies() { Git2Repository::init(temp_dir.path()).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.test] command = "echo test" @@ -143,7 +143,7 @@ fn test_install_creates_executable_hooks() { Git2Repository::init(temp_dir.path()).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.pre-commit] command = "echo test" @@ -180,7 +180,7 @@ fn test_install_report_print_summary() { Git2Repository::init(temp_dir.path()).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.pre-commit] command = "echo test" @@ -215,7 +215,7 @@ fn test_install_with_complex_dependencies() { Git2Repository::init(temp_dir.path()).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.setup] command = "echo setup" @@ -274,7 +274,7 @@ modifies_repository = false // Create main config with import fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" imports = ["imports/base.toml"] diff --git a/tests/main_install_tests.rs b/tests/main_install_tests.rs index c97d0d1..26c91c0 100644 --- a/tests/main_install_tests.rs +++ b/tests/main_install_tests.rs @@ -16,7 +16,7 @@ fn test_install_in_empty_git_repo() { // Create simple config fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.test] command = "echo test" @@ -45,7 +45,7 @@ fn test_install_with_force_flag() { Git2Repository::init(temp_dir.path()).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.test] command = "echo test" @@ -73,7 +73,7 @@ fn test_install_with_shared_strategy() { Git2Repository::init(temp_dir.path()).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.test] command = "echo test" @@ -102,7 +102,7 @@ fn test_install_with_per_worktree_strategy() { Git2Repository::init(temp_dir.path()).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.test] command = "echo test" @@ -131,7 +131,7 @@ fn test_install_with_detect_strategy() { Git2Repository::init(temp_dir.path()).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.test] command = "echo test" @@ -193,7 +193,7 @@ fn test_install_outside_git_repo_fails() { fn test_install_without_config() { let temp_dir = TempDir::new().unwrap(); Git2Repository::init(temp_dir.path()).unwrap(); - // No hooks.toml file + // No .peter-hook.toml file let output = Command::new(bin_path()) .current_dir(temp_dir.path()) @@ -201,7 +201,7 @@ fn test_install_without_config() { .output() .expect("Failed to execute"); - // May succeed or fail depending on whether hooks.toml is required + // May succeed or fail depending on whether .peter-hook.toml is required assert!(output.status.code().is_some()); } @@ -211,7 +211,7 @@ fn test_install_creates_hooks_directory() { Git2Repository::init(temp_dir.path()).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.pre-commit] command = "echo test" @@ -236,7 +236,7 @@ fn test_install_with_multiple_hooks() { Git2Repository::init(temp_dir.path()).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.pre-commit] command = "echo pre-commit" @@ -271,7 +271,7 @@ fn test_install_with_hook_group() { Git2Repository::init(temp_dir.path()).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.format] command = "cargo fmt" @@ -305,7 +305,7 @@ fn test_install_output_contains_summary() { Git2Repository::init(temp_dir.path()).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.test] command = "echo test" @@ -342,7 +342,7 @@ fn test_install_twice_without_force() { Git2Repository::init(temp_dir.path()).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.test] command = "echo test" @@ -376,7 +376,7 @@ fn test_install_with_complex_config() { Git2Repository::init(temp_dir.path()).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.format] command = "cargo fmt" @@ -430,7 +430,7 @@ fn test_install_with_invalid_config_fails() { // Create truly invalid TOML (syntax error) fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.broken # Unclosed bracket @@ -457,7 +457,7 @@ fn test_install_preserves_existing_managed_hooks() { Git2Repository::init(temp_dir.path()).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.test] command = "echo test" @@ -489,7 +489,7 @@ fn test_install_with_env_vars_in_config() { Git2Repository::init(temp_dir.path()).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.test] command = "echo test" @@ -517,7 +517,7 @@ fn test_install_with_working_dir_in_config() { Git2Repository::init(temp_dir.path()).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.test] command = "echo test" @@ -545,7 +545,7 @@ fn test_install_default_worktree_strategy() { Git2Repository::init(temp_dir.path()).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.test] command = "echo test" @@ -578,7 +578,7 @@ fn test_install_in_subdirectory() { // Create config in root fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.test] command = "echo test" @@ -606,7 +606,7 @@ fn test_install_with_template_variables() { Git2Repository::init(temp_dir.path()).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.test] command = "echo {HOOK_DIR}" @@ -633,7 +633,7 @@ fn test_install_with_run_at_root() { Git2Repository::init(temp_dir.path()).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.test] command = "echo test" @@ -661,7 +661,7 @@ fn test_install_with_run_always() { Git2Repository::init(temp_dir.path()).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.test] command = "echo test" @@ -689,7 +689,7 @@ fn test_install_exit_code_on_success() { Git2Repository::init(temp_dir.path()).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.test] command = "echo test" diff --git a/tests/main_lint_advanced_tests.rs b/tests/main_lint_advanced_tests.rs index eae049a..7653be9 100644 --- a/tests/main_lint_advanced_tests.rs +++ b/tests/main_lint_advanced_tests.rs @@ -22,7 +22,7 @@ fn test_lint_with_debug_shows_file_list() { } fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.check] command = "echo checking" @@ -48,7 +48,7 @@ fn test_lint_with_group_shows_hook_count() { let temp_dir = TempDir::new().unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.a] command = "echo a" @@ -83,7 +83,7 @@ fn test_lint_shows_config_path() { let temp_dir = TempDir::new().unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.test] command = "echo test" @@ -110,7 +110,7 @@ fn test_lint_dry_run_shows_what_would_run() { fs::write(temp_dir.path().join("test.rs"), "fn main() {}").unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.rust-check] command = "cargo clippy" @@ -145,7 +145,7 @@ fn test_lint_nonexistent_hook_shows_error() { let temp_dir = TempDir::new().unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.existing] command = "echo test" @@ -173,7 +173,7 @@ fn test_lint_no_files_match_pattern() { fs::write(temp_dir.path().join("test.txt"), "content").unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.rust-check] command = "echo checking rust" @@ -199,7 +199,7 @@ fn test_lint_hierarchical_config() { // Root config fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.root] command = "echo root" @@ -212,7 +212,7 @@ modifies_repository = false let subdir = temp_dir.path().join("sub"); fs::create_dir(&subdir).unwrap(); fs::write( - subdir.join("hooks.toml"), + subdir.join(".peter-hook.toml"), r#" [hooks.nested] command = "echo nested" @@ -245,7 +245,7 @@ fn test_lint_execution_types() { for (name, exec_type) in execution_types { fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), format!( r#" [hooks.test] diff --git a/tests/main_lint_tests.rs b/tests/main_lint_tests.rs index 1a76fff..8cc2d00 100644 --- a/tests/main_lint_tests.rs +++ b/tests/main_lint_tests.rs @@ -14,7 +14,7 @@ fn test_lint_basic_execution() { let temp_dir = TempDir::new().unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.test] command = "echo test" @@ -39,7 +39,7 @@ fn test_lint_without_git_repo() { let temp_dir = TempDir::new().unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.test] command = "echo test" @@ -64,7 +64,7 @@ fn test_lint_with_dry_run() { let temp_dir = TempDir::new().unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.test] command = "echo test" @@ -89,7 +89,7 @@ fn test_lint_nonexistent_hook() { let temp_dir = TempDir::new().unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.test] command = "echo test" @@ -117,7 +117,7 @@ fn test_lint_with_file_patterns() { fs::write(temp_dir.path().join("test.rs"), "fn main() {}").unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.rust-check] command = "echo checking" @@ -155,7 +155,7 @@ fn test_lint_with_debug_flag() { let temp_dir = TempDir::new().unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.test] command = "echo test" @@ -181,7 +181,7 @@ fn test_lint_in_git_repo() { Git2Repository::init(temp_dir.path()).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.test] command = "echo test" @@ -205,7 +205,7 @@ fn test_lint_with_group() { let temp_dir = TempDir::new().unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.a] command = "echo a" @@ -240,7 +240,7 @@ fn test_lint_discovers_files() { fs::write(temp_dir.path().join("file2.txt"), "content").unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.test] command = "echo test" @@ -277,7 +277,7 @@ fn test_lint_respects_gitignore() { fs::write(temp_dir.path().join("test.txt"), "not ignored").unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.test] command = "echo test" @@ -317,7 +317,7 @@ fn test_lint_exit_codes() { let temp_dir = TempDir::new().unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.test] command = "echo test" diff --git a/tests/main_list_advanced_tests.rs b/tests/main_list_advanced_tests.rs index 72ca7c0..59f942b 100644 --- a/tests/main_list_advanced_tests.rs +++ b/tests/main_list_advanced_tests.rs @@ -15,7 +15,7 @@ fn test_list_shows_detailed_hook_info() { Git2Repository::init(temp_dir.path()).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.test1] command = "echo test1" @@ -74,7 +74,7 @@ fn test_list_shows_managed_vs_custom() { // Create managed hook via install fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.managed] command = "echo managed" diff --git a/tests/main_list_tests.rs b/tests/main_list_tests.rs index 8f83ae0..e8045ff 100644 --- a/tests/main_list_tests.rs +++ b/tests/main_list_tests.rs @@ -47,7 +47,7 @@ fn test_list_after_install() { Git2Repository::init(temp_dir.path()).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.pre-commit] command = "echo test" @@ -80,7 +80,7 @@ fn test_list_multiple_hooks() { Git2Repository::init(temp_dir.path()).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.pre-commit] command = "echo pre-commit" @@ -122,7 +122,7 @@ fn test_list_shows_managed_status() { Git2Repository::init(temp_dir.path()).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.test] command = "echo test" @@ -172,7 +172,7 @@ fn test_list_from_subdirectory() { fs::create_dir(&subdir).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.test] command = "echo test" @@ -260,7 +260,7 @@ fn test_list_shows_executable_status() { Git2Repository::init(temp_dir.path()).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.test] command = "echo test" diff --git a/tests/main_run_advanced_tests.rs b/tests/main_run_advanced_tests.rs index e95260f..b7f3834 100644 --- a/tests/main_run_advanced_tests.rs +++ b/tests/main_run_advanced_tests.rs @@ -21,7 +21,7 @@ fn test_run_with_debug_shows_extravaganza_message() { index.write().unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.pre-commit] command = "echo test" @@ -54,7 +54,7 @@ fn test_run_dry_run_doesnt_execute() { index.write().unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.pre-commit] command = "echo modified > test.txt" @@ -101,7 +101,7 @@ fn test_run_with_many_changed_files() { index.write().unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.check] command = "echo checking" @@ -140,7 +140,7 @@ fn test_run_post_commit_uses_commit_range() { .unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.post-commit] command = "echo post-commit" @@ -177,7 +177,7 @@ fn test_run_post_merge_uses_commit_range() { .unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.post-merge] command = "echo post-merge" @@ -214,7 +214,7 @@ fn test_run_post_checkout_uses_commit_range() { .unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.post-checkout] command = "echo post-checkout" @@ -239,7 +239,7 @@ fn test_run_commit_msg_no_file_filtering() { Git2Repository::init(temp_dir.path()).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.commit-msg] command = "echo checking message" @@ -264,7 +264,7 @@ fn test_run_prepare_commit_msg_no_file_filtering() { Git2Repository::init(temp_dir.path()).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.prepare-commit-msg] command = "echo preparing" @@ -289,7 +289,7 @@ fn test_run_pre_push_uses_push_mode() { Git2Repository::init(temp_dir.path()).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.pre-push] command = "echo pre-push" @@ -314,7 +314,7 @@ fn test_run_unknown_event_uses_working_directory_mode() { Git2Repository::init(temp_dir.path()).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.custom-event] command = "echo custom" @@ -345,7 +345,7 @@ fn test_run_executes_hooks_and_shows_results() { index.write().unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.pre-commit] command = "echo Executing hook" diff --git a/tests/main_run_comprehensive_tests.rs b/tests/main_run_comprehensive_tests.rs index 6ea9eea..65ce9a6 100644 --- a/tests/main_run_comprehensive_tests.rs +++ b/tests/main_run_comprehensive_tests.rs @@ -22,7 +22,7 @@ fn test_run_pre_commit_with_real_execution() { // Create hook config fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.pre-commit] command = "echo Running pre-commit" @@ -55,7 +55,7 @@ fn test_run_with_actual_file_filtering() { // Create hook that only targets Rust files fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.rust-check] command = "echo Checking Rust files" @@ -82,7 +82,7 @@ fn test_run_with_hierarchical_configs() { // Root config fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.root] command = "echo root" @@ -95,7 +95,7 @@ modifies_repository = false let subdir = temp_dir.path().join("subdir"); fs::create_dir(&subdir).unwrap(); fs::write( - subdir.join("hooks.toml"), + subdir.join(".peter-hook.toml"), r#" [hooks.nested] command = "echo nested" @@ -128,7 +128,7 @@ fn test_run_shows_no_hooks_message() { Git2Repository::init(temp_dir.path()).unwrap(); // Empty config - fs::write(temp_dir.path().join("hooks.toml"), "").unwrap(); + fs::write(temp_dir.path().join(".peter-hook.toml"), "").unwrap(); let output = Command::new(bin_path()) .current_dir(temp_dir.path()) @@ -162,7 +162,7 @@ fn test_run_all_supported_hook_types() { for hook_type in hook_types { fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), format!( r#" [hooks.{hook_type}] @@ -193,7 +193,7 @@ fn test_run_with_failing_hook_propagates_error() { Git2Repository::init(temp_dir.path()).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.failing] command = "exit 1" @@ -224,7 +224,7 @@ fn test_run_debug_mode_shows_extra_output() { index.write().unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.test] command = "echo test" @@ -267,7 +267,7 @@ fn test_run_with_multiple_changed_files() { index.write().unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.check] command = "echo checking files" diff --git a/tests/main_run_tests.rs b/tests/main_run_tests.rs index 17fcd65..54ef98e 100644 --- a/tests/main_run_tests.rs +++ b/tests/main_run_tests.rs @@ -29,7 +29,7 @@ fn test_run_pre_commit_hook() { Git2Repository::init(temp_dir.path()).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.pre-commit] command = "echo pre-commit" @@ -55,7 +55,7 @@ fn test_run_with_all_files_flag() { Git2Repository::init(temp_dir.path()).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.test] command = "echo test" @@ -81,7 +81,7 @@ fn test_run_with_dry_run_flag() { Git2Repository::init(temp_dir.path()).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.test] command = "echo test" @@ -107,7 +107,7 @@ fn test_run_commit_msg_hook() { Git2Repository::init(temp_dir.path()).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.commit-msg] command = "echo commit-msg" @@ -132,7 +132,7 @@ fn test_run_pre_push_hook() { Git2Repository::init(temp_dir.path()).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.pre-push] command = "echo pre-push" @@ -157,7 +157,7 @@ fn test_run_post_commit_hook() { Git2Repository::init(temp_dir.path()).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.post-commit] command = "echo post-commit" @@ -182,7 +182,7 @@ fn test_run_nonexistent_hook() { Git2Repository::init(temp_dir.path()).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.test] command = "echo test" @@ -208,7 +208,7 @@ fn test_run_with_git_args() { Git2Repository::init(temp_dir.path()).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.pre-commit] command = "echo test" @@ -236,7 +236,7 @@ fn test_run_with_debug_flag() { Git2Repository::init(temp_dir.path()).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.test] command = "echo test" @@ -276,7 +276,7 @@ fn test_run_with_group() { Git2Repository::init(temp_dir.path()).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.format] command = "echo format" @@ -308,7 +308,7 @@ fn test_run_with_dependencies() { Git2Repository::init(temp_dir.path()).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.first] command = "echo first" @@ -346,7 +346,7 @@ fn test_run_from_subdirectory() { fs::create_dir(&subdir).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.test] command = "echo test" @@ -371,7 +371,7 @@ fn test_run_all_files_and_dry_run_together() { Git2Repository::init(temp_dir.path()).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.test] command = "echo test" @@ -398,7 +398,7 @@ fn test_run_post_merge_hook() { Git2Repository::init(temp_dir.path()).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.post-merge] command = "echo post-merge" @@ -423,7 +423,7 @@ fn test_run_post_checkout_hook() { Git2Repository::init(temp_dir.path()).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.post-checkout] command = "echo post-checkout" @@ -448,7 +448,7 @@ fn test_run_prepare_commit_msg_hook() { Git2Repository::init(temp_dir.path()).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.prepare-commit-msg] command = "echo prepare" @@ -473,7 +473,7 @@ fn test_run_with_file_patterns() { Git2Repository::init(temp_dir.path()).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.rust-check] command = "echo checking rust" @@ -499,7 +499,7 @@ fn test_run_with_run_always() { Git2Repository::init(temp_dir.path()).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.always] command = "echo always" @@ -525,7 +525,7 @@ fn test_run_with_run_at_root() { Git2Repository::init(temp_dir.path()).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.root-hook] command = "echo at root" @@ -551,7 +551,7 @@ fn test_run_with_env_vars() { Git2Repository::init(temp_dir.path()).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.with-env] command = "echo test" @@ -577,7 +577,7 @@ fn test_run_no_hooks_configured() { Git2Repository::init(temp_dir.path()).unwrap(); // Empty config - fs::write(temp_dir.path().join("hooks.toml"), "").unwrap(); + fs::write(temp_dir.path().join(".peter-hook.toml"), "").unwrap(); let output = Command::new(bin_path()) .current_dir(temp_dir.path()) @@ -602,7 +602,7 @@ fn test_run_with_parallel_group() { Git2Repository::init(temp_dir.path()).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.test1] command = "echo test1" @@ -635,7 +635,7 @@ fn test_run_with_sequential_group() { Git2Repository::init(temp_dir.path()).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.first] command = "echo first" @@ -668,7 +668,7 @@ fn test_run_exit_code_on_hook_failure() { Git2Repository::init(temp_dir.path()).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.failing] command = "false" diff --git a/tests/main_uninstall_tests.rs b/tests/main_uninstall_tests.rs index c9513ad..2fb051f 100644 --- a/tests/main_uninstall_tests.rs +++ b/tests/main_uninstall_tests.rs @@ -48,7 +48,7 @@ fn test_uninstall_after_install() { Git2Repository::init(temp_dir.path()).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.test] command = "echo test" @@ -139,7 +139,7 @@ fn test_uninstall_from_subdirectory() { // Install hooks first fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.test] command = "echo test" @@ -173,7 +173,7 @@ fn test_uninstall_twice_idempotent() { Git2Repository::init(temp_dir.path()).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.test] command = "echo test" @@ -214,7 +214,7 @@ fn test_uninstall_multiple_hooks() { Git2Repository::init(temp_dir.path()).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.pre-commit] command = "echo pre-commit" @@ -288,7 +288,7 @@ fn test_install_then_uninstall_then_install() { Git2Repository::init(temp_dir.path()).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.test] command = "echo test" @@ -352,7 +352,7 @@ fn test_uninstall_in_nested_repo() { // Install hooks fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.test] command = "echo test" diff --git a/tests/main_validate_advanced_tests.rs b/tests/main_validate_advanced_tests.rs index 7840227..16ae262 100644 --- a/tests/main_validate_advanced_tests.rs +++ b/tests/main_validate_advanced_tests.rs @@ -28,7 +28,7 @@ modifies_repository = false .unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" imports = ["lib/base.toml"] @@ -59,7 +59,7 @@ fn test_validate_with_trace_imports_and_json() { Git2Repository::init(temp_dir.path()).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.test] command = "echo test" @@ -89,7 +89,7 @@ fn test_validate_shows_hook_names() { Git2Repository::init(temp_dir.path()).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.hook1] command = "echo 1" @@ -125,7 +125,7 @@ fn test_validate_empty_config_shows_message() { let temp_dir = TempDir::new().unwrap(); Git2Repository::init(temp_dir.path()).unwrap(); - fs::write(temp_dir.path().join("hooks.toml"), "").unwrap(); + fs::write(temp_dir.path().join(".peter-hook.toml"), "").unwrap(); let output = Command::new(bin_path()) .current_dir(temp_dir.path()) @@ -150,7 +150,7 @@ fn test_validate_with_merge_diagnostics() { let base = temp_dir.path().join("base"); fs::create_dir(&base).unwrap(); fs::write( - base.join("hooks.toml"), + base.join(".peter-hook.toml"), r#" [hooks.shared] command = "echo shared" @@ -161,9 +161,9 @@ modifies_repository = false // Create override config fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" -imports = ["base/hooks.toml"] +imports = ["base/.peter-hook.toml"] [hooks.shared] command = "echo overridden" @@ -200,7 +200,7 @@ modifies_repository = false .unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" imports = ["lib/base.toml"] @@ -231,7 +231,7 @@ fn test_validate_json_output_format() { Git2Repository::init(temp_dir.path()).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.test] command = "echo test" diff --git a/tests/main_validate_tests.rs b/tests/main_validate_tests.rs index 4ccb192..aa7c208 100644 --- a/tests/main_validate_tests.rs +++ b/tests/main_validate_tests.rs @@ -15,7 +15,7 @@ fn test_validate_valid_config() { Git2Repository::init(temp_dir.path()).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.test] command = "echo test" @@ -41,7 +41,7 @@ fn test_validate_invalid_toml() { Git2Repository::init(temp_dir.path()).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), "[hooks.broken\nno closing bracket", ) .unwrap(); @@ -76,7 +76,7 @@ fn test_validate_with_trace_imports() { Git2Repository::init(temp_dir.path()).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.test] command = "echo test" @@ -101,7 +101,7 @@ fn test_validate_with_json_output() { Git2Repository::init(temp_dir.path()).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.test] command = "echo test" @@ -142,7 +142,7 @@ modifies_repository = false // Create main config with relative import fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" imports = ["imports/base.toml"] @@ -169,7 +169,7 @@ fn test_validate_circular_dependencies() { Git2Repository::init(temp_dir.path()).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.a] command = "echo a" @@ -216,7 +216,7 @@ fn test_validate_from_subdirectory() { fs::create_dir(&subdir).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.test] command = "echo test" @@ -240,7 +240,7 @@ fn test_validate_with_debug() { Git2Repository::init(temp_dir.path()).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.test] command = "echo test" @@ -265,7 +265,7 @@ fn test_validate_config_with_groups() { Git2Repository::init(temp_dir.path()).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.test1] command = "echo test1" @@ -296,7 +296,7 @@ fn test_validate_shows_hook_count() { Git2Repository::init(temp_dir.path()).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.test] command = "echo test" diff --git a/tests/requires_files_integration_tests.rs b/tests/requires_files_integration_tests.rs index bc97dbe..4f4024c 100644 --- a/tests/requires_files_integration_tests.rs +++ b/tests/requires_files_integration_tests.rs @@ -41,8 +41,8 @@ fn setup_test_repo_with_config(config_content: &str) -> TempDir { .output() .unwrap(); - // Write hooks.toml - fs::write(repo_path.join("hooks.toml"), config_content).unwrap(); + // Write .peter-hook.toml + fs::write(repo_path.join(".peter-hook.toml"), config_content).unwrap(); temp_dir } @@ -271,7 +271,7 @@ modifies_repository = false [groups.pre-commit] includes = ["test-hook"] "#; - fs::write(repo_path.join("hooks.toml"), root_config).unwrap(); + fs::write(repo_path.join(".peter-hook.toml"), root_config).unwrap(); // Create subdirectory with its own config let subdir = repo_path.join("subdir"); @@ -288,7 +288,7 @@ files = ["*.txt"] [groups.pre-commit] includes = ["test-hook"] "#; - fs::write(subdir.join("hooks.toml"), child_config).unwrap(); + fs::write(subdir.join(".peter-hook.toml"), child_config).unwrap(); // Create and stage a file in subdirectory fs::write(subdir.join("test.txt"), "content").unwrap(); diff --git a/tests/resolver_comprehensive_tests.rs b/tests/resolver_comprehensive_tests.rs index 0040f2c..4c4bc58 100644 --- a/tests/resolver_comprehensive_tests.rs +++ b/tests/resolver_comprehensive_tests.rs @@ -18,7 +18,7 @@ fn test_find_config_file_exists() { let temp_dir = TempDir::new().unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.test] command = "echo test" @@ -50,7 +50,7 @@ fn test_find_config_file_in_parent() { // Create config in parent fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.test] command = "echo test" @@ -77,7 +77,7 @@ fn test_resolve_hook_by_name() { Git2Repository::init(temp_dir.path()).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.test] command = "echo test" @@ -102,7 +102,7 @@ fn test_resolve_nonexistent_hook() { Git2Repository::init(temp_dir.path()).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.test] command = "echo test" @@ -125,7 +125,7 @@ fn test_resolve_hook_group() { Git2Repository::init(temp_dir.path()).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.a] command = "echo a" @@ -158,7 +158,7 @@ fn test_resolve_with_file_filtering() { Git2Repository::init(temp_dir.path()).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.test] command = "echo test" @@ -184,7 +184,7 @@ fn test_resolve_for_lint_mode() { Git2Repository::init(temp_dir.path()).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.test] command = "echo test" @@ -209,7 +209,7 @@ fn test_resolve_for_lint_with_file_patterns() { fs::write(temp_dir.path().join("test.rs"), "fn main() {}").unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.test] command = "echo test" @@ -232,7 +232,7 @@ fn test_resolve_for_lint_nonexistent_hook() { Git2Repository::init(temp_dir.path()).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.test] command = "echo test" @@ -254,7 +254,7 @@ fn test_resolver_from_nested_directory() { // Root config fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.test] command = "echo test" @@ -280,7 +280,7 @@ fn test_resolve_hook_with_dependencies() { Git2Repository::init(temp_dir.path()).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.first] command = "echo first" @@ -307,7 +307,7 @@ fn test_resolve_multiple_hooks_same_event() { Git2Repository::init(temp_dir.path()).unwrap(); fs::write( - temp_dir.path().join("hooks.toml"), + temp_dir.path().join(".peter-hook.toml"), r#" [hooks.pre-commit] command = "echo test" diff --git a/tests/security_tests.rs b/tests/security_tests.rs index d843f3b..1783605 100644 --- a/tests/security_tests.rs +++ b/tests/security_tests.rs @@ -66,7 +66,7 @@ timeout_seconds = 5 [groups.pre-commit] includes = ["injection-attempt"] "#; - fs::write(repo_path.join("hooks.toml"), config).unwrap(); + fs::write(repo_path.join(".peter-hook.toml"), config).unwrap(); // Create and stage a file fs::write(repo_path.join("test.txt"), "content").unwrap(); @@ -111,7 +111,7 @@ timeout_seconds = 5 [groups.pre-commit] includes = ["path-traversal"] "#; - fs::write(repo_path.join("hooks.toml"), config).unwrap(); + fs::write(repo_path.join(".peter-hook.toml"), config).unwrap(); // Create and stage a file fs::write(repo_path.join("test.txt"), "content").unwrap(); @@ -154,7 +154,7 @@ timeout_seconds = 5 [groups.pre-commit] includes = ["env-leak-attempt"] "#; - fs::write(repo_path.join("hooks.toml"), config).unwrap(); + fs::write(repo_path.join(".peter-hook.toml"), config).unwrap(); // Create and stage a file fs::write(repo_path.join("test.txt"), "content").unwrap(); @@ -215,7 +215,7 @@ execution_type = "other" [groups.pre-commit] includes = ["file-processor"] "#; - fs::write(repo_path.join("hooks.toml"), config).unwrap(); + fs::write(repo_path.join(".peter-hook.toml"), config).unwrap(); // Create files with evil names (shell-quote them for filesystem) for name in &evil_names { @@ -269,7 +269,7 @@ timeout_seconds = 5 [groups.pre-commit] includes = ["symlink-test"] "#; - fs::write(repo_path.join("hooks.toml"), config).unwrap(); + fs::write(repo_path.join(".peter-hook.toml"), config).unwrap(); // Create and stage a file fs::write(repo_path.join("test.txt"), "content").unwrap(); @@ -310,7 +310,7 @@ env = { MALICIOUS_VAR = "value; rm -rf /" } [groups.pre-commit] includes = ["env-injection"] "#; - fs::write(repo_path.join("hooks.toml"), config).unwrap(); + fs::write(repo_path.join(".peter-hook.toml"), config).unwrap(); // Create and stage a file fs::write(repo_path.join("test.txt"), "content").unwrap(); @@ -360,7 +360,7 @@ execution_type = "other" [groups.pre-commit] includes = ["special-chars"] "#; - fs::write(repo_path.join("hooks.toml"), config).unwrap(); + fs::write(repo_path.join(".peter-hook.toml"), config).unwrap(); // Create files with spaces and special characters fs::write(repo_path.join("file with spaces.txt"), "content").unwrap(); @@ -407,7 +407,7 @@ timeout_seconds = 5 [groups.pre-commit] includes = ["case-test"] "#; - fs::write(repo_path.join("hooks.toml"), config).unwrap(); + fs::write(repo_path.join(".peter-hook.toml"), config).unwrap(); // Create and stage a file fs::write(repo_path.join("test.txt"), "content").unwrap(); @@ -454,7 +454,7 @@ timeout_seconds = 5 [groups.pre-commit] includes = ["nested"] "#; - fs::write(repo_path.join("hooks.toml"), config).unwrap(); + fs::write(repo_path.join(".peter-hook.toml"), config).unwrap(); // Create and stage a file fs::write(repo_path.join("test.txt"), "content").unwrap(); @@ -497,7 +497,7 @@ timeout_seconds = 5 [groups.pre-commit] includes = ["unicode"] "#; - fs::write(unicode_dir.join("hooks.toml"), config).unwrap(); + fs::write(unicode_dir.join(".peter-hook.toml"), config).unwrap(); // Create and stage a file fs::write(unicode_dir.join("test.txt"), "content").unwrap(); @@ -533,7 +533,7 @@ timeout_seconds = 5 [groups.pre-commit] includes = ["null-test"] "#; - fs::write(repo_path.join("hooks.toml"), config).unwrap(); + fs::write(repo_path.join(".peter-hook.toml"), config).unwrap(); // Create filename with null byte (filesystem will reject or sanitize) let evil_name = "file\0evil.txt"; @@ -572,7 +572,7 @@ timeout_seconds = 5 [groups.pre-commit] includes = ["whitelist"] "#; - fs::write(repo_path.join("hooks.toml"), config).unwrap(); + fs::write(repo_path.join(".peter-hook.toml"), config).unwrap(); // Create and stage a file fs::write(repo_path.join("test.txt"), "content").unwrap(); @@ -617,7 +617,7 @@ timeout_seconds = 5 [groups.pre-commit] includes = ["cmd-sub"] "#; - fs::write(repo_path.join("hooks.toml"), config).unwrap(); + fs::write(repo_path.join(".peter-hook.toml"), config).unwrap(); // Create and stage a file fs::write(repo_path.join("test.txt"), "content").unwrap(); diff --git a/tests/stress_tests.rs b/tests/stress_tests.rs index bf18c7e..cb759e5 100644 --- a/tests/stress_tests.rs +++ b/tests/stress_tests.rs @@ -61,7 +61,7 @@ fn test_deep_hierarchy_10_levels() { let temp_dir = setup_test_repo(); let repo_path = temp_dir.path(); - // Create 10-level deep directory structure with hooks.toml at each level + // Create 10-level deep directory structure with .peter-hook.toml at each level let mut current_path = repo_path.to_path_buf(); for level in 0..10 { current_path = current_path.join(format!("level{level}")); @@ -80,7 +80,7 @@ includes = ["hook-level-{level}"] description = "Hooks at level {level}" "# ); - fs::write(current_path.join("hooks.toml"), config).unwrap(); + fs::write(current_path.join(".peter-hook.toml"), config).unwrap(); } // Create and stage a file at the deepest level @@ -143,7 +143,7 @@ timeout_seconds = 30 includes = ["file-counter"] description = "File processing" "#; - fs::write(repo_path.join("hooks.toml"), config).unwrap(); + fs::write(repo_path.join(".peter-hook.toml"), config).unwrap(); // Create 1000 text files in nested directories let start_creation = Instant::now(); @@ -235,7 +235,7 @@ execution_strategy = "parallel" ) .unwrap(); - fs::write(repo_path.join("hooks.toml"), config).unwrap(); + fs::write(repo_path.join(".peter-hook.toml"), config).unwrap(); // Create and stage a file fs::write(repo_path.join("test.txt"), "content").unwrap(); @@ -324,7 +324,7 @@ execution_strategy = "sequential" ) .unwrap(); - fs::write(repo_path.join("hooks.toml"), config).unwrap(); + fs::write(repo_path.join(".peter-hook.toml"), config).unwrap(); // Create and stage a file fs::write(repo_path.join("test.txt"), "content").unwrap(); @@ -401,7 +401,7 @@ description = "Multiple groups" "#, ); - fs::write(repo_path.join("hooks.toml"), config).unwrap(); + fs::write(repo_path.join(".peter-hook.toml"), config).unwrap(); // Measure validate command performance let start = Instant::now(); @@ -470,7 +470,7 @@ execution_strategy = "parallel" ) .unwrap(); - fs::write(repo_path.join("hooks.toml"), config).unwrap(); + fs::write(repo_path.join(".peter-hook.toml"), config).unwrap(); // Just validate the config (don't execute to save time) let start = Instant::now(); @@ -535,7 +535,7 @@ timeout_seconds = 30 [groups.pre-commit] includes = ["tree-walker"] "#; - fs::write(repo_path.join("hooks.toml"), config).unwrap(); + fs::write(repo_path.join(".peter-hook.toml"), config).unwrap(); // Stage all files let start_stage = Instant::now(); @@ -620,7 +620,7 @@ execution_strategy = "parallel" "#, ); - fs::write(repo_path.join("hooks.toml"), config).unwrap(); + fs::write(repo_path.join(".peter-hook.toml"), config).unwrap(); // Create and stage a file fs::write(repo_path.join("test.txt"), "content").unwrap(); diff --git a/tests/timeout_integration_tests.rs b/tests/timeout_integration_tests.rs index ca100cd..2d3ff64 100644 --- a/tests/timeout_integration_tests.rs +++ b/tests/timeout_integration_tests.rs @@ -41,8 +41,8 @@ fn setup_test_repo_with_config(config_content: &str) -> TempDir { .output() .unwrap(); - // Write hooks.toml - fs::write(repo_path.join("hooks.toml"), config_content).unwrap(); + // Write .peter-hook.toml + fs::write(repo_path.join(".peter-hook.toml"), config_content).unwrap(); temp_dir }