.PHONY: help setup clean test lint format doc bench audit coverage release check-all dev-setup
help:
@echo "Available targets:"
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf " \033[36m%-15s\033[0m %s\n", $$1, $$2}'
setup:
@echo "🔧 Setting up development environment..."
@if [ ! -d ".githooks" ]; then mkdir -p .githooks; fi
@if [ ! -f ".githooks/commit-msg" ]; then \
echo "📝 Creating commit message hook..."; \
echo "#!/bin/bash" > .githooks/commit-msg; \
echo "# Validate commit message format" >> .githooks/commit-msg; \
echo "if command -v npx >/dev/null 2>&1 && [ -f package.json ]; then" >> .githooks/commit-msg; \
echo " npx commitlint --edit \$$1" >> .githooks/commit-msg; \
echo "else" >> .githooks/commit-msg; \
echo " if ! grep -qE '^(feat|fix|docs|style|refactor|test|chore|perf|ci|build|revert)(\\(.+\\))?: .+' \$$1; then" >> .githooks/commit-msg; \
echo " echo 'Invalid commit message format. Use: type(scope): description'" >> .githooks/commit-msg; \
echo " exit 1" >> .githooks/commit-msg; \
echo " fi" >> .githooks/commit-msg; \
echo "fi" >> .githooks/commit-msg; \
chmod +x .githooks/commit-msg; \
fi
@echo "🔗 Configuring git hooks..."
@git config --local core.hooksPath .githooks
@if [ -f ".gitmessage" ]; then \
echo "📋 Setting commit message template..."; \
git config --local commit.template .gitmessage; \
fi
@if command -v npm >/dev/null 2>&1 && [ -f package.json ]; then \
echo "📦 Installing Node.js dependencies..."; \
npm install; \
fi
@echo "🦀 Checking Rust components..."
@if ! rustup component list --installed | grep -q rustfmt; then rustup component add rustfmt; fi
@if ! rustup component list --installed | grep -q clippy; then rustup component add clippy; fi
@echo "🔧 Git hooks configuration..."
@if command -v pre-commit >/dev/null 2>&1; then \
echo "💡 Pre-commit is available. Choose your hook system:"; \
echo " • Manual hooks (current): Already configured in .githooks/"; \
echo " • Enterprise pre-commit: Run 'make pre-commit-install'"; \
echo "✅ Manual git hooks remain active"; \
else \
echo "✅ Manual git hooks configured in .githooks/"; \
echo "💡 For enterprise-grade hooks: brew install pre-commit && make pre-commit-install"; \
fi
@echo "✅ Development environment setup complete"
dev-setup: setup
@echo "Development setup complete. Run 'make help' for available commands."
clean:
@echo "🧹 Cleaning build artifacts..."
@cargo clean
@rm -f debug_*
@rm -f test_*
@find . -name "*.tmp" -delete
@echo "✅ Clean complete"
test:
@echo "🧪 Running tests..."
@timeout 30s cargo test --lib --verbose
test-release:
@echo "🧪 Running tests in release mode..."
@timeout 30s cargo test --lib --release --verbose
test-all-features:
@echo "🧪 Running tests with all features..."
@timeout 30s cargo test --lib --all-features --verbose || [ $$? -eq 124 ]
test-no-default:
@echo "🧪 Running tests without default features..."
@timeout 30s cargo test --lib --no-default-features --verbose || [ $$? -eq 124 ]
test-nocapture:
@echo "🧪 Running tests with output visible..."
@timeout 30s cargo test -- --nocapture
test-lib:
@echo "🧪 Running library tests..."
@timeout 30s cargo test --lib
test-integration:
@echo "🧪 Running integration tests..."
@timeout 120s cargo test --test integration_tests
@timeout 30s cargo test --test edge_cases_and_errors
@timeout 30s cargo test --test performance_and_stress
@timeout 30s cargo test --test indentation_style_preservation
@timeout 30s cargo test --test complex_keys
test-security:
@echo "🔒 Running security tests..."
@timeout 30s cargo test --test security_limits
@timeout 30s cargo test test_nested_alias_expansion_limit
format:
@echo "🎨 Formatting code..."
@cargo fmt
format-check:
@echo "🎨 Checking code formatting..."
@cargo fmt --all -- --check
lint:
@echo "📎 Running clippy..."
@timeout 30s cargo clippy --all-targets --all-features -- -D warnings
lint-fix:
@echo "📎 Running clippy with fixes..."
@timeout 30s cargo clippy --all-targets --all-features --fix -- -D warnings
clippy-strict:
@echo "📎 Running clippy with strict CI settings..."
@timeout 30s cargo clippy --all-targets --all-features -- -D warnings -D clippy::all -D clippy::pedantic -W clippy::nursery \
-A clippy::needless_raw_string_hashes \
-A clippy::format_push_string \
-A clippy::single_char_pattern \
-A clippy::unreadable_literal \
-A clippy::manual_string_new \
-A clippy::write_with_newline \
-A clippy::uninlined_format_args \
-A clippy::semicolon_if_nothing_returned \
-A clippy::explicit_iter_loop \
-A clippy::inefficient_to_string \
-A clippy::match_same_arms \
-A clippy::doc_markdown \
-A clippy::too_many_lines
audit:
@echo "🔒 Running security audit..."
@if ! command -v cargo-audit >/dev/null; then cargo install cargo-audit; fi
@cargo audit
deny:
@echo "🔒 Running cargo deny checks..."
@if ! command -v cargo-deny >/dev/null; then cargo install cargo-deny; fi
@timeout 15s cargo deny check
doc:
@echo "📚 Building documentation..."
@cargo doc --all-features --no-deps
doc-open:
@echo "📚 Building and opening documentation..."
@cargo doc --all-features --no-deps --open
doc-private:
@echo "📚 Building documentation with private items..."
@cargo doc --all-features --no-deps --document-private-items
bench:
@echo "⚡ Running benchmarks..."
@cargo bench
bench-compile:
@echo "⚡ Compiling benchmarks..."
@cargo bench --no-run
coverage:
@echo "📊 Generating coverage report..."
@if ! command -v cargo-llvm-cov >/dev/null; then cargo install cargo-llvm-cov; fi
@timeout 60s cargo llvm-cov --lib --lcov --output-path lcov.info
@echo "Coverage report generated: lcov.info"
coverage-html:
@echo "📊 Generating HTML coverage report..."
@if ! command -v cargo-llvm-cov >/dev/null; then cargo install cargo-llvm-cov; fi
@timeout 60s cargo llvm-cov --lib --html
@echo "HTML coverage report generated in target/llvm-cov/html/"
coverage-tarpaulin:
@echo "📊 Generating coverage with tarpaulin..."
@if ! command -v cargo-tarpaulin >/dev/null; then cargo install cargo-tarpaulin; fi
@timeout 60s cargo tarpaulin --lib --out lcov --output-dir .
build:
@echo "🔨 Building project..."
@cargo build
build-release:
@echo "🔨 Building project in release mode..."
@timeout 30s cargo build --release
build-all-features:
@echo "🔨 Building project with all features..."
@cargo build --all-features
examples:
@echo "💡 Running examples..."
@cargo run --example library_comparison
package:
@echo "📦 Packaging crate..."
@cargo package
package-list:
@echo "📦 Listing package contents..."
@cargo package --list
check:
@echo "✅ Running basic checks..."
@make format-check
@make lint
@make test
@echo "✅ All basic checks passed"
check-all:
@echo "✅ Running comprehensive checks..."
@make format-check
@make lint
@make test-all-features
@make test-no-default
@make audit
@make doc
@make bench-compile
@make check-markdown
@echo "✅ All comprehensive checks passed"
ci:
@echo "🔄 Running CI pipeline locally..."
@make format-check
@make clippy-strict
@make test-lib
@make test-integration
@make test-security
@make deny
@echo "✅ CI pipeline completed successfully!"
quick-check:
@echo "⚡ Running quick checks..."
@make format
@make lint
@make test-lib
@echo "✅ Quick checks passed!"
release-check:
@echo "🚀 Checking release readiness..."
@make check-all
@echo "Checking version consistency..."
@CARGO_VERSION=$$(grep '^version = ' Cargo.toml | sed 's/version = "\(.*\)"/\1/'); \
if [ -f GitVersion.yml ]; then \
GITVERSION_VERSION=$$(grep 'next-version:' GitVersion.yml | sed 's/next-version: "\?\(.*\)"\?/\1/' | sed 's/-.*//'); \
if [ "$$CARGO_VERSION" != "$$GITVERSION_VERSION" ]; then \
echo "❌ Version mismatch: Cargo.toml=$$CARGO_VERSION, GitVersion.yml=$$GITVERSION_VERSION"; \
exit 1; \
fi; \
fi
@echo "✅ Release check passed"
release:
@echo "🚀 Manual Release Process"
@echo ""
@echo "📋 Release Steps:"
@echo " 1. Update version in Cargo.toml"
@echo " 2. Update version in GitVersion.yml"
@echo " 3. Run: make release-check"
@echo " 4. Commit changes with: git commit -m 'chore: release version X.Y.Z [skip ci]'"
@echo " 5. Create tag: git tag vX.Y.Z"
@echo " 6. Push: git push origin main && git push origin vX.Y.Z"
@echo ""
@echo "💡 Or use GitHub Actions 'Manual Version Bump' workflow"
release-patch:
@echo "🚀 Patch Release Guide"
@$(MAKE) release
release-minor:
@echo "🚀 Minor Release Guide"
@$(MAKE) release
release-major:
@echo "🚀 Major Release Guide"
@$(MAKE) release
pre-commit:
@echo "🔍 Running pre-commit checks..."
@make format
@make lint
@make test
@echo "✅ Pre-commit checks passed"
pre-push:
@echo "🔍 Running pre-push checks..."
@make check-all
@echo "✅ Pre-push checks passed"
commit-lint:
@echo "📝 Testing commit message format..."
@if [ -z "$(MSG)" ]; then \
echo "Usage: make commit-lint MSG='feat: add new feature'"; \
exit 1; \
fi
@echo "$(MSG)" | if command -v npx >/dev/null 2>&1 && [ -f package.json ]; then \
npx commitlint; \
else \
if ! grep -qE '^(feat|fix|docs|style|refactor|test|chore|perf|ci|build|revert)(\(.+\))?: .+' -; then \
echo 'Invalid commit message format. Use: type(scope): description'; \
exit 1; \
fi; \
fi
@echo "✅ Commit message format is valid"
git-status:
@git status --porcelain | if [ $$(wc -l) -eq 0 ]; then \
echo "✅ Working directory clean"; \
else \
echo "📂 Working directory changes:"; \
git status --short; \
fi
pre-commit-install:
@echo "🔧 Installing pre-commit hooks..."
@if ! command -v pre-commit >/dev/null; then \
echo "❌ pre-commit not found. Install with: brew install pre-commit"; \
exit 1; \
fi
@echo "📝 Switching from manual git hooks to pre-commit..."
@git config --unset-all core.hooksPath || true
@pre-commit install --install-hooks
@pre-commit install --hook-type commit-msg
@echo "✅ Pre-commit hooks installed (manual .githooks disabled)"
pre-commit-run:
@echo "🔍 Running pre-commit hooks on all files..."
@timeout 120s pre-commit run --all-files
pre-commit-dev:
@echo "🔍 Running pre-commit hooks..."
@pre-commit run --all-files
pre-commit-update:
@echo "⬆️ Updating pre-commit hooks..."
@pre-commit autoupdate
pre-commit-clean:
@echo "🧹 Cleaning pre-commit cache..."
@pre-commit clean
pre-commit-rust:
@echo "🦀 Running Rust-specific pre-commit hooks..."
@pre-commit run rust-fmt rust-clippy rust-check rust-test rust-doc
pre-commit-quick:
@echo "⚡ Running quick pre-commit hooks..."
@SKIP=rust-test,rust-coverage,rust-audit,rust-deny pre-commit run --all-files
pre-commit-security:
@echo "🔒 Running security-focused pre-commit hooks..."
@pre-commit run detect-private-key detect-secrets rust-audit rust-deny
install-tools:
@echo "🔧 Installing development tools..."
@if ! command -v cargo-audit >/dev/null; then cargo install cargo-audit; fi
@if ! command -v cargo-llvm-cov >/dev/null; then cargo install cargo-llvm-cov; fi
@if ! command -v cargo-criterion >/dev/null; then cargo install cargo-criterion; fi
@if ! command -v cargo-flamegraph >/dev/null; then cargo install flamegraph; fi
@echo "✅ Development tools installed"
version:
@echo "📋 Version Information:"
@echo "Cargo version: $$(grep '^version = ' Cargo.toml | sed 's/version = "\(.*\)"/\1/')"
@if [ -f GitVersion.yml ]; then \
echo "GitVersion next: $$(grep 'next-version:' GitVersion.yml | sed 's/next-version: "\?\(.*\)"\?/\1/')"; \
fi
@if command -v git >/dev/null && git rev-parse --git-dir >/dev/null 2>&1; then \
echo "Git commit: $$(git rev-parse --short HEAD)"; \
echo "Git branch: $$(git branch --show-current)"; \
fi
debug-env:
@echo "🔍 Development Environment:"
@echo "Rust version: $$(rustc --version)"
@echo "Cargo version: $$(cargo --version)"
@if command -v node >/dev/null; then echo "Node.js version: $$(node --version)"; fi
@if command -v npm >/dev/null; then echo "npm version: $$(npm --version)"; fi
@echo "Git hooks path: $$(git config --get core.hooksPath || echo 'default')"
@echo "Commit template: $$(git config --get commit.template || echo 'none')"
check-markdown:
@echo "📝 Checking markdown formatting..."
@for file in *.md; do \
if [ -f "$$file" ]; then \
echo "Checking $$file..."; \
echo "=== MD022 Issues in $$file ==="; \
awk 'NR > 1 && /^#{1,6} / && prev_line != "" {print "Line " NR ": Missing blank line before heading: " $$0} {prev_line = $$0}' "$$file" || true; \
echo "=== MD032 Issues in $$file ==="; \
awk 'NR > 1 && (/^[-*+] / || /^[0-9]+\. /) && prev_line != "" && prev_line !~ /^[-*+] / && prev_line !~ /^[0-9]+\. / {print "Line " NR ": Missing blank line before list: " $$0} {prev_line = $$0}' "$$file" || true; \
echo ""; \
fi; \
done
fix-markdown:
@echo "📝 Fixing markdown issues (MD022: blanks around headings, MD031: blanks around fences, MD032: blanks around lists, bold-as-headings)..."
@for file in *.md docs/*.md; do \
if [ -f "$$file" ]; then \
echo "Processing $$file..."; \
awk 'BEGIN { \
prev_line = ""; \
in_code_block = 0; \
last_was_blank = 0; \
need_blank_after = 0 \
} \
/^```/ { \
if (!in_code_block) { \
if (NR > 1 && !last_was_blank) { \
print "" \
} \
in_code_block = 1; \
print; \
need_blank_after = 0 \
} else { \
in_code_block = 0; \
print; \
need_blank_after = 1 \
} \
prev_line = $$0; \
last_was_blank = 0; \
next \
} \
in_code_block { \
print; \
prev_line = $$0; \
last_was_blank = 0; \
next \
} \
/^#{1,6} \*\*.*\*\*/ { \
gsub(/\*\*/, "", $$0) \
} \
/^#{1,6} / { \
if (need_blank_after) { \
print ""; \
need_blank_after = 0 \
} \
if (NR > 1 && !last_was_blank && prev_line !~ /^#{1,6} /) { \
print "" \
} \
print; \
prev_line = $$0; \
last_was_blank = 0; \
next \
} \
/^[-*+] / || /^[0-9]+\. / { \
if (need_blank_after) { \
print ""; \
need_blank_after = 0 \
} \
if (NR > 1 && !last_was_blank && prev_line !~ /^[-*+] / && prev_line !~ /^[0-9]+\. / && prev_line !~ /^ /) { \
print "" \
} \
print; \
prev_line = $$0; \
last_was_blank = 0; \
next \
} \
/^$$/ { \
if (need_blank_after) { \
need_blank_after = 0 \
} \
print; \
prev_line = $$0; \
last_was_blank = 1; \
next \
} \
{ \
if (need_blank_after) { \
print ""; \
need_blank_after = 0 \
} \
if (prev_line ~ /^#{1,6} / && !last_was_blank) { \
print "" \
} \
if ((prev_line ~ /^[-*+] / || prev_line ~ /^[0-9]+\. /) && $$0 !~ /^[-*+] / && $$0 !~ /^[0-9]+\. / && $$0 !~ /^ / && !last_was_blank) { \
print "" \
} \
print; \
prev_line = $$0; \
last_was_blank = 0 \
}' "$$file" > "$$file.tmp" && mv "$$file.tmp" "$$file"; \
echo "Fixed $$file"; \
fi; \
done
@echo "✅ Markdown formatting completed!"
check-markdown-detailed:
@echo "📝 Detailed markdown check..."
@for file in *.md; do \
if [ -f "$$file" ]; then \
echo "=== Analyzing $$file ==="; \
awk ' \
BEGIN { prev_line = ""; line_num = 0; issues = 0 } \
{ line_num++ } \
/^#{1,6} / { \
if (line_num > 1 && prev_line !~ /^$$/) { \
print "MD022 - Line " line_num ": Missing blank line before heading: " $$0; \
issues++ \
} \
} \
/^[-*+] / || /^[0-9]+\. / { \
if (line_num > 1 && prev_line !~ /^$$/ && prev_line !~ /^[-*+] / && prev_line !~ /^[0-9]+\. / && prev_line !~ /^ /) { \
print "MD032 - Line " line_num ": Missing blank line before list: " $$0; \
issues++ \
} \
} \
{ prev_line = $$0 } \
END { \
if (issues == 0) print "✅ No issues found"; \
else print "❌ Found " issues " issues" \
}' "$$file"; \
echo ""; \
fi; \
done
fmt: format
build-all: build-all-features
test-verbose: test-nocapture
docs: doc
checks: check