From 21ed48009823df02be06d105500fffbe89721756 Mon Sep 17 00:00:00 2001 From: Simon Marsh Date: Sun, 4 May 2025 09:13:28 +0100 Subject: [PATCH] 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 --- sign-my-commit | 213 ++++++++++++++++++++++++++-------------------- squash-my-commits | 10 ++- 2 files changed, 129 insertions(+), 94 deletions(-) diff --git a/sign-my-commit b/sign-my-commit index 7a4b0be89..1ce4ebbac 100755 --- a/sign-my-commit +++ b/sign-my-commit @@ -16,7 +16,7 @@ usage() { cat <, check the signature on a specific commit + --force, override some checks and continue on errors --help, display this message PGP specific options: - --print , specify fingerprint of GPG key to use if you + --print , 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 , 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 <= 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 diff --git a/squash-my-commits b/squash-my-commits index 4ccd57c27..fd51a5909 100755 --- a/squash-my-commits +++ b/squash-my-commits @@ -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 '---'