Skip to content

Commit 503c784

Browse files
committed
FINERACT-2177: Add Git signed commits verification
1 parent 5408921 commit 503c784

File tree

3 files changed

+176
-0
lines changed

3 files changed

+176
-0
lines changed
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# Licensed to the Apache Software Foundation (ASF) under one
2+
# or more contributor license agreements. See the NOTICE file
3+
# distributed with this work for additional information
4+
# regarding copyright ownership. The ASF licenses this file
5+
# to you under the Apache License, Version 2.0 (the
6+
# "License"); you may not use this file except in compliance
7+
# with the License. You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing,
12+
# software distributed under the License is distributed on an
13+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
# KIND, either express or implied. See the License for the
15+
# specific language governing permissions and limitations
16+
# under the License.
17+
18+
name: Fineract Signed Commits Check
19+
20+
on:
21+
pull_request:
22+
types: [opened, synchronize, reopened]
23+
24+
permissions:
25+
contents: read
26+
27+
jobs:
28+
verify-signatures:
29+
name: Verify Commit Signatures
30+
runs-on: ubuntu-latest
31+
timeout-minutes: 1
32+
steps:
33+
- name: Checkout
34+
uses: actions/checkout@v4
35+
with:
36+
fetch-depth: 0
37+
38+
- name: Fetch base branch
39+
run: git fetch origin ${{ github.base_ref }}
40+
41+
- name: Verify signatures
42+
run: |
43+
chmod +x scripts/verify-signed-commits.sh
44+
./scripts/verify-signed-commits.sh \
45+
--base-ref origin/${{ github.base_ref }} \
46+
--head-ref ${{ github.sha }} \
47+
--strict

CONTRIBUTING.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,3 +270,35 @@ This project's committers typically prefer to bring your pull requests in throug
270270
We expect most proposed PRs to typically consist of a single commit. Committers may use _Squash and merge_ to combine your commits at merge time, and if they do so, will rewrite your commit message as they see fit.
271271

272272
Neither of these two are hard absolute rules, but mere conventions. Multiple commits in single PRs make sense in certain cases (e.g. branch backports).
273+
274+
### Signing Your Commits
275+
276+
We encourage contributors to sign commits with GPG keys. Signed commits show a "Verified" badge on GitHub.
277+
278+
To set up GPG signing:
279+
280+
1. Install GPG:
281+
- **Mac**: `brew install gnupg`
282+
- **Linux (Debian/Ubuntu)**: `sudo apt-get install gnupg`
283+
- **Windows**: Download and install [Gpg4win](https://www.gpg4win.org/)
284+
2. Generate a key: `gpg --full-generate-key`
285+
- Select `(1) RSA and RSA`
286+
- Key size: `4096`
287+
- Expiration: `0` (no expiration)
288+
- Enter your name and GitHub email
289+
3. Find your key ID (the string after `rsa4096/` on the `sec` line):
290+
```bash
291+
gpg --list-secret-keys --keyid-format=long
292+
```
293+
4. Configure Git:
294+
```bash
295+
git config --global user.signingkey YOUR_KEY_ID
296+
git config --global commit.gpgsign true
297+
```
298+
5. Add your public key to GitHub:
299+
```bash
300+
gpg --armor --export YOUR_KEY_ID
301+
```
302+
Copy the entire output (including `-----BEGIN PGP PUBLIC KEY BLOCK-----` and `-----END PGP PUBLIC KEY BLOCK-----`) and add it at: GitHub → Settings → SSH and GPG keys → New GPG key
303+
304+
To verify locally before pushing: `./scripts/verify-signed-commits.sh`

scripts/verify-signed-commits.sh

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
#!/bin/bash
2+
3+
# Licensed to the Apache Software Foundation (ASF) under one
4+
# or more contributor license agreements. See the NOTICE file
5+
# distributed with this work for additional information
6+
# regarding copyright ownership. The ASF licenses this file
7+
# to you under the Apache License, Version 2.0 (the
8+
# "License"); you may not use this file except in compliance
9+
# with the License. You may obtain a copy of the License at
10+
#
11+
# http://www.apache.org/licenses/LICENSE-2.0
12+
#
13+
# Unless required by applicable law or agreed to in writing,
14+
# software distributed under the License is distributed on an
15+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16+
# KIND, either express or implied. See the License for the
17+
# specific language governing permissions and limitations
18+
# under the License.
19+
20+
# Usage: ./scripts/verify-signed-commits.sh [--base-ref <ref>] [--head-ref <ref>] [--strict] [--help]
21+
set -e
22+
23+
BASE_REF="origin/develop"
24+
HEAD_REF="HEAD"
25+
STRICT_MODE=false
26+
27+
show_help() {
28+
cat << 'EOF'
29+
Usage: ./scripts/verify-signed-commits.sh [OPTIONS]
30+
31+
Options:
32+
--base-ref <ref> Base reference (default: origin/develop)
33+
--head-ref <ref> Head reference (default: HEAD)
34+
--strict Exit with error if unsigned commits found
35+
--help Show this help
36+
EOF
37+
}
38+
39+
while [[ $# -gt 0 ]]; do
40+
case $1 in
41+
--base-ref) BASE_REF="$2"; shift 2 ;;
42+
--head-ref) HEAD_REF="$2"; shift 2 ;;
43+
--strict) STRICT_MODE=true; shift ;;
44+
--help) show_help; exit 0 ;;
45+
*) echo "Unknown option: $1"; show_help; exit 1 ;;
46+
esac
47+
done
48+
49+
MERGE_BASE=$(git merge-base "$BASE_REF" "$HEAD_REF" 2>/dev/null || echo "")
50+
if [ -z "$MERGE_BASE" ]; then
51+
COMMIT_RANGE="$HEAD_REF~10..$HEAD_REF"
52+
else
53+
COMMIT_RANGE="$MERGE_BASE..$HEAD_REF"
54+
fi
55+
56+
echo "Verifying commit signatures in range: $COMMIT_RANGE"
57+
58+
COMMITS=$(git log --format="%H%x1f%G?%x1f%an%x1f%s" "$COMMIT_RANGE" 2>/dev/null || echo "")
59+
if [ -z "$COMMITS" ]; then
60+
echo "No commits to verify."
61+
exit 0
62+
fi
63+
64+
UNSIGNED_COUNT=0
65+
66+
while IFS=$'\x1f' read -r HASH SIG_STATUS AUTHOR SUBJECT; do
67+
[ -z "$HASH" ] && continue
68+
SHORT_HASH="${HASH:0:7}"
69+
70+
case "$SIG_STATUS" in
71+
N)
72+
UNSIGNED_COUNT=$((UNSIGNED_COUNT + 1))
73+
if [ -n "$GITHUB_ACTIONS" ]; then
74+
echo "::error title=Unsigned Commit::Commit $SHORT_HASH by $AUTHOR is not signed."
75+
else
76+
echo "❌ Unsigned: $SHORT_HASH - $SUBJECT ($AUTHOR)"
77+
fi
78+
;;
79+
*)
80+
echo "✅ Signed: $SHORT_HASH - $SUBJECT"
81+
;;
82+
esac
83+
done <<< "$COMMITS"
84+
85+
echo ""
86+
echo "Summary: $UNSIGNED_COUNT unsigned commit(s) found."
87+
88+
if [ "$STRICT_MODE" = true ] && [ "$UNSIGNED_COUNT" -gt 0 ]; then
89+
if [ -n "$GITHUB_ACTIONS" ]; then
90+
echo "::error::$UNSIGNED_COUNT unsigned commit(s). See CONTRIBUTING.md#signing-your-commits"
91+
else
92+
echo "$UNSIGNED_COUNT unsigned commit(s). See CONTRIBUTING.md#signing-your-commits"
93+
fi
94+
exit 1
95+
fi
96+
97+
exit 0

0 commit comments

Comments
 (0)