Skip to content

Commit 30dc9e6

Browse files
committed
chore(hooks): add commit-msg hook to enforce Signed-off-by and GPG signature
This commit introduces a commit-msg hook that enforces two requirements for all commits: - The commit message must contain a `Signed-off-by:` line (added with the `-s` flag). - The commit must be cryptographically signed with GPG (using the `-S` flag or global config). The hook dynamically imports the public key used to sign the latest commit, ensuring verification works for any user and key. This change improves the integrity and traceability of our commit history. Signed-off-by: Gil Levkovich <[email protected]>
1 parent 1e14f80 commit 30dc9e6

File tree

3 files changed

+56
-1
lines changed

3 files changed

+56
-1
lines changed

.pre-commit-config.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,11 @@ repos:
1414
entry: contrib/scripts/conventional-commits
1515
language: script
1616
stages: [commit-msg]
17+
- id: signed-commit
18+
name: Signed Commit Enforcer
19+
entry: contrib/scripts/signed-commit
20+
language: script
21+
stages: [commit-msg]
1722

1823
- repo: https://github.com/pre-commit/pre-commit-hooks
1924
rev: v4.3.0

.pre-commit-hooks.yaml

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,16 @@
1+
12
- id: conventional-commits
23
name: Conventional Commits Minder
34
entry: contrib/scripts/conventional-commits
45
language: script
56
description: Conventional Commits Enforcement at the `git commit` client-side level
67
always_run: true
7-
stages: [commit-msg]
8+
stages: [commit-msg]
9+
10+
- id: signed-commit
11+
name: Signed Commit Enforcer
12+
entry: contrib/scripts/signed-commit
13+
language: script
14+
description: Ensures all commits contain a Signed-off-by line
15+
always_run: true
16+
stages: [commit-msg]

contrib/scripts/signed-commit

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
#!/usr/bin/env bash
2+
3+
# Dynamically import the public key used to sign the latest commit
4+
commit_hash=$(git rev-parse HEAD)
5+
key_id=$(git log --show-signature -1 "$commit_hash" 2>&1 | grep 'using RSA key' | awk '{print $NF}')
6+
7+
if [[ -z "$key_id" ]]; then
8+
echo "ERROR: Could not extract GPG key ID from commit signature."
9+
exit 1
10+
fi
11+
12+
if ! gpg --list-keys "$key_id" > /dev/null 2>&1; then
13+
gpg --keyserver hkps://keyserver.ubuntu.com --recv-key "$key_id"
14+
fi
15+
16+
if [[ -z "$1" ]] || [[ ! -f "$1" ]]; then
17+
echo "ERROR: Commit message file not provided or does not exist."
18+
exit 1
19+
fi
20+
21+
# check if signed-off-by line is present (automatically added using -s flag)
22+
if ! grep -q '^Signed-off-by:' "$1"; then
23+
echo "ERROR: Commit message must contain a Signed-off-by line."
24+
echo ""
25+
echo "To sign your commits, use the -s flag:"
26+
echo " git commit -s 'your commit message'"
27+
exit 1
28+
fi
29+
30+
# check if the commit is cryptographically signed using GPG (using -S flag)
31+
if ! git log --show-signature -1 "${commit_hash}" | grep -q "gpg: Signature made"; then
32+
echo "ERROR: Commit is not cryptographically signed with GPG."
33+
echo ""
34+
echo "To sign your commits with GPG, use the -S flag:"
35+
echo " git commit -S -s 'your commit message'"
36+
echo 'or set gpgsign = true in your .gitconfig:'
37+
echo ' git config --global commit.gpgsign true'
38+
echo "and then:"
39+
echo " git commit -s 'your commit message'"
40+
exit 1
41+
fi

0 commit comments

Comments
 (0)