diff --git a/.githooks/commit-msg b/.githooks/commit-msg new file mode 100755 index 00000000..a59728be --- /dev/null +++ b/.githooks/commit-msg @@ -0,0 +1,50 @@ +#!/bin/sh +# Verify commit message follows conventional commit format +# https://www.conventionalcommits.org/ +# +# Uses npx commitlint if Node.js is available (same as CI), +# otherwise falls back to basic shell regex validation. + +commit_msg_file="$1" +commit_msg=$(cat "$commit_msg_file") + +# Skip merge commits +if echo "$commit_msg" | grep -qE "^Merge "; then + exit 0 +fi + +# Try to use commitlint via npx if Node.js is available +if command -v npx >/dev/null 2>&1; then + # Run commitlint with config-conventional rules (same as CI) + echo "$commit_msg" | npx --yes @commitlint/cli@latest --extends @commitlint/config-conventional + exit $? +fi + +# Fallback: basic shell regex validation if Node.js is not available +echo "Note: Node.js not found, using basic commit message validation." +echo " Install Node.js for full commitlint validation (same as CI)." +echo "" + +# Conventional commit types from @commitlint/config-conventional +types="build|chore|ci|docs|feat|fix|perf|refactor|revert|style|test" + +# Pattern: type(optional-scope): description +# The description must start with lowercase and not end with period +pattern="^($types)(\(.+\))?(!)?: .+" + +if ! echo "$commit_msg" | head -1 | grep -qE "$pattern"; then + echo "ERROR: Commit message does not follow conventional commit format." + echo "" + echo "Expected format: (): " + echo "" + echo "Valid types: build, chore, ci, docs, feat, fix, perf, refactor, revert, style, test" + echo "" + echo "Examples:" + echo " feat: add new feature" + echo " fix(parser): resolve parsing issue" + echo " docs: update README" + echo "" + echo "Your commit message:" + echo " $(head -1 "$commit_msg_file")" + exit 1 +fi diff --git a/crates/rmcp/build.rs b/crates/rmcp/build.rs new file mode 100644 index 00000000..05702b40 --- /dev/null +++ b/crates/rmcp/build.rs @@ -0,0 +1,23 @@ +// Install git hooks on build +fn main() { + // Only run in the workspace root (not when building as a dependency) + let manifest_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap(); + let workspace_root = std::path::Path::new(&manifest_dir) + .parent() + .and_then(|p| p.parent()); + + if let Some(root) = workspace_root { + let githooks_dir = root.join(".githooks"); + let git_dir = root.join(".git"); + + // Only configure if we're in the actual workspace (not a dependency) + // and git directory exists + if githooks_dir.exists() && git_dir.exists() { + // Configure git to use our hooks directory + let _ = std::process::Command::new("git") + .args(["config", "core.hooksPath", ".githooks"]) + .current_dir(root) + .output(); + } + } +}