Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 75 additions & 14 deletions .github/hooks/install.sh
Original file line number Diff line number Diff line change
@@ -1,25 +1,86 @@
#!/bin/bash
#
# Install git hooks from .github/hooks/ into .git/hooks/ via symlinks.
#
# Usage:
# sh .github/hooks/install.sh # Install all hooks
# sh .github/hooks/install.sh pre-push-coverage # Install only coverage hook
# sh .github/hooks/install.sh pre-push # Install only quality gates hook
# sh .github/hooks/install.sh pre-push pre-push-coverage # Install both pre-push hooks
#
# Available hooks:
# commit-msg — Angular conventional commit format check
# pre-push — Quality gates (cargo fmt, clippy, test) [blocking]
# pre-push-coverage — Coverage report check [non-blocking]
#

# Define the directory containing the custom hook scripts
HOOKS_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"

# Define the Git hooks directory
GIT_HOOKS_DIR="$(git rev-parse --git-dir)/hooks"

# List of hooks to install
HOOKS=("commit-msg" "pre-push")

# Create the hooks directory if it doesn't exist
mkdir -p "$GIT_HOOKS_DIR"

# Install each hook
for hook in "${HOOKS[@]}"; do
if [ -f "$HOOKS_DIR/$hook" ]; then
ln -sf "$HOOKS_DIR/$hook" "$GIT_HOOKS_DIR/$hook"
echo "Installed $hook hook"
else
echo "Hook $hook not found in $HOOKS_DIR"
install_symlink() {
local source="$1"
local target="$2"

if [ ! -f "$HOOKS_DIR/$source" ]; then
echo "Hook '$source' not found in $HOOKS_DIR"
return 1
fi

ln -sf "$HOOKS_DIR/$source" "$GIT_HOOKS_DIR/$target"
echo "Installed $source hook"
}

# Collect requested hooks
if [ -n "$1" ]; then
REQUESTED="$*"
else
REQUESTED="commit-msg pre-push"
fi

# Check if both pre-push hooks are requested
HAS_PREPUSH=false
HAS_COVERAGE=false
for hook in $REQUESTED; do
case "$hook" in
pre-push) HAS_PREPUSH=true ;;
pre-push-coverage) HAS_COVERAGE=true ;;
esac
done

# Install non-pre-push hooks as symlinks
for hook in $REQUESTED; do
case "$hook" in
pre-push|pre-push-coverage) ;; # handled below
*) install_symlink "$hook" "$hook" ;;
esac
done

# Install pre-push hook(s)
if $HAS_PREPUSH && $HAS_COVERAGE; then
# Both requested — create a wrapper that runs both
cat > "$GIT_HOOKS_DIR/pre-push" << 'WRAPPER'
#!/bin/sh
# Auto-generated wrapper — runs both pre-push hooks.
# Re-run install.sh to regenerate.

REPO_ROOT="$(git rev-parse --show-toplevel)"
HOOKS_SRC="$REPO_ROOT/.github/hooks"

# Coverage check (non-blocking)
"$HOOKS_SRC/pre-push-coverage" "$@"

# Quality gates (blocking)
"$HOOKS_SRC/pre-push" "$@" || exit $?
exit 0
WRAPPER
chmod +x "$GIT_HOOKS_DIR/pre-push"
echo "Installed pre-push + pre-push-coverage hooks (combined)"
elif $HAS_PREPUSH; then
install_symlink "pre-push" "pre-push"
elif $HAS_COVERAGE; then
install_symlink "pre-push-coverage" "pre-push"
fi

echo "Git hooks installation complete."
87 changes: 87 additions & 0 deletions .github/hooks/pre-push-coverage
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
#!/bin/sh
#
# Pre-push hook: checks that a fresh coverage report exists
# when pushing coprocessor changes. Non-blocking (always exits 0).
#

# Preserve Git LFS functionality
command -v git-lfs >/dev/null 2>&1 && git lfs pre-push "$@"

# Get the repo root
REPO_ROOT=$(git rev-parse --show-toplevel)
REPORT_FILE="$REPO_ROOT/coprocessor/fhevm-engine/coverage-report.txt"
COPROCESSOR_DIR="coprocessor/fhevm-engine/"

# Fetch latest remote to ensure accurate comparison
git fetch origin main --quiet 2>/dev/null || true

# Get changed files on this branch vs origin/main, filtered to actual crate directories only
# (ignores root-level config: Makefile, scripts/, Cargo.toml, etc.)
CRATE_CHANGES=$(git diff --name-only "origin/main"...HEAD -- "$COPROCESSOR_DIR" 2>/dev/null | \
while read -r f; do
rel=$(echo "$f" | sed "s|^$COPROCESSOR_DIR||")
crate_name=$(echo "$rel" | cut -d/ -f1)
if [ -f "$REPO_ROOT/$COPROCESSOR_DIR$crate_name/Cargo.toml" ]; then
echo "$crate_name"
fi
done | sort -u)

if [ -z "$CRATE_CHANGES" ]; then
exit 0
fi

# Check if coverage report exists
if [ ! -f "$REPORT_FILE" ]; then
echo ""
echo "WARNING: No coverage report found for coprocessor."
echo " Run 'make coverage-changed' in coprocessor/fhevm-engine/ to generate it."
echo ""
exit 0
fi

# Check report freshness via embedded commit hash
REPORT_COMMIT=$(grep "^# commit:" "$REPORT_FILE" 2>/dev/null | awk '{print $3}')
if [ -z "$REPORT_COMMIT" ]; then
echo ""
echo "WARNING: Coverage report has no commit metadata (regenerate with latest tooling)."
echo " Run 'make coverage-changed' in coprocessor/fhevm-engine/ to regenerate."
echo ""
exit 0
fi

# Get the latest crate-related commit on this branch
LATEST_CRATE_COMMIT=""
for crate in $CRATE_CHANGES; do
c=$(git log -1 --format=%H "origin/main"..HEAD -- "$COPROCESSOR_DIR$crate/" 2>/dev/null)
if [ -n "$c" ]; then
if [ -z "$LATEST_CRATE_COMMIT" ] || [ "$(git log -1 --format=%ct "$c")" -gt "$(git log -1 --format=%ct "$LATEST_CRATE_COMMIT")" ]; then
LATEST_CRATE_COMMIT="$c"
fi
fi
done

if [ -z "$LATEST_CRATE_COMMIT" ]; then
exit 0
fi

# Check if the report was generated at or after the latest crate commit
if ! git merge-base --is-ancestor "$LATEST_CRATE_COMMIT" "$REPORT_COMMIT" 2>/dev/null; then
echo ""
echo "WARNING: Coverage report is stale (generated before latest coprocessor commit)."
echo " Run 'make coverage-changed' in coprocessor/fhevm-engine/ to regenerate."
echo ""
exit 0
fi

# Report is fresh — print summary
TOTAL_LINE=$(grep "^TOTAL" "$REPORT_FILE" 2>/dev/null)
if [ -n "$TOTAL_LINE" ]; then
REGION_PCT=$(echo "$TOTAL_LINE" | awk '{print $4}')
FUNC_PCT=$(echo "$TOTAL_LINE" | awk '{print $7}')
LINE_PCT=$(echo "$TOTAL_LINE" | awk '{print $10}')
echo ""
echo "Coverage: Lines: $LINE_PCT | Functions: $FUNC_PCT | Regions: $REGION_PCT"
echo ""
fi

exit 0
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -89,3 +89,6 @@ mochawesome-report/
#-------------------------------------------------------------------------------
# Build artifacts directory
target/

# Coverage reports (generated locally via `make coverage`)
coverage-report.txt
45 changes: 45 additions & 0 deletions coprocessor/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,51 @@ When using the `aws-kms` signer type, standard `AWS_*` environment variables are
- **AWS_SECRET_ACCESS_KEY** (i.e. password)
- etc.

## Code Coverage

### Setup (one-time)

Install git hooks:

```bash
# Install both pre-push hooks
sh .github/hooks/install.sh pre-push pre-push-coverage

# Install only the coverage pre-push hook (non-blocking)
sh .github/hooks/install.sh pre-push-coverage

# Install only the quality gates pre-push hook (cargo fmt, clippy, test)
sh .github/hooks/install.sh pre-push

# Install all default hooks (commit-msg + quality gates pre-push)
sh .github/hooks/install.sh
```

> To uninstall: `rm .git/hooks/pre-push`

### Running coverage locally

```bash
cd coprocessor/fhevm-engine

# Only cover crates you changed (auto-detects vs main branch)
make coverage-changed

# Cover a specific crate
make coverage-crate CRATE=host-listener

# Cover the full workspace
make coverage
```

All commands save the report to `coverage-report.txt`. If `fhevm-engine-common` is among the changed crates, `coverage-changed` automatically falls back to full workspace coverage since all crates depend on it.

**Prerequisites:** PostgreSQL and LocalStack must be running (same as for regular tests).

### Pre-push hook

When pushing changes that touch coprocessor crate code, the pre-push hook checks for a fresh `coverage-report.txt`. If the report is missing or older than your latest commit, it prints a warning reminding you to run `make coverage-changed`. The hook is **non-blocking** and will never prevent a push.

## Telemetry Style Guide (Tracing + OTEL)

Use `tracing` spans as the default telemetry API.
Expand Down
31 changes: 31 additions & 0 deletions coprocessor/fhevm-engine/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
.PHONY: coverage coverage-crate coverage-changed

# Run coverage for the full workspace
coverage:
cargo llvm-cov clean --workspace --profile coverage
DATABASE_URL=postgresql://postgres:postgres@localhost:5432/coprocessor \
TEST_GLOBAL_LOCALSTACK=1 \
cargo llvm-cov --no-report --workspace --profile coverage -- --test-threads=1
cargo llvm-cov report --profile coverage 2>&1 | tee coverage-report.txt
@echo "# commit: $$(git rev-parse HEAD)" >> coverage-report.txt
@echo ""
@echo "Coverage report saved to coverage-report.txt"
@tail -2 coverage-report.txt

# Run coverage for a specific crate
# Usage: make coverage-crate CRATE=host-listener
coverage-crate:
@test -n "$(CRATE)" || (echo "Usage: make coverage-crate CRATE=<crate-name>" && exit 1)
cargo llvm-cov clean --workspace --profile coverage
DATABASE_URL=postgresql://postgres:postgres@localhost:5432/coprocessor \
TEST_GLOBAL_LOCALSTACK=1 \
cargo llvm-cov --no-report --package $(CRATE) --profile coverage -- --test-threads=1
cargo llvm-cov report --profile coverage 2>&1 | tee coverage-report.txt
@echo "# commit: $$(git rev-parse HEAD)" >> coverage-report.txt
@echo ""
@echo "Coverage report saved to coverage-report.txt"
@tail -2 coverage-report.txt

# Auto-detect changed crates vs main and run coverage only for them
coverage-changed:
@sh scripts/coverage-changed.sh
89 changes: 89 additions & 0 deletions coprocessor/fhevm-engine/scripts/coverage-changed.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
#!/bin/sh
#
# Detect which workspace crates have changed vs main and run coverage
# only for those crates. If fhevm-engine-common changes, run all.
#
set -e

BASE_BRANCH="${BASE_BRANCH:-main}"
REPO_ROOT="$(git rev-parse --show-toplevel)"
ENGINE_DIR="$REPO_ROOT/coprocessor/fhevm-engine"

cd "$ENGINE_DIR"

# Fetch latest remote to ensure accurate comparison
git fetch origin "$BASE_BRANCH" --quiet 2>/dev/null || true

# Compare only your branch's changes against the remote base branch
# Uses three-dot diff against origin/ to exclude changes already on main
CHANGED_FILES=$(git diff --name-only "origin/$BASE_BRANCH"...HEAD -- "$ENGINE_DIR" 2>/dev/null | \
sed "s|^coprocessor/fhevm-engine/||" || true)

if [ -z "$CHANGED_FILES" ]; then
echo "No changes detected vs $BASE_BRANCH. Nothing to cover."
exit 0
fi

# Extract unique directory names from changed file paths, filtered to actual crates
# e.g. "host-listener/src/cmd/mod.rs" → "host-listener" (only if host-listener/Cargo.toml exists)
# Root-level files (Cargo.toml, Makefile, etc.) are skipped by the sed filter
ALL_DIRS=$(echo "$CHANGED_FILES" | sed -n 's|^\([^/]*\)/.*|\1|p' | sort -u)

# Only Cargo.lock affects all crates — other root-level files are ignored
if echo "$CHANGED_FILES" | grep -q '^Cargo\.lock$'; then
echo "Cargo.lock changed — running full workspace coverage."
echo ""
exec make coverage
fi

# Filter to actual crates (directories with Cargo.toml)
CHANGED_CRATES=""
for dir in $ALL_DIRS; do
if [ -f "$dir/Cargo.toml" ]; then
CHANGED_CRATES="$CHANGED_CRATES $dir"
fi
done
CHANGED_CRATES=$(echo "$CHANGED_CRATES" | xargs)

if [ -z "$CHANGED_CRATES" ]; then
echo "No crate-level changes detected. Nothing to cover."
exit 0
fi

echo "Changed crates:"
echo "$CHANGED_CRATES" | tr ' ' '\n' | sed 's/^/ - /'
echo ""

# If fhevm-engine-common changed, run full workspace (all crates depend on it)
if echo "$CHANGED_CRATES" | grep -q "fhevm-engine-common"; then
echo "fhevm-engine-common changed — running full workspace coverage."
echo ""
exec make coverage
fi

# Build --package flags
PKG_FLAGS=""
for crate in $CHANGED_CRATES; do
PKG_FLAGS="$PKG_FLAGS --package $crate"
done

if [ -z "$PKG_FLAGS" ]; then
echo "No Cargo crates changed. Nothing to cover."
exit 0
fi

echo "Running coverage for:$PKG_FLAGS"
echo ""

cargo llvm-cov clean --workspace --profile coverage

DATABASE_URL=postgresql://postgres:postgres@localhost:5432/coprocessor \
TEST_GLOBAL_LOCALSTACK=1 \
cargo llvm-cov --no-report $PKG_FLAGS --profile coverage -- --test-threads=1

cargo llvm-cov report --profile coverage 2>&1 | tee coverage-report.txt
echo "# commit: $(git rev-parse HEAD)" >> coverage-report.txt

echo ""
echo "Coverage report saved to coverage-report.txt"
tail -2 coverage-report.txt
Loading