Skip to content
Open
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
25 changes: 25 additions & 0 deletions .husky/commit-msg
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#!/bin/bash

# . "$(dirname -- "$0")/_/husky.sh"

# ANSI colors
RED="\033[0;31m"
YELLOW="\033[0;33m"
CYAN="\033[0;36m"
GREEN="\033[0;32m"
RESET="\033[0m"

commit_msg_file=$1

# Guard: ensure commit message file exists
if [ -z "$commit_msg_file" ] || [ ! -f "$commit_msg_file" ]; then
echo "⚠️ No commit message file provided or file does not exist."
echo "👉 Skipping DCO sign-off check."
exit 0
fi

if ! grep -q "Signed-off-by:" "$commit_msg_file"; then
echo "${RED} Commit message missing Signed-off-by line ${RESET}"
echo "${GREEN} Use: git commit -s ... ${RESET}"
exit 1
fi
43 changes: 43 additions & 0 deletions .husky/post-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#!/bin/bash

RESET="\033[0m"
RED="\033[0;31m"
GREEN="\033[0;32m"
YELLOW="\033[0;33m"
BLUE="\033[0;34m"
CYAN="\033[0;36m"

# . "$(dirname -- "$0")/_/husky.sh"

echo "${BLUE}Checking last commit signature...${RESET}"

last_commit=$(git rev-parse HEAD)

# Count parents to detect merge commits
parent_count=$(git rev-list --parents -n 1 $last_commit | awk '{print NF-1}')

if [ "$parent_count" -gt 1 ]; then
echo "${BLUE}Skipping merge commit $last_commit${RESET}"
exit 0
fi

sig_status=$(git log --format='%G?' -n 1 $last_commit)

if [ "$sig_status" != "G" ]; then
echo "${RED}❌ Commit $last_commit is not verified!${RESET}"
echo
echo "${YELLOW}👉 How to fix:${RESET}"
echo " 1. Re-sign your last commit with both sign-off (-s) and signature (-S):"
echo " git commit --amend -sS --no-edit"
echo
echo " 2. If you haven't set up commit signing yet:"
echo " - GPG/SSH signing setup: https://docs.github.com/en/authentication/managing-commit-signature-verification/signing-commits"
echo
echo "${CYAN}💡 Tip: You can enable automatic signing for all commits with:${RESET}"
echo " git config --global commit.gpgsign true"
echo
exit 1
# In post-commit we usually *warn* not block, but you can `exit 1` if you want strict enforcement
else
echo "${GREEN}Commit $last_commit is signed and verified.${RESET}"
fi
9 changes: 5 additions & 4 deletions .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#!/bin/sh
cd "$(dirname "$0")/.." # Move to project root (outer folder)
# cd nextjs # Move into Next.js folder
npx lint-staged
#!/usr/bin/env sh

. "$(dirname -- "$0")/_/husky.sh"

npx lint-staged
144 changes: 144 additions & 0 deletions .husky/pre-push
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
#!/bin/bash

# . "$(dirname -- "$0")/_/husky.sh"

# ANSI colors
RED="\033[0;31m"
YELLOW="\033[0;33m"
CYAN="\033[0;36m"
GREEN="\033[0;32m"
RESET="\033[0m"

echo "🔒 Checking commit signatures..."

# Read stdin into a temp file
TMP_FILE=$(mktemp)
cat > "$TMP_FILE"

if [ ! -s "$TMP_FILE" ]; then
echo "No refs to push - skipping commit signature validation."
fi

while read local_ref local_sha remote_ref remote_sha; do
# Determine range of commits
if [ "$remote_sha" = "0000000000000000000000000000000000000000" ]; then
# Use the branch being pushed (may differ from HEAD)
if echo "$local_ref" | grep -q '^refs/heads/'; then
target_branch=${local_ref#refs/heads/}
else
target_branch=$(git rev-parse --abbrev-ref HEAD)
fi

# Find closest local base branch by commit distance (exclude the target itself)
base_branch=$(
git for-each-ref --format='%(refname:short)' refs/heads/ \
| grep -vxF "$target_branch" \
| while IFS= read -r other_branch; do
merge_base=$(git merge-base "$target_branch" "$other_branch") || continue
[ -n "$merge_base" ] || continue
echo "$(git rev-list --count "${merge_base}..${target_branch}") $other_branch"
done \
| sort -n \
| head -n1 \
| awk '{print $2}'
)

if [ -z "$base_branch" ]; then
# Resolve remote default (origin/HEAD) or use $default_branch/main
base_branch=${default_branch:-$(git symbolic-ref --quiet --short refs/remotes/origin/HEAD 2>/dev/null | sed 's@^origin/@@')}
[ -n "$base_branch" ] || base_branch=main
echo "⚠️ Base branch not found — defaulting to '${base_branch}'"
fi

# Prefer local base, else remote tracking
if git show-ref --verify --quiet "refs/heads/$base_branch"; then
base_ref="$base_branch"
else
base_ref="origin/$base_branch"
fi

base_commit=$(git merge-base "$base_ref" "$target_branch" 2>/dev/null || true)
if [ -n "$base_commit" ]; then
range="$base_commit..$local_sha"
else
# Safe minimal fallback: just the tip commit
range="$local_sha"
fi
else
range="$remote_sha..$local_sha"
fi

unsigned_commits=""

echo "commit range ${range}"
# Loop through each commit in range
for commit in $(git rev-list $range); do
# Skip merge commits
parent_count=$(git rev-list --parents -n 1 $commit | awk '{print NF-1}')
if [ "$parent_count" -gt 1 ]; then
echo "${CYAN}ℹ️ Skipping merge commit $commit${RESET}"
continue
fi
sig_status=$(git log --format='%G?' -n 1 $commit)
# Currently only check for "N" - for no signature.
if [ "$sig_status" = "N" ]; then
unsigned_commits="$unsigned_commits $commit"
fi
done

# If there are unsigned commits, prompt the developer
if [ -n "$unsigned_commits" ]; then
echo "${RED}❌ Found unsigned commits:${RESET} $unsigned_commits"
echo
echo "${YELLOW}Do you want to automatically sign these commits? [y/N]${RESET}"
read -r answer </dev/tty

if [ "$answer" = "y" ] || [ "$answer" = "Y" ]; then
# The earliest local unsigned commit is the last in the list
earliest_commit=$(echo $unsigned_commits | awk '{print $NF}')
parent_commit=$(git rev-parse "${earliest_commit}^")

echo "${CYAN}🔧 Auto-signing earliest local unsigned commit $earliest_commit...${RESET}"
# Avoid flattening of merge commits
# Not sure, if we really want to make it this complex, ideally it should be fine even if we flatten merge commits.
# Unless, the merge commit appears on the local branch, when trying to sync with the upstreams
# If we face any issues doing this, we revert to either flattening or maybe check the '--rebase-merges' feasibility. Considering the requirement of git version
git rebase --onto "$parent_commit" "$parent_commit" \
--exec 'if [ $(git rev-list --parents -n 1 HEAD | awk "{print NF-1}") -eq 1 ]; then git commit --amend -sS --no-edit; else echo "Skipping merge commit $(git rev-parse --short HEAD)"; fi'

echo "${GREEN}✅ Earliest unsigned commit has been signed.${RESET}"
echo "Please push again: git push --force-with-lease"
exit 1
else
echo "${YELLOW}You can manually sign commits using:${RESET}"
echo
# Latest commit (HEAD)
echo " - For the latest commit (HEAD):"
echo " git commit --amend -sS --no-edit"
echo
# Earliest local unsigned commit
earliest_commit=$(echo $unsigned_commits | awk '{print $NF}')
short_hash=$(git rev-parse --short $earliest_commit)
parent_commit=$(git rev-parse "${earliest_commit}^")
echo " - For the earliest unsigned commit $short_hash (skipping merge commits):"
echo " git rebase --onto $parent_commit $parent_commit \\"
echo " --exec 'if [ \$(git rev-list --parents -n 1 HEAD | awk \"{print NF-1}\") -eq 1 ]; then git commit --amend -sS --no-edit; else echo \"Skipping merge commit \$(git rev-parse --short HEAD)\"; fi'"
echo
echo "Then push normally with:"
echo " git push"
echo
echo "Helpful resources:"
echo " - GPG signing: https://docs.github.com/en/authentication/managing-commit-signature-verification/signing-commits"
echo
echo "${CYAN}💡 Tip: Enable automatic signing for all commits:${RESET}"
echo " git config --global commit.gpgsign true"
echo " git config --global user.signingkey <YOUR_KEY_ID>"
echo
exit 1
fi
fi
done < "$TMP_FILE"

rm -f "$TMP_FILE"

echo "${GREEN}✅ No unsigned commits found${RESET}"
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"lint:strict": "eslint --max-warnings=0 src",
"format": "prettier --write .",
"format:check": "prettier -c -w .",
"prepare": "husky install"
"prepare": "husky"
},
"dependencies": {
"@dnd-kit/core": "^6.3.1",
Expand Down