Various fixes to sign-my-commit:

- more consistent use of quotes
 - add additional feedback when signature is already detected
 - add --force option
 - only select latest signature if multiple signature headers found

Fix squash-my-commit when signature not requested
This commit is contained in:
Simon Marsh 2025-05-04 09:13:28 +01:00
parent 90e85ae9cf
commit 21ed480098
No known key found for this signature in database
GPG key ID: E9B4156C1659C079
2 changed files with 129 additions and 94 deletions

View file

@ -16,7 +16,7 @@ usage()
{
cat <<EOF
This script can automatically sign commits using supported SSH or PGP
This script can automatically sign commits using supported SSH or PGP
authentication methods. Remember to push the new signature to the server
after signing (using 'git push --force' or the --push option).
@ -34,10 +34,11 @@ Generic options:
--push, force push the result after signature and verification
--verify, check an existing signature on the latest commit
--commit <hash>, check the signature on a specific commit
--force, override some checks and continue on errors
--help, display this message
PGP specific options:
--print <fprint>, specify fingerprint of GPG key to use if you
--print <fprint>, specify fingerprint of GPG key to use if you
don't want to use the first available key
SSH specific options:
@ -47,6 +48,9 @@ SSH specific options:
--method <type>, either 'git' or 'comment' to force SSH signatures
to use a specific method, defaults to 'git'
Some parameters can also be set via environment variables,
see the defaults section in the script for more details.
EOF
}
@ -54,15 +58,16 @@ EOF
##########################################################################
# defaults
DO_PUSH=0
DO_SQUASH=1
AUTH_METHOD=''
MNTNER=''
SSH_KEYFILE=''
SSH_METHOD='git'
GPG_PRINT=''
VERIFY_ONLY=0
COMMIT_SHA=''
DO_PUSH=${DO_PUSH:-0}
DO_SQUASH=${DO_SQUASH:-1}
AUTH_METHOD=${AUTH_METHOD:-''}
MNTNER=${MNTNER:-''}
SSH_KEYFILE=${SSH_KEYFILE:-''}
SSH_METHOD=${SSH_METHOD:-'git'}
GPG_PRINT=${GPG_PRINT:-''}
VERIFY_ONLY=${VERIFY_ONLY:-0}
COMMIT_SHA=${COMMIT_SHA:-''}
FORCE=${FORCE:-0}
##########################################################################
# parse arguments
@ -103,6 +108,9 @@ do
shift
GPG_PRINT="$1"
;;
--force)
FORCE=1
;;
--help)
usage
exit 0
@ -110,14 +118,14 @@ do
*)
if [ -z "$MNTNER" ]
then
MNTNER=$1
MNTNER="$1"
else
>&2 echo "ERROR: Unknown option: $1"
>&2 usage
exit 1
fi
;;
esac
shift
@ -129,7 +137,7 @@ done
# check working directory
if [ ! -d '.git' ] && [ ! -d 'data/mntner' ]
then
>&2 echo "ERROR: This script must be run in the root directory of a registry clone"
>&2 echo 'ERROR: This script must be run in the root directory of a registry clone'
exit 1
fi
@ -151,7 +159,7 @@ then
then
MNTNER=$(git log "$COMMIT_SHA" -n 1 --format=format:%B | \
grep '^### mntner:' | \
cut -d':' -f2 | tr -d ' ')
cut -d':' -f2 | tr -d ' ' | tail -n 1)
if [ -n "$MNTNER" ]
then
echo "Found mntner $MNTNER from signature"
@ -162,7 +170,7 @@ then
then
AUTH_METHOD=$(git log "$COMMIT_SHA" -n 1 --format=format:%B | \
grep '^### method:' | \
cut -d':' -f2 | tr -d ' ')
cut -d':' -f2 | tr -d ' ' | tail -n 1)
if [ -n "$AUTH_METHOD" ]
then
echo "Using $AUTH_METHOD auth method from signature"
@ -191,8 +199,8 @@ gitv_minor=$(git --version | cut -d'.' -f2)
if { [ "$gitv_major" -eq 2 ] && [ "$gitv_minor" -lt 5 ]; } || \
[ "$gitv_major" -lt 2 ]
then
>&2 echo "ERROR: This script requires a git version 2.5"
>&2 echo "---"
>&2 echo 'ERROR: This script requires a git version 2.5'
>&2 echo '---'
>&2 git --version
exit 1
fi
@ -204,37 +212,48 @@ then
# check for untracked or uncommitted changes
if [ -n "$(git status --porcelain)" ]
then
>&2 echo "ERROR: git worktree has unstaged or uncommitted changes"
>&2 echo "This script can only be run once your commit is completed"
>&2 echo "---"
>&2 echo 'ERROR: git worktree has unstaged or uncommitted changes'
>&2 echo 'This script can only be run once your commit is completed'
>&2 echo '---'
>&2 git status
exit 1
fi
# check that the commit has been squashed
if [ "$DO_SQUASH" -eq 1 ]
then
if ! ./squash-my-commits --verify
then
>&2 echo "ERROR: Ensure your commits are squashed before signing"
>&2 echo "Run the included script: ./squash-my-commits"
>&2 echo 'ERROR: Ensure your commits are squashed before signing'
>&2 echo 'Run the included script: ./squash-my-commits'
exit 1
fi
fi
# check for an existing signature
if git log -n 1 --format=format:%B 2>&1 | grep '^### DN42 Signature' > /dev/null
then
>&2 echo "ERROR: The last commit appears to already be signed"
>&2 echo "---"
>&2 git log -n 1 --show-signature
exit 1
>&2 echo 'ERROR: Detected ### DN42 Signature header in commit comment'
>&2 echo 'ERROR: The commit appears to be signed already'
if [ "$FORCE" -ne 0 ]
then
>&2 echo '!!! ignoring errors, continuing'
else
>&2 echo '---'
>&2 git log -n 1 --show-signature
>&2 echo '---'
>&2 echo 'If you want to re-try signing the commit then you must either'
>&2 echo 're-run this script again using the --force option, or'
>&2 echo 'manually remove the "### DN42 Signature" header from the commit'
>&2 echo 'comment (e.g. using git commit --amend)'
exit 1
fi
fi
fi
##########################################################################
# helper functions
# helper functions
# guess a signature method based on the first auth attribute in a MNTNER
guess_mntner_method()
@ -289,7 +308,7 @@ get_pgp_prints()
rm "$pgp_prints"
exit 1
fi
# get the fingerprint from key-cert file
grep '^fingerpr:' "data/key-cert/$auth_method" | \
cut -c21- | tr -d ' ' | \
@ -316,10 +335,18 @@ sign_pgp()
# check first if there is already a signature
if git log -n 1 --show-signature | grep "^gpg" > /dev/null 2>&1
then
>&2 echo "ERROR: The last commit appears to already be signed."
>&2 echo "---"
>&2 git log -n 1 --show-signature
exit 1
>&2 echo 'ERROR: Found existing git signature'
>&2 echo 'ERROR: The last commit appears to be signed already'
if [ "$FORCE" -ne 0 ]
then
>&2 echo '!!! ignoring errors, continuing'
else
>&2 echo "---"
>&2 git log -n 1 --show-signature
>&2 echo "---"
>&2 echo "You can use the --force option for re-try signing"
exit 1
fi
fi
# if the fingerprint wasn't specified, obtain from the MNTNER
@ -360,15 +387,15 @@ sign_pgp()
verify_pgp()
{
echo "Verifying PGP signature"
echo 'Verifying PGP signature'
# requires git 2.5
if ! git verify-commit "$COMMIT_SHA"
then
>&2 echo "ERROR: failed to verify PGP signature"
>&2 echo 'ERROR: failed to verify PGP signature'
exit 1
fi
echo " - PGP signature verified ok"
echo ' - PGP signature verified ok'
# create a list of authorised pgp fingerprints
valid_prints=$(get_pgp_prints)
@ -381,14 +408,14 @@ verify_pgp()
if grep "$print" "$valid_prints" > /dev/null 2>&1
then
echo "Matched fingerprint with auth attribute for $MNTNER"
echo "Successfully verified PGP signature"
echo 'Successfully verified PGP signature'
rm "$valid_prints"
return
fi
done
>&2 echo "ERROR: unable to match key fingerprint with mntner: $MNTNER"
rm "$valid_prints"
rm "$valid_prints"
exit 1
}
@ -411,7 +438,7 @@ filter_ssh_auths()
echo "$line"
;;
esac
done
done
}
# create an allowed signers file using the mntner auth attributes
@ -426,7 +453,7 @@ get_allowed_signers()
check_keyfile()
{
pubkeyfile=''
# guess the public key if a keyfile wasn't specified
if [ -z "$SSH_KEYFILE" ]
then
@ -438,12 +465,12 @@ check_keyfile()
filter_ssh_auths | head -n 1 > "$pubkeyfile"
if [ ! -s "$pubkeyfile" ]
then
>&2 echo "ERROR: Unable to auto determine SSH public key"
>&2 echo "Try specifying the key directly using --key"
>&2 echo 'ERROR: Unable to auto determine SSH public key'
>&2 echo 'Try specifying the key directly using --key'
rm "$pubkeyfile"
exit 1
fi
fi
# check if the pubkey is available in agent
pubkey=$(tr -s ' ' < "$pubkeyfile" | cut -d' ' -f1,2)
if ssh-add -L | grep "^$pubkey" > /dev/null 2>&1
@ -454,14 +481,14 @@ check_keyfile()
# no key found in agent, clean up the keyfile first
rm "$pubkeyfile"
pubkeyfile=''
if [ -d "${HOME}/.ssh" ]
then
# as a last resort, try scanning the 'usual' ssh
# directory to find the key in there
SSH_KEYFILE=$(grep -l "^$pubkey" "${HOME}"/.ssh/*.pub)
fi
fi
if [ -n "$SSH_KEYFILE" ]
then
@ -473,7 +500,7 @@ ERROR: Unable to identify public key in ssh-agent or home directory
- When auto detecting the public key from you mntner object it
- is required that the private key is available in ssh-agent or
- in your ${HOME}/.ssh/ directory.
- Please add your key to ssh-agent or use the --key option to
- Please add your key to ssh-agent or use the --key option to
- specify where the private key is directly.
---
Pubkey: $pubkey
@ -487,14 +514,14 @@ EOF
if ! pubkey=$(ssh-keygen -l -f "$SSH_KEYFILE")
then
>&2 echo "ERROR: $SSH_KEYFILE doesn't look like a valid SSH key"
>&2 echo "Try specifying the public or private key directly using --key"
>&2 echo "File contents:"
>&2 echo 'Try specifying the public or private key directly using --key'
>&2 echo 'File contents:'
>&2 cat "$SSH_KEYFILE"
if [ -n "$pubkeyfile" ]; then rm "$pubkeyfile"; fi
exit 1
fi
echo "Using: $pubkey"
echo "Using: $pubkey"
}
##########################################################################
@ -504,7 +531,7 @@ EOF
sign_ssh_git()
{
check_keyfile
# configure local git signing
git config --local gpg.format ssh
git config --local user.signingKey "$SSH_KEYFILE"
@ -527,9 +554,9 @@ sign_ssh_git()
# was there an error ?
if [ "$result" -ne 0 ]
then
>&2 echo "ERROR: failed to sign commit"
>&2 echo " - Try specifying your key using --key"
>&2 echo " - or adding your key to ssh-agent"
>&2 echo 'ERROR: failed to sign commit'
>&2 echo ' - Try specifying your key using --key'
>&2 echo ' - or adding your key to ssh-agent'
exit 1
fi
@ -542,7 +569,7 @@ sign_ssh_git()
sign_ssh_comment()
{
check_keyfile
# create the signature
sig=$(echo "$COMMIT_SHA" | \
ssh-keygen -Y sign -n dn42 -f "$SSH_KEYFILE")
@ -554,10 +581,10 @@ sign_ssh_comment()
# check for errors
if [ "$result" -ne 0 ]
then
>&2 echo "ERROR: ssh-keygen signing failed"
>&2 echo " - Try specifying your key using --key"
>&2 echo " - or adding the key to ssh-agent"
if [ -n "$pubkeyfile" ]; then rm "$pubkeyfile"; fi
>&2 echo 'ERROR: ssh-keygen signing failed'
>&2 echo ' - Try specifying your key using --key'
>&2 echo ' - or adding the key to ssh-agent'
if [ -n "$pubkeyfile" ]; then rm "$pubkeyfile"; fi
exit 1
fi
@ -584,7 +611,7 @@ sign_ssh()
if ! ssh-keygen -Y sign 2>&1 | grep 'missing namespace' > /dev/null
then
>&2 cat <<EOF
ERROR: This script requires the key signing capability from
ERROR: This script requires the key signing capability from
OpenSSH ssh-keygen that was introduced in version 8.
If you are unable to upgrade ssh-keygen you must use one of the
@ -599,20 +626,20 @@ EOF
fi
# if we have git >= 2.34 the commit can be git signed
if [ "$SSH_METHOD" != "comment" ]
if [ "$SSH_METHOD" != 'comment' ]
then
if { [ "$gitv_major" -eq 2 ] && [ "$gitv_minor" -ge 34 ]; } || \
[ "$gitv_major" -gt 2 ]
then
echo "Detected git version >= 2.34, using git SSH signature"
echo 'Detected git version >= 2.34, using git SSH signature'
sign_ssh_git
return
else
echo "Detected git version < 2.34, cannot sign using git"
echo 'Detected git version < 2.34, cannot sign using git'
fi
fi
echo "Defaulting to comment based signature"
echo 'Defaulting to comment based signature'
sign_ssh_comment
}
@ -628,8 +655,8 @@ verify_ssh_git()
if { [ "$gitv_major" -eq 2 ] && [ "$gitv_minor" -lt 34 ]; } || \
[ "$gitv_major" -lt 2 ]
then
>&2 echo "Detected git version < 2.34, unable to verify git signatures"
>&2 echo "- Upgrade git to at least version 2.34"
>&2 echo 'Detected git version < 2.34, unable to verify git signatures'
>&2 echo '- Upgrade git to at least version 2.34'
exit 1
fi
@ -645,22 +672,22 @@ verify_ssh_git()
# clean up allowed signers file before doing anything else
git config --local --unset gpg.ssh.allowedSignersFile
rm "$allowed"
rm "$allowed"
# did the signature successfully validate ?
if [ "$result" -ne 0 ]
then
>&2 echo "ERROR: failed to verify SSH signature"
>&2 echo 'ERROR: failed to verify SSH signature'
exit 1
fi
echo "SSH signature verified ok"
echo 'SSH signature verified ok'
}
# verify a comment based SSH signature
verify_ssh_comment()
{
echo "Verifying SSH signature comment"
echo 'Verifying SSH signature comment'
# create the allowed signers file
allowed=$(get_allowed_signers)
@ -671,11 +698,11 @@ verify_ssh_comment()
# also extract the SSH signature from the comment
signature=$(mktemp)
begin="-----BEGIN SSH SIGNATURE-----"
end="-----END SSH SIGNATURE-----"
begin='-----BEGIN SSH SIGNATURE-----'
end='-----END SSH SIGNATURE-----'
git log "$COMMIT_SHA" -n 1 --format=format:%B | \
sed "/^$begin\$/,/^$end\$/!d" > "$signature"
# now we can verify the signature
echo "$text" | ssh-keygen -Y verify -f "$allowed" \
-n dn42 -I "$MNTNER" -s "$signature"
@ -686,11 +713,11 @@ verify_ssh_comment()
# did it work ?
if [ "$result" -eq 0 ]
then
echo "Successfully verified SSH sigature"
echo 'Successfully verified SSH sigature'
else
>&2 echo "ERROR: signature verification failed"
>&2 echo 'ERROR: signature verification failed'
exit 1
fi
fi
}
# SSH verify wrapper
@ -698,7 +725,7 @@ verify_ssh()
{
# determine signature type from log comment
method=$(git log "$COMMIT_SHA" -n 1 --format=format:%B | \
grep '^### method:' | cut -d':' -f2 | tr -d ' ')
grep '^### method:' | cut -d':' -f2 | tr -d ' ' | tail -n 1)
case "$method" in
'ssh')
verify_ssh_comment
@ -707,16 +734,16 @@ verify_ssh()
verify_ssh_git
;;
'')
echo "WARNING: No dn42 signature found, attempting git based verification"
echo 'WARNING: No dn42 signature found, attempting git based verification'
verify_ssh_git
;;
*)
>&2 echo "ERROR: commit does not appear to be signed by SSH"
>&2 echo 'ERROR: commit does not appear to be signed by SSH'
>&2 echo "Found signature method: $method"
exit 1
;;
esac
}
}
##########################################################################
#
@ -728,7 +755,7 @@ if [ -z "$AUTH_METHOD" ]
then
if [ "$VERIFY_ONLY" -ne 1 ]
then
echo "Attempting to guess signature method from mntner object"
echo 'Attempting to guess signature method from mntner object'
AUTH_METHOD=$(guess_mntner_method)
fi
fi
@ -738,27 +765,29 @@ case "$AUTH_METHOD" in
pgp)
if [ "$VERIFY_ONLY" -ne 1 ]
then
echo "Signing using PGP key"
echo 'Signing using PGP key'
sign_pgp
fi
echo '---'
verify_pgp
;;
ssh|ssh-git)
if [ "$VERIFY_ONLY" -ne 1 ]
then
echo "Signing using SSH key"
echo 'Signing using SSH key'
sign_ssh
fi
echo '---'
verify_ssh
;;
'')
>&2 echo "ERROR: Unable to automatically determine signing method"
>&2 echo "Use the --ssh or --pgp options to force a particular method"
>&2 echo 'ERROR: Unable to automatically determine signing method'
>&2 echo 'Use the --ssh or --pgp options to force a particular method'
exit 1
;;
*)
>&2 echo "ERROR: Unknown or unimplemented auth method: $AUTH_METHOD"
>&2 echo "Use the --ssh or --pgp options to force a particular method"
>&2 echo 'Use the --ssh or --pgp options to force a particular method'
exit 1
;;
esac
@ -779,7 +808,7 @@ then
else
echo '---'
echo 'Remember to push your changes using: git push --force'
echo '---'
echo '---'
fi
exit 0

View file

@ -30,13 +30,14 @@ usage()
do_push=0
verify_only=0
do_sign=0
for arg
do
case "$arg" in
-S)
do_sign='-S'
do_sign=1
;;
--push)
do_push=1
@ -156,7 +157,12 @@ $(git log --oneline HEAD ^dn42registry/master)"
# and finally squash
git reset --soft dn42registry/master
git commit "$do_sign" -m "$comment"
if [ "$do_sign" -eq 1 ]
then
git commit -S -m "$comment"
else
git commit -m "$comment"
fi
# show what happened
echo '---'