set -f # noglob
set -C # noclobber
-VERSION="@@DEV_VERSION@@"
-if [ "${1:-}" = "-v" ] || [ "${1:-}" = "--version" ]; then
- echo "git-remote-gcrypt version $VERSION" >&2
- exit 0
-fi
-
export GITCEPTION="${GITCEPTION:-}+" # Reuse $Gref except when stacked
Gref="refs/gcrypt/gitception$GITCEPTION"
Gref_rbranch="refs/heads/master"
Packkey_bytes=63 # nbr random bytes for packfile keys, any >= 256 bit is ok
Hashtype=SHA256 # SHA512 SHA384 SHA256 SHA224 supported.
+VERSION="@@DEV_VERSION@@"
+
+# Help function
+show_help() {
+ cat >&2 <<-EOF
+ git-remote-gcrypt version $VERSION
+ GPG-encrypted git remote helper
+
+ Usage: Automatically invoked by git when using gcrypt:: URLs
+ See: man git-remote-gcrypt
+ Or: https://github.com/spwhitton/git-remote-gcrypt
+
+ Options:
+ -h, --help Show this help message
+ -v, --version Show version information
+ --check <URL> Check if URL is a gcrypt repository
+
+ Git Protocol Commands (for debugging):
+ capabilities List remote helper capabilities
+ list List refs in remote repository
+ push <refspec> Push refs to remote repository
+ fetch <sha> <ref> Fetch refs from remote repository
+
+ Environment Variables:
+ GCRYPT_DEBUG=1 Enable verbose debug logging to stderr
+ GCRYPT_TRACE=1 Enable shell tracing (set -x) for rsync/curl commands
+ GCRYPT_FULL_REPACK=1 Force full repack when pushing
+ EOF
+}
+
+# Parse flags
+while getopts "hv-:" opt; do
+ case "$opt" in
+ h)
+ show_help
+ exit 0
+ ;;
+ v)
+ echo "git-remote-gcrypt version $VERSION" >&2
+ exit 0
+ ;;
+ -)
+ # Handle long options
+ case "$OPTARG" in
+ help)
+ show_help
+ exit 0
+ ;;
+ version)
+ echo "git-remote-gcrypt version $VERSION" >&2
+ exit 0
+ ;;
+ *)
+ echo "Unknown option: --$OPTARG" >&2
+ exit 1
+ ;;
+ esac
+ ;;
+ *)
+ exit 1
+ ;;
+ esac
+done
+
+# Handle subcommand help (e.g., git-remote-gcrypt capabilities --help)
+shift $((OPTIND - 1))
+case "${1:-}" in
+ capabilities)
+ if [ "${2:-}" = "-h" ] || [ "${2:-}" = "--help" ]; then
+ cat >&2 <<-EOF
+ capabilities - List git remote helper capabilities
+
+ Usage: echo "capabilities" | git-remote-gcrypt <remote-url>
+ Invoked by git to query what operations this helper supports.
+ EOF
+ exit 0
+ fi
+ ;;
+ list)
+ if [ "${2:-}" = "-h" ] || [ "${2:-}" = "--help" ]; then
+ cat >&2 <<-EOF
+ list - List refs in remote repository
+
+ Usage: echo "list" | git-remote-gcrypt <remote-url>
+ Invoked by git to list available refs (branches/tags).
+ EOF
+ exit 0
+ fi
+ ;;
+ push)
+ if [ "${2:-}" = "-h" ] || [ "${2:-}" = "--help" ]; then
+ cat >&2 <<-EOF
+ push - Push refs to remote repository
+
+ Usage: echo "push <src>:<dst>" | git-remote-gcrypt <remote-url>
+ Invoked by git to push local refs to the remote.
+ EOF
+ exit 0
+ fi
+ ;;
+ fetch)
+ if [ "${2:-}" = "-h" ] || [ "${2:-}" = "--help" ]; then
+ cat >&2 <<-EOF
+ fetch - Fetch refs from remote repository
+
+ Usage: echo "fetch <sha> <ref>" | git-remote-gcrypt <remote-url>
+ Invoked by git to fetch objects from the remote.
+ EOF
+ exit 0
+ fi
+ ;;
+esac
+
Manifestfile=91bd0c092128cf2e60e1a608c31e92caf1f9c1595f83f2890ef17c0e4881aa0a
Hex40="[a-f0-9]"
Hex40=$Hex40$Hex40$Hex40$Hex40$Hex40$Hex40$Hex40$Hex40
echo_git() { xecho "$@" ; } # Code clarity
echo_info() { xecho "gcrypt:" "$@" >&2; }
echo_die() { echo_info "$@" ; exit 1; }
+print_debug() {
+ if [ -n "${GCRYPT_DEBUG:-}" ]; then
+ echo_info "DEBUG:" "$@"
+ fi
+}
isnull() { case "$1" in "") return 0;; *) return 1;; esac; }
isnonnull() { ! isnull "$1"; }
# Fetch repo $1, file $2, tmpfile in $3
GET()
{
+ print_debug "GET $1 $2 $3"
if isurl sftp "$1"
then
- (exec 0>&-; curl -s -S -k "$1/$2") > "$3"
+ (exec 0</dev/null; curl -s -S -k "$1/$2") > "$3"
elif isurl rsync "$1"
then
- (exec 0>&-; rsync -I -W "$(rsynclocation "$1")"/"$2" "$3" >&2)
+ print_debug "Calling rsync..."
+ (
+ if [ -n "${GCRYPT_TRACE:-}" ]; then set -x; fi
+ exec 0</dev/null
+ rsync -I -W "$(rsynclocation "$1")"/"$2" "$3" >&2
+ )
elif isurl rclone "$1"
then
- (exec 0>&-; rclone copyto --error-on-no-transfer "${1#rclone://}"/"$2" "$3" >&2)
+ (exec 0</dev/null; rclone copyto --error-on-no-transfer "${1#rclone://}"/"$2" "$3" >&2)
elif islocalrepo "$1"
then
cat "$1/$2" > "$3"
# Put repo $1, file $2 or fail, tmpfile in $3
PUT()
{
+ print_debug "PUT $1 $2 $3"
if isurl sftp "$1"
then
curl -s -S -k --ftp-create-dirs -T "$3" "$1/$2"
elif isurl rsync "$1"
then
- rsync $Conf_rsync_put_flags -I -W "$3" "$(rsynclocation "$1")"/"$2" >&2
+ print_debug "Calling rsync..."
+ (
+ if [ -n "${GCRYPT_TRACE:-}" ]; then set -x; fi
+ exec 0</dev/null
+ rsync $Conf_rsync_put_flags -I -W "$3" "$(rsynclocation "$1")"/"$2" >&2
+ )
elif isurl rclone "$1"
then
rclone copyto --error-on-no-transfer "$3" "${1#rclone://}"/"$2" >&2
# Put directory for repo $1
PUTREPO()
{
+ print_debug "PUTREPO $1"
if isurl sftp "$1"
then
:
elif isurl rsync "$1"
then
- rsync $Conf_rsync_put_flags -q -r --exclude='*' \
+ print_debug "Calling rsync..."
+ (
+ if [ -n "${GCRYPT_TRACE:-}" ]; then set -x; fi
+ exec 0</dev/null
+ rsync $Conf_rsync_put_flags -q -r --exclude='*' \
"$Localdir/" "$(rsynclocation "$1")" >&2
+ )
elif isurl rclone "$1"
then
rclone mkdir "${1#rclone://}" >&2
REMOVE()
{
local fn_=
+ print_debug "REMOVE $1 $2"
if isurl sftp "$1"
then
# FIXME
echo_info "sftp: Ignore remove request $1/$2"
elif isurl rsync "$1"
then
- xfeed "$2" rsync -I -W -v -r --delete --include-from=- \
+ print_debug "Calling rsync..."
+ (
+ if [ -n "${GCRYPT_TRACE:-}" ]; then set -x; fi
+ # rsync needs stdin for --include-from=-
+ rsync -I -W -v -r --delete --include-from=- \
--exclude='*' "$Localdir"/ "$(rsynclocation "$1")/" >&2
+ ) <<EOF
+$2
+EOF
elif isurl rclone "$1"
then
xfeed "$2" rclone delete -v --include-from=/dev/stdin "${1#rclone://}/" >&2
r_keyfpr=${r_keyfpr%%"$Newline"*}
keyid_=$(xfeed "$r_keyinfo" cut -f 5 -d :)
fprid_=$(xfeed "$r_keyfpr" cut -f 10 -d :)
+ print_debug "Resolved participant $recp_ to fpr: $fprid_"
isnonnull "$fprid_" &&
signers_="$signers_ $keyid_" &&
fi
setvar "$1" "$good_sig"
setvar "$2" "$signers_"
+ print_debug "read_config done"
}
ensure_connected()
return
fi
Did_find_repo=no
+ print_debug "Calling read_config"
read_config @r_sigmatch @r_signers
+ print_debug "Back from read_config"
iseq "${NAME#gcrypt::}" "$URL" || r_name=$NAME
tmp_manifest="$Tempdir/maniF"
tmp_stderr="$Tempdir/stderr"
- GET "$URL" "$Manifestfile" "$tmp_manifest" 2>| "$tmp_stderr" || {
+ print_debug "Getting manifest from $URL file $Manifestfile"
+ # GET "$URL" "$Manifestfile" "$tmp_manifest" 2>| "$tmp_stderr" || {
+ # Debugging: don't capture stderr, let it flow to console
+ GET "$URL" "$Manifestfile" "$tmp_manifest" || {
if ! isnull "$Repoid"; then
cat >&2 "$tmp_stderr"
echo_info "Repository not found: $URL"
cleanup_tmpfiles()
{
+ print_debug "Cleaning up..."
if isnonnull "${Tempdir%%*."$$"}"; then
echo_die "Unexpected Tempdir value: $Tempdir"
fi
NAME=$1 # Remote name
URL=$2 # Remote URL
+ echo_info "git-remote-gcrypt version $VERSION"
+
setup
while read input_
then
exit 100
fi
+elif [ "x$1" = x--version ] || [ "x$1" = x-v ]; then
+ echo "git-remote-gcrypt version $VERSION"
+ exit 0
else
gcrypt_main_loop "$@"
fi
-#!/bin/bash
+#!/usr/bin/env bash
+# SPDX-FileCopyrightText: Copyright 2023 Cathy J. Fitzpatrick <cathy@cathyjf.com>
+# SPDX-License-Identifier: GPL-2.0-or-later
set -efuC -o pipefail
shopt -s inherit_errexit
# Helpers
-print_info() { printf "\033[1;36m%s\033[0m\n" "$1"; }
-print_success() { printf "\033[1;34m✓ %s\033[0m\n" "$1"; }
-print_warn() { printf "\033[1;33m%s\033[0m\n" "$1"; }
-print_err() { printf "\033[1;31m%s\033[0m\n" "$1"; }
+print_info() { printf "\033[1;36m[TEST] %s\033[0m\n" "$1"; }
+print_success() { printf "\033[1;34m[TEST] ✓ %s\033[0m\n" "$1"; }
+print_warn() { printf "\033[1;33m[TEST] WARNING: %s\033[0m\n" "$1"; }
+print_err() { printf "\033[1;31m[TEST] FAIL: %s\033[0m\n" "$1"; }
# Settings
num_commits=5
files_per_commit=3
+
+print_info "Running multi-key clone test..."
random_source="/dev/urandom"
random_data_per_file=1024 # Reduced size for faster testing (1KB)
default_branch="main"
-test_user_name="Gcrypt Test User"
-test_user_email="gcrypt-test@example.com"
-pack_size_limit="12m"
+test_user_name="git-remote-gcrypt"
+test_user_email="git-remote-gcrypt@example.com"
+pack_size_limit="12m"
+
+readonly num_commits files_per_commit random_source random_data_per_file \
+ default_branch test_user_name test_user_email pack_size_limit
+
+# ----------------- Helper Functions -----------------
+indent() {
+ sed 's/^\(.*\)$/ \1/'
+}
+
+section_break() {
+ echo
+ printf '*%.0s' {1..70}
+ echo $'\n'
+}
-# Setup Sandbox
+assert() {
+ (
+ set +e
+ [[ -n ${show_command:-} ]] && set -x
+ "${@}"
+ )
+ local -r status=${?}
+ { [[ ${status} -eq 0 ]] && print_success "Verification succeeded."; } ||
+ print_err "Verification failed."
+ return "${status}"
+}
+
+fastfail() {
+ "$@" || kill -- "-$$"
+}
+# ----------------------------------------------------
+
+umask 077
tempdir=$(mktemp -d)
-trap 'rm -rf "$tempdir"' EXIT
-print_info "Running in sandbox: $tempdir"
-
-# --- KEY GENERATION ---
-# We need to generate keys such that the target key is "buried" deep in the keyring.
-# The bug occurs when GPG tries many keys and fails on earlier ones with a checksum error.
-# We will generate 18 keys.
-# Key 1..17: Decoys (Ed25519) - will be tried and fail (or trigger checksum error).
-# Key 18: Target (Ed25519) - the one we actually encrypt to.
-
-gpg_home="${tempdir}/gpg-home"
-mkdir -p "$gpg_home"
-chmod 700 "$gpg_home"
-export GNUPGHOME="$gpg_home"
-
-# Create a minimal gpg.conf to avoid randomness issues and ensure consistency
-cat >"${gpg_home}/gpg.conf" <<EOF
-use-agent
-pinentry-mode loopback
-no-tty
-EOF
+readonly tempdir
+trap 'rm -Rf -- "${tempdir}"' EXIT
-cat >"${gpg_home}/gpg-agent.conf" <<EOF
-allow-loopback-pinentry
-EOF
+# Setup PATH to use local git-remote-gcrypt
+PATH=$(git rev-parse --show-toplevel):${PATH}
+readonly PATH
+export PATH
-print_info "Step 1: Generating 18 Ed25519 keys (this may take a moment)..."
-num_keys=18
-for i in $(seq 1 $num_keys); do
- # Generate simple Ed25519 key (fast, no expiration)
- # We use a batch file for speed and non-interactivity
- cat >"${tempdir}/gen-key-${i}.batch" <<EOF
-%echo Generating key $i...
-Key-Type: EDDSA
-Key-Curve: ed25519
-Key-Usage: sign
-Subkey-Type: ECDH
-Subkey-Curve: cv25519
-Name-Real: git-remote-gcrypt${i}
-Name-Email: gcrypt${i}@example.com
-Expire-Date: 0
-%no-protection
-%commit
-EOF
- gpg --batch --generate-key "${tempdir}/gen-key-${i}.batch" >/dev/null 2>&1
+# Clean GIT environment
+git_env=$(env | sed -n 's/^\(GIT_[^=]*\)=.*$/\1/p')
+IFS=$'\n' unset ${git_env}
+
+# GPG Setup
+export GNUPGHOME="${tempdir}/gpg"
+mkdir "${GNUPGHOME}"
+cat <<'EOF' >"${GNUPGHOME}/gpg"
+#!/usr/bin/env bash
+set -efuC -o pipefail; shopt -s inherit_errexit
+args=( "${@}" )
+for ((i = 0; i < ${#}; ++i)); do
+ if [[ ${args[${i}]} = "--secret-keyring" ]]; then
+ unset "args[${i}]" "args[$(( i + 1 ))]"
+ break
+ fi
done
+exec gpg "${args[@]}"
+EOF
+chmod +x "${GNUPGHOME}/gpg"
+
+# Git Config
+export GIT_CONFIG_SYSTEM=/dev/null
+export GIT_CONFIG_GLOBAL="${tempdir}/gitconfig"
+mkdir "${tempdir}/template"
+git config --global init.defaultBranch "${default_branch}"
+git config --global user.name "${test_user_name}"
+git config --global user.email "${test_user_email}"
+git config --global init.templateDir "${tempdir}/template"
+git config --global gpg.program "${GNUPGHOME}/gpg"
+
+# Prepare Random Data
+total_files=$((num_commits * files_per_commit))
+random_data_size=$((total_files * random_data_per_file))
+random_data_file="${tempdir}/data"
+head -c "${random_data_size}" "${random_source}" >"${random_data_file}"
+
+###
+section_break
-print_info "Step 2: Collecting fingerprints..."
+print_info "Step 1: Creating multiple GPG keys for participants..."
+num_keys=18 # Buried deep: 17 decoys + 1 valid key
key_fps=()
+(
+ set -x
+ for ((i = 0; i < num_keys; i++)); do
+ gpg --batch --passphrase "" --quick-generate-key \
+ "${test_user_name}${i} <${test_user_email}${i}>"
+ done
+) 2>&1 | indent
# Capture fingerprints
# Integrated fix: use mapfile
mapfile -t key_fps < <(gpg --list-keys --with-colons | awk -F: '/^pub:/ {getline; print $10}')
echo "Generated keys: ${key_fps[*]}" | indent
+# Sanity Check
+if [ "${#key_fps[@]}" -ne "$num_keys" ]; then
+ print_err "FATAL: Expected $num_keys keys, captured ${#key_fps[@]}."
+ print_err " Check grep/awk logic (likely capturing subkeys vs primary keys mismatch)."
+ exit 1
+fi
+print_success "Sanity Check Passed: Captured ${#key_fps[@]} Primary Keys."
+
###
section_break
-# Setup Git
-export GIT_AUTHOR_NAME="$test_user_name"
-export GIT_AUTHOR_EMAIL="$test_user_email"
-export GIT_COMMITTER_NAME="$test_user_name"
-export GIT_COMMITTER_EMAIL="$test_user_email"
-
-print_info "Step 3: Creating repository structure..."
-mkdir "${tempdir}/first"
-(
+print_info "Step 2: Creating source repository..."
+{
+ git init -- "${tempdir}/first"
cd "${tempdir}/first"
- git init -q -b "$default_branch"
- echo "content" >file.txt
- git add file.txt
- git commit -q -m "Initial commit"
-)
+ for ((i = 0; i < num_commits; ++i)); do
+ for ((j = 0; j < files_per_commit; ++j)); do
+ file_index=$((i * files_per_commit + j))
+ random_data_index=$((file_index * random_data_per_file))
+ head -c "${random_data_per_file}" >"$((file_index)).data" < \
+ <(tail -c +"${random_data_index}" "${random_data_file}" || :)
+ done
+ git add .
+ git commit -q -m "Commit #${i}"
+ done
+ git log --format=oneline | indent
+} | indent
-# Prepare Remote Gcrypt Repo
-# We use the file:// backend which just needs a directory.
-# But for gcrypt::, we essentially push to a directory that becomes the encrypted store.
-mkdir -p "${tempdir}/second.git"
+###
+section_break
+
+print_info "Step 3: Creating bare remote..."
+git init --bare -- "${tempdir}/second.git" | indent
+
+###
+section_break
print_info "Step 4: Pushing with SINGULAR participant (Key 2) to bury it..."
-# We explicitly set ONLY the LAST key as the participant.
-# This forces GPG to skip the first (num_keys-1) keys.
-last_key_idx=$((num_keys - 1))
-git config gcrypt.participants "${key_fps[last_key_idx]}"
-git push -f "gcrypt::${tempdir}/second.git#${default_branch}" "${default_branch}"
-) 2>&1
+{
+ (
+ set -x
+ cd "${tempdir}/first"
+ # CRITICAL REPRO: Only encrypt to the LAST key.
+ # All previous keys are in the keyring but are NOT recipients.
+ # This forces GPG to skip the first (num_keys-1) keys.
+ last_key_idx=$((num_keys - 1))
+ git config gcrypt.participants "${key_fps[last_key_idx]}"
+ git config user.signingkey "${key_fps[last_key_idx]}"
+ git push -f "gcrypt::${tempdir}/second.git#${default_branch}" "${default_branch}"
+ ) 2>&1
} | indent
+###
+section_break
-print_info "Step 5: Cloning back - EXPECTING GPG TO ITERATE..."
-# Now we try to clone (pull). GPG will have to decrypt the manifest.
-# Since we have 18 keys in our keyring, and the message is encrypted to Key #18,
-# GPG will try Key 1, 2... 17.
-#
-# With the BUG: GPG encounters a checksum error (due to ECDH/Ed25519 issues in some GPG versions with anonymous/multi-key handling) on an earlier key and ABORTS properly checking the others. git-remote-gcrypt sees the exit code 2 and dies.
-#
-# With the FIX: git-remote-gcrypt ignores the intermediate error and lets GPG continue until it finds Key 18.
-output_file="${tempdir}/output.log"
-(
- cd "${tempdir}"
- # We must force GPG to try keys.
- # Actually, GPG tries all secret keys for which it has an encrypted session key packet.
- # Since we are the participant, it should just find it.
- # BUT, the bug (Debian #885770 / GnuPG T3597) was that *anonymous* recipients (gpg -R) cause this iteration to be fragile.
- # gcrypt defaults to -R (anonymous).
-
- git clone "gcrypt::${tempdir}/second.git#${default_branch}" "third"
-) >"${output_file}" 2>&1
-ret=$?
+print_info "Step 5: Unhappy Path - Test clone with NO matching keys..."
+{
+ original_gnupghome="${GNUPGHOME}"
+ export GNUPGHOME="${tempdir}/gpg-empty"
+ mkdir "${GNUPGHOME}"
+
+ # We expect this to FAIL
+ (
+ set +e
+ git clone -b "${default_branch}" "gcrypt::${tempdir}/second.git#${default_branch}" -- "${tempdir}/fail_test"
+ if [ $? -eq 0 ]; then
+ print_info "ERROR: Clone succeeded unexpectedly with empty keyring!"
+ exit 1
+ fi
+ ) 2>&1 | indent
+
+ echo "Clone failed as expected." | indent
+ export GNUPGHOME="${original_gnupghome}"
+}
+
+###
+section_break
print_info "Step 6: Reproduction Step - Clone with buried key..."
-cat "${output_file}"
+{
+ # Capture output to check for GPG errors
+ output_file="${tempdir}/clone_output"
+ set +e
+ (
+ set -x
+ git clone -b "${default_branch}" "gcrypt::${tempdir}/second.git#${default_branch}" -- "${tempdir}/third"
+ ) >"${output_file}" 2>&1
+ ret=$?
+ set -e
-if grep -q "Checksum error" "${output_file}" && [ $ret -ne 0 ]; then
- print_warn "BUG(REPRODUCED): GPG Checksum error detected AND Clone failed!"
- exit 1
-elif grep -q "Checksum error" "${output_file}" && [ $ret -eq 0 ]; then
- print_success "SUCCESS: Checksum error detected but Clone SUCCEEDED. (Fix is working!)"
-elif [ $ret -eq 0 ]; then
- print_warn "WARNING: Test passed unexpectedly (Checksum error NOT detected at all). Bug trigger might be absent."
-else
- print_warn "WARNING: Clone failed with generic error (Checksum error not detected)."
-fi
+ cat "${output_file}"
+
+ if grep -q "Checksum error" "${output_file}" && [ $ret -ne 0 ]; then
+ print_warn "WARNING: GPG failed with checksum error."
+ print_err "BUG REPRODUCED! Exiting due to earlier GPG failures."
+ exit 1
+ elif grep -q "Checksum error" "${output_file}" && [ $ret -eq 0 ]; then
+ print_success "SUCCESS: Checksum error detected but Clone SUCCEEDED. (Fix is working!)"
+ elif [ $ret -eq 0 ]; then
+ print_warn "WARNING: Clone passed unexpectedly (Checksum error not detected). Bug not triggered."
+ print_err "Exiting due to unexpected pass."
+ exit 1
+ else
+ print_err "ERROR: Clone failed with generic error (Checksum error not detected)."
+ exit 1
+ fi
-# Continue to verify content.
-echo "Verifying content match..."
-assert diff -r --exclude ".git" -- "${tempdir}/first" "${tempdir}/third" 2>&1 | indent
+ # Continue to verify content.
+ print_info "Verifying content match..."
+ assert diff -r --exclude ".git" -- "${tempdir}/first" "${tempdir}/third" 2>&1 | indent
} | indent
-print_info "Step 7: Reproduction Step - Push with buried key..."
-(
- cd "${tempdir}/third"
- echo "new data" >"new_file"
- git add "new_file"
- git commit -q -m "Commit for Step 7"
- git push "gcrypt::${tempdir}/second.git#${default_branch}" "${default_branch}"
-) >"${output_file}" 2>&1
-ret=$?
+###
+section_break
print_info "Step 7: Reproduction Step - Push with buried key..."
-cat "${output_file}"
+{
+ # Capture output to check for GPG errors
+ output_file="${tempdir}/push_output"
+ set +e
+ (
+ set -x
+ cd "${tempdir}/first"
+ # Make a change so we can push
+ echo "new data" >"new_file"
+ git add "new_file"
+ git commit -q -m "Commit for Step 7"
-if grep -q "Checksum error" "${output_file}" && [ $ret -ne 0 ]; then
- print_warn "BUG(REPRODUCED): GPG Checksum error detected (Push) AND Push failed!"
- exit 1
-elif grep -q "Checksum error" "${output_file}" && [ $ret -eq 0 ]; then
- print_success "SUCCESS: Checksum error detected (Push) but Push SUCCEEDED. (Fix is working!)"
-elif [ $ret -eq 0 ]; then
- print_warn "WARNING: Push passed unexpectedly (Checksum error NOT detected at all)."
-else
- print_warn "WARNING: Push failed with generic error (Checksum error not detected)."
-fi
+ # Set signing key for this push
+ last_key_idx=$((num_keys - 1))
+
+ # Regression Check: Ensure we didn't capture subkeys
+ if [ "${#key_fps[@]}" -ne "$num_keys" ]; then
+ print_err "FATAL: Key array corrupted! Expected $num_keys keys, found ${#key_fps[@]}."
+ print_err " This indicates the 'awk' capture logic has regressed (likely capturing subkeys)."
+ exit 1
+ fi
+ print_success "Sanity Check (Step 7): Key count correct (${#key_fps[@]}). AWK fix confirmed active."
+
+ # Visual Verification: Show which key we actually picked.
+ # If the bug were active (subkey capture), this would show 'git-remote-gcrypt8' (Key #9)
+ # With the fix, it must show 'git-remote-gcrypt17' (Key #18)
+ print_info "Selected Key Details:"
+ gpg --list-keys "${key_fps[last_key_idx]}" | indent
+
+ git config gcrypt.participants "${key_fps[last_key_idx]}"
+ git config user.signingkey "${key_fps[last_key_idx]}"
+
+ git push "gcrypt::${tempdir}/second.git#${default_branch}" "${default_branch}"
+ ) >"${output_file}" 2>&1
+ ret=$?
+ set -e
+
+ cat "${output_file}"
+
+ if grep -q "Checksum error" "${output_file}" && [ $ret -ne 0 ]; then
+ print_warn "WARNING: GPG failed with checksum error."
+ print_err "BUG REPRODUCED! Exiting due to earlier GPG failures."
+ exit 1
+ elif grep -q "Checksum error" "${output_file}" && [ $ret -eq 0 ]; then
+ print_success "SUCCESS: Checksum error detected (Push) but Push SUCCEEDED. (Fix is working!)"
+ elif [ $ret -eq 0 ]; then
+ print_warn "WARNING: Push passed unexpectedly (Checksum error not detected). Bug not triggered."
+ print_err "Exiting due to unexpected pass."
+ exit 1
+ else
+ print_err "ERROR: Push failed with generic error (Checksum error not detected)."
+ exit 1
+ fi
} | indent
+
+[ -n "${COV_DIR:-}" ] && print_success "OK. Report: file://${COV_DIR}/index.html"