197 lines
5.7 KiB
Makefile
197 lines
5.7 KiB
Makefile
hooks := "pre-commit pre-push post-checkout"
|
|
hook-script := "scripts/git-hook-template.sh"
|
|
|
|
bold_underlined := BOLD + UNDERLINE
|
|
WARN := "⚠️ " + YELLOW + bold_underlined
|
|
SUCCESS := "✅ " + GREEN + bold_underlined
|
|
ERROR := "❌ " + RED + bold_underlined
|
|
HINT := "💡 " + BOLD
|
|
CHECK := "🔄 " + BOLD
|
|
|
|
default: check-hooks
|
|
@just --list
|
|
|
|
# Run post-checkout checks
|
|
[group('hooks')]
|
|
post-checkout:
|
|
|
|
# Run pre-commit checks
|
|
[group('hooks')]
|
|
pre-commit: format-rust check-rust check-docs check-typos check-spelling check-scripts check-keywords format-nix
|
|
@echo ""
|
|
@echo "{{SUCCESS}}pre-commit passed!{{NORMAL}}"
|
|
@echo ""
|
|
|
|
# Format Rust code
|
|
[group('pre-commit')]
|
|
[group('pre-push')]
|
|
[group('format')]
|
|
[parallel]
|
|
format-rust: (verify-tool "cargo")
|
|
@echo "{{CHECK}}Cargo fmt...{{NORMAL}}"
|
|
@cargo fmt --all
|
|
|
|
# Run cargo check
|
|
[group('pre-commit')]
|
|
[group('pre-push')]
|
|
[group('check')]
|
|
[parallel]
|
|
check-rust:
|
|
@echo "{{CHECK}}Cargo check...{{NORMAL}}"
|
|
@cargo check --workspace --all-targets --all-features
|
|
|
|
# Check documentation for warnings
|
|
[group('pre-commit')]
|
|
[group('pre-push')]
|
|
[group('check')]
|
|
[parallel]
|
|
check-docs:
|
|
@echo "{{CHECK}}Checking docs for warnings...{{NORMAL}}"
|
|
@RUSTDOCFLAGS="--deny warnings" cargo doc --workspace --all-features --no-deps
|
|
|
|
# Check for typos
|
|
[group('pre-commit')]
|
|
[group('pre-push')]
|
|
[group('check')]
|
|
[parallel]
|
|
check-typos: (verify-tool "typos" "typos-cli")
|
|
@echo "{{CHECK}}Checking for spelling typos...{{NORMAL}}"
|
|
@typos
|
|
|
|
# Run codespell
|
|
[group('pre-commit')]
|
|
[group('pre-push')]
|
|
[group('check')]
|
|
[parallel]
|
|
check-spelling: (verify-tool "codespell")
|
|
@echo "{{CHECK}}Checking for code typos...{{NORMAL}}"
|
|
@git ls-files -z | xargs -0 codespell -w
|
|
|
|
# Run shellcheck on all shell scripts
|
|
[group('pre-commit')]
|
|
[group('pre-push')]
|
|
[group('check')]
|
|
[parallel]
|
|
check-scripts: (verify-tool "shellcheck")
|
|
@echo "{{CHECK}}Checking shell scripts...{{NORMAL}}"
|
|
@shellcheck scripts/*.sh
|
|
|
|
# Replicate the custom grep checks from flake.nix
|
|
[group('pre-commit')]
|
|
[group('pre-push')]
|
|
[group('check')]
|
|
[parallel]
|
|
check-keywords: (verify-tool "rg" "ripgrep")
|
|
#! /usr/bin/env bash
|
|
set -e
|
|
echo "{{CHECK}}Checking for forbidden words in staged files...{{NORMAL}}"
|
|
|
|
# Get staged Rust files
|
|
STAGED_FILES=$(git diff --cached --name-only --diff-filter=ACMR | grep '\.rs$' || true)
|
|
|
|
if [ -n "$STAGED_FILES" ]; then
|
|
! echo "$STAGED_FILES" | xargs rg --context=3 --fixed-strings 'radicle.xyz'
|
|
! echo "$STAGED_FILES" | xargs rg --context=3 --fixed-strings 'radicle.zulipchat.com'
|
|
|
|
# For `git2::` we need to exclude raw.rs
|
|
FILTERED_GIT2=$(echo "$STAGED_FILES" | grep '^crates/radicle/.*\.rs$' | grep -v 'crates/radicle/src/git/raw.rs' || true)
|
|
if [ -n "$FILTERED_GIT2" ]; then
|
|
! echo "$FILTERED_GIT2" | xargs rg --context=3 --fixed-strings 'git2::'
|
|
fi
|
|
fi
|
|
|
|
# Format Nix files
|
|
[group('pre-commit')]
|
|
[group('pre-push')]
|
|
[group('format')]
|
|
[parallel]
|
|
format-nix:
|
|
#!/usr/bin/env bash
|
|
if command -v alejandra >/dev/null 2>&1; then
|
|
alejandra --check .
|
|
else
|
|
echo "⏭️ alejandra not found, skipping Nix formatting."
|
|
fi
|
|
|
|
# Run pre-push checks
|
|
[group('hooks')]
|
|
pre-push: format-rust check-rust check-keywords check-docs check-spelling check-scripts check-typos format-nix lint-rust
|
|
@echo ""
|
|
@echo "{{SUCCESS}}pre-push passed!{{NORMAL}}"
|
|
@echo ""
|
|
|
|
# Run Clippy lints
|
|
[group('pre-push')]
|
|
lint-rust: (verify-tool "cargo")
|
|
@echo "{{CHECK}}Cargo clippy...{{NORMAL}}"
|
|
@cargo clippy --workspace --all-targets --all-features -- --deny warnings
|
|
|
|
# Check if required tools are in PATH.
|
|
[private]
|
|
verify-tool tool package_name="":
|
|
#!/usr/bin/env bash
|
|
set -e
|
|
if ! command -v {{tool}} >/dev/null 2>&1; then
|
|
PKG="{{package_name}}"
|
|
if [ -z "$PKG" ]; then
|
|
PKG="{{tool}}"
|
|
fi
|
|
echo "{{ERROR}}Missing required tool: {{tool + NORMAL}}"
|
|
echo "{{HINT}}Use your systems package manager to install '$PKG'.{{NORMAL}}"
|
|
exit 1
|
|
fi
|
|
|
|
# SECURITY: We COPY the hook template instead of symlinking it. This ensures that
|
|
# checking out an untrusted patch won't overwrite your local git hooks. The copied
|
|
# script also checks if sensitive files (like build.rs or justfile) were modified
|
|
# in the patch and prompts for confirmation, preventing arbitrary code execution.
|
|
#
|
|
# Install git hooks
|
|
[group('hooks')]
|
|
[confirm("Overwrite existing hooks '" + hooks + "'?")]
|
|
install-hooks:
|
|
#!/usr/bin/env bash
|
|
set -e
|
|
for hook in {{hooks}}; do
|
|
cp {{hook-script}} ".git/hooks/$hook"
|
|
chmod +x ".git/hooks/$hook"
|
|
done
|
|
echo ""
|
|
echo "{{SUCCESS}}Hooks installed: {{hooks + NORMAL}}"
|
|
|
|
# Check for missing or changed hooks
|
|
[group('hooks')]
|
|
check-hooks:
|
|
#!/usr/bin/env bash
|
|
set -e
|
|
TEMPLATE="{{hook-script}}"
|
|
OUTDATED=()
|
|
MISSING=0
|
|
TOTAL=0
|
|
|
|
for hook in {{hooks}}; do
|
|
TOTAL=$((TOTAL + 1))
|
|
if [ ! -f ".git/hooks/$hook" ]; then
|
|
MISSING=$((MISSING + 1))
|
|
OUTDATED+=("$hook")
|
|
elif ! cmp -s "$TEMPLATE" ".git/hooks/$hook"; then
|
|
OUTDATED+=("$hook")
|
|
fi
|
|
done
|
|
|
|
if [ "$MISSING" -eq "$TOTAL" ] && [ "$TOTAL" -gt 0 ]; then
|
|
echo ""
|
|
echo "{{HINT}}No git hooks are installed. Run 'just install-hooks' to install them.{{NORMAL}}"
|
|
echo ""
|
|
elif [ ${#OUTDATED[@]} -gt 0 ]; then
|
|
echo ""
|
|
echo "{{WARN}}WARNING: The following git hooks are missing or out of date:{{NORMAL}}"
|
|
echo ""
|
|
for hook in "${OUTDATED[@]}"; do
|
|
echo -e "\t$hook"
|
|
done
|
|
echo ""
|
|
echo "{{HINT}}Check them with 'diff scripts/git-hook-template.sh .git/hooks/<hook name>' then run 'just install-hooks'{{NORMAL}}"
|
|
echo ""
|
|
fi
|