Last active
August 18, 2023 15:36
-
-
Save memchr/cd5298f39f1cb9de9702b37f2ff6aa54 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/bash | |
set -e | |
source $HOME/.local/lib/bash/blib.sh | |
load color message | |
colorize | |
msg_style=1 | |
DATA_DIR="${XDG_DATA_HOME:-$HOME/.local/share}/git-id" | |
IDENTITY_DATABASE="${DATA_DIR}/identities.json" | |
[[ ! -f $IDENTITY_DATABASE ]] && echo '[]' > "${IDENTITY_DATABASE}" | |
mkdir -p "${DATA_DIR}" | |
USAGE="\ | |
usage: git id [<options>] <identity> | |
or: git id -w -m <email> [<options>] <identity> | |
<identity> identity | |
-m <email> email address | |
-s, --sign, --signing | |
sign commits | |
-n, --no-sign, --no-signing | |
do not sign commits | |
-f, --sign-format signing key format | |
-k, --ssh-key path to ssh key used for signing commits | |
-t, --ssh-trusted-keys | |
path the file containing trusted ssh public keys | |
-g, --gpg-key OpenPGP key used for signing commits | |
-w, --write, --save | |
save identity | |
-h, --help show this help | |
" | |
unset TEM | |
TEMP=$(getopt -o 'm:snf:k:t:g:whv' --long 'email:,sign,signing,no-sign,no-signing,sign-format:,ssh-key:,ssh-trusted-keys:,gpg-key:,write,save,help,view,show' -n "$0" -- "$@") | |
if [[ $? -ne 0 ]]; then | |
echo "Terminating..." >&2 | |
exit 1 | |
fi | |
eval set -- "$TEMP" | |
declare -A flags=( | |
[email]= | |
[sign]= | |
[sign_format]= | |
[ssh_key]= | |
[ssh_trusted_keys]= | |
[gpg_key]= | |
[save]= | |
) | |
declare -A defaults=( | |
[sign_format]=ssh | |
[ssh_key]="$HOME/.ssh/gitsigning" | |
[ssh_trusted_keys]="$HOME/.ssh/allowed_signers" | |
[gpg_key]= | |
) | |
declare -A identity | |
while true; do | |
case "$1" in | |
-s | --sign | --signing ) | |
flags[sign]=true | |
shift 1 | |
continue | |
;; | |
-n | --no-sign | --no-signing ) | |
flags[nosign]=true | |
shift 1 | |
continue | |
;; | |
-w | --save | --write) | |
flags[save]=1 | |
shift 1 | |
continue | |
;; | |
-m | --email) | |
flags[email]=$2 | |
shift 2 | |
continue | |
;; | |
-f | --sign-format) | |
case "$2" in | |
ssh|gpg) ;; | |
*) | |
error "unknown sign format: $2\n${color[none]}available formats: ssh, gpg" | |
exit 2 | |
;; | |
esac | |
flags[sign_format]=$2 | |
shift 2 | |
continue | |
;; | |
-k | --ssh-key) | |
flags[ssh_key]=$2 | |
shift 2 | |
continue | |
;; | |
-t | --ssh-trusted-keys) | |
flags[ssh_trusted_keys]=$2 | |
shift 2 | |
continue | |
;; | |
-g | --gpg-key) | |
flags[gpg_key]=$2 | |
shift 2 | |
continue | |
;; | |
-v | --show | --view) | |
flags[view]=1 | |
shift 1 | |
continue | |
;; | |
-h | --help) | |
echo -n "$USAGE" | |
exit | |
;; | |
--) | |
shift | |
break | |
;; | |
esac | |
done | |
main() { | |
local name=$1 | |
case "$name" in | |
# local user | |
local) | |
(( flags[sign] )) && | |
msg "user is local, signing will not be used." | |
git config user.name local && git config user.email '<>' && git config commit.gpgsign false | |
;; | |
'') | |
error "no identity provided" | |
;; | |
*) | |
if (( flags[save] )); then | |
save_identity "$name" | |
elif (( flags[view] )); then | |
view_identity "$name" | |
else | |
set_identity "$name" | |
fi | |
;; | |
esac | |
} | |
set_identity() { | |
local name=$1 | |
identity=() | |
read_identity "$name" | |
msg "Using identity '$name'" | |
git config user.name "$name" | |
git config user.email "${identity[email]}" | |
if [[ ${identity[sign]} == true ]] && [[ ${flags[nosign]} != true ]]; then | |
msg2 "${color[none]}enable commit signing using '${identity[sign_format]}'" | |
git config commit.gpgsign true | |
case "${identity[sign_format]}" in | |
ssh) | |
msg2 "${color[none]}signing key is ${color[yellow]}'${identity[ssh_key]}'${color[none]}" | |
msg2 "${color[none]}allowed signers file is ${color[yellow]}'${identity[ssh_trusted_keys]}'${color[none]}" | |
git config gpg.format ssh | |
git config gpg.ssh.allowedsignersfile "${identity[ssh_trusted_keys]}" | |
git config user.signingkey "${identity[ssh_key]}" | |
;; | |
gpg) | |
msg2 "${color[none]}signing key is ${color[yellow]}'${identity[gpg_key]}'${color[none]}" | |
git config gpg.format openpgp | |
git config user.signingkey "${identity[gpg_key]}" | |
;; | |
esac | |
fi | |
} | |
read_identity() { | |
local _jq_filter=' | |
.[$name] | | |
.email, | |
.sign // "", | |
.sign_format // "", | |
.ssh_key // "", | |
.ssh_trusted_keys // "", | |
.gpg_key // "" | |
' | |
local _tmp | |
identity[name]=$1 | |
mapfile -t _tmp < <( | |
jq -r --arg name "$1" "$_jq_filter" < "${IDENTITY_DATABASE}" | |
) | |
if [[ -z ${_tmp[0]} ]]; then | |
error "user '$name' does not exist" | |
exit 2 | |
fi | |
identity[email]="$(get_property email "${_tmp[0]}")" | |
identity[sign]="$(get_property sign "${_tmp[1]}")" | |
identity[sign_format]="$(get_property sign_format "${_tmp[2]}")" | |
identity[ssh_key]="$(get_property ssh_key "${_tmp[3]}")" | |
identity[ssh_trusted_keys]="$(get_property ssh_trusted_keys "${_tmp[4]}")" | |
identity[gpg_key]="$(get_property gpg_key "${_tmp[5]}")" | |
} | |
INFO_FMT="\ | |
name : %s | |
email : %s | |
commit signing : %s | |
signing key format : %s | |
gpg key : %s | |
ssh key : %s | |
allowed signer file : %s | |
" | |
view_identity() { | |
local name=$1 | |
identity=() | |
read_identity "$name" | |
printf "$INFO_FMT" \ | |
"${identity[name]}" \ | |
"${identity[email]}" \ | |
"${identity[sign]}" \ | |
"${identity[sign_format]}" \ | |
"${identity[gpg_key]}" \ | |
"${identity[ssh_key]}" \ | |
"${identity[ssh_trusted_keys]}" | |
} | |
save_identity() { | |
local name=$1 | |
local email="$(get_property_with_null email)" | |
local sign="$(get_property_with_null sign)" | |
local sign_format="$(get_property_with_null sign_format)" | |
local ssh_key="$(get_property_with_null ssh_key)" | |
local ssh_trusted_keys="$(get_property_with_null ssh_trusted_keys)" | |
local gpg_key="$(get_property_with_null gpg_key)" | |
local _jq_filter=("{") | |
for v in email sign sign_format ssh_key ssh_trusted_keys gpg_key; do | |
if [[ ${!v} =~ ^(true|false)$ ]]; then | |
_jq_filter+=("$v: ${!v},") | |
elif [[ ${!v} != null ]]; then | |
_jq_filter+=("$v: \"${!v}\",") | |
fi | |
done | |
_jq_filter+=("}") | |
exec 3< <(jq ". * {$name: ${_jq_filter[*]}}" < "${IDENTITY_DATABASE}") | |
wait | |
cat <&3 > "${IDENTITY_DATABASE}" | |
} | |
# get a property with fallbacks and defaults | |
get_property() { | |
local key=$1 | |
local fallback=$2 | |
fallback=$(eval "echo \"${fallback}\"") | |
echo "${flags[$key]:-"${fallback:-"${defaults[$key]}"}"}" | |
} | |
# output null if there are no fallbacks or defaults, suitable for json | |
get_property_with_null() { | |
local key=$1 | |
local fallback=$2 | |
local ret=$(get_property "$key" "$fallback") | |
echo "${ret:-null}" | |
} | |
main "$@" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment