Skip to content

feat(cli): add hook migration suggestions - #169

Merged
errfld merged 2 commits into
mainfrom
gh-147/migrate-hooks
Jun 23, 2026
Merged

feat(cli): add hook migration suggestions#169
errfld merged 2 commits into
mainfrom
gh-147/migrate-hooks

Conversation

@errfld

@errfld errfld commented Jun 23, 2026

Copy link
Copy Markdown
Owner

Summary

  • Add git smee migrate-hooks as a read-only migration helper for existing unmanaged Git hooks.
  • Scan Git's effective hooks directory, ignore git-smee-managed wrappers, and emit parseable TOML suggestions that preserve legacy hook scripts by path.
  • Document the migration helper and add CLI integration coverage for no hooks, unmanaged hooks, managed wrappers, and mixed cases.

Fixes #147

Test plan

  • cargo fmt --all -- --check
  • cargo clippy --workspace --all-targets --all-features -- -D warnings
  • cargo test --workspace --all-targets --all-features

Summary by CodeRabbit

  • New Features

    • Added git smee migrate-hooks command that scans for unmanaged Git hooks and generates TOML-formatted suggestions for migration review.
  • Documentation

    • Updated CLI command reference with documentation for the new migrate-hooks command.
  • Tests

    • Added integration tests for the migrate-hooks command covering scenarios with no unmanaged hooks, multiple unmanaged hooks, and mixed managed/unmanaged setups.

@coderabbitai

coderabbitai Bot commented Jun 23, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@errfld, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 54 minutes and 9 seconds. Learn how PR review limits work.

Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file).

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits.

🚦 How do rate limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan refill rate.

For paid Pro and Pro+ PR reviews, CodeRabbit uses rolling per-developer review limits. Reviews become available again as older review attempts age out of the rolling limit window.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 82f6263b-d79b-459f-9302-256cb5b5ea4f

📥 Commits

Reviewing files that changed from the base of the PR and between 218e93d and 71ee6a7.

📒 Files selected for processing (3)
  • README.md
  • crates/git-smee-cli/src/main.rs
  • crates/git-smee-cli/tests/cli_integration.rs
📝 Walkthrough

Walkthrough

Adds a migrate-hooks CLI subcommand to git-smee-cli. When invoked it scans the effective .git/hooks directory, classifies each hook file as managed (contains the git-smee header) or unmanaged, and prints parseable TOML suggestion entries for unmanaged hooks. README documentation and three integration tests are also added.

migrate-hooks subcommand

Layer / File(s) Summary
CLI subcommand registration and dispatch
crates/git-smee-cli/src/main.rs
Adds Command::MigrateHooks to the clap Command enum and extends the main match dispatch to call run_migrate_hooks().
MigrationReport logic and output formatting
crates/git-smee-cli/src/main.rs
Implements MigrationReport to scan the hooks directory by LifeCyclePhase, separate managed from unmanaged hooks, and render TOML suggestion output. Helper functions handle phase-name joining, TOML string escaping, and trailing-newline normalization.
Integration tests
crates/git-smee-cli/tests/cli_integration.rs
Three tests cover: no unmanaged hooks (prints "none found"), unmanaged hooks only (output parses as valid SmeeConfig with expected phases, .sample files excluded), and mixed managed/unmanaged (only unmanaged entries suggested, managed hook referenced in an ignored-hooks comment).
README documentation
README.md
Adds git smee migrate-hooks to the CLI commands list and documents its read-only TOML suggestion behavior and managed-wrapper handling.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related PRs

  • errfld/git-smee#162: Extends main.rs with a status subcommand that also scans the hooks directory by LifeCyclePhase and classifies hooks using the same managed wrapper marker — the detection logic is closely shared with this PR's migrate-hooks classification.

Poem

🐇 Hop through the hooks, one file at a time,
No wrappers forgotten, no scripts left behind.
I scan every phase with a careful eye,
Print out some TOML and bid chaos goodbye.
Read-only, safe — that's the rabbit's creed! 🌿

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat(cli): add hook migration suggestions' clearly and concisely describes the main change: adding a new CLI feature for hook migration assistance.
Linked Issues check ✅ Passed The implementation fulfills all acceptance criteria from issue #147: dry-run as default behavior, unmanaged hook detection, TOML output parsing compatibility, managed wrapper exclusion, and comprehensive test coverage for all specified scenarios.
Out of Scope Changes check ✅ Passed All changes are directly related to implementing the migrate-hooks subcommand: README documentation, CLI command implementation, and integration tests covering the feature's core functionality.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch gh-147/migrate-hooks

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 218e93d784

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread crates/git-smee-cli/src/main.rs Outdated
lines.push(format!("[[{}]]", phase.as_str()));
lines.push(format!(
"command = \"{}\"",
toml_escape_basic_string(&format!(".git/hooks/{}", phase.as_str()))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Point migrated commands at a preserved legacy copy

For an existing .git/hooks/pre-commit, this suggestion emits command = ".git/hooks/pre-commit", but git smee install --force writes the managed wrapper back to that same path (install_hook uses hooks_dir.join(hook_name)). After copying the generated TOML, the wrapper calls git-smee run pre-commit, which executes .git/hooks/pre-commit again and recurses; if the user moves/deletes the old hook to install without --force, the suggested command is missing. Generate a command pointing at a path outside the effective hooks directory, or make the required move explicit in the generated TOML.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 71ee6a7: migrate-hooks now suggests .git-smee/legacy/<phase> outside the managed hooks directory and the generated TODO tells users to move .git/hooks/<phase> there before installing. Local gates passed: fmt, clippy, full workspace tests.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/git-smee-cli/src/main.rs`:
- Around line 266-278: The path pattern in the command field for unmanaged hooks
is generating `.git/hooks/<phase>` which is the exact file that git-smee
overwrites during installation, causing infinite recursion. Change the path
pattern from `.git/hooks/{}` to `.git-smee/legacy/{}` in the format string
within the loop over self.unmanaged_hooks. Additionally, update the TODO comment
to instruct users to move their legacy hook script to the new
`.git-smee/legacy/<phase>` location before running git smee install, instead of
the current message about preserving or refactoring the legacy hook script.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: ad239c02-bb76-4d7c-ae31-f33cacec75c6

📥 Commits

Reviewing files that changed from the base of the PR and between 4fc8bcd and 218e93d.

📒 Files selected for processing (3)
  • README.md
  • crates/git-smee-cli/src/main.rs
  • crates/git-smee-cli/tests/cli_integration.rs

Comment thread crates/git-smee-cli/src/main.rs
@errfld
errfld merged commit 2d82c20 into main Jun 23, 2026
28 checks passed
@errfld
errfld deleted the gh-147/migrate-hooks branch June 23, 2026 07:38

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 71ee6a7650

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +269 to +272
lines.push(format!(
"command = \"{}\"",
toml_escape_basic_string(&format!(".git-smee/legacy/{}", phase.as_str()))
));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Forward hook arguments to legacy scripts

When migrating any hook that receives positional arguments (for example commit-msg, prepare-commit-msg, post-checkout, or pre-push), this suggestion only runs the legacy script path. PlatformCommandRunner::run invokes Unix commands as sh -c <command> -- <hook-args>, and sh -c '.git-smee/legacy/commit-msg' -- .git/COMMIT_EDITMSG does not pass those args through to the script; the legacy hook sees zero arguments unless the command explicitly forwards "$@". This changes the behavior of common legacy hooks that expect Git's original arguments, so the generated command should include arg forwarding (and an equivalent Windows strategy) for preserved scripts.

Useful? React with 👍 / 👎.


fn run_migrate_hooks() -> Result<(), Box<dyn std::error::Error>> {
repository::ensure_in_repo_root()?;
let installer = installer::FileSystemHookInstaller::from_default()?;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Avoid mutating hooks directories during migration

In repositories where core.hooksPath points at a directory that does not exist yet, constructing FileSystemHookInstaller here creates that directory before the scan (from_path_with_force calls create_dir_all). That contradicts the new migrate-hooks output/README promise that the command is read-only/dry-run, and a user checking a custom hooks path can be left with filesystem changes even when there are no hooks to migrate. Resolve Git's hooks path directly for this read-only command instead of using the installer constructor.

Useful? React with 👍 / 👎.

Comment on lines +233 to +235
if !hook_path.is_file() {
continue;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Skip non-executable hooks on Unix

On Unix repositories, Git ignores hook files that are not executable (git-scm githooks documents: “Hooks that don't have the executable bit set are ignored”), but this scan treats any regular file named after a phase as an unmanaged hook. Copying the generated TOML can therefore start running a previously disabled pre-commit or commit-msg file after git smee install, changing repository behavior during migration. Check executable permissions on Unix, or report disabled hooks separately, before adding them to unmanaged_hooks.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add migration helper for existing hook files

2 participants